Skip to content

Instantly share code, notes, and snippets.

@zeebo
Created July 27, 2021 17:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeebo/aeb001d35ca3a6e0ebd85eb52e3ef4c2 to your computer and use it in GitHub Desktop.
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.
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