Skip to content

Commit ba1c5de

Browse files
authored
strconv.atoi: fix string.int() returning numbers for non number characters (fix #18875) (#18925)
1 parent 7b306e9 commit ba1c5de

File tree

2 files changed

+42
-9
lines changed

2 files changed

+42
-9
lines changed

vlib/strconv/atoi.v

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) {
8989
// Use compile-time constants for common cases.
9090
cutoff := strconv.max_u64 / u64(base) + u64(1)
9191
max_val := if bit_size == 64 { strconv.max_u64 } else { (u64(1) << u64(bit_size)) - u64(1) }
92+
basem1 := base - 1
9293

9394
mut n := u64(0)
9495
for i in start_index .. s.len {
9596
mut c := s[i]
9697

9798
// manage underscore inside the number
9899
if c == `_` {
99-
// println("Here: ${s#[i..]}")
100100
if i == start_index || i >= (s.len - 1) {
101101
// println("_ limit")
102102
return u64(0), 1
@@ -109,21 +109,25 @@ pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) {
109109
continue
110110
}
111111

112+
mut sub_count := 0
113+
112114
// get the 0-9 digit
113115
c -= 48 // subtract the rune `0`
114116

115117
// check if we are in the superior base rune interval [A..Z]
116-
if c >= base {
117-
c -= 7
118-
}
119-
120-
// check if we are in the superior base rune interval [a..z]
121-
if c >= base {
122-
c -= 32 // subtract the `A` - `0` rune to obtain the value of the digit
118+
if c >= 17 { // (65 - 48)
119+
sub_count++
120+
c -= 7 // subtract the `A` - `0` rune to obtain the value of the digit
121+
122+
// check if we are in the superior base rune interval [a..z]
123+
if c >= 42 { // (97 - 7 - 48)
124+
sub_count++
125+
c -= 32 // subtract the `a` - `0` rune to obtain the value of the digit
126+
}
123127
}
124128

125129
// check for digit over base
126-
if c >= base {
130+
if c > basem1 || (sub_count == 0 && c > 9) {
127131
return n, i + 1
128132
}
129133

vlib/strconv/atoi_test.v

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ fn test_atoi() {
2727
}
2828

2929
fn test_parse_int() {
30+
// symbols coverage
31+
assert strconv.parse_int('1234567890', 10, 32)! == 1234567890
32+
assert strconv.parse_int('19aAbBcCdDeEfF', 16, 64)! == 0x19aAbBcCdDeEfF
3033
// Different bases
3134
assert strconv.parse_int('16', 16, 0)! == 0x16
3235
assert strconv.parse_int('16', 8, 0)! == 0o16
@@ -83,6 +86,32 @@ fn test_common_parse_uint2() {
8386
assert error == 4
8487
}
8588

89+
fn test_common_parse_uint2_fail() {
90+
mut ascii_characters := [' ', '!', '"', '#', '\$', '%', '&', "'", '(', ')', '*', '+', ',',
91+
'-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|',
92+
'}', '~']
93+
mut special_characters := [':', ';', '<', '=', '>', '?', '@', 'X', 'Y', 'Z', '[', '\\', ']',
94+
'^', '_', '`']
95+
96+
num0, err0 := strconv.common_parse_uint2('1Ab', 16, 32)
97+
assert num0 == 427
98+
assert err0 == 0
99+
100+
for ch in ascii_characters {
101+
// println("ch: [${ch}]")
102+
txt_str := '${ch[0]:c}12Ab'
103+
num, err := strconv.common_parse_uint2(txt_str, 16, 32)
104+
assert err != 0
105+
}
106+
107+
for ch in special_characters {
108+
// println("ch: [${ch}]")
109+
txt_str := '${ch[0]:c}12Ab'
110+
num, err := strconv.common_parse_uint2(txt_str, 16, 32)
111+
assert err != 0
112+
}
113+
}
114+
86115
fn test_common_parse_uint2_compatibility() {
87116
test_list := [
88117
'1234,1234',

0 commit comments

Comments
 (0)