Created
July 27, 2021 17:02
-
-
Save zeebo/aeb001d35ca3a6e0ebd85eb52e3ef4c2 to your computer and use it in GitHub Desktop.
patch to strconv to allow heap allocations to be removed for ParseInt(string(someBytes), ...), etc.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git src/strconv/atoi.go src/strconv/atoi.go | |
index 631b487d97..916d60e60a 100644 | |
--- src/strconv/atoi.go | |
+++ src/strconv/atoi.go | |
@@ -33,20 +33,22 @@ func (e *NumError) Error() string { | |
func (e *NumError) Unwrap() error { return e.Err } | |
+func copyString(x string) string { return string([]byte(x)) } | |
+ | |
func syntaxError(fn, str string) *NumError { | |
- return &NumError{fn, str, ErrSyntax} | |
+ return &NumError{fn, copyString(str), ErrSyntax} | |
} | |
func rangeError(fn, str string) *NumError { | |
- return &NumError{fn, str, ErrRange} | |
+ return &NumError{fn, copyString(str), ErrRange} | |
} | |
func baseError(fn, str string, base int) *NumError { | |
- return &NumError{fn, str, errors.New("invalid base " + Itoa(base))} | |
+ return &NumError{fn, copyString(str), errors.New("invalid base " + Itoa(base))} | |
} | |
func bitSizeError(fn, str string, bitSize int) *NumError { | |
- return &NumError{fn, str, errors.New("invalid bit size " + Itoa(bitSize))} | |
+ return &NumError{fn, copyString(str), errors.New("invalid bit size " + Itoa(bitSize))} | |
} | |
const intSize = 32 << (^uint(0) >> 63) | |
@@ -203,7 +205,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) { | |
un, err = ParseUint(s, base, bitSize) | |
if err != nil && err.(*NumError).Err != ErrRange { | |
err.(*NumError).Func = fnParseInt | |
- err.(*NumError).Num = s0 | |
+ err.(*NumError).Num = copyString(s0) | |
return 0, err | |
} | |
@@ -237,7 +239,7 @@ func Atoi(s string) (int, error) { | |
if s[0] == '-' || s[0] == '+' { | |
s = s[1:] | |
if len(s) < 1 { | |
- return 0, &NumError{fnAtoi, s0, ErrSyntax} | |
+ return 0, syntaxError(fnAtoi, s0) | |
} | |
} | |
@@ -245,7 +247,7 @@ func Atoi(s string) (int, error) { | |
for _, ch := range []byte(s) { | |
ch -= '0' | |
if ch > 9 { | |
- return 0, &NumError{fnAtoi, s0, ErrSyntax} | |
+ return 0, syntaxError(fnAtoi, s0) | |
} | |
n = n*10 + int(ch) | |
} | |
diff --git src/strconv/atoi_test.go src/strconv/atoi_test.go | |
index 867fa66a14..51c7595243 100644 | |
--- src/strconv/atoi_test.go | |
+++ src/strconv/atoi_test.go | |
@@ -609,6 +609,34 @@ func TestNumErrorUnwrap(t *testing.T) { | |
} | |
} | |
+func TestParseAllocationsFromBytes(t *testing.T) { | |
+ s := []byte(fmt.Sprintf("%d", 1<<63-1)) | |
+ | |
+ intAllocs := testing.AllocsPerRun(1000, func() { | |
+ out, _ := ParseInt(string(s), 10, 64) | |
+ BenchSink += int(out) | |
+ }) | |
+ if intAllocs != 0 { | |
+ t.Error("ParseInt: wanted 0 allocs but got:", intAllocs) | |
+ } | |
+ | |
+ uintAllocs := testing.AllocsPerRun(1000, func() { | |
+ out, _ := ParseUint(string(s), 10, 64) | |
+ BenchSink += int(out) | |
+ }) | |
+ if uintAllocs != 0 { | |
+ t.Error("ParseUint: wanted 0 allocs but got:", uintAllocs) | |
+ } | |
+ | |
+ atoiAllocs := testing.AllocsPerRun(1000, func() { | |
+ out, _ := Atoi(string(s)) | |
+ BenchSink += int(out) | |
+ }) | |
+ if atoiAllocs != 0 { | |
+ t.Error("Atoi: wanted 0 allocs but got:", atoiAllocs) | |
+ } | |
+} | |
+ | |
func BenchmarkParseInt(b *testing.B) { | |
b.Run("Pos", func(b *testing.B) { | |
benchmarkParseInt(b, 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment