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
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
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
50void GNet::LineBuffer::add( const char * data , std::size_t size )
51{
52 m_in.append( data , size ) ;
53}
54
55void GNet::LineBuffer::add( const std::string & s )
56{
57 m_in.append( s ) ;
58}
59
60void GNet::LineBuffer::extensionStart( const char * data , std::size_t size )
61{
62 if( data )
63 m_in.extend( data , size ) ;
64}
65
67{
68 m_in.discard( m_pos ) ;
69 m_pos = 0U ;
70}
71
72bool 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
142bool 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
148bool 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
166void GNet::LineBuffer::expect( std::size_t n )
167{
168 m_expect = n ;
169}
170
172{
173 return ( m_expect + 1U ) == 0U ;
174}
175
176std::string GNet::LineBuffer::eol() const
177{
178 return m_eol ;
179}
180
181void 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
190void 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
199const char * GNet::LineBuffer::data() const
200{
201 G_ASSERT( m_out.m_data != nullptr ) ;
202 return m_out.m_data ;
203}
204
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
231GNet::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
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
248{
249 return LineBufferConfig( std::string(1U,'\n') ) ;
250}
251
253{
254 return LineBufferConfig( std::string() ) ;
255}
256
258{
259 return LineBufferConfig( std::string("\r\n",2U) ) ;
260}
261
263{
264 return LineBufferConfig( std::string("\r\n",2U) , 998U + 2U , /*fmin=*/2U ) ; // RFC-2822
265}
266
268{
269 return crlf() ;
270}
271
273{
274 return crlf() ;
275}
A configuration structure for GNet::LineBuffer.
Definition: glinebuffer.h:325
static LineBufferConfig crlf()
Convenience factory function.
static LineBufferConfig transparent()
Convenience factory function.
static LineBufferConfig http()
Convenience factory function.
static LineBufferConfig autodetect()
Convenience factory function.
static LineBufferConfig newline()
Convenience factory function.
static LineBufferConfig pop()
Convenience factory function.
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.
static LineBufferConfig smtp()
Convenience factory function.
Provides information abount the state of a line buffer.
Definition: glinebuffer.h:383
LineBuffer(const LineBufferConfig &)
Constructor.
Definition: glinebuffer.cpp:28
void expect(std::size_t n)
Requests that the next 'n' bytes extracted be extracted in one contiguous block, without regard to li...
bool transparent() const
Returns true if the current expect() value is infinite.
void clear()
Clears the internal data.
Definition: glinebuffer.cpp:39
void extensionStart(const char *, std::size_t)
A pseudo-private method used by the implementation of the apply() method template.
Definition: glinebuffer.cpp:60
bool more(bool fragments=false)
Returns true if there is more data() to be had.
Definition: glinebuffer.cpp:72
const char * data() const
Returns a pointer for the current line, expect()ed fixed-size block, or line fragment.
std::string eol() const
Returns the end-of-line string as passed in to the constructor, or as auto-detected.
void extensionEnd()
A pseudo-private method used by the implementation of the apply() method template.
Definition: glinebuffer.cpp:66
void add(const std::string &data)
Adds a data segment.
Definition: glinebuffer.cpp:55
LineBufferState state() const
Returns information about the current state of the line-buffer.
A pair of character buffers, one kept by value and the other being an ephemeral extension.
Definition: glinestore.h:42
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