Skip to content

Commit

Permalink
remove bitSize as a parameter to atoi
Browse files Browse the repository at this point in the history
  • Loading branch information
awalterschulze committed Jan 29, 2025
1 parent bd8334e commit 557b62e
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 109 deletions.
30 changes: 15 additions & 15 deletions json/strconv/atoi.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ func lower(c byte) byte {
return c | ('x' - 'X')
}

const intSize = 32 << (^uint(0) >> 63)

// IntSize is the size in bits of an int or uint value.
const IntSize = intSize

const maxUint64 = 1<<64 - 1

// ParseUint is like [ParseInt] but for unsigned numbers.
//
// A sign prefix is not permitted.
func ParseUint(s string, bitSize int) (uint64, error) {
func ParseUint(s string) (uint64, error) {
return parseUint(s, 64)
}

func parseUint(s string, bitSize int) (uint64, error) {
const fnParseUint = "ParseUint"
base := 10

Expand Down Expand Up @@ -62,9 +61,7 @@ func ParseUint(s string, bitSize int) (uint64, error) {
}

if bitSize == 0 {
bitSize = IntSize
} else if bitSize < 0 || bitSize > 64 {
return 0, BitSizeError(fnParseUint, s0, bitSize)
bitSize = 64
}

// Cutoff is the smallest number such that cutoff*base > maxUint64.
Expand Down Expand Up @@ -147,7 +144,11 @@ func ParseUint(s string, bitSize int) (uint64, error) {
// appropriate bitSize and sign.
//
// [integer literals]: https://go.dev/ref/spec#Integer_literals
func ParseInt(s string, bitSize int) (i int64, err error) {
func ParseInt(s string) (i int64, err error) {
return parseInt(s, 64)
}

func parseInt(s string, bitSize int) (i int64, err error) {
const fnParseInt = "ParseInt"

if s == "" {
Expand All @@ -166,14 +167,14 @@ func ParseInt(s string, bitSize int) (i int64, err error) {

// Convert unsigned and check range.
var un uint64
un, err = ParseUint(s, bitSize)
un, err = parseUint(s, bitSize)
if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Func = fnParseInt
return 0, err
}

if bitSize == 0 {
bitSize = IntSize
bitSize = 64
}

cutoff := uint64(1 << uint(bitSize-1))
Expand All @@ -195,8 +196,7 @@ func Atoi(s string) (int, error) {
const fnAtoi = "Atoi"

sLen := len(s)
if intSize == 32 && (0 < sLen && sLen < 10) ||
intSize == 64 && (0 < sLen && sLen < 19) {
if 0 < sLen && sLen < 19 {
// Fast path for small integers that fit int type.
s0 := s
if s[0] == '-' || s[0] == '+' {
Expand All @@ -221,7 +221,7 @@ func Atoi(s string) (int, error) {
}

// Slow path for invalid, big, or underscored integers.
i64, err := ParseInt(s, 0)
i64, err := parseInt(s, 0)
if nerr, ok := err.(*NumError); ok {
nerr.Func = fnAtoi
}
Expand Down
122 changes: 28 additions & 94 deletions json/strconv/atoi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func init() {
func TestParseUint64(t *testing.T) {
for i := range parseUint64Tests {
test := &parseUint64Tests[i]
out, err := ParseUint(test.in, 64)
out, err := ParseUint(test.in)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseUint(%q, 10, 64) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
Expand All @@ -372,7 +372,7 @@ func TestParseUint64(t *testing.T) {
func TestParseInt64(t *testing.T) {
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
out, err := ParseInt(test.in, 64)
out, err := ParseInt(test.in)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseInt(%q, 10, 64) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
Expand All @@ -381,78 +381,38 @@ func TestParseInt64(t *testing.T) {
}

func TestParseUint(t *testing.T) {
switch IntSize {
case 32:
for i := range parseUint32Tests {
test := &parseUint32Tests[i]
out, err := ParseUint(test.in, 0)
if uint64(test.out) != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
}
case 64:
for i := range parseUint64Tests {
test := &parseUint64Tests[i]
out, err := ParseUint(test.in, 0)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
for i := range parseUint64Tests {
test := &parseUint64Tests[i]
out, err := ParseUint(test.in)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseUint(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
}
}

func TestParseInt(t *testing.T) {
switch IntSize {
case 32:
for i := range parseInt32Tests {
test := &parseInt32Tests[i]
out, err := ParseInt(test.in, 0)
if int64(test.out) != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
}
case 64:
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
out, err := ParseInt(test.in, 0)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
out, err := ParseInt(test.in)
if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("ParseInt(%q, 10, 0) = %v, %v want %v, %v",
test.in, out, err, test.out, test.err)
}
}
}

func TestAtoi(t *testing.T) {
switch IntSize {
case 32:
for i := range parseInt32Tests {
test := &parseInt32Tests[i]
out, err := Atoi(test.in)
var testErr error
if test.err != nil {
testErr = &NumError{"Atoi", test.err.(*NumError).Err}
}
if int(test.out) != out || !reflect.DeepEqual(testErr, err) {
t.Errorf("Atoi(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, testErr)
}
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
out, err := Atoi(test.in)
var testErr error
if test.err != nil {
testErr = &NumError{"Atoi", test.err.(*NumError).Err}
}
case 64:
for i := range parseInt64Tests {
test := &parseInt64Tests[i]
out, err := Atoi(test.in)
var testErr error
if test.err != nil {
testErr = &NumError{"Atoi", test.err.(*NumError).Err}
}
if test.out != int64(out) || !reflect.DeepEqual(testErr, err) {
t.Errorf("Atoi(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, testErr)
}
if test.out != int64(out) || !reflect.DeepEqual(testErr, err) {
t.Errorf("Atoi(%q) = %v, %v want %v, %v",
test.in, out, err, test.out, testErr)
}
}
}
Expand Down Expand Up @@ -487,30 +447,6 @@ func equalError(a, b error) bool {
return a.Error() == b.Error()
}

func TestParseIntBitSize(t *testing.T) {
for i := range parseBitSizeTests {
test := &parseBitSizeTests[i]
testErr := test.errStub("ParseInt", test.arg)
_, err := ParseInt("0", test.arg)
if !equalError(testErr, err) {
t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
test.arg, err, testErr)
}
}
}

func TestParseUintBitSize(t *testing.T) {
for i := range parseBitSizeTests {
test := &parseBitSizeTests[i]
testErr := test.errStub("ParseUint", test.arg)
_, err := ParseUint("0", test.arg)
if !equalError(testErr, err) {
t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
test.arg, err, testErr)
}
}
}

func TestNumError(t *testing.T) {
for _, test := range numErrorTests {
err := &NumError{
Expand Down Expand Up @@ -556,7 +492,7 @@ func benchmarkParseInt(b *testing.B, neg int) {
b.Run(cs.name, func(b *testing.B) {
s := fmt.Sprintf("%d", cs.num*int64(neg))
for i := 0; i < b.N; i++ {
out, _ := ParseInt(s, 64)
out, _ := ParseInt(s)
BenchSink += int(out)
}
})
Expand All @@ -578,12 +514,10 @@ func benchmarkAtoi(b *testing.B, neg int) {
{"26bit", 1<<26 - 1},
{"31bit", 1<<31 - 1},
}
if IntSize == 64 {
cases = append(cases, []benchCase{
{"56bit", 1<<56 - 1},
{"63bit", 1<<63 - 1},
}...)
}
cases = append(cases, []benchCase{
{"56bit", 1<<56 - 1},
{"63bit", 1<<63 - 1},
}...)
for _, cs := range cases {
b.Run(cs.name, func(b *testing.B) {
s := fmt.Sprintf("%d", cs.num*int64(neg))
Expand Down

0 comments on commit 557b62e

Please sign in to comment.