Skip to content

Commit

Permalink
strconv.atoi: fix string.int() returning numbers for non number chara…
Browse files Browse the repository at this point in the history
…cters (fix #18875) (#18925)
  • Loading branch information
penguindark committed Jul 22, 2023
1 parent 7b306e9 commit ba1c5de
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
22 changes: 13 additions & 9 deletions vlib/strconv/atoi.v
Expand Up @@ -89,14 +89,14 @@ 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 {
mut c := s[i]

// 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
Expand All @@ -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
}

Expand Down
29 changes: 29 additions & 0 deletions vlib/strconv/atoi_test.v
Expand Up @@ -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
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit ba1c5de

Please sign in to comment.