E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

glinebuffer.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // glinebuffer.cpp
19 //
20
21 #include "gdef.h"
22 #include "glimits.h"
23 #include "glinebuffer.h"
24 #include "gtest.h"
25 #include "gdebug.h"
26 #include "gassert.h"
27 #include <algorithm>
28
29 static const size_t c_line_limit = G::limits::net_line_limit ;
30
31 GNet::LineBuffer::LineBuffer( LineBufferConfig config ) :
32 m_auto(config.eol().empty()) ,
33 m_eol(config.eol()) ,
34 m_warn_limit(config.warn()) ,
35 m_fail_limit(config.fail()?config.fail():c_line_limit) ,
36 m_extra_data(nullptr) ,
37 m_extra_size(0U) ,
38 m_expect(0U) ,
39 m_warned(false) ,
40 m_pos(0U) ,
41 m_line_data(nullptr) ,
42 m_line_size(0U) ,
43 m_eol_size(0U)
44 {
45 }
46
47 void GNet::LineBuffer::add( const char * data , size_t size )
48 {
49 if( size )
50 {
51 precheck( size ) ;
52 m_store.append( data , size ) ;
53 }
54 }
55
56 void GNet::LineBuffer::add( const std::string & s )
57 {
58 if( !s.empty() )
59 {
60 precheck( s.size() ) ;
61 m_store.append( s ) ;
62 }
63 }
64
65 void GNet::LineBuffer::addextra( const char * data , size_t size )
66 {
67 if( G::Test::enabled("line-buffer-simple") )
68 {
69 add( data , size ) ; // no zero-copy optimisation
70 }
71 else if( size )
72 {
73 precheck( size ) ;
74 m_extra_data = data ; G_ASSERT( m_extra_size == 0U ) ;
75 m_extra_size = size ;
76
77 // make sure no eol spans store and extra
78 while( !m_store.empty() && m_extra_size != 0U && !m_eol.empty() &&
79 m_eol.find(m_store.at(m_store.size()-1U)) != std::string::npos &&
80 m_eol.find(*m_extra_data) != std::string::npos )
81 {
82 m_store.append( 1U , *m_extra_data++ ) ;
83 m_extra_size-- ;
84 }
85 }
86 }
87
88 void GNet::LineBuffer::precheck( size_t n )
89 {
90 G_ASSERT( n != 0U ) ;
91 bool arithmetic_overflow = (m_store.size() + n) < m_store.size() ;
92 bool fail_limit_overflow = (m_store.size() + n) > m_fail_limit ;
93 if( arithmetic_overflow || fail_limit_overflow )
94 throw ErrorOverflow() ;
95 }
96
97 bool GNet::LineBuffer::more()
98 {
99 const size_t npos = std::string::npos ;
100 size_t pos = npos ;
101 const char * p = nullptr ;
102 m_line_data = nullptr ;
103
104 G_ASSERT( m_pos <= (m_store.size()+m_extra_size) ) ;
105 if( m_pos == (m_store.size()+m_extra_size) )
106 {
107 // finished iterating, no residue
108 //
109 m_store.clear() ;
110 m_extra_size = 0U ;
111 m_pos = 0U ;
112 return false ;
113 }
114 else if( m_expect != 0U )
115 {
116 consolidate() ;
117 if( (m_pos+m_expect) <= m_store.size() )
118 {
119 // got all expected
120 //
121 m_eol_size = 0U ;
122 m_line_data = m_store.data() + m_pos ;
123 m_line_size = m_expect ;
124 m_pos += m_expect ;
125 m_expect = 0U ;
126 return true ;
127 }
128 else
129 {
130 // expecting more
131 //
132 return false ;
133 }
134 }
135 else if( !detect() )
136 {
137 // no eol detected
138 //
139 consolidate() ;
140 return false ;
141 }
142 else if( m_pos < m_store.size() && (pos=m_store.find(m_eol,m_pos)) != npos )
143 {
144 // complete line in store
145 //
146 m_eol_size = m_eol.size() ;
147 m_line_data = m_store.data() + m_pos ;
148 m_line_size = pos - m_pos ;
149 linecheck( m_line_size ) ;
150 m_pos = pos + m_eol_size ;
151 return true ;
152 }
153 else if( m_pos < m_store.size() && m_extra_size == 0U )
154 {
155 // finished iterating, leave the residue
156 //
157 if( m_pos ) m_store.erase( 0U , m_pos ) ;
158 m_pos = 0U ;
159 return false ;
160 }
161 else if( (p=extraeol()) != nullptr )
162 {
163 G_ASSERT( m_extra_size != 0U ) ;
164 if( m_pos < m_store.size() )
165 {
166 // line spans store and extra
167 //
168 size_t n_lhs = m_store.size() - m_pos ;
169 size_t n_rhs = std::distance(m_extra_data,p) + m_eol.size() ;
170 m_store.append( m_extra_data , n_rhs ) ;
171 m_extra_data += n_rhs ;
172 m_extra_size -= n_rhs ;
173 m_eol_size = m_eol.size() ;
174 m_line_data = m_store.data() + m_pos ;
175 m_line_size = n_lhs + n_rhs - m_eol_size ;
176 linecheck( m_line_size ) ;
177 m_pos += ( n_lhs + n_rhs ) ;
178 return true ;
179 }
180 else
181 {
182 // complete line in extra
183 //
184 m_eol_size = m_eol.size() ;
185 m_line_data = m_extra_data + m_pos - m_store.size() ;
186 m_line_size = std::distance( m_line_data , p ) ;
187 linecheck( m_line_size ) ;
188 m_pos += ( m_line_size + m_eol_size ) ;
189 return true ;
190 }
191 }
192 else if( m_pos < m_store.size() )
193 {
194 // finished iterating, residue in store and extra
195 //
196 G_ASSERT( m_extra_size != 0U ) ;
197 consolidate() ;
198 if( m_pos ) m_store.erase( 0U , m_pos ) ;
199 m_pos = 0U ;
200 return false ;
201 }
202 else
203 {
204 // finished iterating, residue in extra
205 //
206 G_ASSERT( m_extra_size != 0U ) ;
207 size_t n = m_pos - m_store.size() ;
208 m_store.assign( m_extra_data + n , m_extra_size - n ) ;
209 m_extra_size = 0U ;
210 m_pos = 0U ;
211 return false ;
212 }
213 }
214
215 const char * GNet::LineBuffer::extraeol() const
216 {
217 G_ASSERT( m_store.find(m_eol,m_pos) == std::string::npos ) ;
218 G_ASSERT( m_extra_size != 0U ) ;
219
220 const char * end = m_extra_data + m_extra_size ;
221 const char * start = m_extra_data ;
222 if( m_pos > m_store.size() )
223 start += ( m_pos-m_store.size() ) ;
224
225 const char * p = std::search( start , end , m_eol.data() , m_eol.data()+m_eol.size() ) ;
226 return p == end ? nullptr : p ;
227 }
228
229 void GNet::LineBuffer::consolidate()
230 {
231 if( m_extra_size )
232 {
233 m_store.append( m_extra_data , m_extra_size ) ;
234 m_extra_size = 0U ;
235 }
236 }
237
238 bool GNet::LineBuffer::detect()
239 {
240 const size_t npos = std::string::npos ;
241 if( m_auto )
242 {
243 consolidate() ; // could do better
244 size_t pos = m_store.find( '\n' ) ;
245 if( pos != npos )
246 {
247 if( pos > 0U && m_store.at(pos-1U) == '\r' )
248 m_eol = std::string( "\r\n" , 2U ) ;
249 else
250 m_eol = std::string( 1U , '\n' ) ;
251 m_auto = false ;
252 }
253 }
254 return !m_eol.empty() ;
255 }
256
257 void GNet::LineBuffer::expect( size_t n )
258 {
259 m_expect = n ;
260 }
261
262 std::string GNet::LineBuffer::eol() const
263 {
264 return m_eol ;
265 }
266
267 void GNet::LineBuffer::linecheck( size_t n )
268 {
269 if( !m_warned && m_warn_limit != 0U && n > m_warn_limit )
270 {
271 G_WARNING( "GNet::LineBuffer::check: very long line detected: " << n << " > " << m_warn_limit ) ;
272 m_warned = true ;
273 }
274 }
275
276 size_t GNet::LineBuffer::eolSize() const
277 {
278 return m_eol_size ;
279 }
280
281 size_t GNet::LineBuffer::lineSize() const
282 {
283 return m_line_size ;
284 }
285
286 const char * GNet::LineBuffer::lineData() const
287 {
288 G_ASSERT( m_line_data != nullptr ) ;
289 return m_line_data ;
290 }
291
292 // ==
293
294 GNet::LineBufferConfig::LineBufferConfig( const std::string & eol , size_t warn , size_t fail ) :
295 m_eol(eol) ,
296 m_warn(warn) ,
297 m_fail(fail)
298 {
299 }
300
301 GNet::LineBufferConfig GNet::LineBufferConfig::newline()
302 {
303 return LineBufferConfig( std::string(1U,'\n') ) ;
304 }
305
306 GNet::LineBufferConfig GNet::LineBufferConfig::autodetect()
307 {
308 return LineBufferConfig( std::string() ) ;
309 }
310
311 GNet::LineBufferConfig GNet::LineBufferConfig::crlf()
312 {
313 return LineBufferConfig( std::string("\r\n",2U) ) ;
314 }
315
316 GNet::LineBufferConfig GNet::LineBufferConfig::smtp()
317 {
318 return LineBufferConfig( std::string("\r\n",2U) , 998U + 2U ) ; // RFC-2822
319 }
320
321 GNet::LineBufferConfig GNet::LineBufferConfig::pop()
322 {
323 return crlf() ;
324 }
325
326 GNet::LineBufferConfig GNet::LineBufferConfig::http()
327 {
328 return crlf() ;
329 }
330
331 /// \file glinebuffer.cpp
GNet::LineBufferConfig::newline
static LineBufferConfig newline()
Convenience factory function.
Definition: glinebuffer.cpp:301
GNet::LineBufferConfig::http
static LineBufferConfig http()
Convenience factory function.
Definition: glinebuffer.cpp:326
GNet::LineBufferConfig::pop
static LineBufferConfig pop()
Convenience factory function.
Definition: glinebuffer.cpp:321
GNet::LineBuffer::expect
void expect(size_t n)
The next 'n' bytes extracted will be extracted in one contiguous block, without regard to line ending...
Definition: glinebuffer.cpp:257
gtest.h
GNet::LineBuffer::more
bool more()
Returns true if there is lineData() to be had.
Definition: glinebuffer.cpp:97
glinebuffer.h
glimits.h
gassert.h
GNet::LineBufferConfig
A configuration structure for GNet::LineBuffer.
Definition: glinebuffer.h:174
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:69
GNet::LineBufferConfig::autodetect
static LineBufferConfig autodetect()
Convenience factory function.
Definition: glinebuffer.cpp:306
GNet::LineBufferConfig::LineBufferConfig
LineBufferConfig(const std::string &eol=std::string(1U,'\n'), size_t warn=0U, size_t fail=0U)
Constructor.
Definition: glinebuffer.cpp:294
GNet::LineBuffer::LineBuffer
LineBuffer(LineBufferConfig)
Constructor.
Definition: glinebuffer.cpp:31
GNet::LineBufferConfig::smtp
static LineBufferConfig smtp()
Convenience factory function.
Definition: glinebuffer.cpp:316
GNet::LineBufferConfig::crlf
static LineBufferConfig crlf()
Convenience factory function.
Definition: glinebuffer.cpp:311
GNet::LineBuffer::add
void add(const std::string &data)
Adds a data segment.
Definition: glinebuffer.cpp:56
GNet::LineBuffer::lineSize
size_t lineSize() const
Returns the size of the current line, excluding the line ending.
Definition: glinebuffer.cpp:281
GNet::LineBuffer::eolSize
size_t eolSize() const
Returns the size of current line ending.
Definition: glinebuffer.cpp:276
GNet::LineBuffer::eol
std::string eol() const
Returns the end-of-line string as passed in to the constructor, or as auto-detected.
Definition: glinebuffer.cpp:262
GNet::LineBuffer::lineData
const char * lineData() const
Returns a pointer for the current line, including the line ending.
Definition: glinebuffer.cpp:286
gdebug.h