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