foobar2000 SDK  2015-01-14
string_base.cpp
Go to the documentation of this file.
1 #include "pfc.h"
2 
3 namespace pfc {
4 
6 {
7  char temp[8];
8  t_size len = utf8_encode_char(p_char,temp);
9  if (len>0) add_string(temp,len);
10 }
11 
13 {
14  const char * str = get_ptr();
15  t_size ptr,trunc = 0;
16  bool need_trunc = false;
17  for(ptr=0;str[ptr];)
18  {
19  unsigned c;
20  t_size delta = utf8_decode_char(str+ptr,c);
21  if (delta==0) break;
22  if (c==skip)
23  {
24  need_trunc = true;
25  trunc = ptr;
26  }
27  else
28  {
29  need_trunc = false;
30  }
31  ptr += delta;
32  }
33  if (need_trunc) truncate(trunc);
34 }
35 
37  t_uint64 length = p_seconds;
38  unsigned weeks,days,hours,minutes,seconds;
39 
40  weeks = (unsigned)( ( length / (60*60*24*7) ) );
41  days = (unsigned)( ( length / (60*60*24) ) % 7 );
42  hours = (unsigned) ( ( length / (60 * 60) ) % 24);
43  minutes = (unsigned) ( ( length / (60 ) ) % 60 );
44  seconds = (unsigned) ( ( length ) % 60 );
45 
46  if (weeks) {
47  m_buffer << weeks << "wk ";
48  }
49  if (days || weeks) {
50  m_buffer << days << "d ";
51  }
52  if (hours || days || weeks) {
53  m_buffer << hours << ":" << format_uint(minutes,2) << ":" << format_uint(seconds,2);
54  } else {
55  m_buffer << minutes << ":" << format_uint(seconds,2);
56  }
57 }
58 
59 bool is_path_separator(unsigned c)
60 {
61  return c=='\\' || c=='/' || c=='|' || c==':';
62 }
63 
64 bool is_path_bad_char(unsigned c)
65 {
66 #ifdef _WINDOWS
67  return c=='\\' || c=='/' || c=='|' || c==':' || c=='*' || c=='?' || c=='\"' || c=='>' || c=='<';
68 #else
69  return c=='/' || c=='*' || c=='?';
70 #endif
71 }
72 
73 
74 
75 char * strdup_n(const char * src,t_size len)
76 {
77  len = strlen_max(src,len);
78  char * ret = (char*)malloc(len+1);
79  if (ret)
80  {
81  memcpy(ret,src,len);
82  ret[len]=0;
83  }
84  return ret;
85 }
86 
88 {
89  fn += pfc::scan_filename(fn);
90  const char * ptr=fn,*dot=0;
91  while(*ptr && *ptr!='?')
92  {
93  if (*ptr=='.') dot=ptr;
94  ptr++;
95  }
96 
97  if (dot && dot>fn) set_string(fn,dot-fn);
98  else set_string(fn);
99 }
100 
102 {
103  fn += pfc::scan_filename(fn);
104  const char * ptr = fn;
105  while(*ptr && *ptr!='?') ptr++;
106  set_string(fn,ptr-fn);
107 }
108 
110 {
111  buffer[0]=0;
112  const char * start = src + pfc::scan_filename(src);
113  const char * end = start + strlen(start);
114  const char * ptr = end-1;
115  while(ptr>start && *ptr!='.')
116  {
117  if (*ptr=='?') end=ptr;
118  ptr--;
119  }
120 
121  if (ptr>=start && *ptr=='.')
122  {
123  ptr++;
124  t_size len = end-ptr;
125  if (len<PFC_TABSIZE(buffer))
126  {
127  memcpy(buffer,ptr,len*sizeof(char));
128  buffer[len]=0;
129  }
130  }
131 }
132 
133 
134 bool has_path_bad_chars(const char * param)
135 {
136  while(*param)
137  {
138  if (is_path_bad_char(*param)) return true;
139  param++;
140  }
141  return false;
142 }
143 
144 void float_to_string(char * out,t_size out_max,double val,unsigned precision,bool b_sign) {
146  t_size outptr;
147 
148  if (out_max == 0) return;
149  out_max--;//for null terminator
150 
151  outptr = 0;
152 
153  if (outptr == out_max) {out[outptr]=0;return;}
154 
155  if (val<0) {out[outptr++] = '-'; val = -val;}
156  else if (b_sign) {out[outptr++] = '+';}
157 
158  if (outptr == out_max) {out[outptr]=0;return;}
159 
160 
161  {
162  double powval = pow((double)10.0,(double)precision);
163  temp << (t_int64)floor(val * powval + 0.5);
164  //_i64toa(blargh,temp,10);
165  }
166 
167  const t_size temp_len = temp.length();
168  if (temp_len <= precision)
169  {
170  out[outptr++] = '0';
171  if (outptr == out_max) {out[outptr]=0;return;}
172  out[outptr++] = '.';
173  if (outptr == out_max) {out[outptr]=0;return;}
174  t_size d;
175  for(d=precision-temp_len;d;d--)
176  {
177  out[outptr++] = '0';
178  if (outptr == out_max) {out[outptr]=0;return;}
179  }
180  for(d=0;d<temp_len;d++)
181  {
182  out[outptr++] = temp[d];
183  if (outptr == out_max) {out[outptr]=0;return;}
184  }
185  }
186  else
187  {
188  t_size d = temp_len;
189  const char * src = temp;
190  while(*src)
191  {
192  if (d-- == precision)
193  {
194  out[outptr++] = '.';
195  if (outptr == out_max) {out[outptr]=0;return;}
196  }
197  out[outptr++] = *(src++);
198  if (outptr == out_max) {out[outptr]=0;return;}
199  }
200  }
201  out[outptr] = 0;
202 }
203 
204 
205 
206 static double pfc_string_to_float_internal(const char * src)
207 {
208  bool neg = false;
209  t_int64 val = 0;
210  int div = 0;
211  bool got_dot = false;
212 
213  while(*src==' ') src++;
214 
215  if (*src=='-') {neg = true;src++;}
216  else if (*src=='+') src++;
217 
218  while(*src)
219  {
220  if (*src>='0' && *src<='9')
221  {
222  int d = *src - '0';
223  val = val * 10 + d;
224  if (got_dot) div--;
225  src++;
226  }
227  else if (*src=='.' || *src==',')
228  {
229  if (got_dot) break;
230  got_dot = true;
231  src++;
232  }
233  else if (*src=='E' || *src=='e')
234  {
235  src++;
236  div += atoi(src);
237  break;
238  }
239  else break;
240  }
241  if (neg) val = -val;
242  return (double) val * exp_int(10, div);
243 }
244 
245 double string_to_float(const char * src,t_size max) {
246  //old function wants an oldstyle nullterminated string, and i don't currently care enough to rewrite it as it works appropriately otherwise
247  char blargh[128];
248  if (max > 127) max = 127;
249  t_size walk;
250  for(walk = 0; walk < max && src[walk]; walk++) blargh[walk] = src[walk];
251  blargh[walk] = 0;
252  return pfc_string_to_float_internal(blargh);
253 }
254 
255 
256 
257 void string_base::convert_to_lower_ascii(const char * src,char replace)
258 {
259  reset();
260  PFC_ASSERT(replace>0);
261  while(*src)
262  {
263  unsigned c;
264  t_size delta = utf8_decode_char(src,c);
265  if (delta==0) {c = replace; delta = 1;}
266  else if (c>=0x80) c = replace;
267  add_byte((char)c);
268  src += delta;
269  }
270 }
271 
272 void convert_to_lower_ascii(const char * src,t_size max,char * out,char replace)
273 {
274  t_size ptr = 0;
275  PFC_ASSERT(replace>0);
276  while(ptr<max && src[ptr])
277  {
278  unsigned c;
279  t_size delta = utf8_decode_char(src+ptr,c,max-ptr);
280  if (delta==0) {c = replace; delta = 1;}
281  else if (c>=0x80) c = replace;
282  *(out++) = (char)c;
283  ptr += delta;
284  }
285  *out = 0;
286 }
287 
288 t_size strstr_ex(const char * p_string,t_size p_string_len,const char * p_substring,t_size p_substring_len) throw()
289 {
290  p_string_len = strlen_max(p_string,p_string_len);
291  p_substring_len = strlen_max(p_substring,p_substring_len);
292  t_size index = 0;
293  while(index + p_substring_len <= p_string_len)
294  {
295  if (memcmp(p_string+index,p_substring,p_substring_len) == 0) return index;
296  t_size delta = utf8_char_len(p_string+index,p_string_len - index);
297  if (delta == 0) break;
298  index += delta;
299  }
300  return ~0;
301 }
302 
303 unsigned atoui_ex(const char * p_string,t_size p_string_len)
304 {
305  unsigned ret = 0; t_size ptr = 0;
306  while(ptr<p_string_len)
307  {
308  char c = p_string[ptr];
309  if (! ( c >= '0' && c <= '9' ) ) break;
310  ret = ret * 10 + (unsigned)( c - '0' );
311  ptr++;
312  }
313  return ret;
314 }
315 
316 int strcmp_nc(const char* p1, size_t n1, const char * p2, size_t n2) throw() {
317  t_size idx = 0;
318  for(;;)
319  {
320  if (idx == n1 && idx == n2) return 0;
321  else if (idx == n1) return -1;//end of param1
322  else if (idx == n2) return 1;//end of param2
323 
324  char c1 = p1[idx], c2 = p2[idx];
325  if (c1<c2) return -1;
326  else if (c1>c2) return 1;
327 
328  idx++;
329  }
330 }
331 
332 int strcmp_ex(const char* p1,t_size n1,const char* p2,t_size n2) throw()
333 {
334  n1 = strlen_max(p1,n1); n2 = strlen_max(p2,n2);
335  return strcmp_nc(p1, n1, p2, n2);
336 }
337 
338 t_uint64 atoui64_ex(const char * src,t_size len) {
339  len = strlen_max(src,len);
340  t_uint64 ret = 0, mul = 1;
341  t_size ptr = len;
342  t_size start = 0;
343 // start += skip_spacing(src+start,len-start);
344 
345  while(ptr>start)
346  {
347  char c = src[--ptr];
348  if (c>='0' && c<='9')
349  {
350  ret += (c-'0') * mul;
351  mul *= 10;
352  }
353  else
354  {
355  ret = 0;
356  mul = 1;
357  }
358  }
359  return ret;
360 }
361 
362 
363 t_int64 atoi64_ex(const char * src,t_size len)
364 {
365  len = strlen_max(src,len);
366  t_int64 ret = 0, mul = 1;
367  t_size ptr = len;
368  t_size start = 0;
369  bool neg = false;
370 // start += skip_spacing(src+start,len-start);
371  if (start < len && src[start] == '-') {neg = true; start++;}
372 // start += skip_spacing(src+start,len-start);
373 
374  while(ptr>start)
375  {
376  char c = src[--ptr];
377  if (c>='0' && c<='9')
378  {
379  ret += (c-'0') * mul;
380  mul *= 10;
381  }
382  else
383  {
384  ret = 0;
385  mul = 1;
386  }
387  }
388  return neg ? -ret : ret;
389 }
390 
391 int stricmp_ascii_partial( const char * str, const char * substr) throw() {
392  size_t walk = 0;
393  for(;;) {
394  char c1 = str[walk];
395  char c2 = substr[walk];
396  c1 = ascii_tolower(c1); c2 = ascii_tolower(c2);
397  if (c2 == 0) return 0; // substr terminated = ret0 regardless of str content
398  if (c1<c2) return -1; // ret -1 early
399  else if (c1>c2) return 1; // ret 1 early
400  // else c1 == c2 and c2 != 0 so c1 != 0 either
401  ++walk; // go on
402  }
403 }
404 
405 int stricmp_ascii_ex(const char * const s1,t_size const len1,const char * const s2,t_size const len2) throw() {
406  t_size walk1 = 0, walk2 = 0;
407  for(;;) {
408  char c1 = (walk1 < len1) ? s1[walk1] : 0;
409  char c2 = (walk2 < len2) ? s2[walk2] : 0;
410  c1 = ascii_tolower(c1); c2 = ascii_tolower(c2);
411  if (c1<c2) return -1;
412  else if (c1>c2) return 1;
413  else if (c1 == 0) return 0;
414  walk1++;
415  walk2++;
416  }
417 
418 }
419 int stricmp_ascii(const char * s1,const char * s2) throw() {
420  for(;;) {
421  char c1 = *s1, c2 = *s2;
422 
423  if (c1 > 0 && c2 > 0) {
424  c1 = ascii_tolower_lookup(c1);
425  c2 = ascii_tolower_lookup(c2);
426  } else {
427  if (c1 == 0 && c2 == 0) return 0;
428  }
429  if (c1<c2) return -1;
430  else if (c1>c2) return 1;
431  else if (c1 == 0) return 0;
432 
433  s1++;
434  s2++;
435  }
436 }
437 
438 static int naturalSortCompareInternal( const char * s1, const char * s2, bool insensitive) throw() {
439  for( ;; ) {
440  unsigned c1, c2;
441  size_t d1 = utf8_decode_char( s1, c1 );
442  size_t d2 = utf8_decode_char( s2, c2 );
443  if (d1 == 0 && d2 == 0) {
444  return 0;
445  }
446  if (char_is_numeric( c1 ) && char_is_numeric( c2 ) ) {
447  // Numeric block in both strings, do natural sort magic here
448  size_t l1 = 1, l2 = 1;
449  while( char_is_numeric( s1[l1] ) ) ++l1;
450  while( char_is_numeric( s2[l2] ) ) ++l2;
451 
452  size_t l = max_t(l1, l2);
453  for(size_t w = 0; w < l; ++w) {
454  char digit1, digit2;
455 
456  t_ssize off;
457 
458  off = w + l1 - l;
459  if (off >= 0) {
460  digit1 = s1[w - l + l1];
461  } else {
462  digit1 = 0;
463  }
464  off = w + l2 - l;
465  if (off >= 0) {
466  digit2 = s2[w - l + l2];
467  } else {
468  digit2 = 0;
469  }
470  if (digit1 < digit2) return -1;
471  if (digit1 > digit2) return 1;
472  }
473 
474  s1 += l1; s2 += l2;
475  continue;
476  }
477 
478 
479  if (insensitive) {
480  c1 = charLower( c1 );
481  c2 = charLower( c2 );
482  }
483  if (c1 < c2) return -1;
484  if (c1 > c2) return 1;
485 
486  s1 += d1; s2 += d2;
487  }
488 }
489 int naturalSortCompare( const char * s1, const char * s2) throw() {
490  int v = naturalSortCompareInternal( s1, s2, true );
491  if (v) return v;
492  v = naturalSortCompareInternal( s1, s2, false );
493  if (v) return v;
494  return strcmp(s1, s2);
495 }
496 
497 int naturalSortCompareI( const char * s1, const char * s2) throw() {
498  return naturalSortCompareInternal( s1, s2, true );
499 }
500 
501 
502 format_float::format_float(double p_val,unsigned p_width,unsigned p_prec)
503 {
504  char temp[64];
505  float_to_string(temp,64,p_val,p_prec,false);
506  temp[63] = 0;
507  t_size len = strlen(temp);
508  if (len < p_width)
509  m_buffer.add_chars(' ',p_width-len);
510  m_buffer += temp;
511 }
512 
513 char format_hex_char(unsigned p_val)
514 {
515  PFC_ASSERT(p_val < 16);
516  return (p_val < 10) ? p_val + '0' : p_val - 10 + 'A';
517 }
518 
519 format_hex::format_hex(t_uint64 p_val,unsigned p_width)
520 {
521  if (p_width > 16) p_width = 16;
522  else if (p_width == 0) p_width = 1;
523  char temp[16];
524  unsigned n;
525  for(n=0;n<16;n++)
526  {
527  temp[15-n] = format_hex_char((unsigned)(p_val & 0xF));
528  p_val >>= 4;
529  }
530 
531  for(n=0;n<16 && temp[n] == '0';n++) {}
532 
533  if (n > 16 - p_width) n = 16 - p_width;
534 
535  char * out = m_buffer;
536  for(;n<16;n++)
537  *(out++) = temp[n];
538  *out = 0;
539 }
540 
541 char format_hex_char_lowercase(unsigned p_val)
542 {
543  PFC_ASSERT(p_val < 16);
544  return (p_val < 10) ? p_val + '0' : p_val - 10 + 'a';
545 }
546 
548 {
549  if (p_width > 16) p_width = 16;
550  else if (p_width == 0) p_width = 1;
551  char temp[16];
552  unsigned n;
553  for(n=0;n<16;n++)
554  {
555  temp[15-n] = format_hex_char_lowercase((unsigned)(p_val & 0xF));
556  p_val >>= 4;
557  }
558 
559  for(n=0;n<16 && temp[n] == '0';n++) {}
560 
561  if (n > 16 - p_width) n = 16 - p_width;
562 
563  char * out = m_buffer;
564  for(;n<16;n++)
565  *(out++) = temp[n];
566  *out = 0;
567 }
568 
569 format_uint::format_uint(t_uint64 val,unsigned p_width,unsigned p_base)
570 {
571 
572  enum {max_width = PFC_TABSIZE(m_buffer) - 1};
573 
574  if (p_width > max_width) p_width = max_width;
575  else if (p_width == 0) p_width = 1;
576 
577  char temp[max_width];
578 
579  unsigned n;
580  for(n=0;n<max_width;n++)
581  {
582  temp[max_width-1-n] = format_hex_char((unsigned)(val % p_base));
583  val /= p_base;
584  }
585 
586  for(n=0;n<max_width && temp[n] == '0';n++) {}
587 
588  if (n > max_width - p_width) n = max_width - p_width;
589 
590  char * out = m_buffer;
591 
592  for(;n<max_width;n++)
593  *(out++) = temp[n];
594  *out = 0;
595 }
596 
598 {
599  unsigned div = 1;
600  for(unsigned n=0;n<p_point;n++) div *= 10;
601 
602  if (p_val < 0) {m_buffer << "-";p_val = -p_val;}
603 
604 
605  m_buffer << format_int(p_val / div) << "." << format_int(p_val % div, p_point);
606 }
607 
608 format_int::format_int(t_int64 p_val,unsigned p_width,unsigned p_base)
609 {
610  bool neg = false;
611  t_uint64 val;
612  if (p_val < 0) {neg = true; val = (t_uint64)(-p_val);}
613  else val = (t_uint64)p_val;
614 
615  enum {max_width = PFC_TABSIZE(m_buffer) - 1};
616 
617  if (p_width > max_width) p_width = max_width;
618  else if (p_width == 0) p_width = 1;
619 
620  if (neg && p_width > 1) p_width --;
621 
622  char temp[max_width];
623 
624  unsigned n;
625  for(n=0;n<max_width;n++)
626  {
627  temp[max_width-1-n] = format_hex_char((unsigned)(val % p_base));
628  val /= p_base;
629  }
630 
631  for(n=0;n<max_width && temp[n] == '0';n++) {}
632 
633  if (n > max_width - p_width) n = max_width - p_width;
634 
635  char * out = m_buffer;
636 
637  if (neg) *(out++) = '-';
638 
639  for(;n<max_width;n++)
640  *(out++) = temp[n];
641  *out = 0;
642 }
643 
644 format_hexdump_lowercase::format_hexdump_lowercase(const void * p_buffer,t_size p_bytes,const char * p_spacing)
645 {
646  t_size n;
647  const t_uint8 * buffer = (const t_uint8*)p_buffer;
648  for(n=0;n<p_bytes;n++)
649  {
650  if (n > 0 && p_spacing != 0) m_formatter << p_spacing;
651  m_formatter << format_hex_lowercase(buffer[n],2);
652  }
653 }
654 
655 format_hexdump::format_hexdump(const void * p_buffer,t_size p_bytes,const char * p_spacing)
656 {
657  t_size n;
658  const t_uint8 * buffer = (const t_uint8*)p_buffer;
659  for(n=0;n<p_bytes;n++)
660  {
661  if (n > 0 && p_spacing != 0) m_formatter << p_spacing;
662  m_formatter << format_hex(buffer[n],2);
663  }
664 }
665 
666 
667 
668 string_replace_extension::string_replace_extension(const char * p_path,const char * p_ext)
669 {
670  m_data = p_path;
671  t_size dot = m_data.find_last('.');
672  if (dot < m_data.scan_filename())
673  {//argh
674  m_data += ".";
675  m_data += p_ext;
676  }
677  else
678  {
679  m_data.truncate(dot+1);
680  m_data += p_ext;
681  }
682 }
683 
685 {
686  t_size ptr = scan_filename(p_path);
687  if (ptr > 1) {
688  if (is_path_separator(p_path[ptr-1]) && !is_path_separator(p_path[ptr-2])) --ptr;
689  }
690  m_data.set_string(p_path,ptr);
691 }
692 
693 t_size scan_filename(const char * ptr)
694 {
695  t_size n;
696  t_size _used = strlen(ptr);
697  for(n=_used-1;n!=~0;n--)
698  {
699  if (is_path_separator(ptr[n])) return n+1;
700  }
701  return 0;
702 }
703 
704 
705 
706 t_size string_find_first(const char * p_string,char p_tofind,t_size p_start) {
707  for(t_size walk = p_start; p_string[walk]; ++walk) {
708  if (p_string[walk] == p_tofind) return walk;
709  }
710  return ~0;
711 }
712 t_size string_find_last(const char * p_string,char p_tofind,t_size p_start) {
713  return string_find_last_ex(p_string,~0,&p_tofind,1,p_start);
714 }
715 t_size string_find_first(const char * p_string,const char * p_tofind,t_size p_start) {
716  return string_find_first_ex(p_string,~0,p_tofind,~0,p_start);
717 }
718 t_size string_find_last(const char * p_string,const char * p_tofind,t_size p_start) {
719  return string_find_last_ex(p_string,~0,p_tofind,~0,p_start);
720 }
721 
722 t_size string_find_first_ex(const char * p_string,t_size p_string_length,char p_tofind,t_size p_start) {
723  for(t_size walk = p_start; walk < p_string_length && p_string[walk]; ++walk) {
724  if (p_string[walk] == p_tofind) return walk;
725  }
726  return ~0;
727 }
728 t_size string_find_last_ex(const char * p_string,t_size p_string_length,char p_tofind,t_size p_start) {
729  return string_find_last_ex(p_string,p_string_length,&p_tofind,1,p_start);
730 }
731 t_size string_find_first_ex(const char * p_string,t_size p_string_length,const char * p_tofind,t_size p_tofind_length,t_size p_start) {
732  p_string_length = strlen_max(p_string,p_string_length); p_tofind_length = strlen_max(p_tofind,p_tofind_length);
733  if (p_string_length >= p_tofind_length) {
734  t_size max = p_string_length - p_tofind_length;
735  for(t_size walk = p_start; walk <= max; walk++) {
736  if (_strcmp_partial_ex(p_string+walk,p_string_length-walk,p_tofind,p_tofind_length) == 0) return walk;
737  }
738  }
739  return ~0;
740 }
741 t_size string_find_last_ex(const char * p_string,t_size p_string_length,const char * p_tofind,t_size p_tofind_length,t_size p_start) {
742  p_string_length = strlen_max(p_string,p_string_length); p_tofind_length = strlen_max(p_tofind,p_tofind_length);
743  if (p_string_length >= p_tofind_length) {
744  t_size max = min_t<t_size>(p_string_length - p_tofind_length,p_start);
745  for(t_size walk = max; walk != (t_size)(-1); walk--) {
746  if (_strcmp_partial_ex(p_string+walk,p_string_length-walk,p_tofind,p_tofind_length) == 0) return walk;
747  }
748  }
749  return ~0;
750 }
751 
752 t_size string_find_first_nc(const char * p_string,t_size p_string_length,char c,t_size p_start) {
753  for(t_size walk = p_start; walk < p_string_length; walk++) {
754  if (p_string[walk] == c) return walk;
755  }
756  return ~0;
757 }
758 
759 t_size string_find_first_nc(const char * p_string,t_size p_string_length,const char * p_tofind,t_size p_tofind_length,t_size p_start) {
760  if (p_string_length >= p_tofind_length) {
761  t_size max = p_string_length - p_tofind_length;
762  for(t_size walk = p_start; walk <= max; walk++) {
763  if (memcmp(p_string+walk, p_tofind, p_tofind_length) == 0) return walk;
764  }
765  }
766  return ~0;
767 }
768 
769 
770 bool string_is_numeric(const char * p_string,t_size p_length) throw() {
771  bool retval = false;
772  for(t_size walk = 0; walk < p_length && p_string[walk] != 0; walk++) {
773  if (!char_is_numeric(p_string[walk])) {retval = false; break;}
774  retval = true;
775  }
776  return retval;
777 }
778 
779 
780 void string_base::end_with(char p_char) {
781  if (!ends_with(p_char)) add_byte(p_char);
782 }
783 bool string_base::ends_with(char c) const {
785  return length > 0 && get_ptr()[length-1] == c;
786 }
787 
790 }
791 
793  size_t l = this->length();
794  if (l == 0) return 0;
795  return this->get_ptr()[l-1];
796 }
798  size_t l = this->length();
799  if (l > 0) this->truncate( l - 1 );
800 }
801 
803  size_t l = this->length();
804  const char * const p = this->get_ptr();
805  while( l > 0 && char_is_numeric( p[l-1] ) ) --l;
806  truncate( l );
807 }
808 
809 bool is_multiline(const char * p_string,t_size p_len) {
810  for(t_size n = 0; n < p_len && p_string[n]; n++) {
811  switch(p_string[n]) {
812  case '\r':
813  case '\n':
814  return true;
815  }
816  }
817  return false;
818 }
819 
820 static t_uint64 pow10_helper(unsigned p_extra) {
821  t_uint64 ret = 1;
822  for(unsigned n = 0; n < p_extra; n++ ) ret *= 10;
823  return ret;
824 }
825 
826 static uint64_t safeMulAdd(uint64_t prev, unsigned scale, uint64_t add) {
827  if (add >= scale || scale == 0) throw pfc::exception_invalid_params();
828  uint64_t v = prev * scale + add;
829  if (v / scale != prev) throw pfc::exception_invalid_params();
830  return v;
831 }
832 
833 static size_t parseNumber(const char * in, uint64_t & outNumber) {
834  size_t walk = 0;
835  uint64_t total = 0;
836  for (;;) {
837  char c = in[walk];
838  if (!pfc::char_is_numeric(c)) break;
839  unsigned v = (unsigned)(c - '0');
840  uint64_t newVal = total * 10 + v;
841  if (newVal / 10 != total) throw pfc::exception_overflow();
842  total = newVal;
843  ++walk;
844  }
845  outNumber = total;
846  return walk;
847 }
848 
849 double parse_timecode(const char * in) {
850  char separator = 0;
851  uint64_t seconds = 0;
852  unsigned colons = 0;
853  for (;;) {
854  uint64_t number = 0;
855  size_t digits = parseNumber(in, number);
856  if (digits == 0) throw pfc::exception_invalid_params();
857  in += digits;
858  char nextSeparator = *in;
859  switch (separator) { // *previous* separator
860  case '.':
861  if (nextSeparator != 0) throw pfc::exception_bug_check();
862  return (double)seconds + (double)pfc::exp_int(10, -(int)digits) * number;
863  case 0: // is first number in the string
864  seconds = number;
865  break;
866  case ':':
867  if (colons == 2) throw pfc::exception_invalid_params();
868  ++colons;
869  seconds = safeMulAdd(seconds, 60, number);
870  break;
871  }
872 
873  if (nextSeparator == 0) {
874  // end of string
875  return (double)seconds;
876  }
877 
878  ++in;
879  separator = nextSeparator;
880  }
881 }
882 
883 format_time_ex::format_time_ex(double p_seconds,unsigned p_extra) {
884  t_uint64 pow10 = pow10_helper(p_extra);
885  t_uint64 ticks = pfc::rint64(pow10 * p_seconds);
886 
887  m_buffer << pfc::format_time(ticks / pow10);
888  if (p_extra>0) {
889  m_buffer << "." << pfc::format_uint(ticks % pow10, p_extra);
890  }
891 }
892 
893 void stringToUpperAppend(string_base & out, const char * src, t_size len) {
894  while(len && *src) {
895  unsigned c; t_size d;
896  d = utf8_decode_char(src,c,len);
897  if (d==0 || d>len) break;
898  out.add_char(charUpper(c));
899  src+=d;
900  len-=d;
901  }
902 }
903 void stringToLowerAppend(string_base & out, const char * src, t_size len) {
904  while(len && *src) {
905  unsigned c; t_size d;
906  d = utf8_decode_char(src,c,len);
907  if (d==0 || d>len) break;
908  out.add_char(charLower(c));
909  src+=d;
910  len-=d;
911  }
912 }
914  t_size w1 = 0, w2 = 0;
915  for(;;) {
916  unsigned c1, c2; t_size d1, d2;
917  d1 = utf8_decode_char(s1.m_ptr + w1, c1, s1.m_len - w1);
918  d2 = utf8_decode_char(s2.m_ptr + w2, c2, s2.m_len - w2);
919  if (d1 == 0 && d2 == 0) return 0;
920  else if (d1 == 0) return -1;
921  else if (d2 == 0) return 1;
922  else {
923  c1 = charLower(c1); c2 = charLower(c2);
924  if (c1 < c2) return -1;
925  else if (c1 > c2) return 1;
926  }
927  w1 += d1; w2 += d2;
928  }
929 }
930 int stringCompareCaseInsensitive(const char * s1, const char * s2) {
931  for(;;) {
932  unsigned c1, c2; t_size d1, d2;
933  d1 = utf8_decode_char(s1,c1);
934  d2 = utf8_decode_char(s2,c2);
935  if (d1 == 0 && d2 == 0) return 0;
936  else if (d1 == 0) return -1;
937  else if (d2 == 0) return 1;
938  else {
939  c1 = charLower(c1); c2 = charLower(c2);
940  if (c1 < c2) return -1;
941  else if (c1 > c2) return 1;
942  }
943  s1 += d1; s2 += d2;
944  }
945 }
946 
948  t_uint64 scale = 1;
949  const char * unit = "B";
950  const char * const unitTable[] = {"B","KB","MB","GB","TB"};
951  for(t_size walk = 1; walk < PFC_TABSIZE(unitTable); ++walk) {
952  t_uint64 next = scale * 1024;
953  if (size < next) break;
954  scale = next; unit = unitTable[walk];
955  }
956  *this << ( size / scale );
957 
958  if (scale > 1 && length() < 3) {
959  t_size digits = 3 - length();
960  const t_uint64 mask = pow_int(10,digits);
961  t_uint64 remaining = ( (size * mask / scale) % mask );
962  while(digits > 0 && (remaining % 10) == 0) {
963  remaining /= 10; --digits;
964  }
965  if (digits > 0) {
966  *this << "." << format_uint(remaining, (t_uint32)digits);
967  }
968  }
969  *this << " " << unit;
970  m_scale = scale;
971 }
972 
974 {
975  const char * ptr = get_ptr() + start;
976  for(t_size n=start;*ptr;n++)
977  {
978  if (*ptr==10 || *ptr==13)
979  {
980  truncate(n);
981  return true;
982  }
983  ptr++;
984  }
985  return false;
986 }
987 
988 bool string_base::fix_eol(const char * append,t_size start)
989 {
990  const bool rv = truncate_eol(start);
991  if (rv) add_string(append);
992  return rv;
993 }
994 
995 bool string_base::limit_length(t_size length_in_chars,const char * append)
996 {
997  bool rv = false;
998  const char * base = get_ptr(), * ptr = base;
999  while(length_in_chars && utf8_advance(ptr)) length_in_chars--;
1000  if (length_in_chars==0)
1001  {
1002  truncate(ptr-base);
1003  add_string(append);
1004  rv = true;
1005  }
1006  return rv;
1007 }
1008 
1010  size_t at = scan_filename();
1011 #ifdef _WIN32
1012  while(at > 0 && (*this)[at-1] == '\\') --at;
1013  if (at > 0 && (*this)[at-1] == ':' && (*this)[at] == '\\') ++at;
1014 #else
1015  // Strip trailing /
1016  while(at > 0 && (*this)[at-1] == '/') --at;
1017 
1018  // Hit empty? Bring root / back to life
1019  if (at == 0 && (*this)[0] == '/') ++at;
1020 
1021  // Deal with proto://
1022  if (at > 0 && (*this)[at-1] == ':') {
1023  while((*this)[at] == '/') ++at;
1024  }
1025 #endif
1026  this->truncate( at );
1027 }
1028 
1029 t_size string_base::replace_string ( const char * replace, const char * replaceWith, t_size start) {
1030  string_formatter temp;
1031  size_t srcDone = 0, walk = start;
1032  size_t occurances = 0;
1033  const char * const source = this->get_ptr();
1034 
1035  const size_t replaceLen = strlen( replace );
1036  for(;;) {
1037  const char * ptr = strstr( source + walk, replace );
1038  if (ptr == NULL) {
1039  // end
1040  if (srcDone == 0) {
1041  return 0; // string not altered
1042  }
1043  temp.add_string( source + srcDone );
1044  break;
1045  }
1046  ++occurances;
1047  walk = ptr - source;
1048  temp.add_string( source + srcDone, walk - srcDone );
1049  temp.add_string( replaceWith );
1050  walk += replaceLen;
1051  srcDone = walk;
1052  }
1053  this->set_string( temp );
1054  return occurances;
1055 
1056 }
1057 void urlEncodeAppendRaw(pfc::string_base & out, const char * in, t_size inSize) {
1058  for(t_size walk = 0; walk < inSize; ++walk) {
1059  const char c = in[walk];
1060  if (c == ' ') out.add_byte('+');
1061  else if (pfc::char_is_ascii_alphanumeric(c) || c == '_') out.add_byte(c);
1062  else out << "%" << pfc::format_hex((t_uint8)c, 2);
1063  }
1064 }
1065 void urlEncodeAppend(pfc::string_base & out, const char * in) {
1066  for(;;) {
1067  const char c = *(in++);
1068  if (c == 0) break;
1069  else if (c == ' ') out.add_byte('+');
1070  else if (pfc::char_is_ascii_alphanumeric(c) || c == '_') out.add_byte(c);
1071  else out << "%" << pfc::format_hex((t_uint8)c, 2);
1072  }
1073 }
1074 void urlEncode(pfc::string_base & out, const char * in) {
1075  out.reset(); urlEncodeAppend(out, in);
1076 }
1077 
1078 unsigned char_to_dec(char c) {
1079  if (c >= '0' && c <= '9') return (unsigned)(c - '0');
1080  else throw exception_invalid_params();
1081 }
1082 
1083 unsigned char_to_hex(char c) {
1084  if (c >= '0' && c <= '9') return (unsigned)(c - '0');
1085  else if (c >= 'a' && c <= 'f') return (unsigned)(c - 'a' + 10);
1086  else if (c >= 'A' && c <= 'F') return (unsigned)(c - 'A' + 10);
1087  else throw exception_invalid_params();
1088 }
1089 
1090 
1091 static const t_uint8 ascii_tolower_table[128] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F};
1092 
1093 uint32_t charLower(uint32_t param)
1094 {
1095  if (param<128) {
1096  return ascii_tolower_table[param];
1097  }
1098 #ifdef _WIN32
1099  else if (param<0x10000) {
1100  return (unsigned)CharLowerW((WCHAR*)param);
1101  }
1102 #endif
1103  else return param;
1104 }
1105 
1106 uint32_t charUpper(uint32_t param)
1107 {
1108  if (param<128) {
1109  if (param>='a' && param<='z') param += 'A' - 'a';
1110  return param;
1111  }
1112 #ifdef _WIN32
1113  else if (param<0x10000) {
1114  return (unsigned)CharUpperW((WCHAR*)param);
1115  }
1116 #endif
1117  else return param;
1118 }
1119 
1120 
1121 bool stringEqualsI_ascii(const char * p1,const char * p2) throw() {
1122  for(;;)
1123  {
1124  char c1 = *p1;
1125  char c2 = *p2;
1126  if (c1 > 0 && c2 > 0) {
1127  if (ascii_tolower_table[c1] != ascii_tolower_table[c2]) return false;
1128  } else {
1129  if (c1 == 0 && c2 == 0) return true;
1130  if (c1 == 0 || c2 == 0) return false;
1131  if (c1 != c2) return false;
1132  }
1133  ++p1; ++p2;
1134  }
1135 }
1136 
1137 bool stringEqualsI_utf8(const char * p1,const char * p2) throw()
1138 {
1139  for(;;)
1140  {
1141  char c1 = *p1;
1142  char c2 = *p2;
1143  if (c1 > 0 && c2 > 0) {
1144  if (ascii_tolower_table[c1] != ascii_tolower_table[c2]) return false;
1145  ++p1; ++p2;
1146  } else {
1147  if (c1 == 0 && c2 == 0) return true;
1148  if (c1 == 0 || c2 == 0) return false;
1149  unsigned w1,w2; t_size d1,d2;
1150  d1 = utf8_decode_char(p1,w1);
1151  d2 = utf8_decode_char(p2,w2);
1152  if (d1 == 0 || d2 == 0) return false; // bad UTF-8, bail
1153  if (w1 != w2) {
1154  if (charLower(w1) != charLower(w2)) return false;
1155  }
1156  p1 += d1;
1157  p2 += d2;
1158  }
1159  }
1160 }
1161 
1162 char ascii_tolower_lookup(char c) {
1163  PFC_ASSERT( c >= 0);
1164  return (char)ascii_tolower_table[c];
1165 }
1166 
1168 #ifdef _WIN32
1169  end_with(c);
1170 #else
1171  end_with_slash();
1172 #endif
1173 }
1174 
1175 
1176  bool string_has_prefix( const char * string, const char * prefix ) {
1177  for(size_t w = 0; ; ++w ) {
1178  char c = prefix[w];
1179  if (c == 0) return true;
1180  if (string[w] != c) return false;
1181  }
1182  }
1183  bool string_has_prefix_i( const char * string, const char * prefix ) {
1184  const char * p1 = string; const char * p2 = prefix;
1185  for(;;) {
1186  unsigned w1, w2; size_t d1, d2;
1187  d1 = utf8_decode_char(p1, w1);
1188  d2 = utf8_decode_char(p2, w2);
1189  if (d2 == 0) return true;
1190  if (d1 == 0) return false;
1191  if (w1 != w2) {
1192  if (charLower(w1) != charLower(w2)) return false;
1193  }
1194  p1 += d1; p2 += d2;
1195  }
1196  }
1197  bool string_has_suffix( const char * string, const char * suffix ) {
1198  size_t len = strlen( string );
1199  size_t suffixLen = strlen( suffix );
1200  if (suffixLen > len) return false;
1201  size_t base = len - suffixLen;
1202  return memcmp( string + base, suffix, suffixLen * sizeof(char)) == 0;
1203  }
1204  bool string_has_suffix_i( const char * string, const char * suffix ) {
1205  for(;;) {
1206  if (*string == 0) return false;
1207  if (stringEqualsI_utf8( string, suffix )) return true;
1208  if (!utf8_advance(string)) return false;
1209  }
1210  }
1211 
1212 } //namespace pfc
char ascii_tolower_lookup(char c)
bool stringEqualsI_ascii(const char *p1, const char *p2)
char format_hex_char_lowercase(unsigned p_val)
virtual t_size get_length() const
Definition: string_base.h:199
void set_string(const char *p_string, t_size p_length=~0)
bool string_has_suffix(const char *string, const char *suffix)
bool fix_eol(const char *append=" (...)", t_size start=0)
format_fixedpoint(t_int64 p_val, unsigned p_point)
double exp_int(double base, int exp)
Definition: other.cpp:143
char m_buffer[64]
Definition: string_base.h:583
uint8_t t_uint8
Definition: int_types.h:9
char m_buffer[17]
Definition: string_base.h:595
t_size find_last(char p_char, t_size p_start=~0) const
Definition: string_base.h:237
t_size strstr_ex(const char *p_string, t_size p_string_len, const char *p_substring, t_size p_substring_len)
void add_chars(t_uint32 p_char, t_size p_count)
Definition: string_base.h:43
unsigned char_to_hex(char c)
bool stringEqualsI_utf8(const char *p1, const char *p2)
t_uint64 pow_int(t_uint64 base, t_uint64 exp)
Definition: other.cpp:128
bool is_path_separator(unsigned c)
Definition: string_base.cpp:59
virtual void add_string(const char *p_string, t_size p_length=~0)=0
uint64_t t_uint64
Definition: int_types.h:3
void truncate(t_size len)
Definition: string_base.h:389
static uint64_t safeMulAdd(uint64_t prev, unsigned scale, uint64_t add)
double string_to_float(const char *src, t_size max)
format_time_ex(double p_seconds, unsigned p_extra=3)
bool string_is_numeric(const char *p_string, t_size p_length)
void fix_dir_separator(char c= '\\')
format_hexdump_lowercase(const void *p_buffer, t_size p_bytes, const char *p_spacing=" ")
void truncate_to_parent_path()
t_size utf8_encode_char(unsigned c, char *out)
Definition: utf8.cpp:113
const char * m_ptr
Definition: string_base.h:13
string_filename(const char *fn)
Definition: string_base.cpp:87
int _strcmp_partial_ex(const t_char *p_string, t_size p_string_length, const t_char *p_substring, t_size p_substring_length)
Definition: string_base.h:1069
int stricmp_ascii_ex(const char *const s1, t_size const len1, const char *const s2, t_size const len2)
string_formatter m_formatter
Definition: string_base.h:626
bool is_multiline(const char *p_string, t_size p_len)
bool string_has_suffix_i(const char *string, const char *suffix)
t_size scan_filename() const
Definition: string_base.h:234
format_file_size_short(t_uint64 size)
int stringCompareCaseInsensitiveEx(string_part_ref s1, string_part_ref s2)
int naturalSortCompare(const char *s1, const char *s2)
bool ends_with(char c) const
char_t ascii_tolower(char_t c)
Definition: string_base.h:60
format_time(t_uint64 p_seconds)
Definition: string_base.cpp:36
string_fixed_t< 127 > m_buffer
Definition: string_base.h:494
static size_t parseNumber(const char *in, uint64_t &outNumber)
void urlEncode(pfc::string_base &out, const char *in)
bool utf8_advance(const char *&var)
Definition: string_base.h:178
int strcmp_ex(const char *p1, t_size n1, const char *p2, t_size n2)
t_size string_find_first_ex(const char *p_string, t_size p_string_length, char p_tofind, t_size p_start)
int stricmp_ascii_partial(const char *str, const char *substr)
bool limit_length(t_size length_in_chars, const char *append=" (...)")
t_size string_find_first_nc(const char *p_string, t_size p_string_length, char c, t_size p_start)
void stringToLowerAppend(string_base &out, const char *src, t_size len)
string_extension(const char *src)
t_size strlen_max(const char *ptr, t_size max)
Definition: string_base.h:91
virtual void add_string(const char *p_string, t_size p_string_size=~0)=0
size_t t_size
Definition: int_types.h:48
string8_fastalloc string_formatter
Definition: string_base.h:614
string_replace_extension(const char *p_path, const char *p_ext)
double parse_timecode(const char *in)
int strcmp_nc(const char *p1, size_t n1, const char *p2, size_t n2)
t_uint64 atoui64_ex(const char *src, t_size len)
t_int64 rint64(double p_val)
Definition: primitives.h:713
void urlEncodeAppendRaw(pfc::string_base &out, const char *in, t_size inSize)
t_size scan_filename(const char *ptr)
void skip_trailing_char(unsigned c= ' ')
Definition: string_base.cpp:12
char * strdup_n(const char *src, t_size len)
Definition: string_base.cpp:75
virtual void set_string(const char *p_string, t_size p_length=~0)
Definition: string_base.h:197
void stringToUpperAppend(string_base &out, const char *src, t_size len)
format_uint(t_uint64 p_val, unsigned p_width=0, unsigned p_base=10)
format_hexdump(const void *p_buffer, t_size p_bytes, const char *p_spacing=" ")
string_fixed_t< 127 > m_buffer
Definition: string_base.h:484
void SHARED_EXPORT scale(const audio_sample *p_source, t_size p_count, audio_sample *p_output, audio_sample p_scale)
p_source/p_output can point to same buffer
Definition: audio_math.cpp:63
char last_char() const
unsigned atoui_ex(const char *p_string, t_size p_string_len)
t_int64 atoi64_ex(const char *src, t_size len)
string_formatter m_buffer
Definition: string_base.h:650
bool string_has_prefix_i(const char *string, const char *prefix)
t_size string_find_last_ex(const char *p_string, t_size p_string_length, char p_tofind, t_size p_start)
t_size utf8_decode_char(const char *src, unsigned &out, t_size src_bytes)
Definition: utf8.cpp:64
bool string_has_prefix(const char *string, const char *prefix)
void add_char(t_uint32 c)
Definition: string_base.cpp:5
void urlEncodeAppend(pfc::string_base &out, const char *in)
uint32_t charLower(uint32_t param)
int naturalSortCompareI(const char *s1, const char *s2)
string_formatter m_formatter
Definition: string_base.h:639
void float_to_string(char *out, t_size out_max, double val, unsigned precision, bool b_sign)
pfc::sized_int_t< sizeof(size_t) >::t_signed t_ssize
Definition: int_types.h:49
void end_with(char c)
char getDefaultSeparator()
Definition: pathUtils.cpp:124
uint32_t charUpper(uint32_t param)
virtual const char * get_ptr() const =0
t_size string_find_last(const char *p_string, char p_tofind, t_size p_start)
format_int(t_int64 p_val, unsigned p_width=0, unsigned p_base=10)
char m_buffer[64]
Definition: string_base.h:571
int stringCompareCaseInsensitive(const char *s1, const char *s2)
format_float(double p_val, unsigned p_width=0, unsigned p_prec=7)
void convert_to_lower_ascii(const char *src, char replace= '?')
unsigned char_to_dec(char c)
int stricmp_ascii(const char *s1, const char *s2)
void add_byte(char c)
Definition: string_base.h:42
static const t_uint8 ascii_tolower_table[128]
bool truncate_eol(t_size start=0)
bool char_is_numeric(char_t p_char)
Definition: string_base.h:99
void truncate_last_char()
bool is_path_bad_char(unsigned c)
Definition: string_base.cpp:64
t_size string_find_first(const char *p_string, char p_tofind, t_size p_start)
char format_hex_char(unsigned p_val)
static int naturalSortCompareInternal(const char *s1, const char *s2, bool insensitive)
format_hex(t_uint64 p_val, unsigned p_width=0)
void truncate_number_suffix()
virtual void truncate(t_size len)=0
static t_uint64 pow10_helper(unsigned p_extra)
bool char_is_ascii_alphanumeric(char p_char)
Definition: string_base.h:104
static double pfc_string_to_float_internal(const char *src)
T max_t(const T &item1, const T &item2)
Definition: primitives.h:553
bool has_path_bad_chars(const char *param)
string_filename_ext(const char *fn)
t_size utf8_char_len(const char *s, t_size max=~0)
Definition: utf8.cpp:246
void convert_to_lower_ascii(const char *src, t_size max, char *out, char replace)
string_directory(const char *p_path)
format_hex_lowercase(t_uint64 p_val, unsigned p_width=0)
uint32_t t_uint32
Definition: int_types.h:5
t_size replace_string(const char *replace, const char *replaceWith, t_size start=0)
int64_t t_int64
Definition: int_types.h:2
t_size length() const
For compatibility with old conventions.
Definition: string_base.h:208