E-MailRelay

glinestore.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 glinestore.cpp
19///
20
21#include "gdef.h"
22#include "glinestore.h"
23#include "gexception.h"
24#include "gstr.h"
25#include "gassert.h"
26#include <iterator>
27
28 namespace GNet
29{
30 class LineStoreIterator ;
31}
32
33//| \class GNet::LineStoreIterator
34/// An iterator class for GNet::LineStore.
35///
36 class GNet::LineStoreIterator : public std::iterator<std::bidirectional_iterator_tag,char,ptrdiff_t>
37{
38 public:
39 G_EXCEPTION( Error , "line buffer internal error" ) ;
40 LineStoreIterator() = default;
41 ~LineStoreIterator() = default;
42 explicit LineStoreIterator( const LineStore & line_store , bool end = false ) :
43 m_p(&line_store) ,
44 m_pos(end?line_store.size():0U)
45 {
46 }
47 LineStoreIterator( const LineStoreIterator & other ) :
48 m_p(other.m_p) ,
49 m_pos(other.m_pos)
50 {
51 }
52 LineStoreIterator( LineStoreIterator && other ) noexcept :
53 m_p(other.m_p) ,
54 m_pos(other.m_pos)
55 {
56 }
57 void swap( LineStoreIterator & other ) noexcept
58 {
59 using std::swap ;
60 swap( m_p , other.m_p ) ;
61 swap( m_pos , other.m_pos ) ;
62 }
63 LineStoreIterator & operator=( const LineStoreIterator & other )
64 {
65 LineStoreIterator(other).swap(*this) ; return *this ;
66 }
67 LineStoreIterator & operator=( LineStoreIterator && other ) noexcept
68 {
69 LineStoreIterator(other).swap(*this) ;
70 return *this ;
71 }
72 LineStoreIterator & operator++()
73 {
74 m_pos++ ;
75 return *this ;
76 }
77 LineStoreIterator & operator--()
78 {
79 m_pos-- ;
80 return *this ;
81 }
82 bool operator==( const LineStoreIterator & other ) const
83 {
84 return m_pos == other.m_pos ;
85 }
86 bool operator!=( const LineStoreIterator & other ) const
87 {
88 return m_pos != other.m_pos ;
89 }
90 bool operator<( const LineStoreIterator & other ) const
91 {
92 return m_pos < other.m_pos ;
93 }
94 bool operator<=( const LineStoreIterator & other ) const
95 {
96 return m_pos <= other.m_pos ;
97 }
98 bool operator>( const LineStoreIterator & other ) const
99 {
100 return m_pos > other.m_pos ;
101 }
102 bool operator>=( const LineStoreIterator & other ) const
103 {
104 return m_pos >= other.m_pos ;
105 }
106 char operator*() const
107 {
108 G_ASSERT( m_p != nullptr ) ;
109 if( m_p == nullptr )
110 throw Error() ;
111 return m_p->at( m_pos ) ;
112 }
113 char operator[]( std::size_t n ) const
114 {
115 G_ASSERT( m_p != nullptr ) ;
116 if( m_p == nullptr )
117 throw Error() ;
118 return m_p->at( m_pos + n ) ;
119 }
120 void operator+=( ptrdiff_t n )
121 {
122 if( n < 0 )
123 m_pos -= static_cast<std::size_t>(-n) ;
124 else
125 m_pos += static_cast<std::size_t>(n) ;
126 }
127 void operator-=( ptrdiff_t n )
128 {
129 if( n < 0 )
130 m_pos += static_cast<std::size_t>(-n) ;
131 else
132 m_pos -= static_cast<std::size_t>(n) ;
133 }
134 ptrdiff_t distanceTo( const LineStoreIterator & other ) const
135 {
136 if( other.m_pos >= m_pos )
137 return static_cast<ptrdiff_t>(other.m_pos-m_pos) ;
138 else
139 return -static_cast<ptrdiff_t>(m_pos-other.m_pos) ;
140 }
141 std::size_t pos() const
142 {
143 return ( m_p == nullptr || m_pos >= m_p->size() ) ? std::string::npos : m_pos ;
144 }
145
146 private:
147 const LineStore * m_p{nullptr} ;
148 std::size_t m_pos{0U} ;
149} ;
150
151 namespace GNet
152{
153 inline LineStoreIterator operator+( const LineStoreIterator & in , ptrdiff_t n )
154 {
155 LineStoreIterator result( in ) ;
156 result += n ;
157 return result ;
158 }
159 inline LineStoreIterator operator-( const LineStoreIterator & in , ptrdiff_t n )
160 {
161 LineStoreIterator result( in ) ;
162 result -= n ;
163 return result ;
164 }
165 inline LineStoreIterator operator+( ptrdiff_t n , const LineStoreIterator & in )
166 {
167 LineStoreIterator result( in ) ;
168 result += n ;
169 return result ;
170 }
171 inline ptrdiff_t operator-( const LineStoreIterator & a , const LineStoreIterator & b )
172 {
173 return a.distanceTo( b ) ;
174 }
175 inline void swap( LineStoreIterator & a , LineStoreIterator & b ) noexcept
176 {
177 a.swap( b ) ;
178 }
179}
180
181 namespace GNet
182{
183 namespace LineStoreImp
184 {
185 template <typename T1, typename T2> bool std_equal( T1 p1 , T1 end1 , T2 p2 , T2 end2 )
186 {
187 // (std::equal with four iterators is c++14 or later)
188 for( ; p1 != end1 && p2 != end2 ; ++p1 , ++p2 )
189 {
190 if( !(*p1 == *p2) )
191 return false ;
192 }
193 return p1 == end1 && p2 == end2 ;
194 }
195 }
196}
197
198// ==
199
200 GNet::LineStore::LineStore()
201= default;
202
203 void GNet::LineStore::append( const std::string & s )
204{
205 consolidate() ;
206 m_store.append( s ) ;
207}
208
209 void GNet::LineStore::append( const char * data , std::size_t size )
210{
211 consolidate() ;
212 m_store.append( data , size ) ;
213}
214
215 void GNet::LineStore::extend( const char * data , std::size_t size )
216{
217 consolidate() ;
218 m_extra_data = data ;
219 m_extra_size = size ;
220}
221
222 void GNet::LineStore::clear()
223{
224 m_store.clear() ;
225 m_extra_size = 0U ;
226}
227
228 void GNet::LineStore::consolidate()
229{
230 if( m_extra_size )
231 m_store.append( m_extra_data , m_extra_size ) ;
232 m_extra_size = 0U ;
233}
234
235 void GNet::LineStore::discard( std::size_t n )
236{
237 if( n == 0U )
238 {
239 if( m_extra_size )
240 {
241 m_store.append( m_extra_data , m_extra_size ) ;
242 m_extra_size = 0U ;
243 }
244 }
245 else if( n < m_store.size() )
246 {
247 m_store.erase( 0U , n ) ;
248 if( m_extra_size )
249 {
250 m_store.append( m_extra_data , m_extra_size ) ;
251 m_extra_size = 0U ;
252 }
253 }
254 else if( n == m_store.size() )
255 {
256 m_store.clear() ;
257 if( m_extra_size )
258 {
259 m_store.assign( m_extra_data , m_extra_size ) ;
260 m_extra_size = 0U ;
261 }
262 }
263 else if( n < size() )
264 {
265 std::size_t offset = n - m_store.size() ;
266 m_store.clear() ;
267 if( m_extra_size )
268 {
269 G_ASSERT( m_extra_size >= offset ) ;
270 m_store.assign( m_extra_data+offset , m_extra_size-offset ) ;
271 m_extra_size = 0U ;
272 }
273 }
274 else
275 {
276 clear() ;
277 }
278}
279
280std::size_t GNet::LineStore::find( char c , std::size_t startpos ) const
281{
282 G_ASSERT( startpos <= size() ) ;
283 std::size_t result = std::string::npos ;
284 const std::size_t store_size = m_store.size() ;
285 if( startpos < store_size )
286 {
287 result = m_store.find( c , startpos ) ;
288 }
289 if( result == std::string::npos && m_extra_size != 0U )
290 {
291 const std::size_t offset = startpos > store_size ? (startpos-store_size) : 0U ;
292 const char * const begin = m_extra_data + offset ;
293 const char * const end = m_extra_data + m_extra_size ;
294 G_ASSERT( begin >= m_extra_data && begin <= end ) ;
295 const char * p = std::find( begin , end , c ) ;
296 if( p != end )
297 result = store_size + std::distance(m_extra_data,p) ;
298 }
299 G_ASSERT( result == std::find(LineStoreIterator(*this)+startpos,LineStoreIterator(*this,true),c).pos() ) ;
300 return result ;
301}
302
303std::size_t GNet::LineStore::find( const std::string & s , std::size_t startpos ) const
304{
305 const std::size_t npos = std::string::npos ;
306 std::size_t result = npos ;
307 if( s.size() == 2U )
308 {
309 const char c0 = s[0] ;
310 const char c1 = s[1] ;
311 const std::size_t end = size() ;
312 for( std::size_t pos = startpos ; pos != npos ; ++pos )
313 {
314 pos = find( c0 , pos ) ;
315 if( pos == npos ) break ;
316 if( (pos+1U) != end && at(pos+1U) == c1 )
317 {
318 result = pos ;
319 break ;
320 }
321 }
322 G_ASSERT( result == search(s.begin(),s.end(),startpos) ) ;
323 }
324 else if( s.size() == 1U )
325 {
326 result = find( s[0] , startpos ) ;
327 G_ASSERT( result == search(s.begin(),s.end(),startpos) ) ;
328 }
329 else
330 {
331 result = search( s.begin() , s.end() , startpos ) ;
332 }
333 return result ;
334}
335
336std::size_t GNet::LineStore::search( std::string::const_iterator begin , std::string::const_iterator end ,
337 std::size_t startpos ) const
338{
339 return std::search( LineStoreIterator(*this)+startpos , LineStoreIterator(*this,true) , begin , end ).pos() ; // NOLINT narrowing
340}
341
342std::size_t GNet::LineStore::findSubStringAtEnd( const std::string & s , std::size_t startpos ) const
343{
344 namespace imp = LineStoreImp ;
345 if( s.empty() )
346 {
347 return 0U ;
348 }
349 else
350 {
351 std::size_t result = std::string::npos ;
352 std::size_t s_size = s.size() ;
353 std::string::const_iterator s_start = s.begin() ;
354 std::string::const_iterator s_end = s.end() ;
355 // for progressivley shorter leading substrings...
356 for( --s_size , --s_end ; s_start != s_end ; --s_size , --s_end )
357 {
358 if( (size()-startpos) >= s_size ) // if we have enough
359 {
360 // compare leading substring with the end of the store
361 const LineStoreIterator end( *this , true ) ;
362 LineStoreIterator p = end - s_size ; // NOLINT narrowing
363 if( imp::std_equal(s_start,s_end,p,end) )
364 {
365 result = p.pos() ;
366 break ;
367 }
368 }
369 }
370 return result ;
371 }
372}
373
374 const char * GNet::LineStore::data( std::size_t pos , std::size_t n ) const
375{
376 return (const_cast<LineStore*>(this))->dataimp( pos , n ) ;
377}
378
379 const char * GNet::LineStore::dataimp( std::size_t pos , std::size_t n )
380{
381 G_ASSERT( (n==0U && size()==0U) || (pos+n) <= size() ) ;
382 if( n == 0U && size() == 0U )
383 {
384 return "" ;
385 }
386 else if( n == 0U && pos == size() )
387 {
388 return "" ;
389 }
390 else if( (pos+n) <= m_store.size() )
391 {
392 return m_store.data() + pos ;
393 }
394 else if( pos >= m_store.size() )
395 {
396 std::size_t offset = pos - m_store.size() ;
397 return m_extra_data + offset ;
398 }
399 else
400 {
401 std::size_t nmove = pos + n - m_store.size() ;
402 m_store.append( m_extra_data , nmove ) ;
403 m_extra_data += nmove ;
404 m_extra_size -= nmove ;
405 return m_store.data() + pos ;
406 }
407}
408
409std::string GNet::LineStore::str() const
410{
411 std::string result( m_store ) ;
412 if( m_extra_size )
413 result.append( m_extra_data , m_extra_size ) ;
414 return result ;
415}
416
417std::string GNet::LineStore::head( std::size_t n ) const
418{
419 std::string result = G::Str::head( m_store , n ) ;
420 if( result.size() < n && m_extra_size )
421 result.append( m_extra_data , std::min(n-result.size(),m_extra_size) ) ;
422 return result ;
423}
424
GNet::LineStoreIterator
An iterator class for GNet::LineStore.
Definition: glinestore.cpp:37
GNet::LineStore
A pair of character buffers, one kept by value and the other being an ephemeral extension.
Definition: glinestore.h:42
GNet::LineStore::discard
void discard(std::size_t n)
Discards the first 'n' bytes and consolidates the residue.
Definition: glinestore.cpp:235
GNet::LineStore::append
void append(const std::string &)
Appends to the store (by copying).
Definition: glinestore.cpp:203
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
GNet::LineStore::head
std::string head(std::size_t n) const
Returns the leading sub-string of str() of up to 'n' characters.
Definition: glinestore.cpp:417
GNet::LineStore::clear
void clear()
Clears all data.
Definition: glinestore.cpp:222
GNet::LineStore::LineStore
LineStore()
Default constructor.
GNet::LineStore::str
std::string str() const
Returns the complete string.
Definition: glinestore.cpp:409
GNet::LineStore::find
std::size_t find(char c, std::size_t startpos=0U) const
Finds the given character.
Definition: glinestore.cpp:280
GNet::LineStore::size
std::size_t size() const
Returns the overall size.
Definition: glinestore.h:131
GNet::LineStore::consolidate
void consolidate()
Consolidates the extension into the store.
Definition: glinestore.cpp:228
GNet::LineStore::extend
void extend(const char *, std::size_t)
Sets the extension.
Definition: glinestore.cpp:215
GNet::LineStore::findSubStringAtEnd
std::size_t findSubStringAtEnd(const std::string &s, std::size_t startpos=0U) const
Finds a non-empty leading substring 's' that appears at the end of the data.
Definition: glinestore.cpp:342
GNet::LineStore::at
char at(std::size_t n) const
Returns the n'th character.
Definition: glinestore.h:124
G::Str::head
static std::string head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1273
gassert.h
gexception.h
glinestore.h
gstr.h
GNet
Network classes.
Definition: gdef.h:1115