From 0db44549937cf88dbac379a868f3169e09feaba4 Mon Sep 17 00:00:00 2001 From: Team Humaki Date: Thu, 17 Sep 2026 21:56:53 -0700 Subject: [PATCH] parse: honor target bit size for integer strings ParseInt/ParseUint used bitSize 0 (platform int), so values that fit in int but not int8/uint8/etc wrapped with a nil error. Strings that overflow int64 already failed. Pass the destination width so the two paths agree. Fixes #260 --- number.go | 40 ++++++++++++++++++++++++++++++++++++++-- number_test.go | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/number.go b/number.go index a58dc4d1..0c25c97a 100644 --- a/number.go +++ b/number.go @@ -405,7 +405,7 @@ func parseNumber[T Number](s string) (T, error) { } func parseInt[T integer](s string) (T, error) { - v, err := strconv.ParseInt(trimDecimal(s), 0, 0) + v, err := strconv.ParseInt(trimDecimal(s), 0, intBitSize[T]()) if err != nil { return 0, err } @@ -414,7 +414,7 @@ func parseInt[T integer](s string) (T, error) { } func parseUint[T unsigned](s string) (T, error) { - v, err := strconv.ParseUint(strings.TrimLeft(trimDecimal(s), "+"), 0, 0) + v, err := strconv.ParseUint(strings.TrimLeft(trimDecimal(s), "+"), 0, uintBitSize[T]()) if err != nil { return 0, err } @@ -422,6 +422,42 @@ func parseUint[T unsigned](s string) (T, error) { return T(v), nil } +func intBitSize[T integer]() int { + var z T + switch any(z).(type) { + case int8: + return 8 + case int16: + return 16 + case int32: + return 32 + case int64: + return 64 + case int: + return strconv.IntSize + default: + return 0 + } +} + +func uintBitSize[T unsigned]() int { + var z T + switch any(z).(type) { + case uint8: + return 8 + case uint16: + return 16 + case uint32: + return 32 + case uint64: + return 64 + case uint: + return strconv.IntSize + default: + return 0 + } +} + func parseFloat[T float](s string) (T, error) { var t T diff --git a/number_test.go b/number_test.go index 9d84d18d..1b84fd47 100644 --- a/number_test.go +++ b/number_test.go @@ -167,8 +167,6 @@ func generateNumberTestCases(samples []any) []testCase { _ = min _ = max - _ = underflowString - _ = overflowString kind := reflect.TypeOf(zero).Kind() isSint := kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 @@ -258,11 +256,11 @@ func generateNumberTestCases(samples []any) []testCase { testCases = append(testCases, testCase{"10.0E9", float64(10000000000.000000), false}) } - if isUint && underflowString != nil { + if isInt && underflowString != nil { testCases = append(testCases, testCase{underflowString, zero, true}) } - if kind == reflect.Uint64 && isUint && overflowString != nil { + if isInt && overflowString != nil { testCases = append(testCases, testCase{overflowString, zero, true}) } @@ -390,6 +388,35 @@ func TestNumber(t *testing.T) { } } +func TestIntegerStringOutOfRange(t *testing.T) { + t.Parallel() + c := qt.New(t) + + _, err := cast.ToInt8E("128") + c.Assert(err, qt.IsNotNil) + _, err = cast.ToInt8E("-129") + c.Assert(err, qt.IsNotNil) + v, err := cast.ToInt8E("127") + c.Assert(err, qt.IsNil) + c.Assert(v, qt.Equals, int8(127)) + + _, err = cast.ToUint8E("256") + c.Assert(err, qt.IsNotNil) + v8, err := cast.ToUint8E("255") + c.Assert(err, qt.IsNil) + c.Assert(v8, qt.Equals, uint8(255)) + + _, err = cast.ToInt16E("32768") + c.Assert(err, qt.IsNotNil) + _, err = cast.ToUint16E("65536") + c.Assert(err, qt.IsNotNil) + + _, err = cast.ToInt32E("2147483648") + c.Assert(err, qt.IsNotNil) + _, err = cast.ToUint32E("4294967296") + c.Assert(err, qt.IsNotNil) +} + func BenchmarkNumber(b *testing.B) { type testCase struct { name string