--- parser3/src/main/pa_int.C 2026/01/06 13:07:58 1.1 +++ parser3/src/main/pa_int.C 2026/01/07 14:19:45 1.6 @@ -9,7 +9,35 @@ #include "pa_string.h" #include "pa_exception.h" -volatile const char * IDENT_PA_STRING_C="$Id: pa_int.C,v 1.1 2026/01/06 13:07:58 moko Exp $" IDENT_PA_INT_H; +volatile const char * IDENT_PA_INT_C="$Id: pa_int.C,v 1.6 2026/01/07 14:19:45 moko Exp $" IDENT_PA_INT_H; + +#ifdef PA_WIDE_INT +int check4int(pa_wint avalue){ + if( (avalue > INT_MAX) || (avalue < INT_MIN)) + throw Exception("number.format", 0, "%s is out of regular int range", pa_itoa(avalue)); + return (int)avalue; +} +#endif + +char* pa_itoa(int i){ // custom to support INT_MIN + const int base=10; + char buf[MAX_NUMBER + 1]; + char* pos=buf + MAX_NUMBER; + *pos='\0'; + + bool negative=i < 0; + unsigned int n= i < 0 ? 0u-(unsigned int)i : (unsigned int)i; + + do { + *(--pos)=(char)(n % base) + '0'; + n/=base; + } while (n > 0); + + if (negative) { + *(--pos) = '-'; + } + return pa_strdup(pos, buf + MAX_NUMBER - pos); +} // pa_atoui is based on Manuel Novoa III _strto_l for uClibc @@ -116,7 +144,7 @@ int pa_atoi(const char* str, int base, c } if(negative){ - const uint min_abs = (uint)(-( (uint)INT_MIN + 1 )) + 1; + const uint min_abs = (uint)0 - (uint)INT_MIN; uint result=pa_ato_any(str, base, problem_source, min_abs); if(result==min_abs) return INT_MIN; return -(int)result; @@ -149,7 +177,7 @@ pa_wint pa_atowi(const char* str, int ba } if(negative){ - const pa_uwint min_abs = (pa_uwint)(-( (pa_wint)PA_WINT_MIN + 1 )) + 1; + const pa_uwint min_abs = (pa_uwint)0 - (pa_wint)PA_WINT_MIN; pa_uwint result=pa_ato_any(str, base, problem_source, min_abs); if(result==min_abs) return PA_WINT_MIN; return -(pa_wint)result; @@ -202,3 +230,137 @@ double pa_atod(const char* str, const St return negative ? -result : result; } + +// format: %[flags][width][.precision]type https://msdn.microsoft.com/ru-ru/library/56e442dc(en-us,VS.80).aspx +// flags: '-', '+', ' ', '#', '0' https://msdn.microsoft.com/ru-ru/library/8aky45ct(en-us,VS.80).aspx +// width, precision: non negative decimal number + +#define MAX_FORMAT_LEN 10 // %+0XX.XXg\0 + +enum FormatType { + FormatInvalid, + FormatInt, + FormatUInt, + FormatDouble +}; + +FormatType format_type(const char* fmt){ + enum FormatState { + Percent, + Flags, + Width, + Precision, + Done + } state=Percent; + + FormatType result=FormatInvalid; + + const char* pos=fmt; + while(char c=*(pos++)){ + switch(state){ + case Percent: + if(c=='%'){ + state=Flags; + } else { + return FormatInvalid; // 1st char must be '%' only + } + break; + case Flags: + if(strchr("-+ #0", c)!=0){ + break; + } + // go to the next step + case Width: + if(c=='.'){ + state=Precision; + break; + } + // go to the next step + case Precision: + if(c>='0' && c<='9'){ + if(state == Flags) state=Width; // no more flags + break; + } else if(c=='d' || c=='i'){ + result=FormatInt; + } else if(strchr("feEgG", c)!=0){ + result=FormatDouble; + } else if(strchr("uoxX", c)!=0){ + result=FormatUInt; + } else { + return FormatInvalid; // invalid char + } + state=Done; + break; + case Done: + return FormatInvalid; // no chars allowed after 'type' + } + } + return pos-fmt > MAX_FORMAT_LEN ? FormatInvalid : result; +} + +#ifdef PA_WIDE_INT + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +// old VS: %I64d +#define PA_INTMOD "I64" +#else +// C99: %lld +#define PA_INTMOD "ll" +#endif + +static const char* wide_int_fmt(const char *fmt, char *dst){ + const size_t len = strlen(fmt); + const size_t head = len - 1; + memcpy(dst, fmt, head); + memcpy(dst + head, PA_INTMOD, strlen(PA_INTMOD)); + dst[head + strlen(PA_INTMOD)] = fmt[head]; + dst[head + strlen(PA_INTMOD) + 1] = '\0'; + return dst; +} +#endif + + +const char* format_double(double value, const char* fmt) { + char local_buf[MAX_NUMBER]; + int size=-1; + + if(fmt && strlen(fmt)){ + switch(format_type(fmt)){ + case FormatDouble: + size=snprintf(local_buf, sizeof(local_buf), fmt, value); + break; + case FormatUInt: + if(value >= 0){ // on Apple M1 (uint) is 0 +#ifdef PA_WIDE_INT + char fmt_buf[MAX_FORMAT_LEN+4]; + size=snprintf(local_buf, sizeof(local_buf), wide_int_fmt(fmt, fmt_buf), (unsigned long long)clip2wint(value)); // WUINT_MAX == WINT_MAX +#else + size=snprintf(local_buf, sizeof(local_buf), fmt, clip2uint(value)); +#endif + } else { + // for unsigned formats, wrap negatives to uint as unsigned wint would exceed the 53-bit range + size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value)); + } + break; + case FormatInt:{ +#ifdef PA_WIDE_INT + char fmt_buf[MAX_FORMAT_LEN+4]; + size=snprintf(local_buf, sizeof(local_buf), wide_int_fmt(fmt, fmt_buf), (long long)clip2wint(value)); +#else + size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value)); +#endif + break; + } + case FormatInvalid: + throw Exception(PARSER_RUNTIME, 0, "Incorrect format string '%s' was specified.", fmt); + } + } else + return pa_itoa(clip2wint(value)); + + if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing + throw Exception(PARSER_RUNTIME, 0, "Error occurred white executing snprintf with format string '%s'.", fmt); + } + + return pa_strdup(local_buf, (size_t)size); +} +