]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: move atoi to internal/runtime/strconv
authorMichael Pratt <mpratt@google.com>
Wed, 14 May 2025 19:53:58 +0000 (15:53 -0400)
committerGopher Robot <gobot@golang.org>
Mon, 19 May 2025 19:46:19 +0000 (12:46 -0700)
Moving to a smaller package allows its use in other internal/runtime
packages.

This isn't internal/strconvlite since it can't be used directly by
strconv.

For #73193.

Change-Id: I6a6a636c9c8b3f06b5fd6c07fe9dd5a7a37d1429
Reviewed-on: https://go-review.googlesource.com/c/go/+/672697
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Michael Pratt <mpratt@google.com>

14 files changed:
src/cmd/internal/objabi/pkgspecial.go
src/go/build/deps_test.go
src/internal/runtime/math/math.go
src/internal/runtime/strconv/atoi.go [new file with mode: 0644]
src/internal/runtime/strconv/atoi_test.go [new file with mode: 0644]
src/runtime/export_test.go
src/runtime/malloc.go
src/runtime/mgcpacer.go
src/runtime/mheap.go
src/runtime/os_linux.go
src/runtime/proc.go
src/runtime/runtime1.go
src/runtime/string.go
src/runtime/string_test.go

index 55b66b60558a5af725c3b3ab10434b8ec0870d7c..d4773b1ecff9bfccfa1544c2e23413830c38e122 100644 (file)
@@ -53,6 +53,7 @@ var runtimePkgs = []string{
        "internal/runtime/gc",
        "internal/runtime/maps",
        "internal/runtime/math",
+       "internal/runtime/strconv",
        "internal/runtime/sys",
        "internal/runtime/syscall",
 
index b261af47e2167c2d67aa96cb44c3d4b216bca892..4f366b34a169492e1892dd7337e574dabde844da 100644 (file)
@@ -97,6 +97,7 @@ var depsRules = `
        < internal/runtime/gc
        < internal/runtime/math
        < internal/runtime/maps
+       < internal/runtime/strconv
        < runtime
        < sync/atomic
        < internal/sync
index e0fdc3438d3a9d144fcc9a2e2c71c878f73c06ce..7b616cff797740b1055dfef1a74da55f493810fa 100644 (file)
@@ -8,7 +8,10 @@ import "internal/goarch"
 
 const (
        MaxUint32  = ^uint32(0)
+       MaxUint64  = ^uint64(0)
        MaxUintptr = ^uintptr(0)
+
+       MaxInt64 = int64(MaxUint64 >> 1)
 )
 
 // MulUintptr returns a * b and whether the multiplication overflowed.
diff --git a/src/internal/runtime/strconv/atoi.go b/src/internal/runtime/strconv/atoi.go
new file mode 100644 (file)
index 0000000..87b3faf
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright 2025 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 strconv
+
+import (
+       "internal/runtime/math"
+)
+
+// Atoi64 parses an int64 from a string s.
+// The bool result reports whether s is a number
+// representable by a value of type int64.
+func Atoi64(s string) (int64, bool) {
+       if s == "" {
+               return 0, false
+       }
+
+       neg := false
+       if s[0] == '-' {
+               neg = true
+               s = s[1:]
+       }
+
+       un := uint64(0)
+       for i := 0; i < len(s); i++ {
+               c := s[i]
+               if c < '0' || c > '9' {
+                       return 0, false
+               }
+               if un > math.MaxUint64/10 {
+                       // overflow
+                       return 0, false
+               }
+               un *= 10
+               un1 := un + uint64(c) - '0'
+               if un1 < un {
+                       // overflow
+                       return 0, false
+               }
+               un = un1
+       }
+
+       if !neg && un > uint64(math.MaxInt64) {
+               return 0, false
+       }
+       if neg && un > uint64(math.MaxInt64)+1 {
+               return 0, false
+       }
+
+       n := int64(un)
+       if neg {
+               n = -n
+       }
+
+       return n, true
+}
+
+// Atoi is like Atoi64 but for integers
+// that fit into an int.
+func Atoi(s string) (int, bool) {
+       if n, ok := Atoi64(s); n == int64(int(n)) {
+               return int(n), ok
+       }
+       return 0, false
+}
+
+// Atoi32 is like Atoi but for integers
+// that fit into an int32.
+func Atoi32(s string) (int32, bool) {
+       if n, ok := Atoi64(s); n == int64(int32(n)) {
+               return int32(n), ok
+       }
+       return 0, false
+}
+
diff --git a/src/internal/runtime/strconv/atoi_test.go b/src/internal/runtime/strconv/atoi_test.go
new file mode 100644 (file)
index 0000000..49cd6f1
--- /dev/null
@@ -0,0 +1,105 @@
+// Copyright 2025 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 strconv_test
+
+import (
+       "internal/runtime/strconv"
+       "testing"
+)
+
+const intSize = 32 << (^uint(0) >> 63)
+
+type atoi64Test struct {
+       in  string
+       out int64
+       ok  bool
+}
+
+var atoi64tests = []atoi64Test{
+       {"", 0, false},
+       {"0", 0, true},
+       {"-0", 0, true},
+       {"1", 1, true},
+       {"-1", -1, true},
+       {"12345", 12345, true},
+       {"-12345", -12345, true},
+       {"012345", 12345, true},
+       {"-012345", -12345, true},
+       {"12345x", 0, false},
+       {"-12345x", 0, false},
+       {"98765432100", 98765432100, true},
+       {"-98765432100", -98765432100, true},
+       {"20496382327982653440", 0, false},
+       {"-20496382327982653440", 0, false},
+       {"9223372036854775807", 1<<63 - 1, true},
+       {"-9223372036854775807", -(1<<63 - 1), true},
+       {"9223372036854775808", 0, false},
+       {"-9223372036854775808", -1 << 63, true},
+       {"9223372036854775809", 0, false},
+       {"-9223372036854775809", 0, false},
+}
+
+func TestAtoi(t *testing.T) {
+       switch intSize {
+       case 32:
+               for i := range atoi32tests {
+                       test := &atoi32tests[i]
+                       out, ok := strconv.Atoi(test.in)
+                       if test.out != int32(out) || test.ok != ok {
+                               t.Errorf("Atoi(%q) = (%v, %v) want (%v, %v)",
+                                       test.in, out, ok, test.out, test.ok)
+                       }
+               }
+       case 64:
+               for i := range atoi64tests {
+                       test := &atoi64tests[i]
+                       out, ok := strconv.Atoi(test.in)
+                       if test.out != int64(out) || test.ok != ok {
+                               t.Errorf("Atoi(%q) = (%v, %v) want (%v, %v)",
+                                       test.in, out, ok, test.out, test.ok)
+                       }
+               }
+       }
+}
+
+type atoi32Test struct {
+       in  string
+       out int32
+       ok  bool
+}
+
+var atoi32tests = []atoi32Test{
+       {"", 0, false},
+       {"0", 0, true},
+       {"-0", 0, true},
+       {"1", 1, true},
+       {"-1", -1, true},
+       {"12345", 12345, true},
+       {"-12345", -12345, true},
+       {"012345", 12345, true},
+       {"-012345", -12345, true},
+       {"12345x", 0, false},
+       {"-12345x", 0, false},
+       {"987654321", 987654321, true},
+       {"-987654321", -987654321, true},
+       {"2147483647", 1<<31 - 1, true},
+       {"-2147483647", -(1<<31 - 1), true},
+       {"2147483648", 0, false},
+       {"-2147483648", -1 << 31, true},
+       {"2147483649", 0, false},
+       {"-2147483649", 0, false},
+}
+
+func TestAtoi32(t *testing.T) {
+       for i := range atoi32tests {
+               test := &atoi32tests[i]
+               out, ok := strconv.Atoi32(test.in)
+               if test.out != out || test.ok != ok {
+                       t.Errorf("Atoi32(%q) = (%v, %v) want (%v, %v)",
+                               test.in, out, ok, test.out, test.ok)
+               }
+       }
+}
+
index e7f5d426e4065e166769bef4b7bc0c4db9915a79..a9cc767e30e05de2d2c99c87475b557a428c5371 100644 (file)
@@ -35,8 +35,6 @@ var ReadRandomFailed = &readRandomFailed
 
 var Fastlog2 = fastlog2
 
-var Atoi = atoi
-var Atoi32 = atoi32
 var ParseByteCount = parseByteCount
 
 var Nanotime = nanotime
index ffaf92debc374a23730c3b5c1c1a00904fbce1fc..ccdebb26fbbf67612cb9912d22945e58279c6bae 100644 (file)
@@ -630,7 +630,7 @@ func mallocinit() {
        }
        // Initialize the memory limit here because the allocator is going to look at it
        // but we haven't called gcinit yet and we're definitely going to allocate memory before then.
-       gcController.memoryLimit.Store(maxInt64)
+       gcController.memoryLimit.Store(math.MaxInt64)
 }
 
 // sysAlloc allocates heap arena space for at least n bytes. The
@@ -1816,7 +1816,7 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
 func nextSample() int64 {
        if MemProfileRate == 0 {
                // Basically never sample.
-               return maxInt64
+               return math.MaxInt64
        }
        if MemProfileRate == 1 {
                // Sample immediately.
index 2e05244d95ce982246934e495e831297735a621b..044792d6bd6f574a38eb225e99c8a819ba1d289d 100644 (file)
@@ -8,6 +8,8 @@ import (
        "internal/cpu"
        "internal/goexperiment"
        "internal/runtime/atomic"
+       "internal/runtime/math"
+       "internal/runtime/strconv"
        _ "unsafe" // for go:linkname
 )
 
@@ -1311,7 +1313,7 @@ func readGOGC() int32 {
        if p == "off" {
                return -1
        }
-       if n, ok := atoi32(p); ok {
+       if n, ok := strconv.Atoi32(p); ok {
                return n
        }
        return 100
@@ -1355,7 +1357,7 @@ func setMemoryLimit(in int64) (out int64) {
 func readGOMEMLIMIT() int64 {
        p := gogetenv("GOMEMLIMIT")
        if p == "" || p == "off" {
-               return maxInt64
+               return math.MaxInt64
        }
        n, ok := parseByteCount(p)
        if !ok {
index 5a27ab5e78f3ed549965c481ce120899b204b6f2..c5e55f583ec4a937e830f7769c3e895fb87b2b6c 100644 (file)
@@ -1324,7 +1324,7 @@ HaveSpan:
                // that we expect to page in.
                inuse := gcController.mappedReady.Load()
                // Be careful about overflow, especially with uintptrs. Even on 32-bit platforms
-               // someone can set a really big memory limit that isn't maxInt64.
+               // someone can set a really big memory limit that isn't math.MaxInt64.
                if uint64(scav)+inuse > uint64(limit) {
                        bytesToScavenge = uintptr(uint64(scav) + inuse - uint64(limit))
                        forceScavenge = true
index f24d18027be8af2d0f54e5de79ba5da2855905f0..3071e3220216a362eef8107382cd262e8a8dfebd 100644 (file)
@@ -8,6 +8,7 @@ import (
        "internal/abi"
        "internal/goarch"
        "internal/runtime/atomic"
+       "internal/runtime/strconv"
        "internal/runtime/syscall"
        "unsafe"
 )
@@ -341,7 +342,7 @@ func getHugePageSize() uintptr {
                return 0
        }
        n-- // remove trailing newline
-       v, ok := atoi(slicebytetostringtmp((*byte)(ptr), int(n)))
+       v, ok := strconv.Atoi(slicebytetostringtmp((*byte)(ptr), int(n)))
        if !ok || v < 0 {
                v = 0
        }
index 1ca800c5fdb0de599bb65b4c69a2590cf95ac9e6..5d3e4e495340edd8cbaabb29d017a6d6f04082bd 100644 (file)
@@ -11,6 +11,7 @@ import (
        "internal/goos"
        "internal/runtime/atomic"
        "internal/runtime/exithook"
+       "internal/runtime/strconv"
        "internal/runtime/sys"
        "internal/stringslite"
        "unsafe"
@@ -900,7 +901,7 @@ func schedinit() {
        lock(&sched.lock)
        sched.lastpoll.Store(nanotime())
        procs := ncpu
-       if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
+       if n, ok := strconv.Atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
                procs = n
        }
        if procresize(procs) != nil {
index 2132ceecb2f0869249572a2c95cfd77f1537bde0..5beaa4dd74640306336bb815c3a4b01ce027bf32 100644 (file)
@@ -8,6 +8,7 @@ import (
        "internal/bytealg"
        "internal/goarch"
        "internal/runtime/atomic"
+       "internal/runtime/strconv"
        "unsafe"
 )
 
@@ -526,13 +527,13 @@ func parsegodebug(godebug string, seen map[string]bool) {
                // is int, not int32, and should only be updated
                // if specified in GODEBUG.
                if seen == nil && key == "memprofilerate" {
-                       if n, ok := atoi(value); ok {
+                       if n, ok := strconv.Atoi(value); ok {
                                MemProfileRate = n
                        }
                } else {
                        for _, v := range dbgvars {
                                if v.name == key {
-                                       if n, ok := atoi32(value); ok {
+                                       if n, ok := strconv.Atoi32(value); ok {
                                                if seen == nil && v.value != nil {
                                                        *v.value = n
                                                } else if v.atomic != nil {
@@ -572,7 +573,7 @@ func setTraceback(level string) {
                fallthrough
        default:
                t = tracebackAll
-               if n, ok := atoi(level); ok && n == int(uint32(n)) {
+               if n, ok := strconv.Atoi(level); ok && n == int(uint32(n)) {
                        t |= uint32(n) << tracebackShift
                }
        }
index 7bb9d58de06753dc74e47dd419e979cd886f5e6e..44d586bc53ee7d50d232c02ee654ac15de9c7c3c 100644 (file)
@@ -8,6 +8,8 @@ import (
        "internal/abi"
        "internal/bytealg"
        "internal/goarch"
+       "internal/runtime/math"
+       "internal/runtime/strconv"
        "internal/runtime/sys"
        "unsafe"
 )
@@ -391,77 +393,6 @@ func gostringn(p *byte, l int) string {
        return s
 }
 
-const (
-       maxUint64 = ^uint64(0)
-       maxInt64  = int64(maxUint64 >> 1)
-)
-
-// atoi64 parses an int64 from a string s.
-// The bool result reports whether s is a number
-// representable by a value of type int64.
-func atoi64(s string) (int64, bool) {
-       if s == "" {
-               return 0, false
-       }
-
-       neg := false
-       if s[0] == '-' {
-               neg = true
-               s = s[1:]
-       }
-
-       un := uint64(0)
-       for i := 0; i < len(s); i++ {
-               c := s[i]
-               if c < '0' || c > '9' {
-                       return 0, false
-               }
-               if un > maxUint64/10 {
-                       // overflow
-                       return 0, false
-               }
-               un *= 10
-               un1 := un + uint64(c) - '0'
-               if un1 < un {
-                       // overflow
-                       return 0, false
-               }
-               un = un1
-       }
-
-       if !neg && un > uint64(maxInt64) {
-               return 0, false
-       }
-       if neg && un > uint64(maxInt64)+1 {
-               return 0, false
-       }
-
-       n := int64(un)
-       if neg {
-               n = -n
-       }
-
-       return n, true
-}
-
-// atoi is like atoi64 but for integers
-// that fit into an int.
-func atoi(s string) (int, bool) {
-       if n, ok := atoi64(s); n == int64(int(n)) {
-               return int(n), ok
-       }
-       return 0, false
-}
-
-// atoi32 is like atoi but for integers
-// that fit into an int32.
-func atoi32(s string) (int32, bool) {
-       if n, ok := atoi64(s); n == int64(int32(n)) {
-               return int32(n), ok
-       }
-       return 0, false
-}
-
 // parseByteCount parses a string that represents a count of bytes.
 //
 // s must match the following regular expression:
@@ -483,7 +414,7 @@ func parseByteCount(s string) (int64, bool) {
        // Handle the easy non-suffix case.
        last := s[len(s)-1]
        if last >= '0' && last <= '9' {
-               n, ok := atoi64(s)
+               n, ok := strconv.Atoi64(s)
                if !ok || n < 0 {
                        return 0, false
                }
@@ -498,7 +429,7 @@ func parseByteCount(s string) (int64, bool) {
        // The one before that must always be a digit or 'i'.
        if c := s[len(s)-2]; c >= '0' && c <= '9' {
                // Trivial 'B' suffix.
-               n, ok := atoi64(s[:len(s)-1])
+               n, ok := strconv.Atoi64(s[:len(s)-1])
                if !ok || n < 0 {
                        return 0, false
                }
@@ -529,17 +460,17 @@ func parseByteCount(s string) (int64, bool) {
        for i := 0; i < power; i++ {
                m *= 1024
        }
-       n, ok := atoi64(s[:len(s)-3])
+       n, ok := strconv.Atoi64(s[:len(s)-3])
        if !ok || n < 0 {
                return 0, false
        }
        un := uint64(n)
-       if un > maxUint64/m {
+       if un > math.MaxUint64/m {
                // Overflow.
                return 0, false
        }
        un *= m
-       if un > uint64(maxInt64) {
+       if un > uint64(math.MaxInt64) {
                // Overflow.
                return 0, false
        }
index cfc0ad7cde87864195aaccf84c3aba0b61e13ec1..522a502a1cfe588134141d5b36682753aa948373 100644 (file)
@@ -390,98 +390,6 @@ func TestString2Slice(t *testing.T) {
 
 const intSize = 32 << (^uint(0) >> 63)
 
-type atoi64Test struct {
-       in  string
-       out int64
-       ok  bool
-}
-
-var atoi64tests = []atoi64Test{
-       {"", 0, false},
-       {"0", 0, true},
-       {"-0", 0, true},
-       {"1", 1, true},
-       {"-1", -1, true},
-       {"12345", 12345, true},
-       {"-12345", -12345, true},
-       {"012345", 12345, true},
-       {"-012345", -12345, true},
-       {"12345x", 0, false},
-       {"-12345x", 0, false},
-       {"98765432100", 98765432100, true},
-       {"-98765432100", -98765432100, true},
-       {"20496382327982653440", 0, false},
-       {"-20496382327982653440", 0, false},
-       {"9223372036854775807", 1<<63 - 1, true},
-       {"-9223372036854775807", -(1<<63 - 1), true},
-       {"9223372036854775808", 0, false},
-       {"-9223372036854775808", -1 << 63, true},
-       {"9223372036854775809", 0, false},
-       {"-9223372036854775809", 0, false},
-}
-
-func TestAtoi(t *testing.T) {
-       switch intSize {
-       case 32:
-               for i := range atoi32tests {
-                       test := &atoi32tests[i]
-                       out, ok := runtime.Atoi(test.in)
-                       if test.out != int32(out) || test.ok != ok {
-                               t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
-                                       test.in, out, ok, test.out, test.ok)
-                       }
-               }
-       case 64:
-               for i := range atoi64tests {
-                       test := &atoi64tests[i]
-                       out, ok := runtime.Atoi(test.in)
-                       if test.out != int64(out) || test.ok != ok {
-                               t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
-                                       test.in, out, ok, test.out, test.ok)
-                       }
-               }
-       }
-}
-
-type atoi32Test struct {
-       in  string
-       out int32
-       ok  bool
-}
-
-var atoi32tests = []atoi32Test{
-       {"", 0, false},
-       {"0", 0, true},
-       {"-0", 0, true},
-       {"1", 1, true},
-       {"-1", -1, true},
-       {"12345", 12345, true},
-       {"-12345", -12345, true},
-       {"012345", 12345, true},
-       {"-012345", -12345, true},
-       {"12345x", 0, false},
-       {"-12345x", 0, false},
-       {"987654321", 987654321, true},
-       {"-987654321", -987654321, true},
-       {"2147483647", 1<<31 - 1, true},
-       {"-2147483647", -(1<<31 - 1), true},
-       {"2147483648", 0, false},
-       {"-2147483648", -1 << 31, true},
-       {"2147483649", 0, false},
-       {"-2147483649", 0, false},
-}
-
-func TestAtoi32(t *testing.T) {
-       for i := range atoi32tests {
-               test := &atoi32tests[i]
-               out, ok := runtime.Atoi32(test.in)
-               if test.out != out || test.ok != ok {
-                       t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)",
-                               test.in, out, ok, test.out, test.ok)
-               }
-       }
-}
-
 func TestParseByteCount(t *testing.T) {
        for _, test := range []struct {
                in  string