E-MailRelay
gstr.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 gstr.cpp
19///
20
21#include "gdef.h"
22#include "gstr.h"
23#include "gassert.h"
24#include <algorithm>
25#include <type_traits> // std::make_unsigned
26#include <stdexcept>
27#include <iterator>
28#include <limits>
29#include <functional>
30#include <iomanip>
31#include <string>
32#include <sstream>
33#include <cstring>
34#include <cerrno>
35#include <cmath>
36
37namespace G
38{
39 namespace StrImp
40 {
41 string_view hexmap() noexcept ;
42 bool isDigit( char c ) noexcept ;
43 bool isHex( char c ) noexcept ;
44 bool isPrintableAscii( char c ) noexcept ;
45 bool isPrintable( char c ) noexcept ;
46 bool isSimple( char c ) noexcept ;
47 char toLower( char c ) noexcept ;
48 char toUpper( char c ) noexcept ;
49 unsigned short toUShort( string_view , bool & overflow , bool & invalid ) noexcept ;
50 unsigned long toULong( string_view , bool & overflow , bool & invalid ) noexcept ;
51 unsigned long toULongHex( string_view , bool limited ) ;
52 unsigned int toUInt( string_view , bool & overflow , bool & invalid ) noexcept ;
53 short toShort( string_view , bool & overflow , bool & invalid ) noexcept ;
54 long toLong( string_view , bool & overflow , bool & invalid ) noexcept ;
55 int toInt( string_view , bool & overflow , bool & invalid ) noexcept ;
56 template <typename U> string_view fromUnsignedToHex( U u , char * out_p ) noexcept ;
57 void strncpy( char * , const char * , std::size_t ) noexcept ;
58 template <typename Tstr, typename Fn> bool readLine( std::istream & , Tstr & , char * , std::size_t , Fn ) ;
59 template <typename S, typename T, typename SV> void splitIntoTokens( const S & in , T & out , const SV & ws ) ;
60 template <typename S, typename T> void splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc ) ;
61 template <typename T> void splitIntoFields( string_view in , T & out , string_view ws ) ;
62 template <typename T> void splitIntoFields( string_view in_in , T & out , string_view ws , char escape , bool remove_escapes ) ;
63 bool ilessc( char c1 , char c2 ) noexcept ;
64 bool imatchc( char c1 , char c2 ) noexcept ;
65 bool imatch( const std::string & a , const std::string & b ) ;
66 template <typename T, typename V> T unique( T in , T end , V repeat , V replacement ) ;
67 bool inList( StringArray::const_iterator begin , StringArray::const_iterator end , const std::string & s , bool i ) ;
68 bool notInList( StringArray::const_iterator begin , StringArray::const_iterator end , const std::string & s , bool i ) ;
69 void join( string_view , std::string & , string_view ) ;
70 template <typename Tout> std::size_t outputHex( Tout out , char c ) ;
71 template <typename Tout> std::size_t outputHex( Tout out , wchar_t c ) ;
72 template <typename Tout, typename Tchar> std::size_t outputPrintable( Tout , Tchar , Tchar , char , bool ) ;
73 bool allOf( string_view s , bool (*fn)(char) ) noexcept ;
74 }
75}
76
77bool G::StrImp::allOf( string_view s , bool (*fn)(char) ) noexcept
78{
79 return std::all_of( s.begin() , s.end() , fn ) ; // (true if empty)
80}
81
82#ifndef G_LIB_SMALL
83std::string G::Str::escaped( string_view s_in )
84{
85 if( s_in.empty() ) return {} ;
86 std::string s( s_in.data() , s_in.size() ) ;
87 escape( s ) ;
88 return s ;
89}
90#endif
91
92std::string G::Str::escaped( string_view s_in , char c_escape , string_view specials_in , string_view specials_out )
93{
94 if( s_in.empty() ) return {} ;
95 std::string s( s_in.data() , s_in.size() ) ;
96 escape( s , c_escape , specials_in , specials_out ) ;
97 return s ;
98}
99
100void G::Str::escape( std::string & s , char c_escape , string_view specials_in , string_view specials_out )
101{
102 G_ASSERT( specials_in.size() == specials_out.size() ) ;
103 std::size_t pos = 0U ;
104 for(;;)
105 {
106 char c_in = '\0' ;
107 pos = s.find_first_of( specials_in.data() , pos , specials_in.size() ) ;
108 if( pos == std::string::npos )
109 break ;
110 else
111 c_in = s.at( pos ) ;
112
113 const std::size_t special_index = specials_in.find( c_in ) ;
114 char c_out = specials_out.at( special_index ) ;
115
116 s.insert( pos , 1U , c_escape ) ;
117 pos++ ;
118 s.at(pos) = c_out ;
119 pos++ ;
120 }
121}
122
123void G::Str::escape( std::string & s )
124{
125 Str::escape( s , '\\' , "\0\\\r\n\t"_sv , "0\\rnt"_sv ) ;
126}
127
128std::string G::Str::dequote( const std::string & s , char qq , char esc , string_view ws , string_view nbws )
129{
130 G_ASSERT( ws.size() == nbws.size() ) ;
131 std::string result ;
132 result.reserve( s.size() ) ;
133 bool in_quote = false ;
134 bool escaped = false ;
135 for( char c : s )
136 {
137 if( c == esc && !escaped )
138 {
139 escaped = true ;
140 result.append( 1U , esc ) ;
141 }
142 else
143 {
144 std::size_t wspos = 0 ;
145 if( c == qq && !escaped && !in_quote )
146 {
147 in_quote = true ;
148 }
149 else if( c == qq && !escaped )
150 {
151 in_quote = false ;
152 }
153 else if( in_quote && (wspos=ws.find(c)) != std::string::npos )
154 {
155 if( escaped )
156 {
157 result.append( 1U , nbws.at(wspos) ) ;
158 }
159 else
160 {
161 result.append( 1U , esc ) ;
162 result.append( 1U , c ) ;
163 }
164 }
165 else
166 {
167 result.append( 1U , c ) ;
168 }
169 escaped = false ;
170 }
171 }
172 return result ;
173}
174
175void G::Str::unescape( std::string & s )
176{
177 unescape( s , '\\' , "0rnt"_sv , "\0\r\n\t"_sv ) ;
178}
179
180void G::Str::unescape( std::string & s , char c_escape , string_view specials_in , string_view specials_out )
181{
182 G_ASSERT( specials_in.size() == specials_out.size() ) ;
183 bool escaped = false ;
184 std::size_t cpos = 0U ;
185 auto out = s.begin() ; // output in-place
186 for( char & c_in : s )
187 {
188 if( escaped && (cpos=specials_in.find(c_in)) != std::string::npos ) // was BS now 'n'
189 *out++ = specials_out.at(cpos) , escaped = false ; // emit NL
190 else if( escaped && c_in == c_escape ) // was BS now BS
191 *out++ = c_escape , escaped = false ; // emit BS
192 else if( escaped ) // was BS
193 *out++ = c_in , escaped = false ; // emit c
194 else if( c_in == c_escape ) // is BS
195 escaped = true ;
196 else
197 *out++ = c_in , escaped = false ;
198 }
199 if( out != s.end() ) s.erase( out , s.end() ) ;
200}
201
202#ifndef G_LIB_SMALL
203std::string G::Str::unescaped( const std::string & s_in )
204{
205 std::string s( s_in ) ;
206 unescape( s ) ;
207 return s ;
208}
209#endif
210
211void G::Str::replace( std::string & s , char from , char to )
212{
213 for( char & c : s )
214 {
215 if( c == from )
216 c = to ;
217 }
218}
219
220void G::Str::replace( StringArray & a , char from , char to )
221{
222 for( std::string & s : a )
223 replace( s , from , to ) ;
224}
225
226bool G::Str::replace( std::string & s , string_view from , string_view to , std::size_t * pos_p )
227{
228 if( from.empty() )
229 return false ;
230
231 std::size_t pos = pos_p == nullptr ? 0 : *pos_p ;
232 if( pos >= s.size() )
233 return false ;
234
235 pos = s.find( from.data() , pos , from.size() ) ;
236 if( pos == std::string::npos )
237 return false ;
238 else if( to.empty() )
239 s.erase( pos , from.size() ) ;
240 else
241 s.replace( pos , from.size() , to.data() , to.size() ) ;
242 if( pos_p != nullptr )
243 *pos_p = pos + to.size() ;
244 return true ;
245}
246
247unsigned int G::Str::replaceAll( std::string & s , string_view from , string_view to )
248{
249 unsigned int count = 0U ;
250 for( std::size_t pos = 0U ; replace(s,from,to,&pos) ; count++ )
251 {;} // no-op
252 return count ;
253}
254
255std::string G::Str::replaced( const std::string & s , char from , char to )
256{
257 std::string result( s ) ;
258 replaceAll( result , {&from,1U} , {&to,1U} ) ;
259 return result ;
260}
261
262void G::Str::removeAll( std::string & s , char c )
263{
264 s.erase( std::remove_if( s.begin() , s.end() , [c](char x){return x==c;} ) , s.end() ) ;
265}
266
267#ifndef G_LIB_SMALL
268std::string G::Str::removedAll( const std::string & s_in , char c )
269{
270 std::string s( s_in ) ;
271 removeAll( s , c ) ;
272 return s ;
273}
274#endif
275
276std::string G::Str::only( string_view chars , string_view s )
277{
278 std::string result ;
279 result.reserve( s.size() ) ;
280 for( char c : s )
281 {
282 if( chars.find(c) != std::string::npos )
283 result.append( 1U , c ) ;
284 }
285 return result ;
286}
287
288std::string & G::Str::trimLeft( std::string & s , string_view ws , std::size_t limit )
289{
290 std::size_t n = s.find_first_not_of( ws.data() , 0U , ws.size() ) ;
291 if( limit != 0U && ( n == std::string::npos || n > limit ) )
292 n = limit >= s.size() ? std::string::npos : limit ;
293 if( n == std::string::npos )
294 s.erase() ;
295 else if( n != 0U )
296 s.erase( 0U , n ) ;
297 return s ;
298}
299
300G::string_view G::Str::trimLeftView( string_view sv , string_view ws , std::size_t limit ) noexcept
301{
302 std::size_t n = sv.find_first_not_of( ws ) ;
303 if( limit != 0U && ( n == std::string::npos || n > limit ) )
304 n = limit >= sv.size() ? std::string::npos : limit ;
305 if( n == std::string::npos )
306 return sv_substr( sv , 0U , 0U ) ;
307 else if( n != 0U )
308 return sv_substr( sv , n ) ;
309 else
310 return sv ;
311}
312
313std::string & G::Str::trimRight( std::string & s , string_view ws , std::size_t limit )
314{
315 std::size_t n = s.find_last_not_of( ws.data() , std::string::npos , ws.size() ) ;
316 if( limit != 0U && ( n == std::string::npos || s.size() > (limit+n+1U) ) )
317 n = limit >= s.size() ? std::string::npos : (s.size()-limit-1U) ;
318 if( n == std::string::npos )
319 s.erase() ;
320 else if( (n+1U) != s.size() )
321 s.resize( n + 1U ) ;
322 return s ;
323}
324
325G::string_view G::Str::trimRightView( string_view sv , string_view ws , std::size_t limit ) noexcept
326{
327 std::size_t n = sv.find_last_not_of( ws ) ;
328 if( limit != 0U && ( n == std::string::npos || sv.size() > (limit+n+1U) ) )
329 n = limit >= sv.size() ? std::string::npos : (sv.size()-limit-1U) ;
330 if( n == std::string::npos )
331 return sv_substr( sv , 0U , 0U ) ;
332 else if( (n+1U) != sv.size() )
333 return sv_substr( sv , 0U , n+1U ) ;
334 else
335 return sv ;
336}
337
338std::string & G::Str::trim( std::string & s , string_view ws )
339{
340 return trimLeft( trimRight(s,ws) , ws ) ;
341}
342
343std::string G::Str::trimmed( const std::string & s_in , string_view ws )
344{
345 std::string s( s_in ) ;
346 return trim( s , ws ) ;
347}
348
349std::string G::Str::trimmed( std::string && s , string_view ws )
350{
351 return std::move( trimLeft(trimRight(s,ws),ws) ) ;
352}
353
355{
356 return trimLeftView( trimRightView(s,ws) , ws ) ;
357}
358
359bool G::StrImp::isDigit( char c ) noexcept
360{
361 auto uc = static_cast<unsigned char>(c) ;
362 return uc >= 48U && uc <= 57U ;
363}
364
365bool G::StrImp::isHex( char c ) noexcept
366{
367 auto uc = static_cast<unsigned char>(c) ;
368 return ( uc >= 48U && uc <= 57U ) || ( uc >= 65U && uc <= 70U ) || ( uc >= 97U && uc <= 102U ) ;
369}
370
371bool G::StrImp::isPrintableAscii( char c ) noexcept
372{
373 auto uc = static_cast<unsigned char>(c) ;
374 return uc >= 32U && uc < 127U ;
375}
376
377bool G::StrImp::isPrintable( char c ) noexcept
378{
379 auto uc = static_cast<unsigned char>(c) ;
380 return ( uc >= 32U && uc < 127U ) || ( uc >= 0xa0 && uc < 0xff ) ;
381}
382
383bool G::StrImp::isSimple( char c ) noexcept
384{
385 auto uc = static_cast<unsigned char>(c) ;
386 return isDigit(c) || c == '-' || c == '_' ||
387 ( uc >= 65U && uc <= 90U ) ||
388 ( uc >= 97U && uc <= 122U ) ;
389}
390
391char G::StrImp::toLower( char c ) noexcept
392{
393 const auto uc = static_cast<unsigned char>(c) ;
394 return ( uc >= 65U && uc <= 90U ) ? static_cast<char>(c+'\x20') : c ;
395}
396
397char G::StrImp::toUpper( char c ) noexcept
398{
399 const auto uc = static_cast<unsigned char>(c) ;
400 return ( uc >= 97U && uc <= 122U ) ? static_cast<char>(c-'\x20') : c ;
401}
402
403bool G::Str::isNumeric( string_view s , bool allow_minus_sign ) noexcept
404{
405 bool bump = allow_minus_sign && s.size() > 1U && s[0] == '-' ;
406 return StrImp::allOf( bump?sv_substr(s,1U):s , StrImp::isDigit ) ; // (true if empty)
407}
408
409#ifndef G_LIB_SMALL
410bool G::Str::isHex( string_view s ) noexcept
411{
412 return StrImp::allOf( s , StrImp::isHex ) ;
413}
414#endif
415
417{
418 return StrImp::allOf( s , StrImp::isPrintableAscii ) ;
419}
420
422{
423 return StrImp::allOf( s , StrImp::isPrintable ) ;
424}
425
427{
428 return StrImp::allOf( s , StrImp::isSimple ) ;
429}
430
431bool G::Str::isInt( string_view s ) noexcept
432{
433 bool overflow = false ;
434 bool invalid = false ;
435 StrImp::toInt( s , overflow , invalid ) ;
436 return !overflow && !invalid ;
437}
438
439#ifndef G_LIB_SMALL
441{
442 bool overflow = false ;
443 bool invalid = false ;
444 StrImp::toUShort( s , overflow , invalid ) ;
445 return !overflow && !invalid ;
446}
447#endif
448
449bool G::Str::isUInt( string_view s ) noexcept
450{
451 bool overflow = false ;
452 bool invalid = false ;
453 StrImp::toUInt( s , overflow , invalid ) ;
454 return !overflow && !invalid ;
455}
456
457bool G::Str::isULong( string_view s ) noexcept
458{
459 bool overflow = false ;
460 bool invalid = false ;
461 StrImp::toULong( s , overflow , invalid ) ;
462 return !overflow && !invalid ;
463}
464
465#ifndef G_LIB_SMALL
466std::string G::Str::fromBool( bool b )
467{
468 return b ? "true" : "false" ;
469}
470#endif
471
472#ifndef G_LIB_SMALL
473std::string G::Str::fromDouble( double d )
474{
475 if( std::isnan(d) ) return "nan" ;
476 if( std::isinf(d) ) return "inf" ;
477 std::ostringstream ss ;
478 ss << std::setprecision(16) << d ;
479 return ss.str() ;
480}
481#endif
482
483#ifndef G_LIB_SMALL
485{
486 bool result = true ;
487 if( imatch( s , "true"_sv ) )
488 {;}
489 else if( imatch( s , "false"_sv ) )
490 result = false ;
491 else
492 throw InvalidFormat( "expected true/false" , s ) ;
493 return result ;
494}
495#endif
496
497#ifndef G_LIB_SMALL
498double G::Str::toDouble( const std::string & s )
499{
500 try
501 {
502 std::size_t end = 0U ;
503 double result = std::stod( s , &end ) ;
504 if( end != s.size() )
505 throw InvalidFormat( "expected floating point number" , s ) ;
506 return result ;
507 }
508 catch( std::invalid_argument & )
509 {
510 throw InvalidFormat( "expected floating point number" , s ) ;
511 }
512 catch( std::out_of_range & )
513 {
514 throw Overflow( s ) ;
515 }
516}
517#endif
518
519#ifndef G_LIB_SMALL
520float G::Str::toFloat( const std::string & s )
521{
522 try
523 {
524 std::size_t end = 0U ;
525 float result = std::stof( s , &end ) ;
526 if( end != s.size() )
527 throw InvalidFormat( "expected floating point number" , s ) ;
528 return result ;
529 }
530 catch( std::invalid_argument & )
531 {
532 throw InvalidFormat( "expected floating point number" , s ) ;
533 }
534 catch( std::out_of_range & )
535 {
536 throw Overflow( s ) ;
537 }
538}
539#endif
540
542{
543 bool overflow = false ;
544 bool invalid = false ;
545 int result = StrImp::toInt( s , overflow , invalid ) ;
546 if( invalid )
547 throw InvalidFormat( "expected integer" , s ) ;
548 if( overflow )
549 throw Overflow( s ) ;
550 return result ;
551}
552
553#ifndef G_LIB_SMALL
555{
556 return !s1.empty() && isInt(s1) ? toInt(s1) : toInt(s2) ;
557}
558#endif
559
560int G::StrImp::toInt( string_view s , bool & overflow , bool & invalid ) noexcept
561{
562 long long_val = toLong( s , overflow , invalid ) ;
563 int result = static_cast<int>( long_val ) ;
564 if( result != long_val )
565 overflow = true ;
566 return result ;
567}
568
569#ifndef G_LIB_SMALL
571{
572 bool overflow = false ;
573 bool invalid = false ;
574 long result = StrImp::toLong( s , overflow , invalid ) ;
575 if( invalid )
576 throw InvalidFormat( "expected long integer" , s ) ;
577 if( overflow )
578 throw Overflow( s ) ;
579 return result ;
580}
581#endif
582
583long G::StrImp::toLong( string_view s , bool & overflow , bool & invalid ) noexcept
584{
585 bool negative = !s.empty() && s[0] == '-' ;
586 bool positive = !s.empty() && s[0] == '+' ;
587 if( (negative || positive) && s.size() == 1U )
588 {
589 invalid = true ;
590 return 0L ;
591 }
592 unsigned long ul = toULong( sv_substr(s,(negative||positive)?1U:0U) , overflow , invalid ) ;
593 static constexpr long long_max = std::numeric_limits<long>::max() ;
594 if( ul > long_max || (negative && (ul==long_max)) )
595 {
596 overflow = true ;
597 return 0L ;
598 }
599 return negative ? -static_cast<long>(ul) : static_cast<long>(ul) ;
600}
601
602#ifndef G_LIB_SMALL
604{
605 bool overflow = false ;
606 bool invalid = false ;
607 short result = StrImp::toShort( s , overflow , invalid ) ;
608 if( invalid )
609 throw InvalidFormat( "expected short integer" , s ) ;
610 if( overflow )
611 throw Overflow( s ) ;
612 return result ;
613}
614#endif
615
616short G::StrImp::toShort( string_view s , bool & overflow , bool & invalid ) noexcept
617{
618 long long_val = toLong( s , overflow , invalid ) ;
619 auto result = static_cast<short>( long_val ) ;
620 if( result != long_val )
621 overflow = true ;
622 return result ;
623}
624
626{
627 return !s1.empty() && isUInt(s1) ? toUInt(s1) : toUInt(s2) ;
628}
629
630#ifndef G_LIB_SMALL
631unsigned int G::Str::toUInt( string_view s , unsigned int default_ )
632{
633 return !s.empty() && isUInt(s) ? toUInt(s) : default_ ;
634}
635#endif
636
637#ifndef G_LIB_SMALL
639{
640 bool overflow = false ;
641 bool invalid = false ;
642 unsigned int result = StrImp::toUInt( s , overflow , invalid ) ;
643 if( invalid )
644 throw InvalidFormat( "expected unsigned integer" , s ) ;
645 if( overflow )
646 result = std::numeric_limits<unsigned int>::max() ;
647 return result ;
648}
649#endif
650
652{
653 bool overflow = false ;
654 bool invalid = false ;
655 unsigned int result = StrImp::toUInt( s , overflow , invalid ) ;
656 if( invalid )
657 throw InvalidFormat( "expected unsigned integer" , s ) ;
658 if( overflow )
659 throw Overflow( s ) ;
660 return result ;
661}
662
663unsigned int G::StrImp::toUInt( string_view s , bool & overflow , bool & invalid ) noexcept
664{
665 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
666 auto result = static_cast<unsigned int>( ulong_val ) ;
667 if( result != ulong_val )
668 overflow = true ;
669 return result ;
670}
671
673{
674 bool overflow = false ;
675 bool invalid = false ;
676 unsigned long result = StrImp::toULong( s , overflow , invalid ) ;
677 if( invalid )
678 throw InvalidFormat( "expected unsigned long integer" , s ) ;
679 if( overflow )
680 result = std::numeric_limits<unsigned long>::max() ;
681 return result ;
682}
683
684#ifndef G_LIB_SMALL
686{
687 return StrImp::toULongHex( s , true ) ;
688}
689#endif
690
691#ifndef G_LIB_SMALL
692unsigned long G::Str::toULong( string_view s , Hex )
693{
694 return StrImp::toULongHex( s , false ) ;
695}
696#endif
697
698unsigned long G::StrImp::toULongHex( string_view s , bool limited )
699{
700 unsigned long n = 0U ;
701 if( s.empty() ) return 0U ;
702 std::size_t i0 = s.find_first_not_of( '0' ) ;
703 if( i0 == std::string::npos ) i0 = 0U ;
704 if( (s.size()-i0) > (sizeof(unsigned long)*2U) )
705 {
706 if( limited ) return ~0UL ;
707 throw Str::Overflow( s ) ;
708 }
709 for( std::size_t i = i0 ; i < s.size() ; i++ )
710 {
711 unsigned int c = static_cast<unsigned char>(s.at(i)) ;
712 if( c >= 97U && c <= 102U ) c -= 87U ;
713 else if( c >= 65U && c <= 70U ) c -= 55U ;
714 else if( c >= 48U && c <= 57U ) c -= 48U ;
715 else throw Str::InvalidFormat( "invalid hexadecimal" , s ) ;
716 n <<= 4U ;
717 n += c ;
718 }
719 return n ;
720}
721
722unsigned long G::Str::toULong( string_view s )
723{
724 bool overflow = false ;
725 bool invalid = false ;
726 unsigned long result = StrImp::toULong( s , overflow , invalid ) ;
727 if( invalid )
728 throw InvalidFormat( "expected unsigned long integer" , s ) ;
729 else if( overflow )
730 throw Overflow( s ) ;
731 return result ;
732}
733
734#ifndef G_LIB_SMALL
736{
737 return !s1.empty() && isULong(s1) ? toULong(s1) : toULong(s2) ;
738}
739#endif
740
741unsigned long G::StrImp::toULong( string_view s , bool & overflow , bool & invalid ) noexcept
742{
743 // note that stoul()/strtoul() do too much in that they skip leading
744 // spaces, allow initial +/- even for unsigned, and are C-locale
745 // dependent -- also there is no string_view version of std::stol()
746 // and std::strtol() requires a null-terminated string and so cannot
747 // take string_view::data()
748
749 return Str::toUnsigned<unsigned long>( s.data() , s.data()+s.size() , overflow , invalid ) ;
750}
751
752#ifndef G_LIB_SMALL
754{
755 bool overflow = false ;
756 bool invalid = false ;
757 unsigned short result = StrImp::toUShort( s , overflow , invalid ) ;
758 if( invalid )
759 throw InvalidFormat( "expected unsigned short integer" , s ) ;
760 if( overflow )
761 result = std::numeric_limits<unsigned short>::max() ;
762 return result ;
763}
764#endif
765
766#ifndef G_LIB_SMALL
767unsigned short G::Str::toUShort( string_view s )
768{
769 bool overflow = false ;
770 bool invalid = false ;
771 unsigned short result = StrImp::toUShort( s , overflow , invalid ) ;
772 if( invalid )
773 throw InvalidFormat( "expected unsigned short integer" , s ) ;
774 else if( overflow )
775 throw Overflow( s ) ;
776 return result ;
777}
778#endif
779
780unsigned short G::StrImp::toUShort( string_view s , bool & overflow , bool & invalid ) noexcept
781{
782 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
783 auto result = static_cast<unsigned short>( ulong_val ) ;
784 if( result != ulong_val )
785 overflow = true ;
786 return result ;
787}
788
789#ifndef G_LIB_SMALL
790G::string_view G::Str::fromULongToHex( unsigned long u , char * out_p ) noexcept
791{
792 return StrImp::fromUnsignedToHex( u , out_p ) ;
793}
794#endif
795
796#ifndef G_LIB_SMALL
797G::string_view G::Str::fromULongLongToHex( unsigned long long u , char * out_p ) noexcept
798{
799 return StrImp::fromUnsignedToHex( u , out_p ) ;
800}
801#endif
802
803template <typename U>
804G::string_view G::StrImp::fromUnsignedToHex( U u , char * out_p ) noexcept
805{
806 // (std::to_chars() is c++17 so do it long-hand)
807 static constexpr unsigned bytes = sizeof(U) ;
808 static constexpr unsigned bits = 8U * bytes ;
809 static constexpr unsigned buffer_size = bytes * 2U ;
810 static const char * map = "0123456789abcdef" ;
811 unsigned int shift = bits - 4U ;
812 char * out = out_p ;
813 for( unsigned i = 0 ; i < (bytes*2U) ; i++ ) // unrollable, no break
814 {
815 *out++ = map[(u>>shift) & 0xfUL] ;
816 shift -= 4U ;
817 }
818 string_view sv( out_p , buffer_size ) ;
819 return sv_substr( sv , std::min( sv.find_first_not_of('0') , static_cast<std::size_t>(buffer_size-1U) ) ) ;
820}
821
822void G::Str::toLower( std::string & s )
823{
824 std::transform( s.begin() , s.end() , s.begin() , StrImp::toLower ) ;
825}
826
828{
829 std::string out = sv_to_string( in ) ;
830 toLower( out ) ;
831 return out ;
832}
833
834void G::Str::toUpper( std::string & s )
835{
836 std::transform( s.begin() , s.end() , s.begin() , StrImp::toUpper ) ;
837}
838
840{
841 std::string out = sv_to_string( in ) ;
842 toUpper( out ) ;
843 return out ;
844}
845
846G::string_view G::StrImp::hexmap() noexcept
847{
848 static constexpr string_view chars_hexmap = "0123456789abcdef"_sv ;
849 static_assert( chars_hexmap.size() == 16U , "" ) ;
850 return chars_hexmap ;
851}
852
853template <typename Tout>
854std::size_t G::StrImp::outputHex( Tout out , char c )
855{
856 std::size_t n = static_cast<unsigned char>( c ) ;
857 n &= 0xffU ;
858 out( hexmap()[(n>>4U)%16U] ) ;
859 out( hexmap()[(n>>0U)%16U] ) ;
860 return 2U ;
861}
862
863template <typename Tout>
864std::size_t G::StrImp::outputHex( Tout out , wchar_t c )
865{
866 using uwchar_t = typename std::make_unsigned<wchar_t>::type ;
867 std::size_t n = static_cast<uwchar_t>( c ) ;
868 n &= 0xffffU ;
869 out( hexmap()[(n>>12U)%16U] ) ;
870 out( hexmap()[(n>>8U)%16U] ) ;
871 out( hexmap()[(n>>4U)%16U] ) ;
872 out( hexmap()[(n>>0U)%16U] ) ;
873 return 4U ;
874}
875
876template <typename Tout, typename Tchar>
877std::size_t G::StrImp::outputPrintable( Tout out , Tchar c , Tchar escape_in , char escape_out , bool eight_bit )
878{
879 using Tuchar = typename std::make_unsigned<Tchar>::type ;
880 const auto uc = static_cast<Tuchar>( c ) ; // NOLINT not bugprone-signed-char-misuse
881 std::size_t n = 1U ;
882 if( c == escape_in )
883 {
884 out( escape_out ) ;
885 n++ ;
886 out( escape_out ) ;
887 }
888 else if( !eight_bit && uc >= 0x20U && uc < 0x7fU )
889 {
890 out( static_cast<char>(c) ) ;
891 }
892 else if( eight_bit && ( uc >= 0x20U && uc != 0x7fU ) )
893 {
894 out( static_cast<char>(c) ) ;
895 }
896 else
897 {
898 out( escape_out ) ;
899 n++ ;
900 char c_out = 'x' ;
901 if( uc == 10U )
902 c_out = 'n' ;
903 else if( uc == 13U )
904 c_out = 'r' ;
905 else if( uc == 9U )
906 c_out = 't' ;
907 else if( uc == 0U )
908 c_out = '0' ;
909 out( c_out ) ;
910 if( c_out == 'x' )
911 n += outputHex( out , c ) ;
912 }
913 return n ;
914}
915
916std::string G::Str::printable( const std::string & in , char escape )
917{
918 std::string result ;
919 result.reserve( in.length()*2U + 1U ) ;
920 for( auto c : in )
921 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , true ) ;
922 return result ;
923}
924
925std::string G::Str::printable( G::string_view in , char escape )
926{
927 std::string result ;
928 result.reserve( in.length()*2U + 1U ) ;
929 for( auto c : in )
930 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , true ) ;
931 return result ;
932}
933
934std::string G::Str::toPrintableAscii( const std::string & in , char escape )
935{
936 std::string result ;
937 result.reserve( in.length()*2U + 1U ) ;
938 for( auto c : in )
939 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , false ) ;
940 return result ;
941}
942
943#ifndef G_LIB_SMALL
944std::string G::Str::toPrintableAscii( const std::wstring & in , wchar_t escape )
945{
946 std::string result ;
947 result.reserve( in.length()*2U + 1U ) ;
948 for( auto c : in )
949 StrImp::outputPrintable( [&result](wchar_t cc){result.append(1U,static_cast<char>(cc));} , c , escape , static_cast<char>(escape) , false ) ;
950 return result ;
951}
952#endif
953
954std::string G::Str::readLineFrom( std::istream & stream , string_view eol )
955{
956 std::string result ;
957 readLine( stream , result , eol , false , 0U ) ;
958 return result ;
959}
960
961std::istream & G::Str::readLine( std::istream & stream , std::string & line , string_view eol ,
962 bool pre_erase , std::size_t limit )
963{
964 if( !stream.good() ) // (new)
965 {
966 // don't pre-erase -- surprising but cf. std::getline()
967 stream.setstate( std::ios_base::failbit ) ;
968 return stream ;
969 }
970
971 if( pre_erase )
972 line.clear() ;
973
974 if( line.empty() && ( eol.empty() || eol.size() == 1U ) && limit == 0U )
975 {
976 std::getline( stream , line , eol.empty() ? '\n' : eol[0] ) ;
977 }
978 else
979 {
980 if( eol.empty() ) eol = string_view( "\n" , 1U ) ; // new
981 const char eol_last = eol.at( eol.size()-1U ) ;
982 const std::size_t eol_size = eol.size() ;
983 bool got_eol = StrImp::readLine( stream , line , nullptr , limit ? limit : line.max_size() ,
984 [eol,eol_size,eol_last](std::string &s,char c)
985 {
986 return
987 ( c == eol_last && s.size() >= eol_size ) ?
988 s.find(eol.data(),s.size()-eol_size,eol_size) == (s.size()-eol_size) :
989 false ;
990 } ) ;
991 if( got_eol )
992 line.erase( line.size() - eol.size() ) ;
993 }
994 return stream ;
995}
996
997std::istream & G::Str::readLine( std::istream & stream , std::string & line , Eol eol ,
998 bool pre_erase , std::size_t limit )
999{
1000 if( eol == Eol::CrLf )
1001 {
1002 return readLine( stream , line , "\r\n"_sv , pre_erase , limit ) ;
1003 }
1004 else // Cr_Lf_CrLf
1005 {
1006 if( pre_erase && stream.good() ) // cf. std::getline()
1007 line.clear() ;
1008
1009 char next = '\0' ;
1010 bool got_eol = StrImp::readLine( stream , line , &next , limit ? limit : line.max_size() ,
1011 [](std::string &,char c)
1012 {
1013 return c == '\n' || c == '\r' ;
1014 } ) ;
1015 if( got_eol )
1016 {
1017 if( line.at(line.size()-1U) == '\r' && next == '\n' )
1018 stream.get() ; // advance over the peeked '\n'
1019 line.erase( line.size()-1U ) ;
1020 }
1021 return stream ;
1022 }
1023}
1024
1025template <typename Tstring, typename Fn>
1026bool G::StrImp::readLine( std::istream & stream , Tstring & line , char * next_p , std::size_t limit , Fn eol_fn )
1027{
1028 bool got_eol = false ;
1029 bool got_some = false ;
1030 std::istream::sentry sentry( stream , true ) ;
1031 if( sentry )
1032 {
1033 using traits = std::istream::traits_type ;
1034 std::size_t count = 0U ;
1035 int c = stream.rdbuf()->sgetc() ; // c = *p
1036 while( count < limit && c != traits::eof() )
1037 {
1038 line.append( 1U , traits::to_char_type(c) ) ;
1039 got_eol = eol_fn( line , traits::to_char_type(c) ) ;
1040 if( got_eol )
1041 break ;
1042
1043 count++ ;
1044 c = stream.rdbuf()->snextc() ; // c = *(++p)
1045 }
1046 got_some = count > 0U ;
1047 if( count == limit )
1048 {
1049 stream.setstate( std::ios_base::failbit ) ;
1050 }
1051 else if( c == traits::eof() )
1052 {
1053 stream.setstate( std::ios_base::eofbit ) ;
1054 }
1055 else
1056 {
1057 got_some = true ;
1058 stream.rdbuf()->sbumpc() ; // ++p
1059 if( next_p )
1060 *next_p = traits::to_char_type( stream.rdbuf()->sgetc() ) ; // next = *p
1061 }
1062 }
1063 if( !got_some )
1064 stream.setstate( std::ios_base::failbit ) ;
1065 return got_eol ;
1066}
1067
1068template <typename S, typename T, typename SV>
1069void G::StrImp::splitIntoTokens( const S & in , T & out , const SV & ws )
1070{
1071 for( std::size_t p = 0U ; p != S::npos ; )
1072 {
1073 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1074 if( p != S::npos )
1075 {
1076 std::size_t end = in.find_first_of( ws.data() , p , ws.size() ) ;
1077 std::size_t len = end == S::npos ? end : (end-p) ;
1078 out.push_back( in.substr(p,len) ) ;
1079 p = end ;
1080 }
1081 }
1082}
1083
1084template <typename S, typename T>
1085void G::StrImp::splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc )
1086{
1087 using string_type = S ;
1088 string_type ews = ws + string_type(1U,esc) ;
1089 for( std::size_t p = 0U ; p != S::npos ; )
1090 {
1091 // find the token start
1092 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1093 if( p == S::npos || ( in.at(p) == esc && (p+1) == in.size() ) )
1094 break ;
1095
1096 // find the token end
1097 std::size_t end = in.find_first_of( ews.data() , p , ews.size() ) ;
1098 while( end != S::npos && end < in.size() && in.at(end) == esc )
1099 end = (end+2) < in.size() ? in.find_first_of( ews.data() , end+2 , ews.size() ) : S::npos ;
1100
1101 // extract the token
1102 std::size_t len = end == std::string::npos ? end : (end-p) ;
1103 string_type w( in.substr(p,len) ) ;
1104
1105 // remove whitespace escapes
1106 for( std::size_t i = 0 ; esc && i < w.size() ; i++ )
1107 {
1108 if( w[i] == esc )
1109 {
1110 if( (i+1) < w.size() && ws.find(w[i+1]) != S::npos )
1111 w.erase( i , 1U ) ;
1112 else
1113 i++ ;
1114 }
1115 }
1116
1117 out.push_back( w ) ;
1118 p = end ;
1119 }
1120}
1121
1122void G::Str::splitIntoTokens( const std::string & in , StringArray & out , string_view ws , char esc )
1123{
1124 if( esc && in.find(esc) != std::string::npos )
1125 StrImp::splitIntoTokens( in , out , sv_to_string(ws) , esc ) ;
1126 else
1127 StrImp::splitIntoTokens( in , out , ws ) ;
1128}
1129
1130G::StringArray G::Str::splitIntoTokens( const std::string & in , string_view ws , char esc )
1131{
1132 StringArray out ;
1133 splitIntoTokens( in , out , ws , esc ) ;
1134 return out ;
1135}
1136
1137template <typename T>
1138void G::StrImp::splitIntoFields( string_view in , T & out , string_view ws )
1139{
1140 if( !in.empty() )
1141 {
1142 std::size_t start = 0U ;
1143 std::size_t pos = 0U ;
1144 for(;;)
1145 {
1146 if( pos >= in.size() ) break ;
1147 pos = in.find_first_of( ws.data() , pos , ws.size() ) ;
1148 if( pos == std::string::npos ) break ;
1149 out.push_back( sv_to_string(in.substr(start,pos-start)) ) ;
1150 pos++ ;
1151 start = pos ;
1152 }
1153 out.push_back( sv_to_string(in.substr(start,pos-start)) ) ;
1154 }
1155}
1156
1157template <typename T>
1158void G::StrImp::splitIntoFields( string_view in_in , T & out , string_view ws ,
1159 char escape , bool remove_escapes )
1160{
1161 std::string ews ; // escape+whitespace
1162 ews.reserve( ws.size() + 1U ) ;
1163 ews.assign( ws.data() , ws.size() ) ;
1164 if( escape != '\0' ) ews.append( 1U , escape ) ;
1165 if( !in_in.empty() )
1166 {
1167 std::string in = sv_to_string( in_in ) ;
1168 std::size_t start = 0U ;
1169 std::size_t pos = 0U ;
1170 for(;;)
1171 {
1172 if( pos >= in.size() ) break ;
1173 pos = in.find_first_of( ews , pos ) ;
1174 if( pos == std::string::npos ) break ;
1175 if( in.at(pos) == escape )
1176 {
1177 if( remove_escapes )
1178 in.erase( pos , 1U ) ;
1179 else
1180 pos++ ;
1181 pos++ ;
1182 }
1183 else
1184 {
1185 out.push_back( in.substr(start,pos-start) ) ;
1186 pos++ ;
1187 start = pos ;
1188 }
1189 }
1190 out.push_back( in.substr(start,pos-start) ) ;
1191 }
1192}
1193
1195 char escape , bool remove_escapes )
1196{
1197 StrImp::splitIntoFields( in , out , string_view(&sep,1U) , escape , remove_escapes ) ;
1198}
1199
1201{
1202 G::StringArray out ;
1203 StrImp::splitIntoFields( in , out , string_view(&sep,1U) ) ;
1204 return out ;
1205}
1206
1207#ifndef G_LIB_SMALL
1208std::string G::Str::join( string_view sep , const StringMap & map , string_view pre ,
1209 string_view post )
1210{
1211 std::string result ;
1212 int n = 0 ;
1213 for( const auto & map_item : map )
1214 result
1215 .append(sep.data(),(n++)?sep.size():0U)
1216 .append(map_item.first)
1217 .append(pre.data(),pre.size())
1218 .append(map_item.second)
1219 .append(post.data(),post.size()) ;
1220 return result ;
1221}
1222#endif
1223
1224std::string G::Str::join( string_view sep , const StringArray & strings )
1225{
1226 std::string result ;
1227 int n = 0 ;
1228 for( const auto & item : strings )
1229 result
1230 .append(sep.data(),(n++)?sep.size():0U)
1231 .append(item) ;
1232 return result ;
1233}
1234
1237 string_view s7 , string_view s8 , string_view s9 )
1238{
1239 std::string result ;
1240 StrImp::join( sep , result , s1 ) ;
1241 StrImp::join( sep , result , s2 ) ;
1242 StrImp::join( sep , result , s3 ) ;
1243 StrImp::join( sep , result , s4 ) ;
1244 StrImp::join( sep , result , s5 ) ;
1245 StrImp::join( sep , result , s6 ) ;
1246 StrImp::join( sep , result , s7 ) ;
1247 StrImp::join( sep , result , s8 ) ;
1248 StrImp::join( sep , result , s9 ) ;
1249 return result ;
1250}
1251
1252void G::StrImp::join( string_view sep , std::string & result , string_view s )
1253{
1254 if( !result.empty() && !s.empty() )
1255 result.append( sep.data() , sep.size() ) ;
1256 result.append( s.data() , s.size() ) ;
1257}
1258
1260{
1261 StringArray result ;
1262 result.reserve( map.size() ) ;
1263 std::transform( map.begin() , map.end() , std::back_inserter(result) ,
1264 [](const StringMap::value_type & pair){return pair.first;} ) ;
1265 return result ;
1266}
1267
1269{
1270 static constexpr string_view chars_ws = " \t\n\r"_sv ;
1271 static_assert( chars_ws.size() == 4U , "" ) ;
1272 return chars_ws ;
1273}
1274
1275#ifndef G_LIB_SMALL
1277{
1278 return sv_substr( alnum_() , 0U , alnum_().size()-1U ) ;
1279}
1280#endif
1281
1283{
1284 static constexpr string_view chars_alnum_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_"_sv ;
1285 static_assert( chars_alnum_.size() == 26U+10U+26U+1U , "" ) ;
1286 return chars_alnum_ ;
1287}
1288
1289#ifndef G_LIB_SMALL
1291{
1292 static constexpr string_view chars_meta = "~<>[]*$|?\\(){}\"`'&;="_sv ; // bash meta-chars plus "~"
1293 return chars_meta ;
1294}
1295#endif
1296
1297std::string G::Str::head( string_view in , std::size_t pos , string_view default_ )
1298{
1299 return
1300 pos == std::string::npos ?
1301 sv_to_string(default_) :
1302 ( pos == 0U ? std::string() : sv_to_string( pos >= in.size() ? in : in.substr(0U,pos) ) ) ;
1303}
1304
1305std::string G::Str::head( string_view in , string_view sep , bool default_empty )
1306{
1307 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1308 return head( in , pos , default_empty ? string_view() : in ) ;
1309}
1310
1311G::string_view G::Str::headView( string_view in , std::size_t pos , string_view default_ ) noexcept
1312{
1313 return
1314 pos == std::string::npos ?
1315 default_ :
1316 ( pos == 0U ? string_view(in.data(),std::size_t(0U)) : ( pos >= in.size() ? in : sv_substr(in,0U,pos) ) ) ;
1317}
1318
1319G::string_view G::Str::headView( string_view in , string_view sep , bool default_empty ) noexcept
1320{
1321 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1322 return headView( in , pos , default_empty ? string_view(in.data(),std::size_t(0U)) : in ) ;
1323}
1324
1325std::string G::Str::tail( string_view in , std::size_t pos , string_view default_ )
1326{
1327 return
1328 pos == std::string::npos ?
1329 sv_to_string(default_) :
1330 ( (pos+1U) >= in.size() ? std::string() : sv_to_string(in.substr(pos+1U)) ) ;
1331}
1332
1333std::string G::Str::tail( string_view in , string_view sep , bool default_empty )
1334{
1335 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1336 if( pos != std::string::npos ) pos += (sep.size()-1U) ;
1337 return tail( in , pos , default_empty ? string_view() : in ) ;
1338}
1339
1340G::string_view G::Str::tailView( string_view in , std::size_t pos , string_view default_ ) noexcept
1341{
1342 return
1343 pos == std::string::npos ?
1344 default_ :
1345 ( (pos+1U) >= in.size() ? string_view() : sv_substr(in,pos+1U) ) ;
1346}
1347
1348G::string_view G::Str::tailView( string_view in , string_view sep , bool default_empty ) noexcept
1349{
1350 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1351 if( pos != std::string::npos ) pos += (sep.size()-1U) ;
1352 return tailView( in , pos , default_empty ? string_view() : in ) ;
1353}
1354
1355bool G::Str::tailMatch( const std::string & in , string_view tail ) noexcept
1356{
1357 return
1358 tail.empty() ||
1359 ( in.size() >= tail.size() && 0 == in.compare(in.size()-tail.size(),tail.size(),tail.data()) ) ;
1360}
1361
1362bool G::Str::headMatch( const std::string & in , string_view head ) noexcept
1363{
1364 return
1365 head.empty() ||
1366 ( in.size() >= head.size() && 0 == in.compare(0U,head.size(),head.data()) ) ;
1367}
1368
1369std::string G::Str::positive()
1370{
1371 return "yes" ;
1372}
1373
1374std::string G::Str::negative()
1375{
1376 return "no" ;
1377}
1378
1379bool G::Str::isPositive( string_view s_in ) noexcept
1380{
1381 string_view s = trimmedView( s_in , ws() ) ;
1382 return !s.empty() && (
1383 sv_imatch(s,"y"_sv) || sv_imatch(s,"yes"_sv) || sv_imatch(s,"t"_sv) ||
1384 sv_imatch(s,"true"_sv) || sv_imatch(s,"1"_sv) || sv_imatch(s,"on"_sv) ) ;
1385}
1386
1387bool G::Str::isNegative( string_view s_in ) noexcept
1388{
1389 string_view s = trimmedView( s_in , ws() ) ;
1390 return !s.empty() && (
1391 sv_imatch(s,"n"_sv) || sv_imatch(s,"no"_sv) || sv_imatch(s,"f"_sv) ||
1392 sv_imatch(s,"false"_sv) || sv_imatch(s,"0"_sv) || sv_imatch(s,"off"_sv) ) ;
1393}
1394
1396{
1397 return a == b ;
1398}
1399
1400bool G::StrImp::ilessc( char c1 , char c2 ) noexcept
1401{
1402 if( c1 >= 'a' && c1 <= 'z' ) c1 -= '\x20' ;
1403 if( c2 >= 'a' && c2 <= 'z' ) c2 -= '\x20' ;
1404 return c1 < c2 ;
1405}
1406
1408{
1409 return std::lexicographical_compare( a.begin() , a.end() , b.begin() , b.end() , StrImp::ilessc ) ; // noexcept?
1410}
1411
1412bool G::StrImp::imatchc( char c1 , char c2 ) noexcept
1413{
1414 return sv_imatch( string_view(&c1,1U) , string_view(&c2,1U) ) ;
1415}
1416
1417#ifndef G_LIB_SMALL
1418bool G::Str::imatch( char c1 , char c2 ) noexcept
1419{
1420 return StrImp::imatchc( c1 , c2 ) ;
1421}
1422#endif
1423
1425{
1426 return sv_imatch( a , b ) ;
1427}
1428
1429#ifndef G_LIB_SMALL
1430bool G::StrImp::imatch( const std::string & a , const std::string & b )
1431{
1432 return sv_imatch( string_view(a) , string_view(b) ) ;
1433}
1434#endif
1435
1437{
1438 return ifindat( s , key , 0U ) ;
1439}
1440
1441std::size_t G::Str::ifindat( string_view s , string_view key , std::size_t pos )
1442{
1443 if( s.empty() || key.empty() || pos >= s.size() ) return std::string::npos ;
1444 auto p = std::search( s.data()+pos , s.data()+s.size() , key.data() , key.data()+key.size() , StrImp::imatchc ) ; // NOLINT narrowing
1445 return p == s.end() ? std::string::npos : std::distance(s.begin(),p) ;
1446}
1447
1448template <typename T, typename V>
1449T G::StrImp::unique( T in , T end , V repeat , V replacement )
1450{
1451 // (maybe use std::adjacent_find())
1452 T out = in ;
1453 while( in != end )
1454 {
1455 T in_next = in ; ++in_next ;
1456 if( *in == repeat && in_next != end && *in_next == repeat )
1457 {
1458 while( in != end && *in == repeat )
1459 ++in ;
1460 *out++ = replacement ;
1461 }
1462 else
1463 {
1464 *out++ = *in++ ;
1465 }
1466 }
1467 return out ; // new end
1468}
1469
1470std::string G::Str::unique( const std::string & s_in , char c , char r )
1471{
1472 std::string s( s_in ) ;
1473 s.erase( StrImp::unique( s.begin() , s.end() , c , r ) , s.end() ) ;
1474 return s ;
1475}
1476
1477std::string G::Str::unique( const std::string & s , char c )
1478{
1479 return s.find(c) == std::string::npos ? s : unique( s , c , c ) ;
1480}
1481
1482void G::StrImp::strncpy( char * dst , const char * src , std::size_t n ) noexcept
1483{
1484 // (because 'strncpy considered dangerous' analytics)
1485 for( ; n ; n-- , dst++ , src++ )
1486 {
1487 *dst = *src ;
1488 if( *src == '\0' )
1489 break ;
1490 }
1491}
1492
1493errno_t G::Str::strncpy_s( char * dst , std::size_t n_dst , const char * src , std::size_t count ) noexcept
1494{
1495 if( dst == nullptr || n_dst == 0U )
1496 return EINVAL ;
1497
1498 if( src == nullptr )
1499 {
1500 *dst = '\0' ;
1501 return EINVAL ;
1502 }
1503
1504 std::size_t n = std::strlen( src ) ;
1505 if( count != truncate && count < n )
1506 n = count ;
1507
1508 if( count == truncate && n >= n_dst )
1509 {
1510 StrImp::strncpy( dst , src , n_dst ) ;
1511 dst[n_dst-1U] = '\0' ;
1512 return 0 ; // STRUNCATE
1513 }
1514 else if( n >= n_dst )
1515 {
1516 *dst = '\0' ;
1517 return ERANGE ; // dst too small
1518 }
1519 else
1520 {
1521 StrImp::strncpy( dst , src , n ) ;
1522 dst[n] = '\0' ;
1523 return 0 ;
1524 }
1525}
1526
static bool isPrintableAscii(string_view s) noexcept
Returns true if every character is a 7-bit, non-control character (ie.
Definition: gstr.cpp:416
static std::size_t ifind(string_view s, string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
Definition: gstr.cpp:1436
static string_view headView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like head() but returning a view into the input string.
Definition: gstr.cpp:1311
static bool match(string_view, string_view) noexcept
Returns true if the two strings are the same.
Definition: gstr.cpp:1395
static bool replace(std::string &s, string_view from, string_view to, std::size_t *pos_p=nullptr)
A string_view overload.
Definition: gstr.cpp:226
static bool isUShort(string_view s) noexcept
Returns true if the string can be converted into an unsigned short without throwing an exception.
Definition: gstr.cpp:440
static string_view tailView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like tail() but returning a view into the input string.
Definition: gstr.cpp:1340
static unsigned int toUInt(string_view s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:651
static string_view alnum() noexcept
Returns a string of seven-bit alphanumeric characters, ie A-Z, a-z and 0-9.
Definition: gstr.cpp:1276
static void toLower(std::string &s)
Replaces all seven-bit upper-case characters in string 's' by lower-case characters.
Definition: gstr.cpp:822
static bool isNumeric(string_view s, bool allow_minus_sign=false) noexcept
Returns true if every character is a decimal digit.
Definition: gstr.cpp:403
static short toShort(string_view s)
Converts string 's' to a short.
Definition: gstr.cpp:603
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:1259
static float toFloat(const std::string &s)
Converts string 's' to a float.
Definition: gstr.cpp:520
static std::istream & readLine(std::istream &stream, std::string &result, string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
Definition: gstr.cpp:961
static int toInt(string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:541
static bool isHex(string_view s) noexcept
Returns true if every character is a hexadecimal digit.
Definition: gstr.cpp:410
static std::string positive()
Returns a default positive string. See isPositive().
Definition: gstr.cpp:1369
static unsigned int replaceAll(std::string &s, string_view from, string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Definition: gstr.cpp:247
static bool iless(string_view, string_view) noexcept
Returns true if the first string is lexicographically less than the first, after seven-bit lower-case...
Definition: gstr.cpp:1407
static unsigned short toUShort(string_view s, Limited)
Converts string 's' to an unsigned short.
Definition: gstr.cpp:753
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
Definition: gstr.cpp:1418
static bool tailMatch(const std::string &in, string_view ending) noexcept
Returns true if the string has the given ending (or the given ending is empty).
Definition: gstr.cpp:1355
static std::size_t ifindat(string_view s, string_view key, std::size_t pos)
Returns the position of the key in 's' at of after position 'pos' using a seven-bit case-insensitive ...
Definition: gstr.cpp:1441
static bool isPrintable(string_view s) noexcept
Returns true if every character is a non-control character (ie.
Definition: gstr.cpp:421
static std::string unescaped(const std::string &s)
Returns the unescape()d version of s.
Definition: gstr.cpp:203
static double toDouble(const std::string &s)
Converts string 's' to a double.
Definition: gstr.cpp:498
static std::string replaced(const std::string &s, char from, char to)
Returns the string 's' with all occurrences of 'from' replaced by 'to'.
Definition: gstr.cpp:255
static std::string fromBool(bool b)
Converts boolean 'b' to a string.
Definition: gstr.cpp:466
static std::string & trimLeft(std::string &s, string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:288
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:1122
static std::string toPrintableAscii(const std::string &in, char escape='\\')
Returns a 7-bit printable representation of the given input string.
Definition: gstr.cpp:934
static string_view trimmedView(string_view s, string_view ws) noexcept
Returns a trim()med view of the input view.
Definition: gstr.cpp:354
static std::string unique(const std::string &s, char c, char r)
Returns a string with repeated 'c' characters replaced by one 'r' character.
Definition: gstr.cpp:1470
static long toLong(string_view s)
Converts string 's' to a long.
Definition: gstr.cpp:570
static string_view alnum_() noexcept
Returns alnum() with an additional trailing underscore character.
Definition: gstr.cpp:1282
static string_view fromULongToHex(unsigned long, char *out) noexcept
Low-level conversion from an unsigned value to a lower-case hex string with no leading zeros.
Definition: gstr.cpp:790
static string_view meta() noexcept
Returns a list of shell meta-characters with a tilde as the first character.
Definition: gstr.cpp:1290
static std::string join(string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1224
static bool isULong(string_view s) noexcept
Returns true if the string can be converted into an unsigned long without throwing an exception.
Definition: gstr.cpp:457
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string. See also only().
Definition: gstr.cpp:262
static std::string lower(string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
Definition: gstr.cpp:827
static std::string readLineFrom(std::istream &stream, string_view eol={})
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:954
static std::string negative()
Returns a default negative string. See isNegative().
Definition: gstr.cpp:1374
static std::string only(string_view allow_chars, string_view s)
Returns the 's' with all occurrences of the characters not appearing in the first string deleted.
Definition: gstr.cpp:276
static bool isUInt(string_view s) noexcept
Returns true if the string can be converted into an unsigned integer without throwing an exception.
Definition: gstr.cpp:449
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:313
static std::string removedAll(const std::string &, char)
Removes all occurrences of the character from the string and returns the result.
Definition: gstr.cpp:268
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , string_view ws = Str::ws() , string_view nbws = Str::ws() )
Dequotes a string by removing unescaped quotes and escaping quoted whitespace, so "qq-aaa-esc-qq-bbb-...
Definition: gstr.cpp:128
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
Definition: gstr.cpp:916
static bool isPositive(string_view) noexcept
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1379
static bool isSimple(string_view s) noexcept
Returns true if every character is alphanumeric or "-" or "_".
Definition: gstr.cpp:426
static bool isNegative(string_view) noexcept
Returns true if the string has a negative meaning, such as "0", "false", "no".
Definition: gstr.cpp:1387
static std::string upper(string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
Definition: gstr.cpp:839
static void unescape(std::string &s, char c_escape, string_view specials_in, string_view specials_out)
Unescapes the string by replacing e-e with e, e-special-in with special-out, and e-other with other.
Definition: gstr.cpp:180
static bool isInt(string_view s) noexcept
Returns true if the string can be converted into an integer without throwing an exception.
Definition: gstr.cpp:431
static void toUpper(std::string &s)
Replaces all seven-bit lower-case characters in string 's' by upper-case characters.
Definition: gstr.cpp:834
static unsigned long toULong(string_view s, Limited)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:672
static std::string fromDouble(double d)
Converts double 'd' to a string.
Definition: gstr.cpp:473
static std::string tail(string_view in, std::size_t pos, string_view default_={})
Returns the last part of the string after the given position.
Definition: gstr.cpp:1325
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
static void splitIntoFields(string_view in, StringArray &out, char sep, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:1194
static string_view fromULongLongToHex(unsigned long long, char *out) noexcept
Low-level conversion from an unsigned value to a lower-case hex string with no leading zeros.
Definition: gstr.cpp:797
static errno_t strncpy_s(char *dst, std::size_t n_dst, const char *src, std::size_t count) noexcept
Does the same as windows strncpy_s().
Definition: gstr.cpp:1493
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:343
static bool toBool(string_view s)
Converts string 's' to a bool.
Definition: gstr.cpp:484
static bool headMatch(const std::string &in, string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1362
static string_view trimLeftView(string_view, string_view ws, std::size_t limit=0U) noexcept
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:300
static string_view ws() noexcept
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1268
static void escape(std::string &s, char c_escape, string_view specials_in, string_view specials_out)
Prefixes each occurrence of one of the special-in characters with the escape character and its corres...
Definition: gstr.cpp:100
static string_view trimRightView(string_view sv, string_view ws, std::size_t limit=0U) noexcept
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:325
static std::string escaped(string_view, char c_escape, string_view specials_in, string_view specials_out)
Returns the escape()d string.
Definition: gstr.cpp:92
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:338
A class like c++17's std::string_view.
Definition: gstringview.h:51
Low-level classes.
Definition: garg.h:30
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstringmap.h:30
STL namespace.
Overload discrimiator for G::Str::toUWhatever() indicating hexadecimal strings.
Definition: gstr.h:59
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.
Definition: gstr.h:56