Skip to content

Commit 76e7bfb

Browse files
prattmicgopherbot
authored andcommitted
runtime: move atoi to internal/runtime/strconv
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 <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Michael Pratt <[email protected]>
1 parent d93bea0 commit 76e7bfb

File tree

14 files changed

+208
-180
lines changed

14 files changed

+208
-180
lines changed

src/cmd/internal/objabi/pkgspecial.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var runtimePkgs = []string{
5353
"internal/runtime/gc",
5454
"internal/runtime/maps",
5555
"internal/runtime/math",
56+
"internal/runtime/strconv",
5657
"internal/runtime/sys",
5758
"internal/runtime/syscall",
5859

src/go/build/deps_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ var depsRules = `
9797
< internal/runtime/gc
9898
< internal/runtime/math
9999
< internal/runtime/maps
100+
< internal/runtime/strconv
100101
< runtime
101102
< sync/atomic
102103
< internal/sync

src/internal/runtime/math/math.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import "internal/goarch"
88

99
const (
1010
MaxUint32 = ^uint32(0)
11+
MaxUint64 = ^uint64(0)
1112
MaxUintptr = ^uintptr(0)
13+
14+
MaxInt64 = int64(MaxUint64 >> 1)
1215
)
1316

1417
// MulUintptr returns a * b and whether the multiplication overflowed.

src/internal/runtime/strconv/atoi.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package strconv
6+
7+
import (
8+
"internal/runtime/math"
9+
)
10+
11+
// Atoi64 parses an int64 from a string s.
12+
// The bool result reports whether s is a number
13+
// representable by a value of type int64.
14+
func Atoi64(s string) (int64, bool) {
15+
if s == "" {
16+
return 0, false
17+
}
18+
19+
neg := false
20+
if s[0] == '-' {
21+
neg = true
22+
s = s[1:]
23+
}
24+
25+
un := uint64(0)
26+
for i := 0; i < len(s); i++ {
27+
c := s[i]
28+
if c < '0' || c > '9' {
29+
return 0, false
30+
}
31+
if un > math.MaxUint64/10 {
32+
// overflow
33+
return 0, false
34+
}
35+
un *= 10
36+
un1 := un + uint64(c) - '0'
37+
if un1 < un {
38+
// overflow
39+
return 0, false
40+
}
41+
un = un1
42+
}
43+
44+
if !neg && un > uint64(math.MaxInt64) {
45+
return 0, false
46+
}
47+
if neg && un > uint64(math.MaxInt64)+1 {
48+
return 0, false
49+
}
50+
51+
n := int64(un)
52+
if neg {
53+
n = -n
54+
}
55+
56+
return n, true
57+
}
58+
59+
// Atoi is like Atoi64 but for integers
60+
// that fit into an int.
61+
func Atoi(s string) (int, bool) {
62+
if n, ok := Atoi64(s); n == int64(int(n)) {
63+
return int(n), ok
64+
}
65+
return 0, false
66+
}
67+
68+
// Atoi32 is like Atoi but for integers
69+
// that fit into an int32.
70+
func Atoi32(s string) (int32, bool) {
71+
if n, ok := Atoi64(s); n == int64(int32(n)) {
72+
return int32(n), ok
73+
}
74+
return 0, false
75+
}
76+
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package strconv_test
6+
7+
import (
8+
"internal/runtime/strconv"
9+
"testing"
10+
)
11+
12+
const intSize = 32 << (^uint(0) >> 63)
13+
14+
type atoi64Test struct {
15+
in string
16+
out int64
17+
ok bool
18+
}
19+
20+
var atoi64tests = []atoi64Test{
21+
{"", 0, false},
22+
{"0", 0, true},
23+
{"-0", 0, true},
24+
{"1", 1, true},
25+
{"-1", -1, true},
26+
{"12345", 12345, true},
27+
{"-12345", -12345, true},
28+
{"012345", 12345, true},
29+
{"-012345", -12345, true},
30+
{"12345x", 0, false},
31+
{"-12345x", 0, false},
32+
{"98765432100", 98765432100, true},
33+
{"-98765432100", -98765432100, true},
34+
{"20496382327982653440", 0, false},
35+
{"-20496382327982653440", 0, false},
36+
{"9223372036854775807", 1<<63 - 1, true},
37+
{"-9223372036854775807", -(1<<63 - 1), true},
38+
{"9223372036854775808", 0, false},
39+
{"-9223372036854775808", -1 << 63, true},
40+
{"9223372036854775809", 0, false},
41+
{"-9223372036854775809", 0, false},
42+
}
43+
44+
func TestAtoi(t *testing.T) {
45+
switch intSize {
46+
case 32:
47+
for i := range atoi32tests {
48+
test := &atoi32tests[i]
49+
out, ok := strconv.Atoi(test.in)
50+
if test.out != int32(out) || test.ok != ok {
51+
t.Errorf("Atoi(%q) = (%v, %v) want (%v, %v)",
52+
test.in, out, ok, test.out, test.ok)
53+
}
54+
}
55+
case 64:
56+
for i := range atoi64tests {
57+
test := &atoi64tests[i]
58+
out, ok := strconv.Atoi(test.in)
59+
if test.out != int64(out) || test.ok != ok {
60+
t.Errorf("Atoi(%q) = (%v, %v) want (%v, %v)",
61+
test.in, out, ok, test.out, test.ok)
62+
}
63+
}
64+
}
65+
}
66+
67+
type atoi32Test struct {
68+
in string
69+
out int32
70+
ok bool
71+
}
72+
73+
var atoi32tests = []atoi32Test{
74+
{"", 0, false},
75+
{"0", 0, true},
76+
{"-0", 0, true},
77+
{"1", 1, true},
78+
{"-1", -1, true},
79+
{"12345", 12345, true},
80+
{"-12345", -12345, true},
81+
{"012345", 12345, true},
82+
{"-012345", -12345, true},
83+
{"12345x", 0, false},
84+
{"-12345x", 0, false},
85+
{"987654321", 987654321, true},
86+
{"-987654321", -987654321, true},
87+
{"2147483647", 1<<31 - 1, true},
88+
{"-2147483647", -(1<<31 - 1), true},
89+
{"2147483648", 0, false},
90+
{"-2147483648", -1 << 31, true},
91+
{"2147483649", 0, false},
92+
{"-2147483649", 0, false},
93+
}
94+
95+
func TestAtoi32(t *testing.T) {
96+
for i := range atoi32tests {
97+
test := &atoi32tests[i]
98+
out, ok := strconv.Atoi32(test.in)
99+
if test.out != out || test.ok != ok {
100+
t.Errorf("Atoi32(%q) = (%v, %v) want (%v, %v)",
101+
test.in, out, ok, test.out, test.ok)
102+
}
103+
}
104+
}
105+

src/runtime/export_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ var ReadRandomFailed = &readRandomFailed
3535

3636
var Fastlog2 = fastlog2
3737

38-
var Atoi = atoi
39-
var Atoi32 = atoi32
4038
var ParseByteCount = parseByteCount
4139

4240
var Nanotime = nanotime

src/runtime/malloc.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ func mallocinit() {
630630
}
631631
// Initialize the memory limit here because the allocator is going to look at it
632632
// but we haven't called gcinit yet and we're definitely going to allocate memory before then.
633-
gcController.memoryLimit.Store(maxInt64)
633+
gcController.memoryLimit.Store(math.MaxInt64)
634634
}
635635

636636
// sysAlloc allocates heap arena space for at least n bytes. The
@@ -1816,7 +1816,7 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
18161816
func nextSample() int64 {
18171817
if MemProfileRate == 0 {
18181818
// Basically never sample.
1819-
return maxInt64
1819+
return math.MaxInt64
18201820
}
18211821
if MemProfileRate == 1 {
18221822
// Sample immediately.

src/runtime/mgcpacer.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"internal/cpu"
99
"internal/goexperiment"
1010
"internal/runtime/atomic"
11+
"internal/runtime/math"
12+
"internal/runtime/strconv"
1113
_ "unsafe" // for go:linkname
1214
)
1315

@@ -1311,7 +1313,7 @@ func readGOGC() int32 {
13111313
if p == "off" {
13121314
return -1
13131315
}
1314-
if n, ok := atoi32(p); ok {
1316+
if n, ok := strconv.Atoi32(p); ok {
13151317
return n
13161318
}
13171319
return 100
@@ -1355,7 +1357,7 @@ func setMemoryLimit(in int64) (out int64) {
13551357
func readGOMEMLIMIT() int64 {
13561358
p := gogetenv("GOMEMLIMIT")
13571359
if p == "" || p == "off" {
1358-
return maxInt64
1360+
return math.MaxInt64
13591361
}
13601362
n, ok := parseByteCount(p)
13611363
if !ok {

src/runtime/mheap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ HaveSpan:
13241324
// that we expect to page in.
13251325
inuse := gcController.mappedReady.Load()
13261326
// Be careful about overflow, especially with uintptrs. Even on 32-bit platforms
1327-
// someone can set a really big memory limit that isn't maxInt64.
1327+
// someone can set a really big memory limit that isn't math.MaxInt64.
13281328
if uint64(scav)+inuse > uint64(limit) {
13291329
bytesToScavenge = uintptr(uint64(scav) + inuse - uint64(limit))
13301330
forceScavenge = true

src/runtime/os_linux.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"internal/abi"
99
"internal/goarch"
1010
"internal/runtime/atomic"
11+
"internal/runtime/strconv"
1112
"internal/runtime/syscall"
1213
"unsafe"
1314
)
@@ -341,7 +342,7 @@ func getHugePageSize() uintptr {
341342
return 0
342343
}
343344
n-- // remove trailing newline
344-
v, ok := atoi(slicebytetostringtmp((*byte)(ptr), int(n)))
345+
v, ok := strconv.Atoi(slicebytetostringtmp((*byte)(ptr), int(n)))
345346
if !ok || v < 0 {
346347
v = 0
347348
}

src/runtime/proc.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"internal/goos"
1212
"internal/runtime/atomic"
1313
"internal/runtime/exithook"
14+
"internal/runtime/strconv"
1415
"internal/runtime/sys"
1516
"internal/stringslite"
1617
"unsafe"
@@ -900,7 +901,7 @@ func schedinit() {
900901
lock(&sched.lock)
901902
sched.lastpoll.Store(nanotime())
902903
procs := ncpu
903-
if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
904+
if n, ok := strconv.Atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 {
904905
procs = n
905906
}
906907
if procresize(procs) != nil {

src/runtime/runtime1.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"internal/bytealg"
99
"internal/goarch"
1010
"internal/runtime/atomic"
11+
"internal/runtime/strconv"
1112
"unsafe"
1213
)
1314

@@ -526,13 +527,13 @@ func parsegodebug(godebug string, seen map[string]bool) {
526527
// is int, not int32, and should only be updated
527528
// if specified in GODEBUG.
528529
if seen == nil && key == "memprofilerate" {
529-
if n, ok := atoi(value); ok {
530+
if n, ok := strconv.Atoi(value); ok {
530531
MemProfileRate = n
531532
}
532533
} else {
533534
for _, v := range dbgvars {
534535
if v.name == key {
535-
if n, ok := atoi32(value); ok {
536+
if n, ok := strconv.Atoi32(value); ok {
536537
if seen == nil && v.value != nil {
537538
*v.value = n
538539
} else if v.atomic != nil {
@@ -572,7 +573,7 @@ func setTraceback(level string) {
572573
fallthrough
573574
default:
574575
t = tracebackAll
575-
if n, ok := atoi(level); ok && n == int(uint32(n)) {
576+
if n, ok := strconv.Atoi(level); ok && n == int(uint32(n)) {
576577
t |= uint32(n) << tracebackShift
577578
}
578579
}

0 commit comments

Comments
 (0)