This replaces five implementations scattered across low level packages.
(And I plan to use it in a sixth soon.)
Three of the five were byte-for-byte identical.
Change-Id: I3bbbeeac63723a487986c912b604e10ad1e042f4
Reviewed-on: https://go-review.googlesource.com/c/go/+/301549
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
# RUNTIME is the core runtime group of packages, all of them very light-weight.
internal/abi, internal/cpu, unsafe
< internal/bytealg
+ < internal/itoa
< internal/unsafeheader
< runtime/internal/sys
< runtime/internal/atomic
--- /dev/null
+// 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:])
+}
--- /dev/null
+// 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)
+ }
+ }
+}
package poll
import (
+ "internal/itoa"
"runtime"
"sync"
"syscall"
if aio.pid == -1 {
return
}
- f, e := syscall.Open("/proc/"+itoa(aio.pid)+"/note", syscall.O_WRONLY)
+ f, e := syscall.Open("/proc/"+itoa.Itoa(aio.pid)+"/note", syscall.O_WRONLY)
if e != nil {
return
}
//go:build plan9
// +build plan9
-// Simple conversions to avoid depending on strconv.
-
package poll
-// Convert integer to decimal string
-func itoa(val int) string {
- if val < 0 {
- return "-" + uitoa(uint(-val))
- }
- return uitoa(uint(val))
-}
-
-// 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:])
-}
-
// stringsHasSuffix is strings.HasSuffix. It reports whether s ends in
// suffix.
func stringsHasSuffix(s, suffix string) bool {
package net
import (
+ "internal/itoa"
"sort"
"golang.org/x/net/dns/dnsmessage"
return "", &DNSError{Err: "unrecognized address", Name: addr}
}
if ip.To4() != nil {
- return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil
+ return itoa.Uitoa(uint(ip[15])) + "." + itoa.Uitoa(uint(ip[14])) + "." + itoa.Uitoa(uint(ip[13])) + "." + itoa.Uitoa(uint(ip[12])) + ".in-addr.arpa.", nil
}
// Must be IPv6
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
import (
"context"
"errors"
+ "internal/itoa"
"io"
"os"
"sync"
if s, ok := lookupOrderName[o]; ok {
return s
}
- return "hostLookupOrder=" + itoa(int(o)) + "??"
+ return "hostLookupOrder=" + itoa.Itoa(int(o)) + "??"
}
// goLookupHost is the native Go implementation of LookupHost.
import (
"errors"
+ "internal/itoa"
"sync"
"time"
)
zoneCache.RUnlock()
}
if !ok { // last resort
- name = uitoa(uint(index))
+ name = itoa.Uitoa(uint(index))
}
return name
}
import (
"errors"
+ "internal/itoa"
"os"
)
func readInterface(i int) (*Interface, error) {
ifc := &Interface{
- Index: i + 1, // Offset the index by one to suit the contract
- Name: netdir + "/ipifc/" + itoa(i), // Name is the full path to the interface path in plan9
+ Index: i + 1, // Offset the index by one to suit the contract
+ Name: netdir + "/ipifc/" + itoa.Itoa(i), // Name is the full path to the interface path in plan9
}
ifcstat := ifc.Name + "/status"
package net
-import "internal/bytealg"
+import (
+ "internal/bytealg"
+ "internal/itoa"
+)
// IP address lengths (bytes).
const (
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).
import (
"context"
"internal/bytealg"
+ "internal/itoa"
"io/fs"
"os"
"syscall"
if port == 0 {
return ""
}
- return itoa(port)
+ return itoa.Itoa(port)
}
- return ip.String() + "!" + itoa(port)
+ return ip.String() + "!" + itoa.Itoa(port)
}
func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error {
"context"
"errors"
"internal/bytealg"
+ "internal/itoa"
"io"
"os"
)
if len(ip) != 0 && !ip.IsUnspecified() {
ips = ip.String()
}
- lines, err := queryCS(ctx, net, ips, itoa(port))
+ lines, err := queryCS(ctx, net, ips, itoa.Itoa(port))
if err != nil {
return
}
return byte(n), ok && ei == 2
}
-// Convert integer to decimal string.
-func itoa(val int) string {
- if val < 0 {
- return "-" + uitoa(uint(-val))
- }
- return uitoa(uint(val))
-}
-
-// 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 {
import (
"context"
+ "internal/itoa"
"io"
"os"
"syscall"
}
ip := ipEmptyString(a.IP)
if a.Zone != "" {
- return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port))
+ return JoinHostPort(ip+"%"+a.Zone, itoa.Itoa(a.Port))
}
- return JoinHostPort(ip, itoa(a.Port))
+ return JoinHostPort(ip, itoa.Itoa(a.Port))
}
func (a *TCPAddr) isWildcard() bool {
package net
import (
+ "internal/itoa"
"syscall"
"time"
)
// Set keep alive period.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
- cmd := "keepalive " + itoa(int(d/time.Millisecond))
+ cmd := "keepalive " + itoa.Itoa(int(d/time.Millisecond))
_, e := fd.ctl.WriteAt([]byte(cmd), 0)
return e
}
import (
"context"
+ "internal/itoa"
"syscall"
)
}
ip := ipEmptyString(a.IP)
if a.Zone != "" {
- return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port))
+ return JoinHostPort(ip+"%"+a.Zone, itoa.Itoa(a.Port))
}
- return JoinHostPort(ip, itoa(a.Port))
+ return JoinHostPort(ip, itoa.Itoa(a.Port))
}
func (a *UDPAddr) isWildcard() bool {
package os
import (
+ "internal/itoa"
"runtime"
"syscall"
"time"
}
func (p *Process) writeProcFile(file string, data string) error {
- f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
+ f, e := OpenFile("/proc/"+itoa.Itoa(p.Pid)+"/"+file, O_WRONLY, 0)
if e != nil {
return e
}
package os
import (
+ "internal/itoa"
"internal/syscall/execenv"
"runtime"
"syscall"
if runtime.GOOS == "windows" && uint(code) >= 1<<16 { // windows uses large hex numbers
res = "exit status " + uitox(uint(code))
} else { // unix systems use small decimal integers
- res = "exit status " + itoa(code) // unix
+ res = "exit status " + itoa.Itoa(code) // unix
}
case status.Signaled():
res = "signal: " + status.Signal().String()
case status.Stopped():
res = "stop signal: " + status.StopSignal().String()
if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
- res += " (trap " + itoa(status.TrapCause()) + ")"
+ res += " (trap " + itoa.Itoa(status.TrapCause()) + ")"
}
case status.Continued():
res = "continued"
package os
-import "syscall"
+import (
+ "internal/itoa"
+ "syscall"
+)
func executable() (string, error) {
- fn := "/proc/" + itoa(Getpid()) + "/text"
+ fn := "/proc/" + itoa.Itoa(Getpid()) + "/text"
f, err := Open(fn)
if err != nil {
return "", err
package signal
import (
+ "internal/itoa"
"os"
"runtime"
"syscall"
}
}
-func itoa(val int) string {
- if val < 0 {
- return "-" + itoa(-val)
- }
- 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:])
-}
-
func postNote(pid int, note string) error {
- f, err := os.OpenFile("/proc/"+itoa(pid)+"/note", os.O_WRONLY, 0)
+ f, err := os.OpenFile("/proc/"+itoa.Itoa(pid)+"/note", os.O_WRONLY, 0)
if err != nil {
return err
}
package os
-// itoa converts val (an int) to a decimal string.
-func itoa(val int) string {
- if val < 0 {
- return "-" + uitoa(uint(-val))
- }
- return uitoa(uint(val))
-}
-
-// uitoa converts val (a uint) 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:])
-}
-
// itox converts val (an int) to a hexdecimal string.
func itox(val int) string {
if val < 0 {
package os
-import "errors"
+import (
+ "errors"
+ "internal/itoa"
+)
// fastrand provided by runtime.
// We generate random temporary file names so that there's a good
func fastrand() uint32
func nextRandom() string {
- return uitoa(uint(fastrand()))
+ return itoa.Uitoa(uint(fastrand()))
}
// CreateTemp creates a new temporary file in the directory dir,
package syscall
import (
+ "internal/itoa"
"internal/syscall/windows/sysdll"
"sync"
"sync/atomic"
case 18:
return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17])
default:
- panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
+ panic("Call " + p.Name + " with too many arguments " + itoa.Itoa(len(a)) + ".")
}
}
package syscall
import (
+ "internal/itoa"
"runtime"
"unsafe"
)
func formatIDMappings(idMap []SysProcIDMap) []byte {
var data []byte
for _, im := range idMap {
- data = append(data, []byte(itoa(im.ContainerID)+" "+itoa(im.HostID)+" "+itoa(im.Size)+"\n")...)
+ data = append(data, []byte(itoa.Itoa(im.ContainerID)+" "+itoa.Itoa(im.HostID)+" "+itoa.Itoa(im.Size)+"\n")...)
}
return data
}
// This is needed since kernel 3.19, because you can't write gid_map without
// disabling setgroups() system call.
func writeSetgroups(pid int, enable bool) error {
- sgf := "/proc/" + itoa(pid) + "/setgroups"
+ sgf := "/proc/" + itoa.Itoa(pid) + "/setgroups"
fd, err := Open(sgf, O_RDWR, 0)
if err != nil {
return err
// for a process and it is called from the parent process.
func writeUidGidMappings(pid int, sys *SysProcAttr) error {
if sys.UidMappings != nil {
- uidf := "/proc/" + itoa(pid) + "/uid_map"
+ uidf := "/proc/" + itoa.Itoa(pid) + "/uid_map"
if err := writeIDMappings(uidf, sys.UidMappings); err != nil {
return err
}
if err := writeSetgroups(pid, sys.GidMappingsEnableSetgroups); err != nil && err != ENOENT {
return err
}
- gidf := "/proc/" + itoa(pid) + "/gid_map"
+ gidf := "/proc/" + itoa.Itoa(pid) + "/gid_map"
if err := writeIDMappings(gidf, sys.GidMappings); err != nil {
return err
}
package syscall
import (
+ "internal/itoa"
"runtime"
"sync"
"unsafe"
return e
}
- fd, e := Open("#d/"+itoa(p[1]), O_RDWR|O_CLOEXEC)
+ fd, e := Open("#d/"+itoa.Itoa(p[1]), O_RDWR|O_CLOEXEC)
if e != nil {
Close(p[0])
Close(p[1])
+++ /dev/null
-// Copyright 2014 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 syscall
-
-var Itoa = itoa
+++ /dev/null
-// Copyright 2009 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 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:])
-}
package syscall
import (
+ "internal/itoa"
"internal/oserror"
"sync"
"unsafe"
return s
}
}
- return "errno " + itoa(int(e))
+ return "errno " + itoa.Itoa(int(e))
}
func (e Errno) Is(target error) bool {
return str
}
}
- return "signal " + itoa(int(s))
+ return "signal " + itoa.Itoa(int(s))
}
var signals = [...]string{}
package syscall
-import "unsafe"
+import (
+ "internal/itoa"
+ "unsafe"
+)
func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr)
func Futimes(fd int, tv []Timeval) (err error) {
// Believe it or not, this is the best we can do on Linux
// (and is what glibc does).
- return Utimes("/proc/self/fd/"+itoa(fd), tv)
+ return Utimes("/proc/self/fd/"+itoa.Itoa(fd), tv)
}
const ImplementsGetwd = true
package syscall_test
import (
- "fmt"
"internal/testenv"
"os"
"runtime"
testSetGetenv(t, "TESTENV", "")
}
-func TestItoa(t *testing.T) {
- // Make most negative integer: 0x8000...
- i := 1
- for i<<1 != 0 {
- i <<= 1
- }
- if i >= 0 {
- t.Fatal("bad math")
- }
- s := syscall.Itoa(i)
- f := fmt.Sprint(i)
- if s != f {
- t.Fatalf("itoa(%d) = %s, want %s", i, s, f)
- }
-}
-
// Check that permuting child process fds doesn't interfere with
// reporting of fork/exec status. See Issue 14979.
func TestExecErrPermutedFds(t *testing.T) {
package syscall
import (
+ "internal/itoa"
"internal/oserror"
"internal/race"
"internal/unsafeheader"
return s
}
}
- return "errno " + itoa(int(e))
+ return "errno " + itoa.Itoa(int(e))
}
func (e Errno) Is(target error) bool {
return str
}
}
- return "signal " + itoa(int(s))
+ return "signal " + itoa.Itoa(int(s))
}
func Read(fd int, p []byte) (n int, err error) {
import (
errorspkg "errors"
+ "internal/itoa"
"internal/oserror"
"internal/race"
"internal/unsafeheader"
if err != nil {
n, err = formatMessage(flags, 0, uint32(e), 0, b, nil)
if err != nil {
- return "winapi error #" + itoa(int(e))
+ return "winapi error #" + itoa.Itoa(int(e))
}
}
// trim terminating \r and \n
return str
}
}
- return "signal " + itoa(int(s))
+ return "signal " + itoa.Itoa(int(s))
}
func LoadCreateSymbolicLink() error {