* Removed nested function from strtonum-gen.h .
* Error messages are not saved in separate static variables.
* Octal numbers can be detected by prefix '0[0-7]...' (this feature is enabled by STN_OCT0).
* One-command blocks are not enclosed by { ... } .
* STN_ZCHAR renamed to STN_WHOLE.
#define STN_MAX ((STN_TYPE)(-1))
static const STN_TYPE S(tops)[STN_DBASES_MASK+1] = { [2] = STN_MAX/2, [8] = STN_MAX/8, [10] = STN_MAX/10, [16] = STN_MAX/16 };
+static const char *S(parse_string)(const char **pp, const uns flags, const uns sign, const uns base, STN_TYPE *num)
+{
+ const STN_TYPE max = STN_MAX;
+ const STN_TYPE top = S(tops)[base];
+ if (!top)
+ return "Unknown base";
+
+ const STN_TYPE sign_max = ((flags & STN_SIGNED) || sign) ? max/2 + sign : max;
+
+ STN_TYPE val = 0;
+ uns digits = 0;
+ int overflow = 0;
+ for (;; (*pp)++)
+ {
+ const uns c = (byte)**pp;
+
+ if (c == '_')
+ {
+ if (flags & STN_UNDERSCORE)
+ continue;
+ else
+ break;
+ }
+
+ const uns d = get_digit(c);
+ if (d >= base)
+ break;
+
+ digits++;
+
+ if (overflow)
+ continue;
+
+ STN_TYPE v = val;
+ if ( (overflow = (v > top || (v *= base) > sign_max - d)) )
+ continue;
+ val = v + d;
+ }
+
+ if (!overflow)
+ {
+ if (!digits)
+ return "Number contains no digits";
+
+ if (sign)
+ val = -val;
+ }
+ else
+ {
+ if (flags & STN_TRUNC)
+ val = sign_max;
+ else
+ return "Numeric overflow";
+ }
+
+ if ((flags & STN_WHOLE) && **pp)
+ return "Invalid character";
+
+ if (num)
+ *num = val;
+
+ return NULL;
+}
+
STN_DECLARE(STN_TYPE, STN_SUFFIX)
{
- const char *p = str;
const char *err = NULL;
uns sign, base;
- err = str_to_num_init(&p, flags, &sign, &base);
-
- auto const char *parse_string(void);
- const char *parse_string(void)
- {
- const STN_TYPE max = STN_MAX;
- const STN_TYPE top = S(tops)[base];
- if (!top)
- {
- return err_unknown_base;
- }
-
- const STN_TYPE sign_max = ((flags & STN_SIGNED) || sign) ? max/2 + sign : max;
-
- STN_TYPE val = 0;
- uns digits = 0;
- int overflow = 0;
- for (;; p++)
- {
- const uns c = (byte)*p;
-
- if (c == '_')
- {
- if (flags & STN_UNDERSCORE)
- continue;
- else
- break;
- }
-
- const uns d = get_digit(c);
- if (d >= base)
- break;
- digits++;
- if (overflow)
- continue;
-
- STN_TYPE v = val;
- if ( (overflow = (v > top || (v *= base) > sign_max - d)) )
- continue;
- val = v + d;
- }
-
- if (!overflow)
- {
- if (!digits)
- {
- return err_no_digits;
- }
-
- if (sign)
- {
- val = -val;
- }
- }
- else
- {
- if (flags & STN_TRUNC)
- val = sign_max;
- else
- {
- return err_numeric_overflow;
- }
- }
-
- if ((flags & STN_ZCHAR) && *p)
- {
- return err_invalid_character;
- }
-
- if (num)
- *num = val;
-
- return NULL;
- }
+ err = str_to_num_init(&str, flags, &sign, &base);
if (!err)
- err = parse_string();
+ err = S(parse_string)(&str, flags, sign, base, num);
if (next)
- *next = p;
+ *next = str;
return err;
}
case 'o':
flags |= STN_OCT;
break;
+ case 'O':
+ flags |= STN_OCT0;
+ break;
case 'B':
flags |= STN_BIN;
break;
case 's':
flags |= STN_SIGNED;
break;
- case 'Z':
- flags |= STN_ZCHAR;
+ case 'W':
+ flags |= STN_WHOLE;
break;
}
}
Out: i32768
# 2
+Run: ../obj/ucw/strtonum-test '0O+-_' '0100_000xc' | grep '^i'
+Out: i32768
+
+# 3
+Run: ../obj/ucw/strtonum-test '0O+-_' '00000000778' | grep '^i'
+Out: i63
+
+# 4
Run: ../obj/ucw/strtonum-test '0XoB+-_' '4_294_967_295xcyv' | grep '^i'
Out: i4294967295
-# 3
+# 5
Run: ../obj/ucw/strtonum-test '0XoB+-_' '4_294_967_296xc' | grep '^e'
Out: e[Numeric overflow]
-# 4
+# 6
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '4_294_967_296xc' | grep '^h'
Out: hffffffff
-# 5
+# 7
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '4_294_967_296xc' | grep '^b'
Out: b13:0x78
-# 6
+# 8
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '0x__4_' | grep '^i'
Out: i4
-# 7
+# 9
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '0x__4_' | grep '^b'
Out: b6:0x0
-# 8
+# 10
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '0xXW' | grep '^e'
Out: e[Number contains no digits]
-# 9
+# 11
Run: ../obj/ucw/strtonum-test '0XoBt+-_' '0xXW' | grep '^b'
Out: b2:0x58
-# 10
+# 12
Run: ../obj/ucw/strtonum-test '0oBt+-_' '0xXW' | grep '^i'
Out: i0
-# 11
+# 13
Run: ../obj/ucw/strtonum-test '0oBt+-_' '0xXW' | grep '^b'
Out: b1:0x78
-# 12
-Run: ../obj/ucw/strtonum-test '0oBt+-_Z' '0xXW' | grep '^e'
+# 14
+Run: ../obj/ucw/strtonum-test '0oBt+-_W' '0xXW' | grep '^e'
Out: e[Invalid character]
-# 13
-Run: ../obj/ucw/strtonum-test '0oBt+-_Z' '0xXW' | grep '^b'
+# 15
+Run: ../obj/ucw/strtonum-test '0oBt+-_W' '0xXW' | grep '^b'
Out: b1:0x78
-# 14
+# 16
Run: ../obj/ucw/strtonum-test '0Bs+-_' '2_147_483_647xxx' | grep '^i'
Out: i2147483647
-# 15
+# 17
Run: ../obj/ucw/strtonum-test '0Bs+-_' '2_147_483_647xxx' | grep '^i'
Out: i2147483647
-# 16
+# 18
Run: ../obj/ucw/strtonum-test '0Bs+-_' '2_147_483_648xxx' | grep '^e'
Out: e[Numeric overflow]
-# 17
+# 19
Run: ../obj/ucw/strtonum-test '0Bs+-_' '-2_147_483_648xxx' | grep '^i'
Out: i-2147483648
-# 18
+# 20
Run: ../obj/ucw/strtonum-test '0Bs+-_' '-2_147_483_649xxx' | grep '^e'
Out: e[Numeric overflow]
-# 19
+# 21
Run: ../obj/ucw/strtonum-test '0Bts+-_' '2_147_483_648xxx' | grep '^i'
Out: i2147483647
-# 20
+# 22
Run: ../obj/ucw/strtonum-test '0Bts+-_' '-2_147_483_649xxx' | grep '^i'
Out: i-2147483648
-# 21
+# 23
Run: ../obj/ucw/strtonum-test '0Bts+-_' '-2_147_483_649xxx' | grep '^i'
Out: i-2147483648
-# 22
+# 24
Run: ../obj/ucw/strtonum-test '0X+-' '0xABCDEFxxx' | grep '^h'
Out: habcdef
-# 23
+# 25
Run: ../obj/ucw/strtonum-test '0X+-_' '0x00_AB_CD_EFxxx' | grep '^h'
Out: habcdef
-# 24
+# 26
Run: ../obj/ucw/strtonum-test '0Xs+-_' '-0x00AB_CDEFxxx' | grep '^h'
Out: hff543211
-# 25
+# 27
Run: ../obj/ucw/strtonum-test '0XBs+-_' '-0x00AB_CDEFxxx' | grep '^h'
Out: hff543211
-# 26
+# 28
Run: ../obj/ucw/strtonum-test '0B+-_' '0B1111_0000_1000_0101_1010xxx' | grep '^h'
Out: hf085a
-# 27
+# 29
Run: ../obj/ucw/strtonum-test '0+-_' '0B1111_0000_1000_0101_1010xxx' | grep '^b'
Out: b1:0x42
-# 28
+# 30
Run: ../obj/ucw/strtonum-test '0o+-_' '0o70105xxx' | grep '^i'
Out: i28741
-# 29
+# 31
Run: ../obj/ucw/strtonum-test '0os+-_' '-0o70105xxx' | grep '^i'
Out: i-28741
-# 30
+# 32
Run: ../obj/ucw/strtonum-test '0os+-_' '-0o___________xxx' | grep '^e'
Out: e[Number contains no digits]
-# 31
+# 33
Run: ../obj/ucw/strtonum-test '2+-_' '10578ABCG' | grep '^i'
Out: i2
-# 32
+# 34
Run: ../obj/ucw/strtonum-test '2s+-_' '-10578ABCG' | grep '^i'
Out: i-2
-# 33
+# 35
Run: ../obj/ucw/strtonum-test '8+-_' '10578ABCG' | grep '^i'
Out: i559
-# 34
+# 36
Run: ../obj/ucw/strtonum-test '8s+-_' '-10578ABCG' | grep '^i'
Out: i-559
-# 35
+# 37
Run: ../obj/ucw/strtonum-test '0+-_' '10578ABCG' | grep '^i'
Out: i10578
-# 36
+# 38
Run: ../obj/ucw/strtonum-test '0s+-_' '-10578ABCG' | grep '^i'
Out: i-10578
-# 37
+# 39
Run: ../obj/ucw/strtonum-test 'h+-_' '10578ABCG' | grep '^i'
Out: i274172604
-# 38
+# 40
Run: ../obj/ucw/strtonum-test 'hs+-_' '-10578ABCG' | grep '^i'
Out: i-274172604
## Testing str_to_uintmax(.) (It is supoposed that uintmax_t is 8 bytes integer)
-# 39
+# 41
Run: ../obj/ucw/strtonum-test 'h+-_' 'FFFF_FFFF_ffff_ffFF' | grep '^H'
Out: Hffffffffffffffff
-# 40
+# 42
Run: ../obj/ucw/strtonum-test 'h+-_' 'FFFF_FFFF_ffff_ffFF' | grep '^I'
Out: I18446744073709551615
-#41
+# 43
Run: ../obj/ucw/strtonum-test '0t+-_' '1844674407370000009551616' | grep '^I'
Out: I18446744073709551615
-#42
+# 44
Run: ../obj/ucw/strtonum-test '0+-_' '18446744073709551616' | grep '^E'
Out: E[Numeric overflow]
-# 43
+# 45
Run: ../obj/ucw/strtonum-test '0+-_' '18446744073709551614' | grep '^H'
Out: Hfffffffffffffffe
-# 44
+# 46
Run: ../obj/ucw/strtonum-test '0s+-_' '9223372036854775807L' | grep '^I'
Out: I9223372036854775807
-# 45
+# 47
Run: ../obj/ucw/strtonum-test '0s+-_' '9223372036854775806L' | grep '^I'
Out: I9223372036854775806
-# 46
+# 48
Run: ../obj/ucw/strtonum-test '0st+-_' '92233720368547758000000L' | grep '^I'
Out: I9223372036854775807
-# 47
+# 49
Run: ../obj/ucw/strtonum-test '0s+-_' '9223372036854775808L' | grep '^E'
Out: E[Numeric overflow]
-# 48
+# 50
Run: ../obj/ucw/strtonum-test '0s+-_' '-9223372036854775808L' | grep '^I'
Out: I-9223372036854775808
-# 49
+# 51
Run: ../obj/ucw/strtonum-test '0s+-_' '-9223372036854775807L' | grep '^I'
Out: I-9223372036854775807
-# 50
+# 52
Run: ../obj/ucw/strtonum-test '0st+-_' '-9223372036854775800000L' | grep '^I'
Out: I-9223372036854775808
-# 51
+# 53
Run: ../obj/ucw/strtonum-test '0s+-_' '-9223372036854775809L' | grep '^E'
Out: E[Numeric overflow]
#include "ucw/chartype.h"
#include "ucw/strtonum.h"
-static const char err_numeric_overflow[] = "Numeric overflow";
-static const char err_no_digits[] = "Number contains no digits";
-static const char err_invalid_character[] = "Invalid character";
-static const char err_unknown_base[] = "Unknown base";
-
-static uns detect_base(const char *p, const uns flags)
+static uns detect_base(const char **pp, const uns flags)
{
- if ((flags & STN_BASES) && *p == '0')
+ if ((flags & STN_BASES0) && **pp == '0')
{
- switch (p[1] & 0xDF)
+ switch ( (*pp)[1] )
{
+ case 'x':
case 'X':
if (flags & STN_HEX)
{
+ *pp += 2;
return 16;
}
break;
+ case 'b':
case 'B':
if (flags & STN_BIN)
{
+ *pp += 2;
return 2;
}
break;
+ case 'o':
case 'O':
if (flags & STN_OCT)
{
+ *pp += 2;
+ return 8;
+ }
+ break;
+
+ case '0'...'7':
+ if (flags & STN_OCT0)
+ {
+ (*pp)++;
return 8;
}
break;
p++;
}
- const uns prefix_base = detect_base(p, flags);
- if (prefix_base)
- {
- p += 2;
- *base = prefix_base;
- }
- else
- {
- *base = flags & STN_DBASES_MASK;
- }
+ *base = detect_base(&p, flags) ? : flags & STN_DBASES_MASK;
*pp = p;
return err;
static inline uns get_digit(const uns c)
{
if (c <= '9')
- {
- return c - '0';
- }
+ return c - '0';
else
{
const int a = c & 0xDF;
STN_HEX = 0x400, // Support hexadecimal numbers (0x...)
STN_BIN = 0x800, // Support binary numbers (0b...)
STN_OCT = 0x1000, // Support octal numbers (0o...)
- STN_UNDERSCORE = 0x2000, // Number can contain underscore characters to increase readability (eg. 1_000_000)
- STN_ZCHAR = 0x4000, // Number can be terminated only by \0 character
+ STN_OCT0 = 0x2000, // Support octal numbers (0[0-7]...)
+ STN_UNDERSCORE = 0x4000, // Number can contain underscore characters to increase readability (eg. 1_000_000)
+ STN_WHOLE = 0x8000, // Number can be terminated only by \0 character
};
#define STN_DBASES_MASK 0x1F
#define STN_SIGNS (STN_MINUS | STN_PLUS)
#define STN_BASES (STN_DEC | STN_HEX | STN_BIN | STN_OCT)
+#define STN_BASES0 (STN_BASES | STN_OCT0)
#define STN_FLAGS (STN_MINUS | STN_PLUS | STN_BASES)
#define STN_UFLAGS (STN_FLAGS | STN_UNDERSCORE)
#define STN_SFLAGS (STN_FLAGS | STN_SIGNED)