From a1fdee4766b969fd0786e3286ce878a2a1142bbd Mon Sep 17 00:00:00 2001 From: Daniel Fiala Date: Mon, 2 Aug 2010 19:54:13 +0200 Subject: [PATCH] Minor fixes to strtonum. * 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. --- ucw/strtonum-gen.h | 144 +++++++++++++++++++++----------------------- ucw/strtonum-test.c | 7 ++- ucw/strtonum-test.t | 110 +++++++++++++++++---------------- ucw/strtonum.c | 40 ++++++------ ucw/strtonum.h | 6 +- 5 files changed, 154 insertions(+), 153 deletions(-) diff --git a/ucw/strtonum-gen.h b/ucw/strtonum-gen.h index f339dcbc..6b1a6f8f 100644 --- a/ucw/strtonum-gen.h +++ b/ucw/strtonum-gen.h @@ -22,92 +22,82 @@ #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; } diff --git a/ucw/strtonum-test.c b/ucw/strtonum-test.c index fd15714d..6fa6f23a 100644 --- a/ucw/strtonum-test.c +++ b/ucw/strtonum-test.c @@ -39,6 +39,9 @@ static uns str_to_flags(const char *str) case 'o': flags |= STN_OCT; break; + case 'O': + flags |= STN_OCT0; + break; case 'B': flags |= STN_BIN; break; @@ -61,8 +64,8 @@ static uns str_to_flags(const char *str) case 's': flags |= STN_SIGNED; break; - case 'Z': - flags |= STN_ZCHAR; + case 'W': + flags |= STN_WHOLE; break; } } diff --git a/ucw/strtonum-test.t b/ucw/strtonum-test.t index f8747b51..5fc427fa 100644 --- a/ucw/strtonum-test.t +++ b/ucw/strtonum-test.t @@ -7,203 +7,211 @@ Run: ../obj/ucw/strtonum-test '0o+-_' '0o100_000xc' | grep '^i' 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] diff --git a/ucw/strtonum.c b/ucw/strtonum.c index 43656fca..1d6cd1cd 100644 --- a/ucw/strtonum.c +++ b/ucw/strtonum.c @@ -12,34 +12,43 @@ #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; @@ -69,16 +78,7 @@ static const char *str_to_num_init(const char **pp, const uns flags, uns *sign, 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; @@ -87,9 +87,7 @@ static const char *str_to_num_init(const char **pp, const uns flags, uns *sign, static inline uns get_digit(const uns c) { if (c <= '9') - { - return c - '0'; - } + return c - '0'; else { const int a = c & 0xDF; diff --git a/ucw/strtonum.h b/ucw/strtonum.h index 597790bc..fd7e87b1 100644 --- a/ucw/strtonum.h +++ b/ucw/strtonum.h @@ -19,13 +19,15 @@ enum str_to_num_flags { 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) -- 2.39.2