@@ -216,10 +216,12 @@ pub fn parse_int(_s string, base int, _bit_size int) !i64 {
216216 return common_parse_int (_s, base, _bit_size, true , true )
217217}
218218
219- // atoi is equivalent to parse_int(s, 10, 0), converted to type int.
220- // It follows V scanner as much as observed.
219+ // atoi_common_check perform basics check on string to parse:
220+ // Test emptiness, + or - sign presence, presence of digit after signs and no
221+ // underscore as first character.
222+ // returns +1 or -1 depending on sign, and s first digit index or an error.
221223@[direct_array_access]
222- pub fn atoi (s string ) ! int {
224+ fn atoi_common_check (s string ) ! ( i64 , int ) {
223225 if s == '' {
224226 return error ('strconv.atoi: parsing "": empty string' )
225227 }
@@ -241,7 +243,15 @@ pub fn atoi(s string) !int {
241243 if s[start_idx] == `_` || s[s.len - 1 ] == `_` {
242244 return error ('strconv.atoi: parsing "${s} ": values cannot start or end with underscores' )
243245 }
246+ return sign, start_idx
247+ }
244248
249+ // atoi_common performs computation for all i8, i16 and i32 type, excluding i64.
250+ // Parse values, and returns consistent error message over differents types.
251+ // s is string to parse, type_min/max are respective types min/max values.
252+ @[direct_array_access]
253+ fn atoi_common (s string , type_min i64 , type_max i64 ) ! i64 {
254+ mut sign, mut start_idx := atoi_common_check (s)!
245255 mut x := i64 (0 )
246256 mut underscored := false
247257 for i in start_idx .. s.len {
@@ -258,16 +268,94 @@ pub fn atoi(s string) !int {
258268 }
259269 underscored = false
260270 x = (x * 10 ) + (c * sign)
261- if sign == 1 && x > i 64_ max_int 32 {
271+ if sign == 1 && x > type_max {
262272 return error ('strconv.atoi: parsing "${s} ": integer overflow' )
263273 } else {
264- if x < i 64_ min_int 32 {
274+ if x < type_min {
265275 return error ('strconv.atoi: parsing "${s} ": integer underflow' )
266276 }
267277 }
268278 }
269279 }
270- return int (x)
280+ return x
281+ }
282+
283+ // atoi is equivalent to parse_int(s, 10, 0), converted to type int.
284+ // It follows V scanner as much as observed.
285+ pub fn atoi (s string ) ! int {
286+ return int (atoi_common (s, i64_ min_int32 , i64_ max_int32 )! )
287+ }
288+
289+ // atoi8 is equivalent to atoi(s), converted to type i8.
290+ // returns an i8 [-128 .. 127] or an error.
291+ pub fn atoi8 (s string ) ! i8 {
292+ return i8 (atoi_common (s, min_i8 , max_i8 )! )
293+ }
294+
295+ // atoi16 is equivalent to atoi(s), converted to type i16.
296+ // returns an i16 [-32678 .. 32767] or an error.
297+ pub fn atoi16 (s string ) ! i16 {
298+ return i16 (atoi_common (s, min_i16 , max_i16 )! )
299+ }
300+
301+ // atoi32 is equivalent to atoi(s), converted to type i32.
302+ // returns an i32 [-2147483648 .. 2147483647] or an error.
303+ pub fn atoi32 (s string ) ! i32 {
304+ return i32 (atoi_common (s, min_i32 , max_i32 )! )
305+ }
306+
307+ // atoi64 converts radix 10 string to i64 type.
308+ // returns an i64 [-9223372036854775808 .. 9223372036854775807] or an error.
309+ @[direct_array_access]
310+ pub fn atoi64 (s string ) ! i64 {
311+ mut sign, mut start_idx := atoi_common_check (s)!
312+ mut x := i64 (0 )
313+ mut underscored := false
314+ for i in start_idx .. s.len {
315+ c := s[i] - `0`
316+ if c == 47 { // 47 = Ascii(`_`) - ascii(`0`) = 95 - 48.
317+ if underscored == true { // Two consecutives underscore
318+ return error ('strconv.atoi64: parsing "${s} ": consecutives underscores are not allowed' )
319+ }
320+ underscored = true
321+ continue // Skip underscore
322+ } else {
323+ if c > 9 {
324+ return error ('strconv.atoi64: parsing "${s} ": invalid radix 10 character' )
325+ }
326+ underscored = false
327+ x = safe_mul10_64bits (x) or { return error ('strconv.atoi64: parsing "${s} ": ${err} ' ) }
328+ x = safe_add_64bits (x, int (c * sign)) or {
329+ return error ('strconv.atoi64: parsing "${s} ": ${err} ' )
330+ }
331+ }
332+ }
333+ return x
334+ }
335+
336+ // safe_add64 performs a signed 64 bits addition and returns an error
337+ // in case of overflow or underflow.
338+ @[inline]
339+ fn safe_add_64bits (a i64 , b i64 ) ! i64 {
340+ if a > 0 && b > (max_i64 - a) {
341+ return error ('integer overflow' )
342+ } else if a < 0 && b < (min_i64 - a) {
343+ return error ('integer underflow' )
344+ }
345+ return a + b
346+ }
347+
348+ // safe_mul10 performs a * 10 multiplication and returns an error
349+ // in case of overflow or underflow.
350+ @[inline]
351+ fn safe_mul10_64bits (a i64 ) ! i64 {
352+ if a > 0 && a > (max_i64 / 10 ) {
353+ return error ('integer overflow' )
354+ }
355+ if a < 0 && a < (min_i64 / 10 ) {
356+ return error ('integer underflow' )
357+ }
358+ return a * 10
271359}
272360
273361const i64_min_int32 = i64 (- 2147483647 ) - 1 // msvc has a bug that treats just i64(min_int) as 2147483648 :-(; this is a workaround for it
0 commit comments