File indexing completed on 2026-04-09 07:49:48
0001 #pragma once
0002
0003 #include <string>
0004 #include <vector>
0005 #include <cstring>
0006 #include <sstream>
0007 #include <fstream>
0008 #include <iostream>
0009 #include <iomanip>
0010 #include <cassert>
0011 #include <algorithm>
0012 #include <csignal>
0013
0014 #include <charconv> // std::from_chars
0015 #include <type_traits>
0016
0017
0018 struct sstr
0019 {
0020 enum { MATCH_ALL, MATCH_START, MATCH_END } ;
0021
0022 static void Write(const char* path, const char* txt );
0023
0024 static bool Match( const char* s, const char* q, bool starting=true );
0025 static bool Match_( const char* s, const char* q, int mode);
0026 static bool MatchAll( const char* s, const char* q);
0027 static bool MatchStart( const char* s, const char* q);
0028 static bool StartsWith( const char* s, const char* q);
0029 static bool MatchEnd( const char* s, const char* q);
0030 static bool EndsWith( const char* s, const char* q);
0031
0032 static bool StartsWithElem(const char* s, const char* qq, char delim=',' );
0033 static bool StartsWithElem(const char* s, std::vector<std::string>& qq );
0034
0035
0036 static constexpr const char* AZaz = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
0037 static bool StartsWithLetterAZaz(const char* q );
0038 static bool Contains( const char* s_ , const char* q_);
0039
0040 static const char* TrimLeading(const char* s);
0041 static const char* TrimTrailing(const char* s);
0042 static const char* Trim(const char* s);
0043
0044 static std::string TrimString(const std::string& str, const std::string& whitespace=" \t" );
0045
0046 static bool HasTail(const std::vector<std::string>& names, const char* end="0x");
0047 static size_t CountTail(const std::vector<std::string>& names, const char* end="0x");
0048 static bool HasTail(const std::string& name, const char* end="0x");
0049
0050 static std::string StripTail(const std::string& name, const char* end="0x");
0051 static std::string StripComment(const std::string& name);
0052
0053 static std::string StripTail(const char* name, const char* end="0x");
0054 static void StripTail( std::vector<std::string>& dst, const std::vector<std::string>& src, const char* end="0x");
0055 static void StripTail_Unique(std::vector<std::string>& dst, const std::vector<std::string>& src, const char* end="0x");
0056 static std::string DescKeySrc(const std::vector<std::string>& key, const std::vector<std::string>& src );
0057
0058 static std::string RemoveSpaces(const char* s);
0059 static std::string Replace(const char* s, char q, char r);
0060
0061
0062
0063 static const char* ReplaceChars(const char* str, const char* repl, char to );
0064 static std::string ReplaceEnd_( const char* s, const char* q, const char* r );
0065 static const char* ReplaceEnd( const char* s, const char* q, const char* r );
0066
0067 static void PrefixSuffixParse(std::vector<std::string>& elem, const char* prefix, const char* suffix, const char* lines);
0068 static void Split( const char* str, char delim, std::vector<std::string>& elem );
0069 static void SplitTrim( const char* str, char delim, std::vector<std::string>& elem );
0070 static void SplitTrimSuppress( const char* str, char delim, std::vector<std::string>& elem );
0071
0072 static int ekv_split( std::vector<std::pair<std::string, std::string> > & ekv, const char* line_, char edelim, char kvdelim) ;
0073
0074
0075 static void Chop( std::pair<std::string, std::string>& head__tail, const char* delim, const char* str );
0076 static void chop( char** head, char** tail, const char* delim, const char* str );
0077 static bool prefix_suffix( char** pfx, char** sfx, const char* start_sfx, const char* str );
0078
0079
0080
0081 template<typename T>
0082 static int split(std::vector<T>& elem, const char* str, char delim );
0083
0084 static int split_count(const char* str, char delim );
0085 static bool looks_like_list(const char* str, char delim, int len_min=1, int len_max=4 );
0086
0087
0088
0089 template<typename T>
0090 static std::string desc( const std::vector<T>& elem );
0091
0092
0093 template<typename ... Args>
0094 static std::string Format_( const char* fmt, Args ... args );
0095
0096 template<typename ... Args>
0097 static const char* Format( const char* fmt, Args ... args );
0098
0099
0100 static std::string FormatIndexDefault_( int idx, const char* hdr=nullptr );
0101 static std::string FormatIndex_( int idx, char prefix, int wid, const char* hdr );
0102 static const char* FormatIndex( int idx, char prefix, int wid, const char* hdr );
0103
0104
0105
0106
0107 template<typename ... Args>
0108 static std::string Join( const char* delim, Args ... args );
0109
0110 template<typename ... Args>
0111 static std::string Concat_( Args ... args );
0112
0113 template<typename ... Args>
0114 static const char* Concat( Args ... args );
0115
0116
0117
0118 static bool Blank(const char* s );
0119 static bool All(const char* s, char q );
0120 static unsigned Count(const char* s, char q );
0121
0122 template<typename T>
0123 static T To(const char* arg) ;
0124
0125 static int AsInt(const char* arg, int fallback=-1 ) ;
0126 static const char* ParseStringIntInt( const char* triplet, int& y, int& z, char delim=':' );
0127
0128 template<typename T>
0129 static void ParsePair( const char* txt, T& x, T& y, char delim=':' );
0130
0131 static bool IsInteger(const char* str);
0132 static bool IsWhitespace(const std::string& s );
0133
0134 static bool isdigit_(char c );
0135 static bool isalnum_(char c );
0136 static bool isupper_(char c );
0137 static bool islower_(char c );
0138
0139
0140 template<typename T>
0141 static T ParseIntSpec( const char* spec, T& scale );
0142
0143 template<typename T>
0144 static T ParseInt( const char* spec );
0145
0146 template<typename T>
0147 static void ParseIntSpecList( std::vector<T>& ii, const char* spec, char delim=',' );
0148
0149 template<typename T>
0150 static void ParseScale( const char* spec, T& scale );
0151
0152 template<typename T>
0153 static std::vector<T>* ParseIntSpecList( const char* spec, char delim=',' ) ;
0154
0155
0156 static void truncated_copy( char* dst, const char* src, int dst_size );
0157 static void Extract( std::vector<long>& vals, const char* s );
0158
0159 template <typename T>
0160 static void Extract_(std::vector<T>& vals, const char* s);
0161
0162
0163 static long ExtractLong( const char* s, long fallback );
0164 static size_t ExtractSize( const char* s, size_t fallback );
0165
0166
0167 static bool LooksLikePath(const char* arg);
0168 static void LoadList(const char* arg, std::vector<std::string>& lines, char delim );
0169 static std::vector<std::string>* LoadList( const char* arg, char delim );
0170
0171 };
0172
0173 inline void sstr::Write(const char* path, const char* txt )
0174 {
0175 std::ofstream fp(path);
0176 fp << txt ;
0177 }
0178
0179 inline bool sstr::Match( const char* s, const char* q, bool starting )
0180 {
0181 return starting ? MatchStart(s, q) : MatchAll(s, q) ;
0182 }
0183
0184 inline bool sstr::Match_( const char* s, const char* q, int mode )
0185 {
0186 bool ret = false ;
0187 switch(mode)
0188 {
0189 case MATCH_ALL: ret = MatchAll( s, q) ; break ;
0190 case MATCH_START: ret = MatchStart(s, q) ; break ;
0191 case MATCH_END: ret = MatchEnd( s, q) ; break ;
0192 }
0193 return ret ;
0194 }
0195
0196 inline bool sstr::MatchAll( const char* s, const char* q)
0197 {
0198 return s && q && strcmp(s, q) == 0 ;
0199 }
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213 inline bool sstr::MatchStart( const char* s, const char* q)
0214 {
0215 return s && q && strlen(q) <= strlen(s) && strncmp(s, q, strlen(q)) == 0 ;
0216 }
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233 inline bool sstr::StartsWith( const char* s, const char* q)
0234 {
0235 return s && q && strlen(q) <= strlen(s) && strncmp(s, q, strlen(q)) == 0 ;
0236 }
0237
0238
0239 inline bool sstr::MatchEnd( const char* s, const char* q)
0240 {
0241 int pos = strlen(s) - strlen(q) ;
0242 return pos > 0 && strncmp(s + pos, q, strlen(q)) == 0 ;
0243 }
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253 inline bool sstr::EndsWith( const char* s, const char* q)
0254 {
0255 int pos = strlen(s) - strlen(q) ;
0256 return pos > 0 && strncmp(s + pos, q, strlen(q)) == 0 ;
0257 }
0258
0259
0260 inline bool sstr::StartsWithElem(const char* s, const char* _qq, char delim )
0261 {
0262 std::vector<std::string> qq ;
0263 Split(_qq, delim, qq );
0264 return StartsWithElem(s, qq);
0265 }
0266
0267 inline bool sstr::StartsWithElem(const char* s, std::vector<std::string>& qq )
0268 {
0269 for(int i=0 ; i < int(qq.size()) ; i++)
0270 {
0271 const char* q = qq[i].c_str();
0272 if(StartsWith(s,q)) return true ;
0273 }
0274 return false ;
0275 }
0276
0277
0278
0279
0280
0281 inline bool sstr::StartsWithLetterAZaz(const char* q )
0282 {
0283 const char* p = q != nullptr && strlen(q) > 0 ? strchr(AZaz, q[0]) : nullptr ;
0284 return p != nullptr ;
0285 }
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297 inline bool sstr::Contains( const char* s_ , const char* q_ )
0298 {
0299 std::string s(s_);
0300 std::string q(q_);
0301 return s.find(q) != std::string::npos ;
0302 }
0303
0304
0305 inline const char* sstr::TrimLeading(const char* s)
0306 {
0307 char* p = strdup(s);
0308 while( *p && ( *p == ' ' || *p == '\n' )) p++ ;
0309 return p ;
0310 }
0311 inline const char* sstr::TrimTrailing(const char* s)
0312 {
0313 char* p = strdup(s);
0314 char* e = p + strlen(p) - 1 ;
0315 while(e > p && ( *e == ' ' || *e == '\n' )) e-- ;
0316 e[1] = '\0' ;
0317 return p ;
0318 }
0319 inline const char* sstr::Trim(const char* s)
0320 {
0321 char* p = strdup(s);
0322 char* e = p + strlen(p) - 1 ;
0323 while(e > p && ( *e == ' ' || *e == '\n' )) e-- ;
0324 *(e+1) = '\0' ;
0325 while( *p && ( *p == ' ' || *p == '\n')) p++ ;
0326 return p ;
0327 }
0328
0329 inline std::string sstr::TrimString(const std::string& str, const std::string& whitespace )
0330 {
0331 const auto beg = str.find_first_not_of(whitespace);
0332 if(beg == std::string::npos) return "" ;
0333 const auto end = str.find_last_not_of(whitespace);
0334 const auto rng = end - beg + 1 ;
0335 return str.substr(beg, rng);
0336 }
0337
0338
0339
0340 inline bool sstr::HasTail(const std::vector<std::string>& names, const char* end)
0341 {
0342 size_t num_names = names.size() ;
0343 size_t num_tail = CountTail(names, end );
0344 return num_names == num_tail ;
0345 }
0346
0347 inline size_t sstr::CountTail(const std::vector<std::string>& names, const char* end)
0348 {
0349 size_t num_names = names.size() ;
0350 size_t count = 0 ;
0351 for(unsigned i=0 ; i < num_names ; i++) if(HasTail(names[i], end)) count += 1 ;
0352 return count ;
0353 }
0354
0355 inline bool sstr::HasTail(const std::string& name, const char* end)
0356 {
0357 bool found = name.find(end) != std::string::npos ;
0358 return found ;
0359 }
0360
0361 inline std::string sstr::StripTail(const std::string& name, const char* end)
0362 {
0363 std::string sname = name.substr(0, name.find(end)) ;
0364 return sname ;
0365 }
0366
0367 inline std::string sstr::StripComment(const std::string& name)
0368 {
0369 const char* end = "#" ;
0370 std::string sname = name.substr(0, name.find(end)) ;
0371 return TrimString(sname) ;
0372 }
0373
0374
0375
0376 inline std::string sstr::StripTail(const char* name_, const char* end)
0377 {
0378 std::string name(name_);
0379 return StripTail(name, end) ;
0380 }
0381
0382 inline void sstr::StripTail(std::vector<std::string>& dst, const std::vector<std::string>& src, const char* end)
0383 {
0384 int num_src = src.size();
0385 for(int i=0 ; i < num_src ; i++ )
0386 {
0387 const std::string& _src = src[i] ;
0388 std::string _dst = StripTail(_src, end);
0389 dst.push_back(_dst) ;
0390 }
0391 }
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406 inline void sstr::StripTail_Unique( std::vector<std::string>& keys, const std::vector<std::string>& src, const char* end )
0407 {
0408 std::vector<std::string> stripped ;
0409 StripTail( stripped, src, end );
0410
0411 int num_src = src.size();
0412 [[maybe_unused]] int num_stripped = stripped.size();
0413 assert( num_src == num_stripped );
0414
0415 for(int i=0 ; i < num_src ; i++)
0416 {
0417 const char* cand0 = stripped[i].c_str();
0418 int count0 = std::count( stripped.begin(), stripped.end(), cand0 );
0419 assert( count0 >= 1 );
0420 if( count0 == 1 )
0421 {
0422 keys.push_back(cand0);
0423 }
0424 else
0425 {
0426 for(int j=0 ; j < 1000000 ; j++ )
0427 {
0428 std::string cand = Format_("%s_%d", cand0, j );
0429 int count_key = std::count( keys.begin(), keys.end(), cand.c_str() );
0430 assert( count_key == 0 || count_key == 1 );
0431 if( count_key == 0 )
0432 {
0433 keys.push_back(cand);
0434 break ;
0435 }
0436 }
0437 }
0438 }
0439 }
0440
0441 inline std::string sstr::DescKeySrc(const std::vector<std::string>& key, const std::vector<std::string>& src )
0442 {
0443 std::stringstream ss ;
0444 ss << "sstr::DescKeySrc" << std::endl ;
0445 int num_src = src.size();
0446 [[maybe_unused]] int num_key = key.size();
0447 assert( num_src == num_key );
0448
0449 for(int i=0 ; i < num_src ; i++)
0450 {
0451 ss << std::setw(4) << i
0452 << " : "
0453 << std::setw(50) << src[i]
0454 << " : "
0455 << std::setw(50) << key[i]
0456 << std::endl
0457 ;
0458 }
0459 std::string str = ss.str();
0460 return str ;
0461 }
0462
0463
0464 inline std::string sstr::RemoveSpaces(const char* s)
0465 {
0466 std::stringstream ss ;
0467 for(int i=0 ; i < int(strlen(s)) ; i++) if(s[i] != ' ') ss << s[i] ;
0468 std::string str = ss.str();
0469 return str ;
0470 }
0471 inline std::string sstr::Replace(const char* s, char q, char r)
0472 {
0473 std::stringstream ss ;
0474 for(int i=0 ; i < int(strlen(s)) ; i++) ss << ( s[i] == q ? r : s[i] ) ;
0475 std::string str = ss.str();
0476 return str ;
0477 }
0478
0479
0480
0481 inline const char* sstr::ReplaceChars(const char* str, const char* repl, char to )
0482 {
0483 char* s = strdup(str);
0484 for(unsigned i=0 ; i < strlen(s) ; i++) if(strchr(repl, s[i]) != nullptr) s[i] = to ;
0485 return s ;
0486 }
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500 inline std::string sstr::ReplaceEnd_( const char* s, const char* q, const char* r )
0501 {
0502 int pos = strlen(s) - strlen(q) ;
0503 assert( pos > 0 && strncmp(s + pos, q, strlen(q)) == 0 );
0504
0505 std::stringstream ss ;
0506 for(int i=0 ; i < pos ; i++) ss << *(s+i) ;
0507 ss << r ;
0508
0509 std::string str = ss.str();
0510 return str ;
0511 }
0512
0513 inline const char* sstr::ReplaceEnd( const char* s, const char* q, const char* r )
0514 {
0515 std::string str = ReplaceEnd_(s, q, r);
0516 return strdup(str.c_str());
0517 }
0518
0519
0520
0521
0522
0523
0524 inline void sstr::PrefixSuffixParse(std::vector<std::string>& elem, const char* prefix, const char* suffix, const char* lines)
0525 {
0526 std::stringstream ss;
0527 ss.str(lines) ;
0528 std::string s;
0529 while (std::getline(ss, s, '\n'))
0530 {
0531 if(s.empty()) continue ;
0532 const char* l = s.c_str();
0533 bool has_prefix = strlen(l) > strlen(prefix) && strncmp(l, prefix, strlen(prefix)) == 0 ;
0534 bool has_suffix = strlen(l) > strlen(suffix) && strncmp(l+strlen(l)-strlen(suffix), suffix, strlen(suffix)) == 0 ;
0535
0536 if(has_prefix && has_suffix)
0537 {
0538 int count = strlen(l) - strlen(prefix) - strlen(suffix) ;
0539 std::string sub = s.substr(strlen(prefix), count );
0540
0541 elem.push_back(sub);
0542 }
0543 }
0544 }
0545
0546
0547
0548
0549 inline void sstr::Split( const char* str, char delim, std::vector<std::string>& elem )
0550 {
0551 std::stringstream ss;
0552 ss.str(str) ;
0553 std::string s;
0554 while (std::getline(ss, s, delim)) elem.push_back(s) ;
0555 }
0556
0557 inline void sstr::SplitTrim( const char* str, char delim, std::vector<std::string>& elem )
0558 {
0559 std::stringstream ss;
0560 ss.str(str) ;
0561 std::string s;
0562 while (std::getline(ss, s, delim)) elem.push_back(Trim(s.c_str())) ;
0563 }
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574 inline void sstr::SplitTrimSuppress( const char* str, char delim, std::vector<std::string>& elem )
0575 {
0576 bool is_multiline = Contains(str,"\n");
0577 char udelim = is_multiline ? '\n' : delim ;
0578
0579 std::stringstream ss;
0580 ss.str(str) ;
0581 std::string s;
0582 while (std::getline(ss, s, udelim))
0583 {
0584 const char* t = Trim(s.c_str());
0585 if(t && strlen(t) > 0)
0586 {
0587 if(t[0] == '#') continue ;
0588 elem.push_back(t) ;
0589 }
0590 }
0591 }
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601 inline int sstr::ekv_split( std::vector<std::pair<std::string, std::string> > & ekv, const char* line_, char edelim, char kvdelim)
0602 {
0603 int err = 0 ;
0604 bool warn = true ;
0605 const char* line = strdup(line_);
0606 typedef std::pair<std::string,std::string> KV ;
0607 std::istringstream f(line);
0608 std::string s;
0609 while (getline(f, s, edelim))
0610 {
0611 std::vector<std::string> kv ;
0612 Split( s.c_str(), kvdelim, kv );
0613
0614 if(kv.size() == 2)
0615 {
0616 ekv.push_back(KV(kv[0],kv[1]));
0617 }
0618 else
0619 {
0620 if(warn) std::cerr
0621 << "sstr::ekv_split ignoring malformed kv [" << s.c_str() << "]\n"
0622 << "sstr::ekv_split line [" << line << "]\n"
0623 ;
0624 err++ ;
0625 std::raise(SIGINT);
0626 }
0627 }
0628 return err ;
0629 }
0630
0631
0632
0633
0634
0635 template<typename T>
0636 inline int sstr::split( std::vector<T>& elem, const char* str, char delim )
0637 {
0638 std::stringstream ss;
0639 ss.str(str) ;
0640 std::string s;
0641 while (std::getline(ss, s, delim))
0642 {
0643 std::istringstream iss(s);
0644 T v ;
0645 iss >> v ;
0646 elem.push_back(v) ;
0647 }
0648 int count = elem.size();
0649 return count ;
0650 }
0651
0652 inline int sstr::split_count( const char* str, char delim )
0653 {
0654 std::stringstream ss;
0655 ss.str(str) ;
0656 std::string s;
0657 int count(0);
0658 while (std::getline(ss, s, delim)) count += 1 ;
0659 return count ;
0660 }
0661
0662 inline bool sstr::looks_like_list(const char* str, char delim, int len_min, int len_max )
0663 {
0664 int len = split_count(str, delim);
0665 return len >= len_min && len <= len_max ;
0666 }
0667
0668
0669
0670
0671 template<typename T>
0672 inline std::string sstr::desc( const std::vector<T>& elem )
0673 {
0674 std::stringstream ss ;
0675 for(int i=0 ; i < int(elem.size()); i++)
0676 {
0677 if(i % 4 == 0) ss << "\n" ;
0678 ss << " " << std::fixed << std::setw(10) << std::setprecision(4) << elem[i] ;
0679 }
0680 ss << "\n" ;
0681 std::string str = ss.str() ;
0682 return str ;
0683 }
0684
0685
0686
0687
0688
0689
0690
0691
0692 inline void sstr::Chop( std::pair<std::string, std::string>& head__tail, const char* delim, const char* str )
0693 {
0694 char* head = strdup(str);
0695 char* p = strstr(head, delim);
0696 if(p) p[0] = '\0' ;
0697 const char* tail = p ? p + strlen(delim) : nullptr ;
0698 head__tail.first = head ;
0699 head__tail.second = tail ? tail : "" ;
0700 }
0701
0702
0703
0704
0705
0706
0707
0708
0709
0710
0711
0712 inline void sstr::chop( char** head, char** tail, const char* delim, const char* str )
0713 {
0714 *head = strdup(str);
0715 char* p = strstr(*head, delim);
0716 if(p) p[0] = '\0' ;
0717 *tail = p ? p + strlen(delim) : nullptr ;
0718 }
0719
0720
0721
0722
0723
0724
0725
0726
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736 inline bool sstr::prefix_suffix( char** pfx, char** sfx, const char* start_sfx, const char* str )
0737 {
0738 if(!str || !start_sfx) return false ;
0739
0740 *pfx = strdup(str);
0741 char* p = strstr(*pfx, start_sfx);
0742
0743 bool has_suffix = p && (p > *pfx) ;
0744 if(has_suffix)
0745 {
0746 *sfx = strdup(p);
0747 p[0] = '\0' ;
0748 }
0749 return has_suffix ;
0750 }
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761
0762
0763
0764
0765 template<typename ... Args>
0766 inline std::string sstr::Format_( const char* fmt, Args ... args )
0767 {
0768 int sz = std::snprintf( nullptr, 0, fmt, args ... ) + 1 ;
0769 assert( sz > 0 );
0770 std::vector<char> buf(sz) ;
0771 std::snprintf( buf.data(), sz, fmt, args ... );
0772 return std::string( buf.begin(), buf.begin() + sz - 1 );
0773 }
0774
0775 template std::string sstr::Format_( const char*, const char*, int, int );
0776 template std::string sstr::Format_( const char*, int );
0777 template std::string sstr::Format_( const char*, unsigned long long );
0778
0779
0780 template<typename ... Args>
0781 inline const char* sstr::Format( const char* fmt, Args ... args )
0782 {
0783 std::string str = Format_(fmt, std::forward<Args>(args)... );
0784 return strdup(str.c_str());
0785 }
0786
0787 template const char* sstr::Format( const char*, const char*, int, int );
0788 template const char* sstr::Format( const char*, int );
0789 template const char* sstr::Format( const char*, unsigned long long );
0790
0791
0792
0793
0794 inline std::string sstr::FormatIndexDefault_( int idx, const char* hdr )
0795 {
0796 char prefix = 'A' ;
0797 int wid = 3 ;
0798 return FormatIndex_(idx, prefix, wid, hdr );
0799 }
0800
0801
0802
0803
0804
0805
0806
0807
0808
0809
0810 inline std::string sstr::FormatIndex_( int idx, char prefix, int wid, const char* hdr )
0811 {
0812 assert( prefix == '\0' || prefix == 'A' || prefix == 'B' || prefix == 'M' || prefix == 'D' );
0813 assert( idx >= 0 );
0814
0815 std::stringstream ss ;
0816 if(hdr) ss << hdr ;
0817
0818
0819 if(prefix != '\0') ss << prefix ;
0820
0821 ss << std::setfill('0') << std::setw(wid) << std::abs(idx) ;
0822 std::string str = ss.str();
0823 return str ;
0824 }
0825
0826
0827
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844 inline const char* sstr::FormatIndex( int idx, char prefix, int wid, const char* hdr )
0845 {
0846 std::string str = FormatIndex_(idx, prefix, wid, hdr );
0847 return strdup(str.c_str());
0848 }
0849
0850
0851
0852
0853 template<typename ... Args>
0854 inline std::string sstr::Join( const char* delim, Args ... args_ )
0855 {
0856 std::vector<const char*> args = {args_ ...};
0857 int num_args = args.size() ;
0858 std::stringstream ss ;
0859 for(int i=0 ; i < num_args ; i++) ss << ( args[i] ? args[i] : "" ) << ( i < num_args - 1 ? delim : "" ) ;
0860 std::string str = ss.str();
0861 return str ;
0862 }
0863 template std::string sstr::Join( const char*, const char*, const char* );
0864 template std::string sstr::Join( const char*, const char*, const char*, const char* );
0865 template std::string sstr::Join( const char*, const char*, const char*, const char*, const char* );
0866
0867
0868
0869
0870
0871 template<typename ... Args>
0872 inline std::string sstr::Concat_( Args ... args_ )
0873 {
0874 std::vector<const char*> args = {args_ ...};
0875 int num_args = args.size() ;
0876 std::stringstream ss ;
0877 for(int i=0 ; i < num_args ; i++) ss << ( args[i] ? args[i] : "" ) ;
0878 std::string str = ss.str();
0879 return str ;
0880 }
0881
0882 template std::string sstr::Concat_( const char*, const char* );
0883 template std::string sstr::Concat_( const char*, const char*, const char* );
0884 template std::string sstr::Concat_( const char*, const char*, const char*, const char* );
0885 template std::string sstr::Concat_( const char*, const char*, const char*, const char*, const char* );
0886
0887
0888
0889
0890
0891 template<typename ... Args>
0892 inline const char* sstr::Concat( Args ... args )
0893 {
0894 std::string str = Concat_(std::forward<Args>(args)... );
0895 return strdup(str.c_str());
0896 }
0897
0898 template const char* sstr::Concat( const char*, const char* );
0899 template const char* sstr::Concat( const char*, const char*, const char* );
0900 template const char* sstr::Concat( const char*, const char*, const char*, const char* );
0901 template const char* sstr::Concat( const char*, const char*, const char*, const char*, const char* );
0902
0903
0904
0905
0906
0907
0908
0909
0910 inline bool sstr::Blank( const char* s )
0911 {
0912 unsigned n = strlen(s) ;
0913 return n == 0 || All(s, ' ') ;
0914 }
0915
0916 inline bool sstr::All( const char* s , char q )
0917 {
0918 unsigned n = strlen(s) ;
0919 return n > 0 && Count(s, q) == n ;
0920
0921 }
0922 inline unsigned sstr::Count( const char* s , char q )
0923 {
0924 unsigned n = strlen(s) ;
0925 unsigned count = 0 ;
0926 for(unsigned i=0 ; i < n ; i++) if( s[i] == q ) count += 1 ;
0927 return count ;
0928 }
0929
0930 template<typename T>
0931 inline T sstr::To(const char* arg )
0932 {
0933 std::string str(arg);
0934 std::istringstream iss(str);
0935 T v ;
0936 iss >> v ;
0937 return v ;
0938 }
0939
0940
0941 template<> inline std::string sstr::To(const char* a )
0942 {
0943 std::string s(a);
0944 return s ;
0945 }
0946
0947
0948
0949
0950 inline int sstr::AsInt(const char* arg, int fallback )
0951 {
0952 char* end ;
0953 char** endptr = &end ;
0954 int base = 10 ;
0955 unsigned long ul = strtoul(arg, endptr, base);
0956 bool end_points_to_terminator = end == arg + strlen(arg) ;
0957 return end_points_to_terminator ? int(ul) : fallback ;
0958 }
0959
0960
0961 inline const char* sstr::ParseStringIntInt( const char* triplet, int& y, int& z, char delim )
0962 {
0963 std::stringstream ss;
0964 ss.str(triplet) ;
0965 std::string s;
0966 std::vector<std::string> elem ;
0967 while (std::getline(ss, s, delim)) elem.push_back(s) ;
0968 assert(elem.size() == 3 );
0969 y = AsInt( elem[1].c_str() );
0970 z = AsInt( elem[2].c_str() );
0971 return strdup(elem[0].c_str());
0972 }
0973
0974
0975 template<typename T>
0976 inline void sstr::ParsePair( const char* txt, T& x, T& y, char delim )
0977 {
0978 std::stringstream ss;
0979 ss.str(txt) ;
0980 std::string s;
0981 std::vector<std::string> elem ;
0982 while (std::getline(ss, s, delim)) elem.push_back(s) ;
0983 int num_elem = elem.size();
0984 bool expect = num_elem == 2 ;
0985 if(!expect) std::cerr
0986 << "sstr::ParsePair"
0987 << " txt [" << ( txt ? txt : "-" ) << "]"
0988 << " delim " << delim
0989 << " num_elem " << num_elem
0990 << std::endl
0991 ;
0992
0993 assert(expect);
0994 x = To<T>( elem[0].c_str() );
0995 y = To<T>( elem[1].c_str() );
0996 }
0997
0998 inline bool sstr::IsInteger(const char* str)
0999 {
1000 if(!str) return false ;
1001 if(strlen(str)==0) return false ;
1002
1003 std::string s(str);
1004 return s.find_first_not_of("0123456789") == std::string::npos ;
1005 }
1006
1007 inline bool sstr::IsWhitespace(const std::string& str )
1008 {
1009 return str.find_first_not_of(" \t\n\v\f\r") == std::string::npos ;
1010 }
1011
1012 inline bool sstr::isdigit_(char c ) { return std::isdigit(static_cast<unsigned char>(c)) ; }
1013 inline bool sstr::isalnum_(char c ) { return std::isalnum(static_cast<unsigned char>(c)) ; }
1014 inline bool sstr::isupper_(char c ) { return std::isupper(static_cast<unsigned char>(c)) ; }
1015 inline bool sstr::islower_(char c ) { return std::islower(static_cast<unsigned char>(c)) ; }
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065 template<typename T>
1066 inline T sstr::ParseIntSpec( const char* spec, T& scale )
1067 {
1068 bool valid = spec != nullptr && strlen(spec) > 0 ;
1069 if(!valid) return 0 ;
1070
1071 bool is_digit = isdigit_(spec[0]);
1072 const char* e = is_digit ? spec : spec + 1 ;
1073
1074 T value = 0ull ;
1075
1076 if( spec[0] == 'X' )
1077 {
1078 T bitshift = std::atol(e);
1079 value = ( 0x1ull << bitshift ) - 1ull ;
1080 scale = 1ull ;
1081 }
1082 else
1083 {
1084 value = To<T>( e) ;
1085 ParseScale<T>(spec, scale);
1086 }
1087 return value*scale ;
1088 }
1089
1090 template<typename T>
1091 inline T sstr::ParseInt( const char* spec)
1092 {
1093 T scale(1) ;
1094 return ParseIntSpec<T>( spec, scale );
1095 }
1096
1097
1098
1099 template<typename T>
1100 inline void sstr::ParseScale( const char* spec, T& scale )
1101 {
1102 bool is_digit = isdigit_(spec[0]);
1103 if(!is_digit)
1104 {
1105 switch(spec[0])
1106 {
1107 case 'h': scale = 100 ; break ;
1108 case 'K': scale = 1'000 ; break ;
1109 case 'H': scale = 100'000 ; break ;
1110 case 'M': scale = 1'000'000 ; break ;
1111 case 'G': scale = 1'000'000'000 ; break ; // maximum 32 bit int 2,147,483,647 , uint 4.29 billion
1112 case 'T': scale = 1'000'000'000'000 ; break ; // 40 bits of integer needed to hold a trillion
1113 }
1114 }
1115 }
1116
1117
1118 /**
1119 sstr::ParseIntSpecList
1120 ------------------------
1121
1122 Parses delimited string into vector of ints, eg:
1123
1124 +---------------------+------------------------------------------------------+
1125 | spec | values |
1126 +=====================+======================================================+
1127 | "M1,2,3,4,5,K1,2" | 1000000,2000000,3000000,4000000,5000000,1000,2000 |
1128 | "M1:5,K1:2" | 1000000,2000000,3000000,4000000,5000000,1000,2000 |
1129 | "1x5,M1x5 | 1,1,1,1,1,1000000,1000000,1000000,1000000,1000000 |
1130 +---------------------+------------------------------------------------------+
1131
1132 The repeated value spec "M1x5" meaning 5 times M1 puts the multiplicity to the right
1133 in order to work with the scale specification to the left.
1134
1135 **/
1136
1137 template<typename T>
1138 inline void sstr::ParseIntSpecList( std::vector<T>& values, const char* spec, char delim ) // static
1139 {
1140 values.clear();
1141 std::stringstream ss;
1142 ss.str(spec) ;
1143 std::string elem ;
1144 T scale = 1 ;
1145 while (std::getline(ss, elem, delim))
1146 {
1147 const char* e = elem.c_str();
1148 const char* p = strstr(e, ":" );
1149 const char* x = strstr(e, "x" );
1150
1151
1152 if( p == nullptr && x == nullptr)
1153 {
1154 values.push_back(ParseIntSpec<T>( e, scale ));
1155 }
1156 else if ( p || x )
1157 {
1158 const char* q = isdigit_(e[0]) ? e : e + 1 ; // jump past scale char if present
1159 T i0, i1 ;
1160 ParsePair<T>( q , i0, i1, p ? ':' : 'x' );
1161 ParseScale<T>( e, scale );
1162
1163 if( p )
1164 {
1165 for(T i=i0 ; i <= i1 ; i++) values.push_back(i*scale) ;
1166 }
1167 else if( x )
1168 {
1169 for(T i=0 ; i < i1 ; i++) values.push_back(i0*scale) ;
1170 }
1171 }
1172
1173
1174 }
1175 }
1176
1177 template<typename T>
1178 inline std::vector<T>* sstr::ParseIntSpecList( const char* spec, char delim )
1179 {
1180 if(spec == nullptr) return nullptr ;
1181 std::vector<T>* ls = new std::vector<T> ;
1182 ParseIntSpecList<T>( *ls, spec, delim );
1183 return ls ;
1184 }
1185
1186 inline void sstr::truncated_copy( char* dst, const char* src, int dst_size )
1187 {
1188 memset(dst, 0, dst_size);
1189
1190 int srclen = strlen(src) ;
1191 int num_char = std::min(dst_size, srclen);
1192 memcpy(dst, src, num_char) ;
1193 }
1194
1195
1196
1197 /**
1198 sstr::Extract
1199 -----------------------
1200
1201 Extract integers from a string into a vector.
1202
1203 The 2nd strtol endptr arg increments p beyond each group of integer digits
1204
1205 **/
1206
1207 inline void sstr::Extract( std::vector<long>& vals, const char* s ) // static
1208 {
1209 bool invalid = strlen(s) == 1 && ( s[0] == '+' || s[0] == '-' ) ;
1210 if(invalid) return ;
1211
1212 char* s0 = strdup(s);
1213 char* p = s0 ;
1214
1215 while (*p)
1216 {
1217 if( (*p >= '0' && *p <= '9') || *p == '+' || *p == '-')
1218 {
1219 // EDGE CASE PROTECTION:
1220 // If it's a sign, make sure the NEXT char is a digit.
1221
1222 if ((*p == '+' || *p == '-') && !(p[1] >= '0' && p[1] <= '9'))
1223 {
1224 p++;
1225 continue;
1226 }
1227
1228 char* endptr;
1229 long val = strtol(p, &endptr, 10);
1230
1231
1232 if (p != endptr)
1233 {
1234 vals.push_back(val);
1235 p = endptr;
1236 }
1237 else
1238 {
1239 p++;
1240 }
1241 }
1242 else
1243 {
1244 p++ ;
1245 }
1246 }
1247 free(s0);
1248 }
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259 template <typename T>
1260 inline void sstr::Extract_(std::vector<T>& vals, const char* s)
1261 {
1262 if (!s) return;
1263 const char* p = s;
1264 const char* end = s + std::strlen(s);
1265
1266 while (p < end)
1267 {
1268 if ((*p >= '0' && *p <= '9') || *p == '+' || *p == '-')
1269 {
1270 const char* parse_start = p;
1271 if constexpr (std::is_unsigned_v<T>)
1272 {
1273 if (*p == '+' || *p == '-') parse_start++;
1274 }
1275
1276 T val;
1277 auto [ptr, ec] = std::from_chars(parse_start, end, val);
1278
1279 if (ec == std::errc()) {
1280 vals.push_back(val);
1281 p = ptr;
1282 } else {
1283 p++;
1284 }
1285 }
1286 else
1287 {
1288 p++;
1289 }
1290 }
1291 }
1292
1293
1294
1295
1296 inline long sstr::ExtractLong( const char* s, long fallback )
1297 {
1298 std::vector<long> vals;
1299 Extract(vals, s);
1300 return vals.size() == 1 ? vals[0] : fallback ;
1301 }
1302
1303
1304
1305 inline size_t sstr::ExtractSize( const char* s, size_t fallback )
1306 {
1307 std::vector<size_t> vals;
1308 Extract_(vals, s);
1309 return vals.size() == 1 ? vals[0] : fallback ;
1310 }
1311
1312
1313
1314
1315
1316 inline bool sstr::LooksLikePath(const char* arg)
1317 {
1318 if(!arg) return false ;
1319 if(strlen(arg) < 2) return false ;
1320 return arg[0] == '/' || arg[0] == '$' ;
1321 }
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334 inline void sstr::LoadList(const char* arg, std::vector<std::string>& lines, char delim )
1335 {
1336 if(arg == nullptr) return ;
1337
1338 if(LooksLikePath(arg) && delim == '\n' )
1339 {
1340 std::ifstream ifs(arg);
1341 std::string line;
1342 while(std::getline(ifs, line)) lines.push_back(line) ;
1343 }
1344 else if( delim == ',' )
1345 {
1346 sstr::Split( arg, delim, lines );
1347 }
1348 else
1349 {
1350 lines.push_back(arg);
1351 }
1352 }
1353
1354 inline std::vector<std::string>* sstr::LoadList( const char* arg, char delim )
1355 {
1356 if(arg == nullptr) return nullptr ;
1357 typedef std::vector<std::string> VS ;
1358 VS* lines = new VS ;
1359 LoadList(arg, *lines, delim );
1360 return lines ;
1361 }
1362
1363