-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmy_strutils.h
602 lines (520 loc) · 22.4 KB
/
my_strutils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
//
// Copyright 2015-2022 by Kevin L. Goodwin [[email protected]]; All rights reserved
//
// This file is part of K.
//
// K is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// K is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along
// with K. If not, see <http://www.gnu.org/licenses/>.
//
#pragma once
#include <algorithm>
#include <cstdarg>
#include <cstring>
#include <tuple>
#include "my_types.h"
#include "ed_mem.h" // for Strdup()
extern void chkdVsnprintf( PChar buf, size_t bufBytes, PCChar format, va_list val );
#define use_vsnprintf chkdVsnprintf
#ifndef WL
// porting abbreviation tool
#if defined(_WIN32)
# define WL(ww,ll) ww
#else
# define WL(ww,ll) ll
#endif
#endif
// PChar vs PCChar
#define TF_Ptr template<typename Ptr>
#if defined(_WIN32) // Microsoft C Compiler for Win32?
// use safeSprintf instead of sprintf, _snprintf or snprintf!
// #define snprintf _snprintf
// #define vsnprintf _vsnprintf
// NB: the above (MSVC RTL *printf fxns) use DIFFERENT format specifiers than MinGW in gnu_printf format mode; see attr_format.h
// therefore restoring support for building with MSVC will 100% run afoul of current code using incompatible format specifiers
#endif
// snprintf_full: use to write multiple formatted strings to a fixed-len buffer
// without performing repeated strlen's.
// returns NZ if buffer was partially (including _not_) written, else Z
extern int snprintf_full( char **ppBuf, size_t *pBufBytesRemaining, PCChar fmt, ... ) ATTR_FORMAT(3, 4);
//--------------------------------------------------------------------------------------------
constexpr char chNUL = '\0';
constexpr char HTAB = '\t';
constexpr char chESC = '\\';
constexpr char chQuot1 = '\'';
constexpr char chQuot2 = '"';
constexpr char chBackTick = '`';
constexpr char chLSQ = '[';
constexpr char chRSQ = ']';
#define SPCTAB " \t"
constexpr int MIN_TAB_WIDTH = 1;
constexpr int MAX_TAB_WIDTH = 8; // we don't support > MAX_TAB_WIDTH cols per tab!
constexpr char TICK = 0x27, BACKTICK = 0x60;
// blank vs. space:
// http://www.cplusplus.com/reference/cctype/isblank/
//
// A blank character is a space character used to separate words within a line of text.
//
// The standard "C" locale considers blank characters the tab character ('\t') and the space character (' ').
//
// http://www.cplusplus.com/reference/cctype/isspace/
//
// For the "C" locale, white-space characters are any of:
// ' ' (0x20) space (SPC)
// '\t' (0x09) horizontal tab (TAB)
// '\n' (0x0a) newline (LF)
// '\v' (0x0b) vertical tab (VT)
// '\f' (0x0c) feed (FF)
// '\r' (0x0d) carriage return (CR)
// char predicates; return int to match http://en.cppreference.com/w/cpp/header/cctype is functions
STIL int notBlank ( int ch ) { return !isblank( ch ); }
extern int isWordChar ( int ch );
STIL int notWordChar ( int ch ) { return !isWordChar( ch ); }
extern int isHJChar ( int ch );
STIL int notHJChar ( int ch ) { return !isHJChar( ch ); }
STIL int isbdigit ( int ch ) { return ch == '0' || ch == '1'; }
STIL int isQuoteEscCh( int ch ) { return chESC==ch; }
// predicate + extract; return exact char matching a char class (or chNUL)
STIL constexpr char isQuoteCh ( char inCh ) { return chQuot2==inCh || chQuot1==inCh ? inCh : chNUL; }
// many stref (and std::string) methods return "index or npos" (the latter
// indicating a "not found" condition); I have chosen to signify a "not found"
// condition with the index equivalent of the cend() iterator value:
STIL sridx nposToEnd( stref str, sridx from ) { return from == eosr ? str.length() : from; }
STIL bool atEnd ( stref str, sridx idx ) { return idx == str.length(); }
STIL bool atEnd ( const std::string &str, sridx idx ) { return idx == str.length(); }
// INNER stringref index to OUTER stringref index
STIL sridx isri2osri( stref osr, stref isr, sridx isri ) { return isri + (isr.data()-osr.data()); }
template < typename cont_inst, typename Pred >
typename cont_inst::size_type ToNextOrEnd( Pred pred, cont_inst src, typename cont_inst::size_type start ) {
if( start < src.length() ) {
for( auto it( src.cbegin() + start ) ; it != src.cend() ; ++it ) {
if( pred( *it ) ) {
return std::distance( src.cbegin(), it );
}
}
}
return std::distance( src.cbegin(), src.cend() );
}
#define SKIP_QUOTED_STR( quoteCh, it, end, EOS_LBL ) \
if( quoteCh=isQuoteCh( *it ) ) { \
for( ++it ; it != end ; ++it ) { \
if( *it == quoteCh ) { \
break; \
} \
if( isQuoteEscCh( *it ) && ++it == end ) { \
goto EOS_LBL; /* [it..end) ends before unquote? */ \
} \
} \
}
template < typename cont_inst, typename Pred >
typename cont_inst::size_type ToNextOrEndSkipQuoted( Pred pred, cont_inst src, typename cont_inst::size_type start ) {
if( start < src.length() ) {
char quoteCh( chNUL );
for( auto it( src.cbegin() + start ) ; it != src.cend() ; ++it ) {
if( pred( *it ) ) {
return std::distance( src.cbegin(), it );
}
SKIP_QUOTED_STR( quoteCh, it, src.cend(), NO_MATCH )
}
}
NO_MATCH:
return std::distance( src.cbegin(), src.cend() );
}
template < typename cont_inst >
typename cont_inst::size_type ToNextOrEndSkipQuoted( const int key, cont_inst src, typename cont_inst::size_type start ) {
if( start < src.length() ) {
char quoteCh( chNUL );
for( auto it( src.cbegin() + start ) ; it != src.cend() ; ++it ) {
if( key == *it ) {
return std::distance( src.cbegin(), it );
}
SKIP_QUOTED_STR( quoteCh, it, src.cend(), NO_MATCH )
}
}
NO_MATCH:
return std::distance( src.cbegin(), src.cend() );
}
//#######################################################################################
extern int strcmp4humans( PCChar s1, PCChar s2 );
extern int strnicmp_LenOfFirstStr( stref s1, stref s2 );
extern int strncmp_LenOfFirstStr( stref s1, stref s2 );
extern bool StrSpnSignedInt( PCChar pszString );
extern std::tuple<int, uintmax_t, stref, UI> conv_u( stref sr, UI numberBase=10 );
extern PCChar Add_es( int count );
extern PCChar Add_s( int count );
extern int FlipCase( int ch );
extern int DoubleBackslashes( PChar pDest, size_t sizeofDest, PCChar pSrc );
extern void StrUnDoubleBackslashes( PChar pszString );
STIL bool StrContainsTabs( stref src ) { return ToBOOL(memchr( src.data(), HTAB, src.length() )); }
STIL bool eq( stref s1, stref s2 ) {
return s1 == s2;
}
STATIC_FXN bool eqi_( char ll, char rr ) {
return std::tolower( ll ) == std::tolower( rr );
}
STIL bool eqi( stref s1, stref s2 ) {
if( s1.length() != s2.length() ) {
return false;
}
for( sridx ix( 0 ); ix < s1.length() ; ++ix ) {
if( !eqi_( s1[ix], s2[ix] ) ) {
return false;
}
}
return true;
}
template < typename comparable > int cmp( comparable c1, comparable c2 ) { return (c1 > c2) - (c1 < c2); }
STIL int cmpi( int c1, int c2 ) { // impl w/highly ASCII-centric optzn taken from http://www.geeksforgeeks.org/write-your-own-strcmp-which-ignores-cases/
const auto cd( 'a'-'A' );
if( c1 == c2 || (c1 ^ cd) == c2 ) { return 0; }
return (c1 | cd) < (c2 | cd) ? -1 : +1;
}
STIL int cmp( stref s1, stref s2 ) {
const auto cmplen( std::min( s1.length(), s2.length() ) );
for( sridx ix( 0 ); ix < cmplen ; ++ix ) {
const auto rv( cmp( s1[ix], s2[ix] ) );
if( rv != 0 ) {
return rv;
}
}
if( s1.length() == s2.length() ) { return 0; }
return s1.length() < s2.length() ? -1 : +1;
}
STIL int cmpi( stref s1, stref s2 ) {
const auto cmplen( std::min( s1.length(), s2.length() ) );
for( sridx ix( 0 ); ix < cmplen ; ++ix ) {
const auto rv( cmpi( s1[ix], s2[ix] ) );
if( rv != 0 ) {
return rv;
}
}
if( s1.length() == s2.length() ) { return 0; }
return s1.length() < s2.length() ? -1 : +1;
}
// the Blank family...
STIL bool IsStringBlank( stref src ) { // note that empty strings are Blank strings!
return std::all_of( src.cbegin(), src.cend(), isblank );
}
//--------------------------------------------------------------------------------
// to avoid signed/unsigned mismatch warnings:
#if 0
STIL int Strlen( PCChar pS ) { return int( strlen(pS) ); }
#else
STIL constexpr int Strlen( PCChar pc ) { // this MAY be faster than RTL version (which typically uses rep scasb); at least it uses stdcall ...
const auto start(pc);
for( ; *pc ; ++pc ) {
}
return pc - start;
}
#endif
TF_Ptr STIL Ptr Eos( Ptr psz ) { return psz + Strlen( psz ); }
//--------------------------------------------------------------------------------
extern sridx scpy( PChar dest, size_t sizeof_dest, stref src );
extern sridx scat( PChar dest, size_t sizeof_dest, stref src, size_t destLen=0 );
#define bcpy( d, s ) scpy( BSOB(d), s )
#define bcat( l, d, s ) scat( BSOB(d), s, l )
extern PChar safeSprintf( PChar dest, size_t sizeofDest, PCChar format, ... ) ATTR_FORMAT(3,4);
STIL std::string & PadRight( std::string &inout, sridx width, char padCh=' ' ) {
if( width > inout.length() ) { // trail-pad with spaces to width
inout.append( width - inout.length(), padCh );
}
return inout;
}
//--------------------------------------------------------------------------------
//
// We have const and non-const versions of each function, so that code can be
// const-correct in all cases. This used to be done using templates, however
// when not-directly-PChar/-PCChar convertible parameters (e.g. a Linebuf) were
// passed, they would NOT automtically convert to PChar or PCChar, and we would
// get a crash akin to the ones seen when a Linebuf is passed as a vararg.
//
// 20061021 kgoodwin
//
//-----------------
struct CharMap {
char disp[257];
bool is [256];
};
extern CharMap g_WordChars;
extern CharMap g_HLJChars;
#if !defined(_WIN32)
extern PChar _strupr( PChar buf );
extern PChar _strlwr( PChar buf );
#endif
extern sridx consec_xdigits( stref sr );
extern sridx consec_bdigits( stref sr );
STIL int Strnicmp( PCChar string1, PCChar string2, size_t count ) { return WL( _strnicmp, strncasecmp )( string1, string2, count ); }
STIL int Stricmp ( PCChar string1, PCChar string2 ) { return WL( _strcmpi , strcasecmp )( string1, string2 ); }
STIL int Strcmp ( PCChar string1, PCChar string2 ) { return strcmp ( string1, string2 ); }
STIL int rb_strcmpi( PCVoid p1, PCVoid p2 ) { return Stricmp( static_cast<PCChar>(p1), static_cast<PCChar>(p2) ); }
extern size_t Strnspn ( PCChar str1, PCChar eos1, PCChar needle );
extern size_t Strncspn ( PCChar str1, PCChar eos1, PCChar needle );
TF_Ptr STIL Ptr StrToNextOrEos( Ptr pszToSearch, PCChar pszToSearchFor ) { return pszToSearch + Strncspn( pszToSearch, nullptr, pszToSearchFor ); }
TF_Ptr STIL Ptr StrPastAny( Ptr pszToSearch, PCChar pszToSearchFor ) { return pszToSearch + Strnspn ( pszToSearch, nullptr, pszToSearchFor ); }
TF_Ptr STIL Ptr StrPastAnyBlanks( Ptr pszToSearch ) { return StrPastAny( pszToSearch, SPCTAB ); }
TF_Ptr STIL Ptr StrToNextBlankOrEos( Ptr pszToSearch ) { return StrToNextOrEos( pszToSearch, SPCTAB ); }
// these are extern vs. inline because the requisite predicates are not public
extern sridx FirstNonBlankOrEnd( stref src, sridx start=0 );
extern sridx FirstBlankOrEnd ( stref src, sridx start=0 );
extern sridx FirstNonWordOrEnd( stref src, sridx start=0 );
extern sridx IdxFirstHJCh ( stref src, sridx start=0 );
//-----------------
extern int Quot2_strcspn( PCChar pszToSearch, PCChar pszToSearchFor );
TF_Ptr STIL Ptr Quot2StrToNextOrEos( Ptr pszToSearch, PCChar pszToSearchFor ) { return pszToSearch + Quot2_strcspn( pszToSearch, pszToSearchFor ); }
TF_Ptr STIL Ptr StrToNextMacroTermOrEos( Ptr pszToSearch ) { return Quot2StrToNextOrEos( pszToSearch, "#" ); }
template<typename strlval>
STIL void trim( strlval &inout ) { // remove leading and trailing whitespace
const auto ox( inout.find_first_not_of( SPCTAB ) );
if( ox != eosr ) {
inout.remove_prefix( ox );
}
const auto ix_last_non_white( inout.find_last_not_of( SPCTAB ) );
const auto dix( inout.length() );
if( ix_last_non_white != dix-1 ) { // any trailing blanks at all?
// ix_last_non_white==eosr means ALL are blanks
const auto rlen( ix_last_non_white==eosr ? dix : dix-1 - ix_last_non_white );
inout.remove_suffix( rlen );
}
}
template<typename strlval>
STIL constexpr bool isQuotedStr( const strlval &in ) { // remove any outer quotes, assuming input has been trimmed
return isQuoteCh( in[0] ) && in[0]==in[in.length()-1];
}
STIL constexpr stref unquote( stref in ) { // remove any outer quotes, assuming input has been trimmed
if( isQuotedStr( in ) ) {
auto out = in;
out.remove_suffix( 1 );
out.remove_prefix( 1 );
return out;
}
return in;
}
template<typename strlval>
void constexpr rmv_trail_blanks( strlval &inout ) {
while( !inout.empty() && isblank( inout.back() ) ) {
inout.pop_back();
}
}
// exists because std::string_view lacks pop_back()
// not a template because std::string lacks remove_suffix( n )
STIL constexpr void rmv_trail_blanks( stref &inout ) {
auto trailSpcs( 0u );
for( auto it( inout.crbegin() ) ; it != inout.crend() && isblank( *it ) ; ++it ) {
++trailSpcs;
}
inout.remove_suffix( trailSpcs );
}
STIL bool chomp( std::string &st ) {
// return: whether at-entry st was not empty; matters because an (at-entry) legit blank line (containing
// only newline chars which this fxn removes) and an empty string will both be returned as st.empty()
if( st.empty() ) {
return false;
}
const auto ixlast( st.length() - 1 );
if( st[ixlast] == 0x0A ) {
st.pop_back();
if( ixlast > 0 && st[ixlast-1] == 0x0D ) {
st.pop_back();
}
}
return true;
}
STIL sridx FirstAlphaOrEnd( stref src, sridx start=0 ) { return ToNextOrEnd( isalpha, src, start ); }
STIL sridx FirstDigitOrEnd( stref src, sridx start=0 ) { return ToNextOrEnd( isdigit, src, start ); }
//#######################################################################################
class lineIterator {
stref d_remainder;
public:
lineIterator( stref remainder ) : d_remainder( remainder ) {}
bool empty() const { return d_remainder.empty(); }
stref next();
};
template <int elements> class FmtStr {
char b[ elements ];
FmtStr() = delete; // no dflt ctor
public:
FmtStr( PCChar format, ... ) ATTR_FORMAT(2,3) { // this is ambiguous vs 'FixedCharArray( PCChar src )'
va_list val;
va_start(val, format);
use_vsnprintf( b, sizeof(b), format, val );
va_end(val);
}
operator PCChar() const { return b; }
int Len() const { return Strlen( b ); }
PCChar c_str() const { return b; }
};
template <size_t elements>
class FixedCharArray {
char d_buf[ elements ];
public:
FixedCharArray() { d_buf[0] = chNUL; }
FixedCharArray( stref src ) { bcpy( d_buf, src ); }
stref sr() const { return stref( d_buf, std::min( elements-1, Strlen( d_buf ) ) ); }
PCChar c_str() const { return d_buf; }
void Vsprintf( PCChar format, va_list val ) { // yes, part of the PUBLIC interface!
use_vsnprintf( BSOB(d_buf), format, val );
}
PCChar Sprintf( PCChar format, ... ) ATTR_FORMAT(2,3) {
va_list args; va_start(args, format);
Vsprintf( format, args );
va_end(args);
return d_buf;
}
PChar SprintfCat( PCChar format, ... ) ATTR_FORMAT(2,3) {
const auto len( Strlen( d_buf ) );
if( len < sizeof( d_buf ) - 1 ) {
va_list args; va_start(args, format);
use_vsnprintf( d_buf+len, sizeof( d_buf )-len, format, args );
va_end(args);
}
return d_buf;
}
private:
// NO_ASGN_OPR(FixedCharArray);
};
template <sridx elements>
class Catbuf { // purpose: allocate automatic (on-stack) buffers that are safely and efficiently cat- and re-copy-able
char d_buf[ elements ];
public:
class wref { // writeable reference to a trailing segment of d_buf
PChar d_bp;
sridx d_len;
public:
// only public interface: copy stref into (trailing segment of d_buf described by) this and
// return a new wref describing the trailing empty segment of d_buf that remains
wref cpy( stref src ) const {
const auto newLen( scpy( d_bp, d_len, src ) ); // index of first un-scpy-written char in d_buf
return wref{ d_bp + newLen, d_len - newLen -1 }; // -1 for ASCIZ len to sizeof conversion
}
// following (bp(), len(), wref()) are FOR Catbuf IMPLEMENTATION (and debug) USE ONLY!
PCChar bp() const { return d_bp ; } // ONLY for stref sr( const wref& wr )
sridx len() const { return d_len; } // ONLY for stref sr( const wref& wr )
wref( PChar bp_, sridx len ) : d_bp( bp_ ), d_len( len ) {}
};
Catbuf() { d_buf[0] = chNUL; } // paranoia
wref Wref() { return wref{ d_buf, elements }; }
stref sr( const wref& wr ) const { return stref( d_buf, (wr.bp() - d_buf) ); }
stref sr() const { return stref( d_buf, std::min( elements-1, Strlen( d_buf ) ) ); }
PCChar c_str() const { return d_buf; }
// Test code:
// Catbuf<5> tbuf;
// auto t0( tbuf.Wref() );
// const auto t1( t0.cpy( "filesettings.ftype_map." ) ); DBG( "%s: t0 = '%s' t1='%" PR_BSR "'", __func__, tbuf.c_str(), BSR(tbuf.sr(t1)) );
// auto t2( t1.cpy( stref(d_key) ) ); DBG( "%s: t1 = '%s' t2='%" PR_BSR "'", __func__, tbuf.c_str(), BSR(tbuf.sr(t2)) );
};
#include "filename.h"
#include <stdlib.h>
//======================================================================================================
//
// partial generalization of an old C'ism, the sequence of strings exemplified
// by the C RTL environment block: each string is 0-terminated, end of sequence
// is 2 0-terminators in a row.
//
// Useful in cases where the inability to store a string (due to "out of space"
// in the fixed-sized buffer) is not catastrophic, but general case of storing a
// sequence of strings w/ NO heap interaction is appreciated.
//
// sample that adds strings to the sequence
//
// Reset(); // clear the sequence
// CPCChar wuc = AddKey( snew, snewEos ); // add a string and rtn ptr to it
// if( !wuc ) { // NULL return is "string doesn't fit"
// DBG_HL_EVENT && DBG(__func__" toolong");
// return false;
// }
//
// if( (lin >= 0) && (d_wucLen > 2) && 0==strnicmp_LenOfFirstStr( "0x", wuc ) ) {
// const int xrun( consec_xdigits( wuc+2 ) );
// if( (d_wucLen==10) && (8==xrun) ) {
// PCChar key = AddKey( wuc+2 );
// }
//
// sample that scans the strings in sequence
//
// for( PCChar pC=Strings() ; *pC ; ) {
// const int len( Strlen( pC ) );
// CPCChar afind = strstr( pCh, pC );
// if( afind && (!found || (afind < found)) ) { found = afind; mlen = len; }
// pC += len+1;
// }
//
// 20110612 kgoodwin
//
template <int elements>
class StringsBuf {
char d_buf[elements];
PChar d_nxtS;
public:
StringsBuf() {
clear();
}
PCChar data() const { return d_buf; }
bool find( stref st ) const {
for( auto pNeedle( d_buf ) ; *pNeedle ; ) {
const stref needle( pNeedle );
if( needle == st ) { return true; }
pNeedle += needle.length() + 1;
}
return false;
}
bool empty() const { return d_buf[0] == chNUL; }
void clear() {
d_buf[0] = chNUL;
d_buf[1] = chNUL; // not ABSOLUTELY necessary, but cheap insurance...
d_nxtS = d_buf;
}
PCChar AddString( stref sr ) {
const auto len( sr.length() );
if( len + 2 > sizeof(d_buf) - (d_nxtS-d_buf) ) {
return "";
}
auto rv( d_nxtS );
memcpy( d_nxtS, sr.data(), len );
d_nxtS += len;
*d_nxtS++ = chNUL;
*d_nxtS = chNUL;
return rv;
}
};
//--------------------------------------------------------------------------------------------
#define DBG_DiceableString 0
#if DBG_DiceableString
#include "my_log.h"
#endif
class DiceableString {
// a heap string containing an ascizz string (an ASCII string with TWO (2) trailing NUL chars)
protected:
PChar d_heapString;
public:
DiceableString( stref src )
: d_heapString( Strdup( src, 1 ) ) // tricky: d_heapString is created with 2 trailing NULs
{}
void DBG() const;
virtual ~DiceableString() { Free0( d_heapString ); }
#if DBG_DiceableString
PCChar GetNext_( PCChar cur ) const {
#else
PCChar GetNext( PCChar cur ) const {
#endif
if( !cur ) return *d_heapString ? d_heapString : nullptr;
const auto rv( Eos( cur ) + 1 );
return *rv ? rv : nullptr;
}
#if DBG_DiceableString
PCChar GetNext( PCChar cur ) const {
const auto rv( GetNext_( cur ) );
::DBG( "rv=%s|", rv );
return rv;
}
#endif
};
//======================================================================================================