E-MailRelay

glinebuffer.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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/// \file glinebuffer.cpp
19///
20
21#include "gdef.h"
22#include "glinebuffer.h"
23#include "gtest.h"
24#include "glog.h"
25#include "gassert.h"
26#include <algorithm>
27
28 GNet::LineBuffer::LineBuffer( const LineBufferConfig & config ) :
29 m_auto(config.eol().empty()) ,
30 m_eol(config.eol()) ,
31 m_warn_limit(config.warn()) ,
32 m_fmin(config.fmin()) ,
33 m_expect(config.expect()) ,
34 m_warned(false) ,
35 m_pos(0U)
36{
37}
38
39 void GNet::LineBuffer::clear()
40{
41 m_in.clear() ;
42 m_out = Output() ;
43 m_pos = 0U ;
44 if( !transparent() )
45 m_expect = 0U ;
46
47 G_ASSERT( m_in.empty() && state().empty() ) ;
48}
49
50 void GNet::LineBuffer::add( const char * data , std::size_t size )
51{
52 m_in.append( data , size ) ;
53}
54
55 void GNet::LineBuffer::add( const std::string & s )
56{
57 m_in.append( s ) ;
58}
59
60 void GNet::LineBuffer::extensionStart( const char * data , std::size_t size )
61{
62 if( data )
63 m_in.extend( data , size ) ;
64}
65
66 void GNet::LineBuffer::extensionEnd()
67{
68 m_in.discard( m_pos ) ;
69 m_pos = 0U ;
70}
71
72 bool GNet::LineBuffer::more( bool fragments )
73{
74 G_ASSERT( m_pos <= m_in.size() ) ;
75 const std::size_t npos = std::string::npos ;
76 std::size_t pos = 0U ;
77
78 if( m_pos == m_in.size() )
79 {
80 // finished iterating, no residue
81 //
82 m_in.clear() ;
83 m_pos = 0U ;
84 return false ;
85 }
86 else if( m_expect != 0U )
87 {
88 if( !transparent() && (m_pos+m_expect) <= m_in.size() )
89 {
90 // got all expected
91 //
92 output( m_expect , 0U , true ) ;
93 m_expect = 0U ;
94 return true ;
95 }
96 else if( fragments && !trivial(m_in.size()) )
97 {
98 // not all expected, return the available fragment
99 //
100 G_ASSERT( m_in.size() > m_pos ) ;
101 std::size_t n = m_in.size() - m_pos ;
102 output( n , 0U ) ;
103 if( !transparent() ) m_expect -= n ;
104 return true ;
105 }
106 else
107 {
108 // expecting more
109 //
110 return false ;
111 }
112 }
113 else if( !detect() )
114 {
115 // no eol-style determined yet
116 //
117 return false ;
118 }
119 else if( (pos=m_in.find(m_eol,m_pos)) != npos )
120 {
121 // complete line available
122 //
123 output( pos-m_pos , m_eol.size() ) ;
124 return true ;
125 }
126 else if( fragments && (pos=m_in.findSubStringAtEnd(m_eol,m_pos)) != m_pos && !trivial(pos) )
127 {
128 // finished iterating, return the residual fragment
129 //
130 pos = pos == npos ? m_in.size() : pos ;
131 output( pos-m_pos , 0U ) ;
132 return true ;
133 }
134 else
135 {
136 // finished iterating
137 //
138 return false ;
139 }
140}
141
142 bool GNet::LineBuffer::trivial( std::size_t pos ) const
143{
144 pos = pos == std::string::npos ? m_in.size() : pos ;
145 return ( pos - m_pos ) < m_fmin ;
146}
147
148 bool GNet::LineBuffer::detect()
149{
150 const std::size_t npos = std::string::npos ;
151 if( m_auto )
152 {
153 std::size_t pos = m_in.find( '\n' ) ;
154 if( pos != npos )
155 {
156 if( pos > 0U && m_in.at(pos-1U) == '\r' )
157 m_eol = std::string( "\r\n" , 2U ) ;
158 else
159 m_eol = std::string( 1U , '\n' ) ;
160 m_auto = false ;
161 }
162 }
163 return !m_eol.empty() ;
164}
165
166 void GNet::LineBuffer::expect( std::size_t n )
167{
168 m_expect = n ;
169}
170
171 bool GNet::LineBuffer::transparent() const
172{
173 return ( m_expect + 1U ) == 0U ;
174}
175
176std::string GNet::LineBuffer::eol() const
177{
178 return m_eol ;
179}
180
181 void GNet::LineBuffer::output( std::size_t size , std::size_t eolsize , bool force_next_is_start_of_line )
182{
183 G_ASSERT( (size+eolsize) != 0U ) ;
184 m_pos += m_out.set( m_in , m_pos , size , eolsize ) ;
185 if( force_next_is_start_of_line )
186 m_out.m_first = true ;
187 check( m_out ) ;
188}
189
190 void GNet::LineBuffer::check( const Output & out )
191{
192 if( !m_warned && m_warn_limit != 0U && out.m_size > m_warn_limit )
193 {
194 G_WARNING( "GNet::LineBuffer::check: very long line detected: " << out.m_size << " > " << m_warn_limit ) ;
195 m_warned = true ;
196 }
197}
198
199 const char * GNet::LineBuffer::data() const
200{
201 G_ASSERT( m_out.m_data != nullptr ) ;
202 return m_out.m_data ;
203}
204
205 GNet::LineBufferState GNet::LineBuffer::state() const
206{
207 return LineBufferState( *this ) ;
208}
209
210// ==
211
212GNet::LineBuffer::Output::Output()
213= default;
214
215std::size_t GNet::LineBuffer::Output::set( LineStore & in , std::size_t pos , std::size_t size , std::size_t eolsize )
216{
217 bool start = m_first || m_eolsize != 0U ; // ie. wrt previous line's eolsize
218 m_first = false ;
219
220 m_size = size ;
221 m_eolsize = eolsize ;
222 if( start ) m_linesize = 0U ;
223 m_linesize += size ;
224 m_data = in.data( pos , size+eolsize ) ;
225 if( start ) m_c0 = size == 0U ? '\0' : m_data[0] ;
226 return size + eolsize ;
227}
228
229// ==
230
231 GNet::LineBufferConfig::LineBufferConfig( const std::string & eol , std::size_t warn ,
232 std::size_t fmin , std::size_t expect ) :
233 m_eol(eol) ,
234 m_warn(warn) ,
235 m_fmin(fmin) ,
236 m_expect(expect)
237{
238}
239
240 GNet::LineBufferConfig GNet::LineBufferConfig::transparent()
241{
242 static constexpr std::size_t inf = ~(std::size_t(0)) ;
243 static_assert( (inf+1U) == 0U , "" ) ;
244 return LineBufferConfig( std::string(1U,'\n') , 0U , 0U , inf ) ;
245}
246
247 GNet::LineBufferConfig GNet::LineBufferConfig::newline()
248{
249 return LineBufferConfig( std::string(1U,'\n') ) ;
250}
251
252 GNet::LineBufferConfig GNet::LineBufferConfig::autodetect()
253{
254 return LineBufferConfig( std::string() ) ;
255}
256
257 GNet::LineBufferConfig GNet::LineBufferConfig::crlf()
258{
259 return LineBufferConfig( std::string("\r\n",2U) ) ;
260}
261
262 GNet::LineBufferConfig GNet::LineBufferConfig::smtp()
263{
264 return LineBufferConfig( std::string("\r\n",2U) , 998U + 2U , /*fmin=*/2U ) ; // RFC-2822
265}
266
267 GNet::LineBufferConfig GNet::LineBufferConfig::pop()
268{
269 return crlf() ;
270}
271
272 GNet::LineBufferConfig GNet::LineBufferConfig::http()
273{
274 return crlf() ;
275}
GNet::LineBufferConfig
A configuration structure for GNet::LineBuffer.
Definition: glinebuffer.h:325
GNet::LineBufferConfig::crlf
static LineBufferConfig crlf()
Convenience factory function.
Definition: glinebuffer.cpp:257
GNet::LineBufferConfig::transparent
static LineBufferConfig transparent()
Convenience factory function.
Definition: glinebuffer.cpp:240
GNet::LineBufferConfig::http
static LineBufferConfig http()
Convenience factory function.
Definition: glinebuffer.cpp:272
GNet::LineBufferConfig::autodetect
static LineBufferConfig autodetect()
Convenience factory function.
Definition: glinebuffer.cpp:252
GNet::LineBufferConfig::newline
static LineBufferConfig newline()
Convenience factory function.
Definition: glinebuffer.cpp:247
GNet::LineBufferConfig::pop
static LineBufferConfig pop()
Convenience factory function.
Definition: glinebuffer.cpp:267
GNet::LineBufferConfig::LineBufferConfig
LineBufferConfig(const std::string &eol=std::string(1U,'\n'), std::size_t warn=0U, std::size_t fmin=0U, std::size_t initial_expect=0U)
Constructor.
Definition: glinebuffer.cpp:231
GNet::LineBufferConfig::smtp
static LineBufferConfig smtp()
Convenience factory function.
Definition: glinebuffer.cpp:262
GNet::LineBufferState
Provides information abount the state of a line buffer.
Definition: glinebuffer.h:383
GNet::LineBuffer::LineBuffer
LineBuffer(const LineBufferConfig &)
Constructor.
Definition: glinebuffer.cpp:28
GNet::LineBuffer::expect
void expect(std::size_t n)
Requests that the next 'n' bytes extracted be extracted in one contiguous block, without regard to li...
Definition: glinebuffer.cpp:166
GNet::LineBuffer::transparent
bool transparent() const
Returns true if the current expect() value is infinite.
Definition: glinebuffer.cpp:171
GNet::LineBuffer::clear
void clear()
Clears the internal data.
Definition: glinebuffer.cpp:39
GNet::LineBuffer::extensionStart
void extensionStart(const char *, std::size_t)
A pseudo-private method used by the implementation of the apply() method template.
Definition: glinebuffer.cpp:60
GNet::LineBuffer::more
bool more(bool fragments=false)
Returns true if there is more data() to be had.
Definition: glinebuffer.cpp:72
GNet::LineBuffer::data
const char * data() const
Returns a pointer for the current line, expect()ed fixed-size block, or line fragment.
Definition: glinebuffer.cpp:199
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:176
GNet::LineBuffer::extensionEnd
void extensionEnd()
A pseudo-private method used by the implementation of the apply() method template.
Definition: glinebuffer.cpp:66
GNet::LineBuffer::add
void add(const std::string &data)
Adds a data segment.
Definition: glinebuffer.cpp:55
GNet::LineBuffer::state
LineBufferState state() const
Returns information about the current state of the line-buffer.
Definition: glinebuffer.cpp:205
GNet::LineStore
A pair of character buffers, one kept by value and the other being an ephemeral extension.
Definition: glinestore.h:42
GNet::LineStore::data
const char * data(std::size_t pos, std::size_t size) const
Returns a pointer for the data at the given position that is contiguous for the given size.
Definition: glinestore.cpp:374
gassert.h
glinebuffer.h
glog.h
gtest.h