diff --git a/vlib/strconv/atoi.v b/vlib/strconv/atoi.v index 76dca67d32c549..601b2d4dba0c50 100644 --- a/vlib/strconv/atoi.v +++ b/vlib/strconv/atoi.v @@ -89,6 +89,7 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { // Use compile-time constants for common cases. cutoff := strconv.max_u64 / u64(base) + u64(1) max_val := if bit_size == 64 { strconv.max_u64 } else { (u64(1) << u64(bit_size)) - u64(1) } + basem1 := base - 1 mut n := u64(0) for i in start_index .. s.len { @@ -96,7 +97,6 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { // manage underscore inside the number if c == `_` { - // println("Here: ${s#[i..]}") if i == start_index || i >= (s.len - 1) { // println("_ limit") return u64(0), 1 @@ -109,21 +109,25 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { continue } + mut sub_count := 0 + // get the 0-9 digit c -= 48 // subtract the rune `0` // check if we are in the superior base rune interval [A..Z] - if c >= base { - c -= 7 - } - - // check if we are in the superior base rune interval [a..z] - if c >= base { - c -= 32 // subtract the `A` - `0` rune to obtain the value of the digit + if c >= 17 { // (65 - 48) + sub_count++ + c -= 7 // subtract the `A` - `0` rune to obtain the value of the digit + + // check if we are in the superior base rune interval [a..z] + if c >= 42 { // (97 - 7 - 48) + sub_count++ + c -= 32 // subtract the `a` - `0` rune to obtain the value of the digit + } } // check for digit over base - if c >= base { + if c > basem1 || (sub_count == 0 && c > 9) { return n, i + 1 } diff --git a/vlib/strconv/atoi_test.v b/vlib/strconv/atoi_test.v index 626bc9dc1f54a4..220e5138c99977 100644 --- a/vlib/strconv/atoi_test.v +++ b/vlib/strconv/atoi_test.v @@ -27,6 +27,9 @@ fn test_atoi() { } fn test_parse_int() { + // symbols coverage + assert strconv.parse_int('1234567890', 10, 32)! == 1234567890 + assert strconv.parse_int('19aAbBcCdDeEfF', 16, 64)! == 0x19aAbBcCdDeEfF // Different bases assert strconv.parse_int('16', 16, 0)! == 0x16 assert strconv.parse_int('16', 8, 0)! == 0o16 @@ -83,6 +86,32 @@ fn test_common_parse_uint2() { assert error == 4 } +fn test_common_parse_uint2_fail() { + mut ascii_characters := [' ', '!', '"', '#', '\$', '%', '&', "'", '(', ')', '*', '+', ',', + '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', + '}', '~'] + mut special_characters := [':', ';', '<', '=', '>', '?', '@', 'X', 'Y', 'Z', '[', '\\', ']', + '^', '_', '`'] + + num0, err0 := strconv.common_parse_uint2('1Ab', 16, 32) + assert num0 == 427 + assert err0 == 0 + + for ch in ascii_characters { + // println("ch: [${ch}]") + txt_str := '${ch[0]:c}12Ab' + num, err := strconv.common_parse_uint2(txt_str, 16, 32) + assert err != 0 + } + + for ch in special_characters { + // println("ch: [${ch}]") + txt_str := '${ch[0]:c}12Ab' + num, err := strconv.common_parse_uint2(txt_str, 16, 32) + assert err != 0 + } +} + fn test_common_parse_uint2_compatibility() { test_list := [ '1234,1234',