Mercurial > foo_out_sdl
diff foosdk/sdk/pfc/string_base.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/pfc/string_base.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,1403 @@ +#include "pfc-lite.h" +#include "string_base.h" +#include "pathUtils.h" +#include "primitives.h" +#include "other.h" +#include <set> +#include <math.h> +#include "splitString2.h" + +namespace pfc { + +bool string_base::is_valid_utf8() const { return pfc::is_valid_utf8(get_ptr()); } +t_size string_base::scan_filename() const { return pfc::scan_filename(get_ptr()); } +t_size string_base::find_first(char p_char, t_size p_start) const { return pfc::string_find_first(get_ptr(), p_char, p_start); } +t_size string_base::find_last(char p_char, t_size p_start) const { return pfc::string_find_last(get_ptr(), p_char, p_start); } +t_size string_base::find_first(const char* p_string, t_size p_start) const { return pfc::string_find_first(get_ptr(), p_string, p_start); } +t_size string_base::find_last(const char* p_string, t_size p_start) const { return pfc::string_find_last(get_ptr(), p_string, p_start); } +bool string_base::has_prefix(const char* prefix) const { return string_has_prefix(get_ptr(), prefix); } +bool string_base::has_prefix_i(const char* prefix) const { return string_has_prefix_i(get_ptr(), prefix); } +bool string_base::has_suffix(const char* suffix) const { return string_has_suffix(get_ptr(), suffix); } +bool string_base::has_suffix_i(const char* suffix) const { return string_has_suffix_i(get_ptr(), suffix); } +bool string_base::equals(const char* other) const { return strcmp(*this, other) == 0; } + +void string_receiver::add_char(t_uint32 p_char) +{ + char temp[8]; + t_size len = utf8_encode_char(p_char,temp); + if (len>0) add_string(temp,len); +} + +void string_base::skip_trailing_chars( const char * lstCharsStr ) { + std::set<unsigned> lstChars; + for ( ;; ) { + unsigned c; + auto delta = utf8_decode_char( lstCharsStr, c ); + if ( delta == 0 ) break; + lstCharsStr += delta; + lstChars.insert( c ); + } + + const char * str = get_ptr(); + t_size ptr,trunc = 0; + bool need_trunc = false; + for(ptr=0;str[ptr];) + { + unsigned c; + t_size delta = utf8_decode_char(str+ptr,c); + if (delta==0) break; + if ( lstChars.count( c ) > 0 ) + { + if (!need_trunc) { + need_trunc = true; + trunc = ptr; + } + } + else + { + need_trunc = false; + } + ptr += delta; + } + if (need_trunc) truncate(trunc); +} + +void string_base::skip_trailing_char(unsigned skip) +{ + const char * str = get_ptr(); + t_size ptr,trunc = 0; + bool need_trunc = false; + for(ptr=0;str[ptr];) + { + unsigned c; + t_size delta = utf8_decode_char(str+ptr,c); + if (delta==0) break; + if (c==skip) + { + if (!need_trunc) { + need_trunc = true; + trunc = ptr; + } + } + else + { + need_trunc = false; + } + ptr += delta; + } + if (need_trunc) truncate(trunc); +} + +string8 format_time(uint64_t p_seconds) { + string8 ret; + t_uint64 length = p_seconds; + unsigned weeks,days,hours,minutes,seconds; + + weeks = (unsigned)( ( length / (60*60*24*7) ) ); + days = (unsigned)( ( length / (60*60*24) ) % 7 ); + hours = (unsigned) ( ( length / (60 * 60) ) % 24); + minutes = (unsigned) ( ( length / (60 ) ) % 60 ); + seconds = (unsigned) ( ( length ) % 60 ); + + if (weeks) { + ret << weeks << "wk "; + } + if (days || weeks) { + ret << days << "d "; + } + if (hours || days || weeks) { + ret << hours << ":" << format_uint(minutes,2) << ":" << format_uint(seconds,2); + } else { + ret << minutes << ":" << format_uint(seconds,2); + } + return ret; +} + +bool is_path_separator(unsigned c) +{ +#ifdef _WIN32 + return c=='\\' || c=='/' || c=='|' || c==':'; +#else + return c == '/'; +#endif +} + +bool is_path_bad_char(unsigned c) +{ +#ifdef _WINDOWS + return c=='\\' || c=='/' || c=='|' || c==':' || c=='*' || c=='?' || c=='\"' || c=='>' || c=='<'; +#else + return c=='/' || c=='*' || c=='?'; +#endif +} + + + +char * strdup_n(const char * src,t_size len) +{ + len = strlen_max(src,len); + char * ret = (char*)malloc(len+1); + if (ret) + { + memcpy(ret,src,len); + ret[len]=0; + } + return ret; +} + +string8 string_filename(const char * fn) +{ + string8 ret; + fn += pfc::scan_filename(fn); + const char * ptr=fn,*dot=0; + while(*ptr && *ptr!='?') + { + if (*ptr=='.') dot=ptr; + ptr++; + } + + if (dot && dot>fn) ret.set_string(fn,dot-fn); + else ret.set_string(fn); + return ret; +} + +const char * extract_ext_v2( const char * filenameDotExt ) { + auto split = strrchr(filenameDotExt, '.'); + return split ? split+1 : ""; +} + +string8 remove_ext_v2( const char * filenameDotExt ) { + auto split = strrchr(filenameDotExt, '.'); + string8 ret; + if ( split ) ret.set_string_nc( filenameDotExt, split-filenameDotExt ); + else ret = filenameDotExt; + return ret; +} + +const char * filename_ext_v2( const char * fn, char slash ) { + if ( slash == 0 ) { + slash = pfc::io::path::getDefaultSeparator(); + } + size_t split = pfc::string_find_last( fn, slash ); + if ( split == SIZE_MAX ) return fn; + return fn + split + 1; +} + +string8 string_filename_ext(const char * fn) +{ + string8 ret; + fn += pfc::scan_filename(fn); + const char * ptr = fn; + while(*ptr && *ptr!='?') ptr++; + ret.set_string(fn,ptr-fn); + return ret; +} + +size_t find_extension_offset(const char * src) { + const char * start = src + pfc::scan_filename(src); + const char * end = start + strlen(start); + const char * ptr = end - 1; + while (ptr > start && *ptr != '.') + { + if (*ptr == '?') end = ptr; + ptr--; + } + + if (ptr >= start && *ptr == '.') + { + return ptr - src; + } + + return SIZE_MAX; +} + +string8 string_extension(const char * src) +{ + string8 ret; + const char * start = src + pfc::scan_filename(src); + const char * end = start + strlen(start); + const char * ptr = end-1; + while(ptr>start && *ptr!='.') + { + if (*ptr=='?') end=ptr; + ptr--; + } + + if (ptr>=start && *ptr=='.') + { + ptr++; + ret.set_string(ptr, end-ptr); + } + return ret; +} + + +bool has_path_bad_chars(const char * param) +{ + while(*param) + { + if (is_path_bad_char(*param)) return true; + param++; + } + return false; +} + +void float_to_string(char * out,t_size out_max,double val,unsigned precision,bool b_sign) { + pfc::string_fixed_t<63> temp; + t_size outptr; + + if (out_max == 0) return; + out_max--;//for null terminator + + outptr = 0; + + if (outptr == out_max) {out[outptr]=0;return;} + + if (val<0) {out[outptr++] = '-'; val = -val;} + else if (val > 0 && b_sign) {out[outptr++] = '+';} + + if (outptr == out_max) {out[outptr]=0;return;} + + + { + double powval = pow((double)10.0,(double)precision); + temp << (t_int64)floor(val * powval + 0.5); + //_i64toa(blargh,temp,10); + } + + const t_size temp_len = temp.length(); + if (temp_len <= precision) + { + out[outptr++] = '0'; + if (outptr == out_max) {out[outptr]=0;return;} + out[outptr++] = '.'; + if (outptr == out_max) {out[outptr]=0;return;} + t_size d; + for(d=precision-temp_len;d;d--) + { + out[outptr++] = '0'; + if (outptr == out_max) {out[outptr]=0;return;} + } + for(d=0;d<temp_len;d++) + { + out[outptr++] = temp[d]; + if (outptr == out_max) {out[outptr]=0;return;} + } + } + else + { + t_size d = temp_len; + const char * src = temp; + while(*src) + { + if (d-- == precision) + { + out[outptr++] = '.'; + if (outptr == out_max) {out[outptr]=0;return;} + } + out[outptr++] = *(src++); + if (outptr == out_max) {out[outptr]=0;return;} + } + } + out[outptr] = 0; +} + + + +static double pfc_string_to_float_internal(const char * src) noexcept +{ + bool neg = false; + t_int64 val = 0; + int div = 0; + bool got_dot = false; + + while(*src==' ') src++; + + if (*src=='-') {neg = true;src++;} + else if (*src=='+') src++; + + while(*src) + { + if (*src>='0' && *src<='9') + { + int d = *src - '0'; + val = val * 10 + d; + if (got_dot) div--; + src++; + } + else if (*src=='.' || *src==',') + { + if (got_dot) break; + got_dot = true; + src++; + } + else if (*src=='E' || *src=='e') + { + src++; + div += atoi(src); + break; + } + else break; + } + if (neg) val = -val; + + if (val != 0) { + // SPECIAL FIX: ensure 0.2 and 0.200000 return the EXACT same float + while (val % 10 == 0) { + val /= 10; ++div; + } + } + return (double) val * exp_int(10, div); +} + +double string_to_float(const char * src) noexcept { + return pfc_string_to_float_internal(src); +} +double string_to_float(const char * src,t_size max) noexcept { + char blargh[128]; + if (max > 127) max = 127; + t_size walk; + for(walk = 0; walk < max && src[walk]; walk++) blargh[walk] = src[walk]; + blargh[walk] = 0; + return pfc_string_to_float_internal(blargh); +} + + + +void string_base::convert_to_lower_ascii(const char * src,char replace) +{ + reset(); + PFC_ASSERT(replace>0); + while(*src) + { + unsigned c; + t_size delta = utf8_decode_char(src,c); + if (delta==0) {c = replace; delta = 1;} + else if (c>=0x80) c = replace; + add_byte((char)c); + src += delta; + } +} + +void convert_to_lower_ascii(const char * src,t_size max,char * out,char replace) +{ + t_size ptr = 0; + PFC_ASSERT(replace>0); + while(ptr<max && src[ptr]) + { + unsigned c; + t_size delta = utf8_decode_char(src+ptr,c,max-ptr); + if (delta==0) {c = replace; delta = 1;} + else if (c>=0x80) c = replace; + *(out++) = (char)c; + ptr += delta; + } + *out = 0; +} + +t_size strstr_ex(const char * p_string,t_size p_string_len,const char * p_substring,t_size p_substring_len) noexcept +{ + p_string_len = strlen_max(p_string,p_string_len); + p_substring_len = strlen_max(p_substring,p_substring_len); + t_size index = 0; + while(index + p_substring_len <= p_string_len) + { + if (memcmp(p_string+index,p_substring,p_substring_len) == 0) return index; + t_size delta = utf8_char_len(p_string+index,p_string_len - index); + if (delta == 0) break; + index += delta; + } + return SIZE_MAX; +} + +unsigned atoui_ex(const char * p_string,t_size p_string_len) noexcept +{ + unsigned ret = 0; t_size ptr = 0; + while(ptr<p_string_len) + { + char c = p_string[ptr]; + if (! ( c >= '0' && c <= '9' ) ) break; + ret = ret * 10 + (unsigned)( c - '0' ); + ptr++; + } + return ret; +} + +int strcmp_nc(const char* p1, size_t n1, const char * p2, size_t n2) noexcept { + t_size idx = 0; + for(;;) + { + if (idx == n1 && idx == n2) return 0; + else if (idx == n1) return -1;//end of param1 + else if (idx == n2) return 1;//end of param2 + + char c1 = p1[idx], c2 = p2[idx]; + if (c1<c2) return -1; + else if (c1>c2) return 1; + + idx++; + } +} + +int strcmp_ex(const char* p1,t_size n1,const char* p2,t_size n2) noexcept +{ + n1 = strlen_max(p1,n1); n2 = strlen_max(p2,n2); + return strcmp_nc(p1, n1, p2, n2); +} + +t_uint64 atoui64_ex(const char * src,t_size len) noexcept { + len = strlen_max(src,len); + t_uint64 ret = 0, mul = 1; + t_size ptr = len; + t_size start = 0; +// start += skip_spacing(src+start,len-start); + + while(ptr>start) + { + char c = src[--ptr]; + if (c>='0' && c<='9') + { + ret += (c-'0') * mul; + mul *= 10; + } + else + { + ret = 0; + mul = 1; + } + } + return ret; +} + + +t_int64 atoi64_ex(const char * src,t_size len) noexcept +{ + len = strlen_max(src,len); + t_int64 ret = 0, mul = 1; + t_size ptr = len; + t_size start = 0; + bool neg = false; +// start += skip_spacing(src+start,len-start); + if (start < len && src[start] == '-') {neg = true; start++;} +// start += skip_spacing(src+start,len-start); + + while(ptr>start) + { + char c = src[--ptr]; + if (c>='0' && c<='9') + { + ret += (c-'0') * mul; + mul *= 10; + } + else + { + ret = 0; + mul = 1; + } + } + return neg ? -ret : ret; +} + + +string8 format_float(double p_val,unsigned p_width,unsigned p_prec) +{ + string8 m_buffer; + char temp[64]; + float_to_string(temp,64,p_val,p_prec,false); + temp[63] = 0; + t_size len = strlen(temp); + if (len < p_width) + m_buffer.add_chars(' ',p_width-len); + m_buffer += temp; + return m_buffer; +} + +char format_hex_char(unsigned p_val) +{ + PFC_ASSERT(p_val < 16); + return (p_val < 10) ? (char)p_val + '0' : (char)p_val - 10 + 'A'; +} + +format_int_t format_hex(t_uint64 p_val,unsigned p_width) +{ + format_int_t ret; + + if (p_width > 16) p_width = 16; + else if (p_width == 0) p_width = 1; + char temp[16]; + unsigned n; + for(n=0;n<16;n++) + { + temp[15-n] = format_hex_char((unsigned)(p_val & 0xF)); + p_val >>= 4; + } + + for(n=0;n<16 && temp[n] == '0';n++) {} + + if (n > 16 - p_width) n = 16 - p_width; + + char * out = ret.m_buffer; + for(;n<16;n++) + *(out++) = temp[n]; + *out = 0; + return ret; +} + +char format_hex_char_lowercase(unsigned p_val) +{ + PFC_ASSERT(p_val < 16); + return (p_val < 10) ? (char)p_val + '0' : (char)p_val - 10 + 'a'; +} + +format_int_t format_hex_lowercase(t_uint64 p_val,unsigned p_width) +{ + format_int_t ret; + if (p_width > 16) p_width = 16; + else if (p_width == 0) p_width = 1; + char temp[16]; + unsigned n; + for(n=0;n<16;n++) + { + temp[15-n] = format_hex_char_lowercase((unsigned)(p_val & 0xF)); + p_val >>= 4; + } + + for(n=0;n<16 && temp[n] == '0';n++) {} + + if (n > 16 - p_width) n = 16 - p_width; + + char * out = ret.m_buffer; + for(;n<16;n++) + *(out++) = temp[n]; + *out = 0; + return ret; +} + +format_int_t format_uint(t_uint64 val,unsigned p_width,unsigned p_base) +{ + format_int_t ret; + + enum {max_width = PFC_TABSIZE(ret.m_buffer) - 1}; + + if (p_width > max_width) p_width = max_width; + else if (p_width == 0) p_width = 1; + + char temp[max_width]; + + unsigned n; + for(n=0;n<max_width;n++) + { + temp[max_width-1-n] = format_hex_char((unsigned)(val % p_base)); + val /= p_base; + } + + for(n=0;n<max_width && temp[n] == '0';n++) {} + + if (n > max_width - p_width) n = max_width - p_width; + + char * out = ret.m_buffer; + + for(;n<max_width;n++) + *(out++) = temp[n]; + *out = 0; + + return ret; +} + +string8 format_fixedpoint(t_int64 p_val,unsigned p_point) +{ + string8 m_buffer; + unsigned div = 1; + for(unsigned n=0;n<p_point;n++) div *= 10; + + if (p_val < 0) {m_buffer << "-";p_val = -p_val;} + + + m_buffer << format_int(p_val / div) << "." << format_int(p_val % div, p_point); + return m_buffer; +} + + +format_int_t format_int(t_int64 p_val,unsigned p_width,unsigned p_base) +{ + format_int_t ret; + bool neg = false; + t_uint64 val; + if (p_val < 0) {neg = true; val = (t_uint64)(-p_val);} + else val = (t_uint64)p_val; + + enum {max_width = PFC_TABSIZE(ret.m_buffer) - 1}; + + if (p_width > max_width) p_width = max_width; + else if (p_width == 0) p_width = 1; + + if (neg && p_width > 1) p_width --; + + char temp[max_width]; + + unsigned n; + for(n=0;n<max_width;n++) + { + temp[max_width-1-n] = format_hex_char((unsigned)(val % p_base)); + val /= p_base; + } + + for(n=0;n<max_width && temp[n] == '0';n++) {} + + if (n > max_width - p_width) n = max_width - p_width; + + char * out = ret.m_buffer; + + if (neg) *(out++) = '-'; + + for(;n<max_width;n++) + *(out++) = temp[n]; + *out = 0; + + return ret; +} + +string8 format_hexdump_lowercase(const void * p_buffer,t_size p_bytes,const char * p_spacing) +{ + string8 m_formatter; + t_size n; + const t_uint8 * buffer = (const t_uint8*)p_buffer; + for(n=0;n<p_bytes;n++) + { + if (n > 0 && p_spacing != 0) m_formatter << p_spacing; + m_formatter << format_hex_lowercase(buffer[n],2); + } + return m_formatter; +} + +string8 format_hexdump(const void * p_buffer,t_size p_bytes,const char * p_spacing) +{ + string8 m_formatter; + t_size n; + const t_uint8 * buffer = (const t_uint8*)p_buffer; + for(n=0;n<p_bytes;n++) + { + if (n > 0 && p_spacing != 0) m_formatter << p_spacing; + m_formatter << format_hex(buffer[n],2); + } + return m_formatter; +} + + + +string8 string_replace_extension(const char * p_path,const char * p_ext) +{ + string8 m_data; + m_data = p_path; + t_size dot = m_data.find_last('.'); + if (dot < m_data.scan_filename()) + {//argh + m_data += "."; + m_data += p_ext; + } + else + { + m_data.truncate(dot+1); + m_data += p_ext; + } + return m_data; +} + +string8 string_directory(const char * p_path) +{ + string8 ret; + t_size ptr = scan_filename(p_path); + if (ptr > 1) { + if (is_path_separator(p_path[ptr-1]) && !is_path_separator(p_path[ptr-2])) --ptr; + } + ret.set_string(p_path,ptr); + return ret; +} + +t_size scan_filename(const char * ptr) +{ + t_size n; + t_size _used = strlen(ptr); + for(n=_used;n!=0;n--) + { + if (is_path_separator(ptr[n-1])) return n; + } + return 0; +} + + + +t_size string_find_first(const char * p_string,char p_tofind,t_size p_start) { + for(t_size walk = p_start; p_string[walk]; ++walk) { + if (p_string[walk] == p_tofind) return walk; + } + return SIZE_MAX; +} +t_size string_find_last(const char * p_string,char p_tofind,t_size p_start) { + return string_find_last_ex(p_string,SIZE_MAX,&p_tofind,1,p_start); +} +t_size string_find_first(const char * p_string,const char * p_tofind,t_size p_start) { + return string_find_first_ex(p_string,SIZE_MAX,p_tofind,SIZE_MAX,p_start); +} +t_size string_find_last(const char * p_string,const char * p_tofind,t_size p_start) { + return string_find_last_ex(p_string,SIZE_MAX,p_tofind,SIZE_MAX,p_start); +} + +t_size string_find_first_ex(const char * p_string,t_size p_string_length,char p_tofind,t_size p_start) { + for(t_size walk = p_start; walk < p_string_length && p_string[walk]; ++walk) { + if (p_string[walk] == p_tofind) return walk; + } + return SIZE_MAX; +} +t_size string_find_last_ex(const char * p_string,t_size p_string_length,char p_tofind,t_size p_start) { + return string_find_last_ex(p_string,p_string_length,&p_tofind,1,p_start); +} +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) { + p_string_length = strlen_max(p_string,p_string_length); p_tofind_length = strlen_max(p_tofind,p_tofind_length); + if (p_string_length >= p_tofind_length) { + t_size max = p_string_length - p_tofind_length; + for(t_size walk = p_start; walk <= max; walk++) { + if (_strcmp_partial_ex(p_string+walk,p_string_length-walk,p_tofind,p_tofind_length) == 0) return walk; + } + } + return SIZE_MAX; +} +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) { + p_string_length = strlen_max(p_string,p_string_length); p_tofind_length = strlen_max(p_tofind,p_tofind_length); + if (p_string_length >= p_tofind_length) { + t_size max = min_t<t_size>(p_string_length - p_tofind_length,p_start); + for(t_size walk = max; walk != (t_size)(-1); walk--) { + if (_strcmp_partial_ex(p_string+walk,p_string_length-walk,p_tofind,p_tofind_length) == 0) return walk; + } + } + return SIZE_MAX; +} + +t_size string_find_first_nc(const char * p_string,t_size p_string_length,char c,t_size p_start) { + for(t_size walk = p_start; walk < p_string_length; walk++) { + if (p_string[walk] == c) return walk; + } + return SIZE_MAX; +} + +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) { + if (p_string_length >= p_tofind_length) { + t_size max = p_string_length - p_tofind_length; + for(t_size walk = p_start; walk <= max; walk++) { + if (memcmp(p_string+walk, p_tofind, p_tofind_length) == 0) return walk; + } + } + return SIZE_MAX; +} + + +bool string_is_numeric(const char * p_string,t_size p_length) noexcept { + bool retval = false; + for(t_size walk = 0; walk < p_length && p_string[walk] != 0; walk++) { + if (!char_is_numeric(p_string[walk])) {retval = false; break;} + retval = true; + } + return retval; +} + + +void string_base::end_with(char p_char) { + if (!ends_with(p_char)) add_byte(p_char); +} +bool string_base::ends_with(char c) const { + t_size length = get_length(); + return length > 0 && get_ptr()[length-1] == c; +} + +void string_base::end_with_slash() { + end_with( io::path::getDefaultSeparator() ); +} + +char string_base::last_char() const { + size_t l = this->length(); + if (l == 0) return 0; + return this->get_ptr()[l-1]; +} +void string_base::truncate_last_char() { + size_t l = this->length(); + if (l > 0) this->truncate( l - 1 ); +} + +void string_base::truncate_number_suffix() { + size_t l = this->length(); + const char * const p = this->get_ptr(); + while( l > 0 && char_is_numeric( p[l-1] ) ) --l; + truncate( l ); +} + +bool is_multiline(const char * p_string,t_size p_len) { + for(t_size n = 0; n < p_len && p_string[n]; n++) { + switch(p_string[n]) { + case '\r': + case '\n': + return true; + } + } + return false; +} + +static t_uint64 pow10_helper(unsigned p_extra) { + t_uint64 ret = 1; + for(unsigned n = 0; n < p_extra; n++ ) ret *= 10; + return ret; +} + +static uint64_t safeMulAdd(uint64_t prev, unsigned scale, uint64_t add) { + if (add >= scale || scale == 0) throw pfc::exception_invalid_params(); + uint64_t v = prev * scale + add; + if (v / scale != prev) throw pfc::exception_invalid_params(); + return v; +} + +static size_t parseNumber(const char * in, uint64_t & outNumber) { + size_t walk = 0; + uint64_t total = 0; + for (;;) { + char c = in[walk]; + if (!pfc::char_is_numeric(c)) break; + unsigned v = (unsigned)(c - '0'); + uint64_t newVal = total * 10 + v; + if (newVal / 10 != total) throw pfc::exception_overflow(); + total = newVal; + ++walk; + } + outNumber = total; + return walk; +} + +double parse_timecode(const char * in) { + char separator = 0; + uint64_t seconds = 0; + unsigned colons = 0; + for (;;) { + uint64_t number = 0; + size_t digits = parseNumber(in, number); + if (digits == 0) throw pfc::exception_invalid_params(); + in += digits; + char nextSeparator = *in; + switch (separator) { // *previous* separator + case '.': + if (nextSeparator != 0) throw pfc::exception_bug_check(); + return (double)seconds + (double)pfc::exp_int(10, -(int)digits) * number; + case 0: // is first number in the string + seconds = number; + break; + case ':': + if (colons == 2) throw pfc::exception_invalid_params(); + ++colons; + seconds = safeMulAdd(seconds, 60, number); + break; + } + + if (nextSeparator == 0) { + // end of string + return (double)seconds; + } + + ++in; + separator = nextSeparator; + } +} + +string8 format_time_ex(double p_seconds,unsigned p_extra) { + string8 ret; + if (p_seconds < 0) {ret << "-"; p_seconds = -p_seconds;} + t_uint64 pow10 = pow10_helper(p_extra); + t_uint64 ticks = pfc::rint64(pow10 * p_seconds); + + ret << pfc::format_time(ticks / pow10); + if (p_extra>0) { + ret << "." << pfc::format_uint(ticks % pow10, p_extra); + } + return ret; +} + +void stringToUpperHere(string_base& p_out, const char* p_source, t_size p_sourceLen) { + p_out.clear(); + stringToUpperAppend(p_out, p_source, p_sourceLen); +} +void stringToLowerHere(string_base& p_out, const char* p_source, t_size p_sourceLen) { + p_out.clear(); + stringToLowerAppend(p_out, p_source, p_sourceLen); +} + +void stringToUpperAppend(string_base & out, const char * src, t_size len) { + while(len && *src) { + unsigned c; t_size d; + d = utf8_decode_char(src,c,len); + if (d==0 || d>len) break; + out.add_char(charUpper(c)); + src+=d; + len-=d; + } +} +void stringToLowerAppend(string_base & out, const char * src, t_size len) { + while(len && *src) { + unsigned c; t_size d; + d = utf8_decode_char(src,c,len); + if (d==0 || d>len) break; + out.add_char(charLower(c)); + src+=d; + len-=d; + } +} + +string8 format_file_size_short(uint64_t size, uint64_t * outUsedScale) { + string8 ret; + t_uint64 scale = 1; + const char * unit = "B"; + const char * const unitTable[] = {"B","KB","MB","GB","TB"}; + for(t_size walk = 1; walk < PFC_TABSIZE(unitTable); ++walk) { + t_uint64 next = scale * 1024; + if (size < next) break; + scale = next; unit = unitTable[walk]; + } + ret << ( size / scale ); + + if (scale > 1 && ret.length() < 3) { + t_size digits = 3 - ret.length(); + const t_uint64 mask = pow_int(10,digits); + t_uint64 remaining = ( (size * mask / scale) % mask ); + while(digits > 0 && (remaining % 10) == 0) { + remaining /= 10; --digits; + } + if (digits > 0) { + ret << "." << format_uint(remaining, (t_uint32)digits); + } + } + ret << " " << unit; + if (outUsedScale != nullptr) *outUsedScale = scale; + return ret; +} + +pfc::string8 format_index(size_t idx) { + return idx == SIZE_MAX ? "<n/a>" : pfc::format_uint(idx); +} + +pfc::string8 format_permutation(const size_t* arg, size_t n) { + pfc::string_formatter ret; + for( size_t walk = 0; walk < n; ++ walk ) { + if (arg[walk] != walk) { + if ( !ret.is_empty() ) ret << ", "; + ret << arg[walk] << "->" << walk; + } + } + return ret; +} +pfc::string8 format_mask(pfc::bit_array const& mask, size_t n) { + pfc::string_formatter ret; + mask.for_each(true, 0, n, [&] (size_t idx) { + if (!ret.is_empty() ) ret << ", "; + ret << idx; + }); + return ret; +} + +bool string_base::truncate_eol(t_size start) +{ + const char * ptr = get_ptr() + start; + for(t_size n=start;*ptr;n++) + { + if (*ptr==10 || *ptr==13) + { + truncate(n); + return true; + } + ptr++; + } + return false; +} + +bool string_base::fix_eol(const char * append,t_size start) +{ + const bool rv = truncate_eol(start); + if (rv) add_string(append); + return rv; +} + +bool string_base::limit_length(t_size length_in_chars,const char * append) +{ + bool rv = false; + const char * base = get_ptr(), * ptr = base; + while(length_in_chars && utf8_advance(ptr)) length_in_chars--; + if (length_in_chars==0) + { + truncate(ptr-base); + add_string(append); + rv = true; + } + return rv; +} + +void string_base::truncate_to_parent_path() { + size_t at = scan_filename(); +#ifdef _WIN32 + while(at > 0 && (*this)[at-1] == '\\') --at; + if (at > 0 && (*this)[at-1] == ':' && (*this)[at] == '\\') ++at; +#else + // Strip trailing / + while(at > 0 && (*this)[at-1] == '/') --at; + + // Hit empty? Bring root / back to life + if (at == 0 && (*this)[0] == '/') ++at; + + // Deal with proto:// + if (at > 0 && (*this)[at-1] == ':') { + while((*this)[at] == '/') ++at; + } +#endif + this->truncate( at ); +} + +size_t string_base::replace_string(const char * replace, const char * replaceWith, t_size start) { + string_formatter temp; + size_t ret = replace_string_ex(temp, replace, replaceWith, start); + if ( ret > 0 ) * this = temp; + return ret; +} +size_t string_base::replace_string_ex (string_base & temp, const char * replace, const char * replaceWith, t_size start) const { + size_t srcDone = 0, walk = start; + size_t occurances = 0; + const char * const source = this->get_ptr(); + bool clear = false; + const size_t replaceLen = strlen( replace ); + for(;;) { + const char * ptr = strstr( source + walk, replace ); + if (ptr == NULL) { + // end + if (srcDone == 0) { + return 0; // string not altered + } + temp.add_string( source + srcDone ); + break; + } + ++occurances; + walk = ptr - source; + if (! clear ) { + temp.reset(); + clear = true; + } + temp.add_string( source + srcDone, walk - srcDone ); + temp.add_string( replaceWith ); + walk += replaceLen; + srcDone = walk; + } + return occurances; +} + +void urlEncodeAppendRaw(pfc::string_base & out, const char * in, t_size inSize) { + for(t_size walk = 0; walk < inSize; ++walk) { + const char c = in[walk]; + if (c == ' ') out.add_byte('+'); + else if (pfc::char_is_ascii_alphanumeric(c) || c == '_') out.add_byte(c); + else out << "%" << pfc::format_hex((t_uint8)c, 2); + } +} +void urlEncodeAppend(pfc::string_base & out, const char * in) { + for(;;) { + const char c = *(in++); + if (c == 0) break; + else if (c == ' ') out.add_byte('+'); + else if (pfc::char_is_ascii_alphanumeric(c) || c == '_') out.add_byte(c); + else out << "%" << pfc::format_hex((t_uint8)c, 2); + } +} +void urlEncode(pfc::string_base & out, const char * in) { + out.reset(); urlEncodeAppend(out, in); +} + +unsigned char_to_dec(char c) { + PFC_ASSERT(c != 0); + if (c >= '0' && c <= '9') return (unsigned)(c - '0'); + else throw exception_invalid_params(); +} + +unsigned char_to_hex(char c) { + if (c >= '0' && c <= '9') return (unsigned)(c - '0'); + else if (c >= 'a' && c <= 'f') return (unsigned)(c - 'a' + 10); + else if (c >= 'A' && c <= 'F') return (unsigned)(c - 'A' + 10); + else throw exception_invalid_params(); +} + + +static constexpr 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}; + +uint32_t charLower(uint32_t param) +{ + if (param<128) { + return ascii_tolower_table[param]; + } +#ifdef PFC_WINDOWS_DESKTOP_APP + else if (param<0x10000) { + return (uint32_t)(size_t)CharLowerW((WCHAR*)(size_t)param); + } +#endif + else return param; +} + +uint32_t charUpper(uint32_t param) +{ + if (param<128) { + if (param>='a' && param<='z') param -= (uint32_t)( 'a' - 'A' ); + return param; + } +#ifdef PFC_WINDOWS_DESKTOP_APP + else if (param<0x10000) { + return (uint32_t)(size_t)CharUpperW((WCHAR*)(size_t)param); + } +#endif + else return param; +} + + +bool stringEqualsI_ascii(const char * p1,const char * p2) noexcept { + for(;;) + { + char c1 = *p1; + char c2 = *p2; + if (c1 > 0 && c2 > 0) { + if (ascii_tolower_table[ (unsigned) c1 ] != ascii_tolower_table[ (unsigned) c2 ]) return false; + } else { + if (c1 == 0 && c2 == 0) return true; + if (c1 == 0 || c2 == 0) return false; + if (c1 != c2) return false; + } + ++p1; ++p2; + } +} + +bool stringEqualsI_utf8(const char * p1,const char * p2) noexcept +{ + for(;;) + { + char c1 = *p1; + char c2 = *p2; + if (c1 > 0 && c2 > 0) { + if (ascii_tolower_table[ (unsigned) c1 ] != ascii_tolower_table[ (unsigned) c2 ]) return false; + ++p1; ++p2; + } else { + if (c1 == 0 && c2 == 0) return true; + if (c1 == 0 || c2 == 0) return false; + unsigned w1,w2; t_size d1,d2; + d1 = utf8_decode_char(p1,w1); + d2 = utf8_decode_char(p2,w2); + if (d1 == 0 || d2 == 0) return false; // bad UTF-8, bail + if (w1 != w2) { + if (charLower(w1) != charLower(w2)) return false; + } + p1 += d1; + p2 += d2; + } + } +} + +char ascii_tolower_lookup(char c) { + PFC_ASSERT( c >= 0); + return (char)ascii_tolower_table[ (unsigned) c ]; +} + +void string_base::fix_dir_separator(char c) { +#ifdef _WIN32 + end_with(c); +#else + end_with_slash(); +#endif +} + + + bool string_has_prefix( const char * string, const char * prefix ) { + for(size_t w = 0; ; ++w ) { + char c = prefix[w]; + if (c == 0) return true; + if (string[w] != c) return false; + } + } + const char* string_skip_prefix_i(const char* string, const char* prefix) { + const char* p1 = string; const char* p2 = prefix; + for (;;) { + unsigned w1, w2; size_t d1, d2; + d1 = utf8_decode_char(p1, w1); + d2 = utf8_decode_char(p2, w2); + if (d2 == 0) return p1; + if (d1 == 0) return nullptr; + if (w1 != w2) { + if (charLower(w1) != charLower(w2)) return nullptr; + } + p1 += d1; p2 += d2; + } + } + bool string_has_prefix_i( const char * string, const char * prefix ) { + return string_skip_prefix_i(string, prefix) != nullptr; + } + bool string_has_suffix( const char * string, const char * suffix ) { + size_t len = strlen( string ); + size_t suffixLen = strlen( suffix ); + if (suffixLen > len) return false; + size_t base = len - suffixLen; + return memcmp( string + base, suffix, suffixLen * sizeof(char)) == 0; + } + bool string_has_suffix_i( const char * string, const char * suffix ) { + for(;;) { + if (*string == 0) return false; + if (stringEqualsI_utf8( string, suffix )) return true; + if (!utf8_advance(string)) return false; + } + } + + char * strDup(const char * src) { +#ifdef _MSC_VER + return _strdup(src); +#else + return strdup(src); +#endif + } + + + string_part_ref string_part_ref::make(const char * ptr, t_size len) { + string_part_ref val = {ptr, len}; return val; + } + + string_part_ref string_part_ref::substring(t_size base) const { + PFC_ASSERT( base <= m_len ); + return make(m_ptr + base, m_len - base); + } + string_part_ref string_part_ref::substring(t_size base, t_size len) const { + PFC_ASSERT( base <= m_len && base + len <= m_len ); + return make(m_ptr + base, len); + } + + string_part_ref string_part_ref::make( const char * str ) {return make( str, strlen(str) ); } + + bool string_part_ref::equals( string_part_ref other ) const { + if ( other.m_len != this->m_len ) return false; + return memcmp( other.m_ptr, this->m_ptr, m_len ) == 0; + } + bool string_part_ref::equals( const char * str ) const { + return equals(make(str) ); + } + + string8 lineEndingsToWin(const char * str) { + string8 ret; + const char * walk = str; + for( ;; ) { + const char * eol = strchr( walk, '\n' ); + if ( eol == nullptr ) { + ret += walk; break; + } + const char * next = eol + 1; + if ( eol > walk ) { + if (eol[-1] == '\r') --eol; + if ( eol > walk ) ret.add_string_nc(walk, eol-walk); + } + ret.add_string_nc("\r\n",2); + walk = next; + } + return ret; + } + + + string8 format_char(char c) { + string8 ret; ret.add_byte(c); return ret; + } + + string8 format_ptr( const void * ptr ) { + string8 temp; + temp << "0x"; + temp << format_hex_lowercase( (size_t) ptr, sizeof(ptr) * 2 ); + return temp; + } + + + string8 format_pad_left(t_size p_chars, t_uint32 p_padding, const char * p_string, t_size p_string_length) { + string8 m_buffer; + t_size source_len = 0, source_walk = 0; + + while (source_walk < p_string_length && source_len < p_chars) { + unsigned dummy; + t_size delta = pfc::utf8_decode_char(p_string + source_walk, dummy, p_string_length - source_walk); + if (delta == 0) break; + source_len++; + source_walk += delta; + } + + m_buffer.add_string(p_string, source_walk); + m_buffer.add_chars(p_padding, p_chars - source_len); + return m_buffer; + } + + string8 format_pad_right(t_size p_chars, t_uint32 p_padding, const char * p_string, t_size p_string_length) { + string8 m_buffer; + t_size source_len = 0, source_walk = 0; + + while (source_walk < p_string_length && source_len < p_chars) { + unsigned dummy; + t_size delta = pfc::utf8_decode_char(p_string + source_walk, dummy, p_string_length - source_walk); + if (delta == 0) break; + source_len++; + source_walk += delta; + } + + m_buffer.add_chars(p_padding, p_chars - source_len); + m_buffer.add_string(p_string, source_walk); + return m_buffer; + } + + string8 stringToUpper(const char * str, size_t len) { + string8 ret; + stringToUpperAppend(ret, str, len); + return ret; + } + string8 stringToLower(const char * str, size_t len) { + string8 ret; + stringToLowerAppend(ret, str, len); + return ret; + } + + pfc::string8 prefixLines(const char* str, const char* prefix, const char * setEOL) { + const auto temp = pfc::splitStringByLines2(str); + pfc::string8 ret; ret.prealloc(1024); + for (auto& line : temp) { + if ( line.length() > 0 ) ret << prefix << line << setEOL; + } + return ret; + } + + pfc::string8 recover_invalid_utf8(const char* in, const char* subst) { + pfc::string8 ret; ret.prealloc(strlen(in)); + for (;;) { + char c = *in; + if (c == 0) break; + if (c < ' ') { + ret += subst; + } else { + ret.add_byte(c); + } + ++in; + } + return ret; + } + static bool is_spacing(char c) { + switch (c) { + case ' ': case '\n': case '\r': case '\t': return true; + default: return false; + } + } + pfc::string8 string_trim_spacing(const char* in) { + const char* temp_ptr = in; + while (is_spacing(*temp_ptr)) temp_ptr++; + const char* temp_start = temp_ptr; + const char* temp_end = temp_ptr; + while (*temp_ptr) + { + if (!is_spacing(*temp_ptr)) temp_end = temp_ptr + 1; + temp_ptr++; + } + + return string_part_ref { temp_start, (size_t)(temp_end - temp_start) }; + } +} //namespace pfc
