E-MailRelay

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