Skip to content

Commit a694918

Browse files
authored
strconv : add atoi8/16/32/64 helper functions with their tests (#23757)
1 parent 1274f46 commit a694918

File tree

2 files changed

+371
-39
lines changed

2 files changed

+371
-39
lines changed

vlib/strconv/atoi.v

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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 > i64_max_int32 {
271+
if sign == 1 && x > type_max {
262272
return error('strconv.atoi: parsing "${s}": integer overflow')
263273
} else {
264-
if x < i64_min_int32 {
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

273361
const 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

Comments
 (0)