From b70b6076e3fd228f66feb12f8ba6be82c4e7926b Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Sun, 14 Nov 2021 20:51:54 -0800 Subject: [PATCH] net/ip, syscall/errno: Reduce code duplication by switching to internal/itoa. internal/itoa wasn't around back in go 1.12 days when tinygo's syscall/errno.go was written. It was only added as of go 1.17 ( https://github.com/golang/go/commit/061a6903a232cb868780b ) so we have to have an internal copy for now. The internal copy should be deleted when tinygo drops support for go 1.16. FWIW, the new version seems nicer. It uses no allocations when converting 0, and although the optimizer might make this moot, uses a multiplication x 10 instead of a mod operation. --- Makefile | 1 + loader/goroot.go | 1 + src/internal/itoa/README.md | 2 ++ src/internal/itoa/itoa.go | 33 ++++++++++++++++++++++++++++ src/internal/itoa/itoa_test.go | 40 ++++++++++++++++++++++++++++++++++ src/net/ip.go | 7 ++++-- src/net/parse.go | 18 --------------- src/syscall/errno.go | 4 +++- src/syscall/str.go | 19 ---------------- 9 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 src/internal/itoa/README.md create mode 100644 src/internal/itoa/itoa.go create mode 100644 src/internal/itoa/itoa_test.go diff --git a/Makefile b/Makefile index a3403f5e..0c40b937 100644 --- a/Makefile +++ b/Makefile @@ -216,6 +216,7 @@ TEST_PACKAGES = \ hash/crc64 \ html \ index/suffixarray \ + internal/itoa \ math \ math/cmplx \ net/mail \ diff --git a/loader/goroot.go b/loader/goroot.go index 8f0acf1b..88b46760 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -228,6 +228,7 @@ func pathsToOverride(needsSyscallPackage bool) map[string]bool { "internal/bytealg/": false, "internal/reflectlite/": false, "internal/task/": false, + "internal/itoa/": false, // TODO: Remove when we drop support for go 1.16 "machine/": false, "net/": true, "os/": true, diff --git a/src/internal/itoa/README.md b/src/internal/itoa/README.md new file mode 100644 index 00000000..6b666d12 --- /dev/null +++ b/src/internal/itoa/README.md @@ -0,0 +1,2 @@ +internal/itoa is new to go as of 1.17. +This directory should be removed when tinygo drops support for go 1.16. diff --git a/src/internal/itoa/itoa.go b/src/internal/itoa/itoa.go new file mode 100644 index 00000000..c6062d9f --- /dev/null +++ b/src/internal/itoa/itoa.go @@ -0,0 +1,33 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Simple conversions to avoid depending on strconv. + +package itoa + +// Itoa converts val to a decimal string. +func Itoa(val int) string { + if val < 0 { + return "-" + Uitoa(uint(-val)) + } + return Uitoa(uint(val)) +} + +// Uitoa converts val to a decimal string. +func Uitoa(val uint) string { + if val == 0 { // avoid string allocation + return "0" + } + var buf [20]byte // big enough for 64bit value base 10 + i := len(buf) - 1 + for val >= 10 { + q := val / 10 + buf[i] = byte('0' + val - q*10) + i-- + val = q + } + // val < 10 + buf[i] = byte('0' + val) + return string(buf[i:]) +} diff --git a/src/internal/itoa/itoa_test.go b/src/internal/itoa/itoa_test.go new file mode 100644 index 00000000..71931c1e --- /dev/null +++ b/src/internal/itoa/itoa_test.go @@ -0,0 +1,40 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package itoa_test + +import ( + "fmt" + "internal/itoa" + "math" + "testing" +) + +var ( + minInt64 int64 = math.MinInt64 + maxInt64 int64 = math.MaxInt64 + maxUint64 uint64 = math.MaxUint64 +) + +func TestItoa(t *testing.T) { + tests := []int{int(minInt64), math.MinInt32, -999, -100, -1, 0, 1, 100, 999, math.MaxInt32, int(maxInt64)} + for _, tt := range tests { + got := itoa.Itoa(tt) + want := fmt.Sprint(tt) + if want != got { + t.Fatalf("Itoa(%d) = %s, want %s", tt, got, want) + } + } +} + +func TestUitoa(t *testing.T) { + tests := []uint{0, 1, 100, 999, math.MaxUint32, uint(maxUint64)} + for _, tt := range tests { + got := itoa.Uitoa(tt) + want := fmt.Sprint(tt) + if want != got { + t.Fatalf("Uitoa(%d) = %s, want %s", tt, got, want) + } + } +} diff --git a/src/net/ip.go b/src/net/ip.go index c672ff49..5f78fcc3 100644 --- a/src/net/ip.go +++ b/src/net/ip.go @@ -14,7 +14,10 @@ package net -import "internal/bytealg" +import ( + "internal/bytealg" + "internal/itoa" +) // IP address lengths (bytes). const ( @@ -533,7 +536,7 @@ func (n *IPNet) String() string { if l == -1 { return nn.String() + "/" + m.String() } - return nn.String() + "/" + uitoa(uint(l)) + return nn.String() + "/" + itoa.Uitoa(uint(l)) } // Parse IPv4 address (d.d.d.d). diff --git a/src/net/parse.go b/src/net/parse.go index 2a840c85..f1f2ccb5 100644 --- a/src/net/parse.go +++ b/src/net/parse.go @@ -64,24 +64,6 @@ func xtoi2(s string, e byte) (byte, bool) { return byte(n), ok && ei == 2 } -// Convert unsigned integer to decimal string. -func uitoa(val uint) string { - if val == 0 { // avoid string allocation - return "0" - } - var buf [20]byte // big enough for 64bit value base 10 - i := len(buf) - 1 - for val >= 10 { - q := val / 10 - buf[i] = byte('0' + val - q*10) - i-- - val = q - } - // val < 10 - buf[i] = byte('0' + val) - return string(buf[i:]) -} - // Convert i to a hexadecimal string. Leading zeros are not printed. func appendHex(dst []byte, i uint32) []byte { if i == 0 { diff --git a/src/syscall/errno.go b/src/syscall/errno.go index b67a685c..ec6cc1fb 100644 --- a/src/syscall/errno.go +++ b/src/syscall/errno.go @@ -1,5 +1,7 @@ package syscall +import "internal/itoa" + // Most code here has been copied from the Go sources: // https://github.com/golang/go/blob/go1.12/src/syscall/syscall_js.go // It has the following copyright note: @@ -18,7 +20,7 @@ package syscall type Errno uintptr func (e Errno) Error() string { - return "errno " + itoa(int(e)) + return "errno " + itoa.Itoa(int(e)) } func (e Errno) Temporary() bool { diff --git a/src/syscall/str.go b/src/syscall/str.go index ea5356d0..b73490a2 100644 --- a/src/syscall/str.go +++ b/src/syscall/str.go @@ -4,25 +4,6 @@ package syscall -func itoa(val int) string { // do it here rather than with fmt to avoid dependency - if val < 0 { - return "-" + uitoa(uint(-val)) - } - return uitoa(uint(val)) -} - -func uitoa(val uint) string { - var buf [32]byte // big enough for int64 - i := len(buf) - 1 - for val >= 10 { - buf[i] = byte(val%10 + '0') - i-- - val /= 10 - } - buf[i] = byte(val + '0') - return string(buf[i:]) -} - // clen returns the index of the first NULL byte in n or len(n) if n contains no NULL byte. func clen(n []byte) int { for i := 0; i < len(n); i++ {