mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
strconv: move all but Quote to internal/strconv
This will let low-level things depend on the canonical routines, even for floating-point printing. Change-Id: I31207dc6584ad90d4e365dbe6eaf20f8662ed22d Reviewed-on: https://go-review.googlesource.com/c/go/+/716000 Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
041f564b3e
commit
b2a346bbd1
32 changed files with 1439 additions and 321 deletions
|
|
@ -69,6 +69,7 @@ var depsRules = `
|
||||||
|
|
||||||
internal/goarch < internal/abi;
|
internal/goarch < internal/abi;
|
||||||
internal/byteorder, internal/cpu, internal/goarch < internal/chacha8rand;
|
internal/byteorder, internal/cpu, internal/goarch < internal/chacha8rand;
|
||||||
|
internal/goarch, math/bits < internal/strconv;
|
||||||
|
|
||||||
# RUNTIME is the core runtime group of packages, all of them very light-weight.
|
# RUNTIME is the core runtime group of packages, all of them very light-weight.
|
||||||
internal/abi,
|
internal/abi,
|
||||||
|
|
@ -80,6 +81,7 @@ var depsRules = `
|
||||||
internal/goexperiment,
|
internal/goexperiment,
|
||||||
internal/goos,
|
internal/goos,
|
||||||
internal/profilerecord,
|
internal/profilerecord,
|
||||||
|
internal/strconv,
|
||||||
internal/trace/tracev2,
|
internal/trace/tracev2,
|
||||||
math/bits,
|
math/bits,
|
||||||
structs
|
structs
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func ParseBool(str string) (bool, error) {
|
||||||
case "0", "f", "F", "false", "FALSE", "False":
|
case "0", "f", "F", "false", "FALSE", "False":
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, syntaxError("ParseBool", str)
|
return false, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatBool returns "true" or "false" according to the value of b.
|
// FormatBool returns "true" or "false" according to the value of b.
|
||||||
|
|
@ -6,7 +6,7 @@ package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -36,23 +36,8 @@ var atobtests = []atobTest{
|
||||||
func TestParseBool(t *testing.T) {
|
func TestParseBool(t *testing.T) {
|
||||||
for _, test := range atobtests {
|
for _, test := range atobtests {
|
||||||
b, e := ParseBool(test.in)
|
b, e := ParseBool(test.in)
|
||||||
if test.err != nil {
|
if b != test.out || e != test.err {
|
||||||
// expect an error
|
t.Errorf("ParseBool(%s) = %v, %v, want %v, %v", test.in, b, e, test.out, test.err)
|
||||||
if e == nil {
|
|
||||||
t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err)
|
|
||||||
} else {
|
|
||||||
// NumError assertion must succeed; it's the only thing we return.
|
|
||||||
if e.(*NumError).Err != test.err {
|
|
||||||
t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if e != nil {
|
|
||||||
t.Errorf("ParseBool(%s) = %s; want nil", test.in, e)
|
|
||||||
}
|
|
||||||
if b != test.out {
|
|
||||||
t.Errorf("ParseBool(%s) = %t; want %t", test.in, b, test.out)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,23 +4,6 @@
|
||||||
|
|
||||||
package strconv
|
package strconv
|
||||||
|
|
||||||
import "internal/stringslite"
|
|
||||||
|
|
||||||
const fnParseComplex = "ParseComplex"
|
|
||||||
|
|
||||||
// convErr splits an error returned by parseFloatPrefix
|
|
||||||
// into a syntax or range error for ParseComplex.
|
|
||||||
func convErr(err error, s string) (syntax, range_ error) {
|
|
||||||
if x, ok := err.(*NumError); ok {
|
|
||||||
x.Func = fnParseComplex
|
|
||||||
x.Num = stringslite.Clone(s)
|
|
||||||
if x.Err == ErrRange {
|
|
||||||
return nil, x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseComplex converts the string s to a complex number
|
// ParseComplex converts the string s to a complex number
|
||||||
// with the precision specified by bitSize: 64 for complex64, or 128 for complex128.
|
// with the precision specified by bitSize: 64 for complex64, or 128 for complex128.
|
||||||
// When bitSize=64, the result still has type complex128, but it will be
|
// When bitSize=64, the result still has type complex128, but it will be
|
||||||
|
|
@ -47,8 +30,6 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
|
||||||
size = 32 // complex64 uses float32 parts
|
size = 32 // complex64 uses float32 parts
|
||||||
}
|
}
|
||||||
|
|
||||||
orig := s
|
|
||||||
|
|
||||||
// Remove parentheses, if any.
|
// Remove parentheses, if any.
|
||||||
if len(s) >= 2 && s[0] == '(' && s[len(s)-1] == ')' {
|
if len(s) >= 2 && s[0] == '(' && s[len(s)-1] == ')' {
|
||||||
s = s[1 : len(s)-1]
|
s = s[1 : len(s)-1]
|
||||||
|
|
@ -59,10 +40,10 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
|
||||||
// Read real part (possibly imaginary part if followed by 'i').
|
// Read real part (possibly imaginary part if followed by 'i').
|
||||||
re, n, err := parseFloatPrefix(s, size)
|
re, n, err := parseFloatPrefix(s, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err, pending = convErr(err, orig)
|
if err != ErrRange {
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
pending = err
|
||||||
}
|
}
|
||||||
s = s[n:]
|
s = s[n:]
|
||||||
|
|
||||||
|
|
@ -88,20 +69,20 @@ func ParseComplex(s string, bitSize int) (complex128, error) {
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
return 0, syntaxError(fnParseComplex, orig)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read imaginary part.
|
// Read imaginary part.
|
||||||
im, n, err := parseFloatPrefix(s, size)
|
im, n, err := parseFloatPrefix(s, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err, pending = convErr(err, orig)
|
if err != ErrRange {
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
pending = err
|
||||||
}
|
}
|
||||||
s = s[n:]
|
s = s[n:]
|
||||||
if s != "i" {
|
if s != "i" {
|
||||||
return 0, syntaxError(fnParseComplex, orig)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
return complex(re, im), pending
|
return complex(re, im), pending
|
||||||
}
|
}
|
||||||
|
|
@ -7,8 +7,7 @@ package strconv_test
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"math/cmplx"
|
"math/cmplx"
|
||||||
"reflect"
|
. "internal/strconv"
|
||||||
. "strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -188,30 +187,24 @@ func TestParseComplex(t *testing.T) {
|
||||||
}
|
}
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
test := &tests[i]
|
test := &tests[i]
|
||||||
if test.err != nil {
|
c, e := ParseComplex(test.in, 128)
|
||||||
test.err = &NumError{Func: "ParseComplex", Num: test.in, Err: test.err}
|
if !sameComplex(c, test.out) || e != test.err {
|
||||||
|
t.Errorf("ParseComplex(%s, 128) = %v, %v, want %v, %v", test.in, c, e, test.out, test.err)
|
||||||
}
|
}
|
||||||
got, err := ParseComplex(test.in, 128)
|
|
||||||
if !reflect.DeepEqual(err, test.err) {
|
|
||||||
t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
|
||||||
}
|
|
||||||
if !(cmplx.IsNaN(test.out) && cmplx.IsNaN(got)) && got != test.out {
|
|
||||||
t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if complex128(complex64(test.out)) == test.out {
|
if complex128(complex64(test.out)) == test.out {
|
||||||
got, err := ParseComplex(test.in, 64)
|
c, e := ParseComplex(test.in, 64)
|
||||||
if !reflect.DeepEqual(err, test.err) {
|
c64 := complex64(c)
|
||||||
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
if !sameComplex(complex128(c64) , test.out) || e != test.err {
|
||||||
}
|
t.Errorf("ParseComplex(%s, 64) = %v, %v, want %v, %v", test.in, c, e, test.out, test.err)
|
||||||
got64 := complex64(got)
|
|
||||||
if complex128(got64) != test.out {
|
|
||||||
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sameComplex(c1, c2 complex128) bool {
|
||||||
|
return cmplx.IsNaN(c1) && cmplx.IsNaN(c2) || c1 == c2
|
||||||
|
}
|
||||||
|
|
||||||
// Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
|
// Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
|
||||||
func TestParseComplexIncorrectBitSize(t *testing.T) {
|
func TestParseComplexIncorrectBitSize(t *testing.T) {
|
||||||
const s = "1.5e308+1.0e307i"
|
const s = "1.5e308+1.0e307i"
|
||||||
|
|
@ -10,8 +10,6 @@ package strconv
|
||||||
// 2) Multiply/divide decimal by powers of two until in range [0.5, 1)
|
// 2) Multiply/divide decimal by powers of two until in range [0.5, 1)
|
||||||
// 3) Multiply by 2^precision and round to get mantissa.
|
// 3) Multiply by 2^precision and round to get mantissa.
|
||||||
|
|
||||||
import "math"
|
|
||||||
|
|
||||||
var optimize = true // set to false to force slow-path conversions for testing
|
var optimize = true // set to false to force slow-path conversions for testing
|
||||||
|
|
||||||
// commonPrefixLenIgnoreCase returns the length of the common
|
// commonPrefixLenIgnoreCase returns the length of the common
|
||||||
|
|
@ -58,11 +56,11 @@ func special(s string) (f float64, n int, ok bool) {
|
||||||
n = 3
|
n = 3
|
||||||
}
|
}
|
||||||
if n == 3 || n == 8 {
|
if n == 3 || n == 8 {
|
||||||
return math.Inf(sign), nsign + n, true
|
return inf(sign), nsign + n, true
|
||||||
}
|
}
|
||||||
case 'n', 'N':
|
case 'n', 'N':
|
||||||
if commonPrefixLenIgnoreCase(s, "nan") == 3 {
|
if commonPrefixLenIgnoreCase(s, "nan") == 3 {
|
||||||
return math.NaN(), 3, true
|
return nan(), 3, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
|
|
@ -546,7 +544,7 @@ func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool
|
||||||
if exp > maxExp { // infinity and range error
|
if exp > maxExp { // infinity and range error
|
||||||
mantissa = 1 << flt.mantbits
|
mantissa = 1 << flt.mantbits
|
||||||
exp = maxExp + 1
|
exp = maxExp + 1
|
||||||
err = rangeError(fnParseFloat, s)
|
err = ErrRange
|
||||||
}
|
}
|
||||||
|
|
||||||
bits := mantissa & (1<<flt.mantbits - 1)
|
bits := mantissa & (1<<flt.mantbits - 1)
|
||||||
|
|
@ -555,9 +553,9 @@ func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool
|
||||||
bits |= 1 << flt.mantbits << flt.expbits
|
bits |= 1 << flt.mantbits << flt.expbits
|
||||||
}
|
}
|
||||||
if flt == &float32info {
|
if flt == &float32info {
|
||||||
return float64(math.Float32frombits(uint32(bits))), err
|
return float64(float32frombits(uint32(bits))), err
|
||||||
}
|
}
|
||||||
return math.Float64frombits(bits), err
|
return float64frombits(bits), err
|
||||||
}
|
}
|
||||||
|
|
||||||
const fnParseFloat = "ParseFloat"
|
const fnParseFloat = "ParseFloat"
|
||||||
|
|
@ -569,7 +567,7 @@ func atof32(s string) (f float32, n int, err error) {
|
||||||
|
|
||||||
mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
|
mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, n, syntaxError(fnParseFloat, s)
|
return 0, n, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
if hex {
|
if hex {
|
||||||
|
|
@ -603,12 +601,12 @@ func atof32(s string) (f float32, n int, err error) {
|
||||||
// Slow fallback.
|
// Slow fallback.
|
||||||
var d decimal
|
var d decimal
|
||||||
if !d.set(s[:n]) {
|
if !d.set(s[:n]) {
|
||||||
return 0, n, syntaxError(fnParseFloat, s)
|
return 0, n, ErrSyntax
|
||||||
}
|
}
|
||||||
b, ovf := d.floatBits(&float32info)
|
b, ovf := d.floatBits(&float32info)
|
||||||
f = math.Float32frombits(uint32(b))
|
f = float32frombits(uint32(b))
|
||||||
if ovf {
|
if ovf {
|
||||||
err = rangeError(fnParseFloat, s)
|
err = ErrRange
|
||||||
}
|
}
|
||||||
return f, n, err
|
return f, n, err
|
||||||
}
|
}
|
||||||
|
|
@ -620,7 +618,7 @@ func atof64(s string) (f float64, n int, err error) {
|
||||||
|
|
||||||
mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
|
mantissa, exp, neg, trunc, hex, n, ok := readFloat(s)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, n, syntaxError(fnParseFloat, s)
|
return 0, n, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
if hex {
|
if hex {
|
||||||
|
|
@ -654,12 +652,12 @@ func atof64(s string) (f float64, n int, err error) {
|
||||||
// Slow fallback.
|
// Slow fallback.
|
||||||
var d decimal
|
var d decimal
|
||||||
if !d.set(s[:n]) {
|
if !d.set(s[:n]) {
|
||||||
return 0, n, syntaxError(fnParseFloat, s)
|
return 0, n, ErrSyntax
|
||||||
}
|
}
|
||||||
b, ovf := d.floatBits(&float64info)
|
b, ovf := d.floatBits(&float64info)
|
||||||
f = math.Float64frombits(b)
|
f = float64frombits(b)
|
||||||
if ovf {
|
if ovf {
|
||||||
err = rangeError(fnParseFloat, s)
|
err = ErrRange
|
||||||
}
|
}
|
||||||
return f, n, err
|
return f, n, err
|
||||||
}
|
}
|
||||||
|
|
@ -693,8 +691,8 @@ func atof64(s string) (f float64, n int, err error) {
|
||||||
// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals
|
// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals
|
||||||
func ParseFloat(s string, bitSize int) (float64, error) {
|
func ParseFloat(s string, bitSize int) (float64, error) {
|
||||||
f, n, err := parseFloatPrefix(s, bitSize)
|
f, n, err := parseFloatPrefix(s, bitSize)
|
||||||
if n != len(s) && (err == nil || err.(*NumError).Err != ErrSyntax) {
|
if n != len(s) {
|
||||||
return 0, syntaxError(fnParseFloat, s)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -446,21 +446,6 @@ func initAtof() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initAtofOnce() {
|
func initAtofOnce() {
|
||||||
// The atof routines return NumErrors wrapping
|
|
||||||
// the error and the string. Convert the table above.
|
|
||||||
for i := range atoftests {
|
|
||||||
test := &atoftests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseFloat", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range atof32tests {
|
|
||||||
test := &atof32tests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseFloat", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate random inputs for tests and benchmarks
|
// Generate random inputs for tests and benchmarks
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
atofRandomTests = make([]atofSimpleTest, 100)
|
atofRandomTests = make([]atofSimpleTest, 100)
|
||||||
|
|
@ -497,7 +482,7 @@ func TestParseFloatPrefix(t *testing.T) {
|
||||||
// correctly as "inf" with suffix.
|
// correctly as "inf" with suffix.
|
||||||
for _, suffix := range []string{" ", "q", "+", "-", "<", "=", ">", "(", ")", "i", "init"} {
|
for _, suffix := range []string{" ", "q", "+", "-", "<", "=", ">", "(", ")", "i", "init"} {
|
||||||
in := test.in + suffix
|
in := test.in + suffix
|
||||||
_, n, err := ParseFloatPrefix(in, 64)
|
_, n, err := parseFloatPrefix(in, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err)
|
t.Errorf("ParseFloatPrefix(%q, 64): err = %v; want no error", in, err)
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package strconv
|
||||||
// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go
|
// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"math/bits"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,7 +28,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
|
||||||
// Exp10 Range.
|
// Exp10 Range.
|
||||||
if man == 0 {
|
if man == 0 {
|
||||||
if neg {
|
if neg {
|
||||||
f = math.Float64frombits(0x8000000000000000) // Negative zero.
|
f = float64frombits(0x8000000000000000) // Negative zero.
|
||||||
}
|
}
|
||||||
return f, true
|
return f, true
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +87,7 @@ func eiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) {
|
||||||
if neg {
|
if neg {
|
||||||
retBits |= 0x8000000000000000
|
retBits |= 0x8000000000000000
|
||||||
}
|
}
|
||||||
return math.Float64frombits(retBits), true
|
return float64frombits(retBits), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
|
func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
|
||||||
|
|
@ -104,7 +103,7 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
|
||||||
// Exp10 Range.
|
// Exp10 Range.
|
||||||
if man == 0 {
|
if man == 0 {
|
||||||
if neg {
|
if neg {
|
||||||
f = math.Float32frombits(0x80000000) // Negative zero.
|
f = float32frombits(0x80000000) // Negative zero.
|
||||||
}
|
}
|
||||||
return f, true
|
return f, true
|
||||||
}
|
}
|
||||||
|
|
@ -163,5 +162,5 @@ func eiselLemire32(man uint64, exp10 int, neg bool) (f float32, ok bool) {
|
||||||
if neg {
|
if neg {
|
||||||
retBits |= 0x80000000
|
retBits |= 0x80000000
|
||||||
}
|
}
|
||||||
return math.Float32frombits(uint32(retBits)), true
|
return float32frombits(uint32(retBits)), true
|
||||||
}
|
}
|
||||||
|
|
@ -4,11 +4,6 @@
|
||||||
|
|
||||||
package strconv
|
package strconv
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"internal/stringslite"
|
|
||||||
)
|
|
||||||
|
|
||||||
// lower(c) is a lower-case letter if and only if
|
// lower(c) is a lower-case letter if and only if
|
||||||
// c is either that lower-case letter or the equivalent upper-case letter.
|
// c is either that lower-case letter or the equivalent upper-case letter.
|
||||||
// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
|
// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'.
|
||||||
|
|
@ -17,47 +12,28 @@ func lower(c byte) byte {
|
||||||
return c | ('x' - 'X')
|
return c | ('x' - 'X')
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrRange indicates that a value is out of range for the target type.
|
type Error int
|
||||||
var ErrRange = errors.New("value out of range")
|
|
||||||
|
|
||||||
// ErrSyntax indicates that a value does not have the right syntax for the target type.
|
const (
|
||||||
var ErrSyntax = errors.New("invalid syntax")
|
_ Error = iota
|
||||||
|
ErrRange
|
||||||
|
ErrSyntax
|
||||||
|
ErrBase
|
||||||
|
ErrBitSize
|
||||||
|
)
|
||||||
|
|
||||||
// A NumError records a failed conversion.
|
func (e Error) Error() string {
|
||||||
type NumError struct {
|
switch e {
|
||||||
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat, ParseComplex)
|
case ErrRange:
|
||||||
Num string // the input
|
return "value out of range"
|
||||||
Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
|
case ErrSyntax:
|
||||||
}
|
return "invalid syntax"
|
||||||
|
case ErrBase:
|
||||||
func (e *NumError) Error() string {
|
return "invalid base"
|
||||||
return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
|
case ErrBitSize:
|
||||||
}
|
return "invalid bit size"
|
||||||
|
}
|
||||||
func (e *NumError) Unwrap() error { return e.Err }
|
return "unknown error"
|
||||||
|
|
||||||
// All ParseXXX functions allow the input string to escape to the error value.
|
|
||||||
// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
|
|
||||||
// the conversion from []byte must allocate a string on the heap.
|
|
||||||
// If we assume errors are infrequent, then we can avoid escaping the input
|
|
||||||
// back to the output by copying it first. This allows the compiler to call
|
|
||||||
// strconv.ParseXXX without a heap allocation for most []byte to string
|
|
||||||
// conversions, since it can now prove that the string cannot escape Parse.
|
|
||||||
|
|
||||||
func syntaxError(fn, str string) *NumError {
|
|
||||||
return &NumError{fn, stringslite.Clone(str), ErrSyntax}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rangeError(fn, str string) *NumError {
|
|
||||||
return &NumError{fn, stringslite.Clone(str), ErrRange}
|
|
||||||
}
|
|
||||||
|
|
||||||
func baseError(fn, str string, base int) *NumError {
|
|
||||||
return &NumError{fn, stringslite.Clone(str), errors.New("invalid base " + Itoa(base))}
|
|
||||||
}
|
|
||||||
|
|
||||||
func bitSizeError(fn, str string, bitSize int) *NumError {
|
|
||||||
return &NumError{fn, stringslite.Clone(str), errors.New("invalid bit size " + Itoa(bitSize))}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const intSize = 32 << (^uint(0) >> 63)
|
const intSize = 32 << (^uint(0) >> 63)
|
||||||
|
|
@ -74,7 +50,7 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||||
const fnParseUint = "ParseUint"
|
const fnParseUint = "ParseUint"
|
||||||
|
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return 0, syntaxError(fnParseUint, s)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
base0 := base == 0
|
base0 := base == 0
|
||||||
|
|
@ -105,13 +81,13 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0, baseError(fnParseUint, s0, base)
|
return 0, ErrBase
|
||||||
}
|
}
|
||||||
|
|
||||||
if bitSize == 0 {
|
if bitSize == 0 {
|
||||||
bitSize = IntSize
|
bitSize = IntSize
|
||||||
} else if bitSize < 0 || bitSize > 64 {
|
} else if bitSize < 0 || bitSize > 64 {
|
||||||
return 0, bitSizeError(fnParseUint, s0, bitSize)
|
return 0, ErrBitSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cutoff is the smallest number such that cutoff*base > maxUint64.
|
// Cutoff is the smallest number such that cutoff*base > maxUint64.
|
||||||
|
|
@ -141,29 +117,29 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||||
case 'a' <= lower(c) && lower(c) <= 'z':
|
case 'a' <= lower(c) && lower(c) <= 'z':
|
||||||
d = lower(c) - 'a' + 10
|
d = lower(c) - 'a' + 10
|
||||||
default:
|
default:
|
||||||
return 0, syntaxError(fnParseUint, s0)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
if d >= byte(base) {
|
if d >= byte(base) {
|
||||||
return 0, syntaxError(fnParseUint, s0)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
if n >= cutoff {
|
if n >= cutoff {
|
||||||
// n*base overflows
|
// n*base overflows
|
||||||
return maxVal, rangeError(fnParseUint, s0)
|
return maxVal, ErrRange
|
||||||
}
|
}
|
||||||
n *= uint64(base)
|
n *= uint64(base)
|
||||||
|
|
||||||
n1 := n + uint64(d)
|
n1 := n + uint64(d)
|
||||||
if n1 < n || n1 > maxVal {
|
if n1 < n || n1 > maxVal {
|
||||||
// n+d overflows
|
// n+d overflows
|
||||||
return maxVal, rangeError(fnParseUint, s0)
|
return maxVal, ErrRange
|
||||||
}
|
}
|
||||||
n = n1
|
n = n1
|
||||||
}
|
}
|
||||||
|
|
||||||
if underscores && !underscoreOK(s0) {
|
if underscores && !underscoreOK(s0) {
|
||||||
return 0, syntaxError(fnParseUint, s0)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
|
|
@ -198,11 +174,10 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
|
||||||
const fnParseInt = "ParseInt"
|
const fnParseInt = "ParseInt"
|
||||||
|
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return 0, syntaxError(fnParseInt, s)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick off leading sign.
|
// Pick off leading sign.
|
||||||
s0 := s
|
|
||||||
neg := false
|
neg := false
|
||||||
switch s[0] {
|
switch s[0] {
|
||||||
case '+':
|
case '+':
|
||||||
|
|
@ -215,9 +190,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
|
||||||
// Convert unsigned and check range.
|
// Convert unsigned and check range.
|
||||||
var un uint64
|
var un uint64
|
||||||
un, err = ParseUint(s, base, bitSize)
|
un, err = ParseUint(s, base, bitSize)
|
||||||
if err != nil && err.(*NumError).Err != ErrRange {
|
if err != nil && err != ErrRange {
|
||||||
err.(*NumError).Func = fnParseInt
|
|
||||||
err.(*NumError).Num = stringslite.Clone(s0)
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,10 +200,10 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
|
||||||
|
|
||||||
cutoff := uint64(1 << uint(bitSize-1))
|
cutoff := uint64(1 << uint(bitSize-1))
|
||||||
if !neg && un >= cutoff {
|
if !neg && un >= cutoff {
|
||||||
return int64(cutoff - 1), rangeError(fnParseInt, s0)
|
return int64(cutoff - 1), ErrRange
|
||||||
}
|
}
|
||||||
if neg && un > cutoff {
|
if neg && un > cutoff {
|
||||||
return -int64(cutoff), rangeError(fnParseInt, s0)
|
return -int64(cutoff), ErrRange
|
||||||
}
|
}
|
||||||
n := int64(un)
|
n := int64(un)
|
||||||
if neg {
|
if neg {
|
||||||
|
|
@ -251,7 +224,7 @@ func Atoi(s string) (int, error) {
|
||||||
if s[0] == '-' || s[0] == '+' {
|
if s[0] == '-' || s[0] == '+' {
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
if len(s) < 1 {
|
if len(s) < 1 {
|
||||||
return 0, syntaxError(fnAtoi, s0)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +232,7 @@ func Atoi(s string) (int, error) {
|
||||||
for _, ch := range []byte(s) {
|
for _, ch := range []byte(s) {
|
||||||
ch -= '0'
|
ch -= '0'
|
||||||
if ch > 9 {
|
if ch > 9 {
|
||||||
return 0, syntaxError(fnAtoi, s0)
|
return 0, ErrSyntax
|
||||||
}
|
}
|
||||||
n = n*10 + int(ch)
|
n = n*10 + int(ch)
|
||||||
}
|
}
|
||||||
|
|
@ -271,9 +244,6 @@ func Atoi(s string) (int, error) {
|
||||||
|
|
||||||
// Slow path for invalid, big, or underscored integers.
|
// Slow path for invalid, big, or underscored integers.
|
||||||
i64, err := ParseInt(s, 10, 0)
|
i64, err := ParseInt(s, 10, 0)
|
||||||
if nerr, ok := err.(*NumError); ok {
|
|
||||||
nerr.Func = fnAtoi
|
|
||||||
}
|
|
||||||
return int(i64), err
|
return int(i64), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5,10 +5,9 @@
|
||||||
package strconv_test
|
package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -316,47 +315,6 @@ var numErrorTests = []numErrorTest{
|
||||||
{"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
|
{"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
// The parse routines return NumErrors wrapping
|
|
||||||
// the error and the string. Convert the tables above.
|
|
||||||
for i := range parseUint64Tests {
|
|
||||||
test := &parseUint64Tests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseUint", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range parseUint64BaseTests {
|
|
||||||
test := &parseUint64BaseTests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseUint", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range parseInt64Tests {
|
|
||||||
test := &parseInt64Tests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseInt", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range parseInt64BaseTests {
|
|
||||||
test := &parseInt64BaseTests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseInt", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range parseUint32Tests {
|
|
||||||
test := &parseUint32Tests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseUint", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range parseInt32Tests {
|
|
||||||
test := &parseInt32Tests[i]
|
|
||||||
if test.err != nil {
|
|
||||||
test.err = &NumError{"ParseInt", test.in, test.err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseUint32(t *testing.T) {
|
func TestParseUint32(t *testing.T) {
|
||||||
for i := range parseUint32Tests {
|
for i := range parseUint32Tests {
|
||||||
test := &parseUint32Tests[i]
|
test := &parseUint32Tests[i]
|
||||||
|
|
@ -475,62 +433,40 @@ func TestAtoi(t *testing.T) {
|
||||||
for i := range parseInt32Tests {
|
for i := range parseInt32Tests {
|
||||||
test := &parseInt32Tests[i]
|
test := &parseInt32Tests[i]
|
||||||
out, err := Atoi(test.in)
|
out, err := Atoi(test.in)
|
||||||
var testErr error
|
if out !=int(test.out) || err != test.err {
|
||||||
if test.err != nil {
|
t.Errorf("Atoi(%q) = %v, %v, want %v, %v", test.in, out, err, test.out, test.err)
|
||||||
testErr = &NumError{"Atoi", test.in, 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 64:
|
case 64:
|
||||||
for i := range parseInt64Tests {
|
for i := range parseInt64Tests {
|
||||||
test := &parseInt64Tests[i]
|
test := &parseInt64Tests[i]
|
||||||
out, err := Atoi(test.in)
|
out, err := Atoi(test.in)
|
||||||
var testErr error
|
if int64(out) != test.out || err != test.err {
|
||||||
if test.err != nil {
|
t.Errorf("Atoi(%q) = %v, %v, want %v, %v", test.in, out, err, test.out, test.err)
|
||||||
testErr = &NumError{"Atoi", test.in, 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func bitSizeErrStub(name string, bitSize int) error {
|
|
||||||
return bitSizeError(name, "0", bitSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func baseErrStub(name string, base int) error {
|
|
||||||
return baseError(name, "0", base)
|
|
||||||
}
|
|
||||||
|
|
||||||
func noErrStub(name string, arg int) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseErrorTest struct {
|
type parseErrorTest struct {
|
||||||
arg int
|
arg int
|
||||||
errStub func(name string, arg int) error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
var parseBitSizeTests = []parseErrorTest{
|
var parseBitSizeTests = []parseErrorTest{
|
||||||
{-1, bitSizeErrStub},
|
{-1, ErrBitSize},
|
||||||
{0, noErrStub},
|
{0, nil},
|
||||||
{64, noErrStub},
|
{64, nil},
|
||||||
{65, bitSizeErrStub},
|
{65, ErrBitSize},
|
||||||
}
|
}
|
||||||
|
|
||||||
var parseBaseTests = []parseErrorTest{
|
var parseBaseTests = []parseErrorTest{
|
||||||
{-1, baseErrStub},
|
{-1, ErrBase},
|
||||||
{0, noErrStub},
|
{0, nil},
|
||||||
{1, baseErrStub},
|
{1, ErrBase},
|
||||||
{2, noErrStub},
|
{2, nil},
|
||||||
{36, noErrStub},
|
{36, nil},
|
||||||
{37, baseErrStub},
|
{37, ErrBase},
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalError(a, b error) bool {
|
func equalError(a, b error) bool {
|
||||||
|
|
@ -546,11 +482,10 @@ func equalError(a, b error) bool {
|
||||||
func TestParseIntBitSize(t *testing.T) {
|
func TestParseIntBitSize(t *testing.T) {
|
||||||
for i := range parseBitSizeTests {
|
for i := range parseBitSizeTests {
|
||||||
test := &parseBitSizeTests[i]
|
test := &parseBitSizeTests[i]
|
||||||
testErr := test.errStub("ParseInt", test.arg)
|
|
||||||
_, err := ParseInt("0", 0, test.arg)
|
_, err := ParseInt("0", 0, test.arg)
|
||||||
if !equalError(testErr, err) {
|
if err != test.err {
|
||||||
t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
|
t.Errorf("ParseInt(\"0\", 0, %v) = 0, %v want 0, %v",
|
||||||
test.arg, err, testErr)
|
test.arg, err, test.err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -558,11 +493,10 @@ func TestParseIntBitSize(t *testing.T) {
|
||||||
func TestParseUintBitSize(t *testing.T) {
|
func TestParseUintBitSize(t *testing.T) {
|
||||||
for i := range parseBitSizeTests {
|
for i := range parseBitSizeTests {
|
||||||
test := &parseBitSizeTests[i]
|
test := &parseBitSizeTests[i]
|
||||||
testErr := test.errStub("ParseUint", test.arg)
|
|
||||||
_, err := ParseUint("0", 0, test.arg)
|
_, err := ParseUint("0", 0, test.arg)
|
||||||
if !equalError(testErr, err) {
|
if err != test.err {
|
||||||
t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
|
t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
|
||||||
test.arg, err, testErr)
|
test.arg, err, test.err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -570,11 +504,10 @@ func TestParseUintBitSize(t *testing.T) {
|
||||||
func TestParseIntBase(t *testing.T) {
|
func TestParseIntBase(t *testing.T) {
|
||||||
for i := range parseBaseTests {
|
for i := range parseBaseTests {
|
||||||
test := &parseBaseTests[i]
|
test := &parseBaseTests[i]
|
||||||
testErr := test.errStub("ParseInt", test.arg)
|
|
||||||
_, err := ParseInt("0", test.arg, 0)
|
_, err := ParseInt("0", test.arg, 0)
|
||||||
if !equalError(testErr, err) {
|
if err != test.err {
|
||||||
t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
|
t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
|
||||||
test.arg, err, testErr)
|
test.arg, err, test.err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -582,35 +515,14 @@ func TestParseIntBase(t *testing.T) {
|
||||||
func TestParseUintBase(t *testing.T) {
|
func TestParseUintBase(t *testing.T) {
|
||||||
for i := range parseBaseTests {
|
for i := range parseBaseTests {
|
||||||
test := &parseBaseTests[i]
|
test := &parseBaseTests[i]
|
||||||
testErr := test.errStub("ParseUint", test.arg)
|
|
||||||
_, err := ParseUint("0", test.arg, 0)
|
_, err := ParseUint("0", test.arg, 0)
|
||||||
if !equalError(testErr, err) {
|
if err != test.err {
|
||||||
t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
|
t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
|
||||||
test.arg, err, testErr)
|
test.arg, err, test.err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumError(t *testing.T) {
|
|
||||||
for _, test := range numErrorTests {
|
|
||||||
err := &NumError{
|
|
||||||
Func: "ParseFloat",
|
|
||||||
Num: test.num,
|
|
||||||
Err: errors.New("failed"),
|
|
||||||
}
|
|
||||||
if got := err.Error(); got != test.want {
|
|
||||||
t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNumErrorUnwrap(t *testing.T) {
|
|
||||||
err := &NumError{Err: ErrSyntax}
|
|
||||||
if !errors.Is(err, ErrSyntax) {
|
|
||||||
t.Error("errors.Is failed, wanted success")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseInt(b *testing.B) {
|
func BenchmarkParseInt(b *testing.B) {
|
||||||
b.Run("Pos", func(b *testing.B) {
|
b.Run("Pos", func(b *testing.B) {
|
||||||
benchmarkParseInt(b, 1)
|
benchmarkParseInt(b, 1)
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
package strconv_test
|
package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
package strconv_test
|
package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
30
src/internal/strconv/deps.go
Normal file
30
src/internal/strconv/deps.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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 "unsafe"
|
||||||
|
|
||||||
|
// Implementations to avoid importing other dependencies.
|
||||||
|
|
||||||
|
// package math
|
||||||
|
|
||||||
|
func float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) }
|
||||||
|
func float32frombits(b uint32) float32 { return *(*float32)(unsafe.Pointer(&b)) }
|
||||||
|
func float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
|
||||||
|
func float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) }
|
||||||
|
|
||||||
|
func inf(sign int) float64 {
|
||||||
|
var v uint64
|
||||||
|
if sign >= 0 {
|
||||||
|
v = 0x7FF0000000000000
|
||||||
|
} else {
|
||||||
|
v = 0xFFF0000000000000
|
||||||
|
}
|
||||||
|
return float64frombits(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNaN(f float64) (is bool) { return f != f }
|
||||||
|
|
||||||
|
func nan() float64 { return float64frombits(0x7FF8000000000001) }
|
||||||
28
src/internal/strconv/export_test.go
Normal file
28
src/internal/strconv/export_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2017 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
|
||||||
|
|
||||||
|
type Uint128 = uint128
|
||||||
|
|
||||||
|
var (
|
||||||
|
MulLog10_2 = mulLog10_2
|
||||||
|
MulLog2_10 = mulLog2_10
|
||||||
|
ParseFloatPrefix = parseFloatPrefix
|
||||||
|
Pow10 = pow10
|
||||||
|
Umul128 = umul128
|
||||||
|
Umul192 = umul192
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDecimal(i uint64) *decimal {
|
||||||
|
d := new(decimal)
|
||||||
|
d.Assign(i)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetOptimize(b bool) bool {
|
||||||
|
old := optimize
|
||||||
|
optimize = b
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"internal/strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -10,7 +10,10 @@
|
||||||
|
|
||||||
package strconv
|
package strconv
|
||||||
|
|
||||||
import "math"
|
const (
|
||||||
|
lowerhex = "0123456789abcdef"
|
||||||
|
upperhex = "0123456789ABCDEF"
|
||||||
|
)
|
||||||
|
|
||||||
type floatInfo struct {
|
type floatInfo struct {
|
||||||
mantbits uint
|
mantbits uint
|
||||||
|
|
@ -71,10 +74,10 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
|
||||||
var flt *floatInfo
|
var flt *floatInfo
|
||||||
switch bitSize {
|
switch bitSize {
|
||||||
case 32:
|
case 32:
|
||||||
bits = uint64(math.Float32bits(float32(val)))
|
bits = uint64(float32bits(float32(val)))
|
||||||
flt = &float32info
|
flt = &float32info
|
||||||
case 64:
|
case 64:
|
||||||
bits = math.Float64bits(val)
|
bits = float64bits(val)
|
||||||
flt = &float64info
|
flt = &float64info
|
||||||
default:
|
default:
|
||||||
panic("strconv: illegal AppendFloat/FormatFloat bitSize")
|
panic("strconv: illegal AppendFloat/FormatFloat bitSize")
|
||||||
|
|
@ -7,7 +7,7 @@ package strconv_test
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
18
src/internal/strconv/import_test.go
Normal file
18
src/internal/strconv/import_test.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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/strconv"
|
||||||
|
|
||||||
|
type uint128 = Uint128
|
||||||
|
|
||||||
|
var (
|
||||||
|
mulLog10_2 = MulLog10_2
|
||||||
|
mulLog2_10 = MulLog2_10
|
||||||
|
parseFloatPrefix = ParseFloatPrefix
|
||||||
|
pow10 = Pow10
|
||||||
|
umul128 = Umul128
|
||||||
|
umul192 = Umul192
|
||||||
|
)
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
package strconv
|
package strconv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"internal/goarch"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FormatUint returns the string representation of i in the given base,
|
// FormatUint returns the string representation of i in the given base,
|
||||||
|
|
@ -200,7 +200,7 @@ func formatBase10(a []byte, u uint64) int {
|
||||||
// or can split the uint64 into uint32-sized chunks.
|
// or can split the uint64 into uint32-sized chunks.
|
||||||
// On most systems, the uint32 math is faster, but not all.
|
// On most systems, the uint32 math is faster, but not all.
|
||||||
// The decision here is based on benchmarking.
|
// The decision here is based on benchmarking.
|
||||||
itoaPure64 = host64bit && runtime.GOARCH != "amd64" && runtime.GOARCH != "arm64" && runtime.GOARCH != "s390x"
|
itoaPure64 = host64bit && goarch.GOARCH != "amd64" && goarch.GOARCH != "arm64" && goarch.GOARCH != "s390x"
|
||||||
|
|
||||||
// 64-bit systems can all use 64-bit div and mod by a constant,
|
// 64-bit systems can all use 64-bit div and mod by a constant,
|
||||||
// which the compiler rewrites to use 64x64→128-bit multiplies.
|
// which the compiler rewrites to use 64x64→128-bit multiplies.
|
||||||
|
|
@ -6,7 +6,7 @@ package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ package strconv_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
. "strconv"
|
. "internal/strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -4,27 +4,7 @@
|
||||||
|
|
||||||
package strconv
|
package strconv
|
||||||
|
|
||||||
type Uint128 = uint128
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BaseError = baseError
|
BaseError = baseError
|
||||||
BitSizeError = bitSizeError
|
BitSizeError = bitSizeError
|
||||||
MulLog10_2 = mulLog10_2
|
|
||||||
MulLog2_10 = mulLog2_10
|
|
||||||
ParseFloatPrefix = parseFloatPrefix
|
|
||||||
Pow10 = pow10
|
|
||||||
Umul128 = umul128
|
|
||||||
Umul192 = umul192
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDecimal(i uint64) *decimal {
|
|
||||||
d := new(decimal)
|
|
||||||
d.Assign(i)
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetOptimize(b bool) bool {
|
|
||||||
old := optimize
|
|
||||||
optimize = b
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,7 @@ package strconv_test
|
||||||
|
|
||||||
import . "strconv"
|
import . "strconv"
|
||||||
|
|
||||||
type uint128 = Uint128
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
baseError = BaseError
|
baseError = BaseError
|
||||||
bitSizeError = BitSizeError
|
bitSizeError = BitSizeError
|
||||||
mulLog10_2 = MulLog10_2
|
|
||||||
mulLog2_10 = MulLog2_10
|
|
||||||
parseFloatPrefix = ParseFloatPrefix
|
|
||||||
pow10 = Pow10
|
|
||||||
umul128 = Umul128
|
|
||||||
umul192 = Umul192
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
286
src/strconv/number.go
Normal file
286
src/strconv/number.go
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
// 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 (
|
||||||
|
"errors"
|
||||||
|
"internal/strconv"
|
||||||
|
"internal/stringslite"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IntSize is the size in bits of an int or uint value.
|
||||||
|
const IntSize = strconv.IntSize
|
||||||
|
|
||||||
|
// ParseBool returns the boolean value represented by the string.
|
||||||
|
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
|
||||||
|
// Any other value returns an error.
|
||||||
|
func ParseBool(str string) (bool, error) {
|
||||||
|
x, err := strconv.ParseBool(str)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("ParseBool", str, 0, 0, err)
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatBool returns "true" or "false" according to the value of b.
|
||||||
|
func FormatBool(b bool) string {
|
||||||
|
return strconv.FormatBool(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendBool appends "true" or "false", according to the value of b,
|
||||||
|
// to dst and returns the extended buffer.
|
||||||
|
func AppendBool(dst []byte, b bool) []byte {
|
||||||
|
return strconv.AppendBool(dst, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseComplex converts the string s to a complex number
|
||||||
|
// with the precision specified by bitSize: 64 for complex64, or 128 for complex128.
|
||||||
|
// When bitSize=64, the result still has type complex128, but it will be
|
||||||
|
// convertible to complex64 without changing its value.
|
||||||
|
//
|
||||||
|
// The number represented by s must be of the form N, Ni, or N±Ni, where N stands
|
||||||
|
// for a floating-point number as recognized by [ParseFloat], and i is the imaginary
|
||||||
|
// component. If the second N is unsigned, a + sign is required between the two components
|
||||||
|
// as indicated by the ±. If the second N is NaN, only a + sign is accepted.
|
||||||
|
// The form may be parenthesized and cannot contain any spaces.
|
||||||
|
// The resulting complex number consists of the two components converted by ParseFloat.
|
||||||
|
//
|
||||||
|
// The errors that ParseComplex returns have concrete type [*NumError]
|
||||||
|
// and include err.Num = s.
|
||||||
|
//
|
||||||
|
// If s is not syntactically well-formed, ParseComplex returns err.Err = ErrSyntax.
|
||||||
|
//
|
||||||
|
// If s is syntactically well-formed but either component is more than 1/2 ULP
|
||||||
|
// away from the largest floating point number of the given component's size,
|
||||||
|
// ParseComplex returns err.Err = ErrRange and c = ±Inf for the respective component.
|
||||||
|
func ParseComplex(s string, bitSize int) (complex128, error) {
|
||||||
|
x, err := strconv.ParseComplex(s, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("ParseComplex", s, 0, bitSize, err)
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFloat converts the string s to a floating-point number
|
||||||
|
// with the precision specified by bitSize: 32 for float32, or 64 for float64.
|
||||||
|
// When bitSize=32, the result still has type float64, but it will be
|
||||||
|
// convertible to float32 without changing its value.
|
||||||
|
//
|
||||||
|
// ParseFloat accepts decimal and hexadecimal floating-point numbers
|
||||||
|
// as defined by the Go syntax for [floating-point literals].
|
||||||
|
// If s is well-formed and near a valid floating-point number,
|
||||||
|
// ParseFloat returns the nearest floating-point number rounded
|
||||||
|
// using IEEE754 unbiased rounding.
|
||||||
|
// (Parsing a hexadecimal floating-point value only rounds when
|
||||||
|
// there are more bits in the hexadecimal representation than
|
||||||
|
// will fit in the mantissa.)
|
||||||
|
//
|
||||||
|
// The errors that ParseFloat returns have concrete type *NumError
|
||||||
|
// and include err.Num = s.
|
||||||
|
//
|
||||||
|
// If s is not syntactically well-formed, ParseFloat returns err.Err = ErrSyntax.
|
||||||
|
//
|
||||||
|
// If s is syntactically well-formed but is more than 1/2 ULP
|
||||||
|
// away from the largest floating point number of the given size,
|
||||||
|
// ParseFloat returns f = ±Inf, err.Err = ErrRange.
|
||||||
|
//
|
||||||
|
// ParseFloat recognizes the string "NaN", and the (possibly signed) strings "Inf" and "Infinity"
|
||||||
|
// as their respective special floating point values. It ignores case when matching.
|
||||||
|
//
|
||||||
|
// [floating-point literals]: https://go.dev/ref/spec#Floating-point_literals
|
||||||
|
func ParseFloat(s string, bitSize int) (float64, error) {
|
||||||
|
x, err := strconv.ParseFloat(s, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("ParseFloat", s, 0, bitSize, err)
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUint is like [ParseInt] but for unsigned numbers.
|
||||||
|
//
|
||||||
|
// A sign prefix is not permitted.
|
||||||
|
func ParseUint(s string, base int, bitSize int) (uint64, error) {
|
||||||
|
x, err := strconv.ParseUint(s, base, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("ParseUint", s, base, bitSize, err)
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseInt interprets a string s in the given base (0, 2 to 36) and
|
||||||
|
// bit size (0 to 64) and returns the corresponding value i.
|
||||||
|
//
|
||||||
|
// The string may begin with a leading sign: "+" or "-".
|
||||||
|
//
|
||||||
|
// If the base argument is 0, the true base is implied by the string's
|
||||||
|
// prefix following the sign (if present): 2 for "0b", 8 for "0" or "0o",
|
||||||
|
// 16 for "0x", and 10 otherwise. Also, for argument base 0 only,
|
||||||
|
// underscore characters are permitted as defined by the Go syntax for
|
||||||
|
// [integer literals].
|
||||||
|
//
|
||||||
|
// The bitSize argument specifies the integer type
|
||||||
|
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
|
||||||
|
// correspond to int, int8, int16, int32, and int64.
|
||||||
|
// If bitSize is below 0 or above 64, an error is returned.
|
||||||
|
//
|
||||||
|
// The errors that ParseInt returns have concrete type [*NumError]
|
||||||
|
// and include err.Num = s. If s is empty or contains invalid
|
||||||
|
// digits, err.Err = [ErrSyntax] and the returned value is 0;
|
||||||
|
// if the value corresponding to s cannot be represented by a
|
||||||
|
// signed integer of the given size, err.Err = [ErrRange] and the
|
||||||
|
// returned value is the maximum magnitude integer of the
|
||||||
|
// appropriate bitSize and sign.
|
||||||
|
//
|
||||||
|
// [integer literals]: https://go.dev/ref/spec#Integer_literals
|
||||||
|
func ParseInt(s string, base int, bitSize int) (i int64, err error) {
|
||||||
|
x, err := strconv.ParseInt(s, base, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("ParseInt", s, base, bitSize, err)
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
|
||||||
|
func Atoi(s string) (int, error) {
|
||||||
|
x, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return x, toError("Atoi", s, 0, 0, err)
|
||||||
|
}
|
||||||
|
return strconv.Atoi(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatComplex converts the complex number c to a string of the
|
||||||
|
// form (a+bi) where a and b are the real and imaginary parts,
|
||||||
|
// formatted according to the format fmt and precision prec.
|
||||||
|
//
|
||||||
|
// The format fmt and precision prec have the same meaning as in [FormatFloat].
|
||||||
|
// It rounds the result assuming that the original was obtained from a complex
|
||||||
|
// value of bitSize bits, which must be 64 for complex64 and 128 for complex128.
|
||||||
|
func FormatComplex(c complex128, fmt byte, prec, bitSize int) string {
|
||||||
|
return strconv.FormatComplex(c, fmt, prec, bitSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatFloat converts the floating-point number f to a string,
|
||||||
|
// according to the format fmt and precision prec. It rounds the
|
||||||
|
// result assuming that the original was obtained from a floating-point
|
||||||
|
// value of bitSize bits (32 for float32, 64 for float64).
|
||||||
|
//
|
||||||
|
// The format fmt is one of
|
||||||
|
// - 'b' (-ddddp±ddd, a binary exponent),
|
||||||
|
// - 'e' (-d.dddde±dd, a decimal exponent),
|
||||||
|
// - 'E' (-d.ddddE±dd, a decimal exponent),
|
||||||
|
// - 'f' (-ddd.dddd, no exponent),
|
||||||
|
// - 'g' ('e' for large exponents, 'f' otherwise),
|
||||||
|
// - 'G' ('E' for large exponents, 'f' otherwise),
|
||||||
|
// - 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or
|
||||||
|
// - 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent).
|
||||||
|
//
|
||||||
|
// The precision prec controls the number of digits (excluding the exponent)
|
||||||
|
// printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats.
|
||||||
|
// For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point.
|
||||||
|
// For 'g' and 'G' it is the maximum number of significant digits (trailing
|
||||||
|
// zeros are removed).
|
||||||
|
// The special precision -1 uses the smallest number of digits
|
||||||
|
// necessary such that ParseFloat will return f exactly.
|
||||||
|
// The exponent is written as a decimal integer;
|
||||||
|
// for all formats other than 'b', it will be at least two digits.
|
||||||
|
func FormatFloat(f float64, fmt byte, prec, bitSize int) string {
|
||||||
|
return strconv.FormatFloat(f, fmt, prec, bitSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendFloat appends the string form of the floating-point number f,
|
||||||
|
// as generated by [FormatFloat], to dst and returns the extended buffer.
|
||||||
|
func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte {
|
||||||
|
return strconv.AppendFloat(dst, f, fmt, prec, bitSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatUint returns the string representation of i in the given base,
|
||||||
|
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
|
||||||
|
// for digit values >= 10.
|
||||||
|
func FormatUint(i uint64, base int) string {
|
||||||
|
return strconv.FormatUint(i, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatInt returns the string representation of i in the given base,
|
||||||
|
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
|
||||||
|
// for digit values >= 10.
|
||||||
|
func FormatInt(i int64, base int) string {
|
||||||
|
return strconv.FormatInt(i, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Itoa is equivalent to [FormatInt](int64(i), 10).
|
||||||
|
func Itoa(i int) string {
|
||||||
|
return strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendInt appends the string form of the integer i,
|
||||||
|
// as generated by [FormatInt], to dst and returns the extended buffer.
|
||||||
|
func AppendInt(dst []byte, i int64, base int) []byte {
|
||||||
|
return strconv.AppendInt(dst, i, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendUint appends the string form of the unsigned integer i,
|
||||||
|
// as generated by [FormatUint], to dst and returns the extended buffer.
|
||||||
|
func AppendUint(dst []byte, i uint64, base int) []byte {
|
||||||
|
return strconv.AppendUint(dst, i, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toError converts from internal/strconv.Error to the error guaranteed by this package's APIs.
|
||||||
|
func toError(fn, s string, base, bitSize int, err error) error {
|
||||||
|
switch err {
|
||||||
|
case strconv.ErrSyntax:
|
||||||
|
return syntaxError(fn, s)
|
||||||
|
case strconv.ErrRange:
|
||||||
|
return rangeError(fn, s)
|
||||||
|
case strconv.ErrBase:
|
||||||
|
return baseError(fn, s, base)
|
||||||
|
case strconv.ErrBitSize:
|
||||||
|
return bitSizeError(fn, s, bitSize)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrRange indicates that a value is out of range for the target type.
|
||||||
|
var ErrRange = errors.New("value out of range")
|
||||||
|
|
||||||
|
// ErrSyntax indicates that a value does not have the right syntax for the target type.
|
||||||
|
var ErrSyntax = errors.New("invalid syntax")
|
||||||
|
|
||||||
|
// A NumError records a failed conversion.
|
||||||
|
type NumError struct {
|
||||||
|
Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat, ParseComplex)
|
||||||
|
Num string // the input
|
||||||
|
Err error // the reason the conversion failed (e.g. ErrRange, ErrSyntax, etc.)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NumError) Error() string {
|
||||||
|
return "strconv." + e.Func + ": " + "parsing " + Quote(e.Num) + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *NumError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
// All ParseXXX functions allow the input string to escape to the error value.
|
||||||
|
// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
|
||||||
|
// the conversion from []byte must allocate a string on the heap.
|
||||||
|
// If we assume errors are infrequent, then we can avoid escaping the input
|
||||||
|
// back to the output by copying it first. This allows the compiler to call
|
||||||
|
// strconv.ParseXXX without a heap allocation for most []byte to string
|
||||||
|
// conversions, since it can now prove that the string cannot escape Parse.
|
||||||
|
|
||||||
|
func syntaxError(fn, str string) *NumError {
|
||||||
|
return &NumError{fn, stringslite.Clone(str), ErrSyntax}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rangeError(fn, str string) *NumError {
|
||||||
|
return &NumError{fn, stringslite.Clone(str), ErrRange}
|
||||||
|
}
|
||||||
|
|
||||||
|
func baseError(fn, str string, base int) *NumError {
|
||||||
|
return &NumError{fn, stringslite.Clone(str), errors.New("invalid base " + Itoa(base))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitSizeError(fn, str string, bitSize int) *NumError {
|
||||||
|
return &NumError{fn, stringslite.Clone(str), errors.New("invalid bit size " + Itoa(bitSize))}
|
||||||
|
}
|
||||||
956
src/strconv/number_test.go
Normal file
956
src/strconv/number_test.go
Normal file
|
|
@ -0,0 +1,956 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Note: These tests are focused mainly on generating the right errors.
|
||||||
|
// The extensive numerical tests are in ../internal/strconv.
|
||||||
|
// Add new tests there instead of here whenever possible.
|
||||||
|
|
||||||
|
package strconv_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"math/cmplx"
|
||||||
|
"reflect"
|
||||||
|
. "strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type atobTest struct {
|
||||||
|
in string
|
||||||
|
out bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var atobtests = []atobTest{
|
||||||
|
{"", false, ErrSyntax},
|
||||||
|
{"asdf", false, ErrSyntax},
|
||||||
|
{"0", false, nil},
|
||||||
|
{"false", false, nil},
|
||||||
|
{"true", true, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseBool(t *testing.T) {
|
||||||
|
for _, test := range atobtests {
|
||||||
|
b, e := ParseBool(test.in)
|
||||||
|
if test.err != nil {
|
||||||
|
// expect an error
|
||||||
|
if e == nil {
|
||||||
|
t.Errorf("ParseBool(%s) = nil; want %s", test.in, test.err)
|
||||||
|
} else {
|
||||||
|
// NumError assertion must succeed; it's the only thing we return.
|
||||||
|
if e.(*NumError).Err != test.err {
|
||||||
|
t.Errorf("ParseBool(%s) = %s; want %s", test.in, e, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if e != nil {
|
||||||
|
t.Errorf("ParseBool(%s) = %s; want nil", test.in, e)
|
||||||
|
}
|
||||||
|
if b != test.out {
|
||||||
|
t.Errorf("ParseBool(%s) = %t; want %t", test.in, b, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var boolString = map[bool]string{
|
||||||
|
true: "true",
|
||||||
|
false: "false",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBool(t *testing.T) {
|
||||||
|
for b, s := range boolString {
|
||||||
|
if f := FormatBool(b); f != s {
|
||||||
|
t.Errorf("FormatBool(%v) = %q; want %q", b, f, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type appendBoolTest struct {
|
||||||
|
b bool
|
||||||
|
in []byte
|
||||||
|
out []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var appendBoolTests = []appendBoolTest{
|
||||||
|
{true, []byte("foo "), []byte("foo true")},
|
||||||
|
{false, []byte("foo "), []byte("foo false")},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendBool(t *testing.T) {
|
||||||
|
for _, test := range appendBoolTests {
|
||||||
|
b := AppendBool(test.in, test.b)
|
||||||
|
if !bytes.Equal(b, test.out) {
|
||||||
|
t.Errorf("AppendBool(%q, %v) = %q; want %q", test.in, test.b, b, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
infp0 = complex(math.Inf(+1), 0)
|
||||||
|
infm0 = complex(math.Inf(-1), 0)
|
||||||
|
inf0p = complex(0, math.Inf(+1))
|
||||||
|
inf0m = complex(0, math.Inf(-1))
|
||||||
|
|
||||||
|
infpp = complex(math.Inf(+1), math.Inf(+1))
|
||||||
|
infpm = complex(math.Inf(+1), math.Inf(-1))
|
||||||
|
infmp = complex(math.Inf(-1), math.Inf(+1))
|
||||||
|
infmm = complex(math.Inf(-1), math.Inf(-1))
|
||||||
|
)
|
||||||
|
|
||||||
|
type atocTest struct {
|
||||||
|
in string
|
||||||
|
out complex128
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseComplex(t *testing.T) {
|
||||||
|
tests := []atocTest{
|
||||||
|
// Clearly invalid
|
||||||
|
{"", 0, ErrSyntax},
|
||||||
|
{" ", 0, ErrSyntax},
|
||||||
|
{"(", 0, ErrSyntax},
|
||||||
|
{")", 0, ErrSyntax},
|
||||||
|
{"i", 0, ErrSyntax},
|
||||||
|
{"+i", 0, ErrSyntax},
|
||||||
|
{"-i", 0, ErrSyntax},
|
||||||
|
{"1I", 0, ErrSyntax},
|
||||||
|
{"10 + 5i", 0, ErrSyntax},
|
||||||
|
{"3+", 0, ErrSyntax},
|
||||||
|
{"3+5", 0, ErrSyntax},
|
||||||
|
{"3+5+5i", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// Parentheses
|
||||||
|
{"()", 0, ErrSyntax},
|
||||||
|
{"(i)", 0, ErrSyntax},
|
||||||
|
{"(0)", 0, nil},
|
||||||
|
{"(1i)", 1i, nil},
|
||||||
|
{"(3.0+5.5i)", 3.0 + 5.5i, nil},
|
||||||
|
{"(1)+1i", 0, ErrSyntax},
|
||||||
|
{"(3.0+5.5i", 0, ErrSyntax},
|
||||||
|
{"3.0+5.5i)", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// NaNs
|
||||||
|
{"NaN", complex(math.NaN(), 0), nil},
|
||||||
|
{"NANi", complex(0, math.NaN()), nil},
|
||||||
|
{"nan+nAni", complex(math.NaN(), math.NaN()), nil},
|
||||||
|
{"+NaN", 0, ErrSyntax},
|
||||||
|
{"-NaN", 0, ErrSyntax},
|
||||||
|
{"NaN-NaNi", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// Infs
|
||||||
|
{"Inf", infp0, nil},
|
||||||
|
{"+inf", infp0, nil},
|
||||||
|
{"-inf", infm0, nil},
|
||||||
|
{"Infinity", infp0, nil},
|
||||||
|
{"+INFINITY", infp0, nil},
|
||||||
|
{"-infinity", infm0, nil},
|
||||||
|
{"+infi", inf0p, nil},
|
||||||
|
{"0-infinityi", inf0m, nil},
|
||||||
|
{"Inf+Infi", infpp, nil},
|
||||||
|
{"+Inf-Infi", infpm, nil},
|
||||||
|
{"-Infinity+Infi", infmp, nil},
|
||||||
|
{"inf-inf", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// Zeros
|
||||||
|
{"0", 0, nil},
|
||||||
|
{"0i", 0, nil},
|
||||||
|
{"-0.0i", 0, nil},
|
||||||
|
{"0+0.0i", 0, nil},
|
||||||
|
{"0e+0i", 0, nil},
|
||||||
|
{"0e-0+0i", 0, nil},
|
||||||
|
{"-0.0-0.0i", 0, nil},
|
||||||
|
{"0e+012345", 0, nil},
|
||||||
|
{"0x0p+012345i", 0, nil},
|
||||||
|
{"0x0.00p-012345i", 0, nil},
|
||||||
|
{"+0e-0+0e-0i", 0, nil},
|
||||||
|
{"0e+0+0e+0i", 0, nil},
|
||||||
|
{"-0e+0-0e+0i", 0, nil},
|
||||||
|
|
||||||
|
// Regular non-zeroes
|
||||||
|
{"0.1", 0.1, nil},
|
||||||
|
{"0.1i", 0 + 0.1i, nil},
|
||||||
|
{"0.123", 0.123, nil},
|
||||||
|
{"0.123i", 0 + 0.123i, nil},
|
||||||
|
{"0.123+0.123i", 0.123 + 0.123i, nil},
|
||||||
|
{"99", 99, nil},
|
||||||
|
{"+99", 99, nil},
|
||||||
|
{"-99", -99, nil},
|
||||||
|
{"+1i", 1i, nil},
|
||||||
|
{"-1i", -1i, nil},
|
||||||
|
{"+3+1i", 3 + 1i, nil},
|
||||||
|
{"30+3i", 30 + 3i, nil},
|
||||||
|
{"+3e+3-3e+3i", 3e+3 - 3e+3i, nil},
|
||||||
|
{"+3e+3+3e+3i", 3e+3 + 3e+3i, nil},
|
||||||
|
{"+3e+3+3e+3i+", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// Separators
|
||||||
|
{"0.1", 0.1, nil},
|
||||||
|
{"0.1i", 0 + 0.1i, nil},
|
||||||
|
{"0.1_2_3", 0.123, nil},
|
||||||
|
{"+0x_3p3i", 0x3p3i, nil},
|
||||||
|
{"0_0+0x_0p0i", 0, nil},
|
||||||
|
{"0x_10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
|
||||||
|
{"+0x_1_0.3p-8+0x_3_0p3i", 0x10.3p-8 + 0x30p3i, nil},
|
||||||
|
{"0x1_0.3p+8-0x_3p3i", 0x10.3p+8 - 0x3p3i, nil},
|
||||||
|
|
||||||
|
// Hexadecimals
|
||||||
|
{"0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
|
||||||
|
{"+0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
|
||||||
|
{"0x10.3p+8-0x3p3i", 0x10.3p+8 - 0x3p3i, nil},
|
||||||
|
{"0x1p0", 1, nil},
|
||||||
|
{"0x1p1", 2, nil},
|
||||||
|
{"0x1p-1", 0.5, nil},
|
||||||
|
{"0x1ep-1", 15, nil},
|
||||||
|
{"-0x1ep-1", -15, nil},
|
||||||
|
{"-0x2p3", -16, nil},
|
||||||
|
{"0x1e2", 0, ErrSyntax},
|
||||||
|
{"1p2", 0, ErrSyntax},
|
||||||
|
{"0x1e2i", 0, ErrSyntax},
|
||||||
|
|
||||||
|
// ErrRange
|
||||||
|
// next float64 - too large
|
||||||
|
{"+0x1p1024", infp0, ErrRange},
|
||||||
|
{"-0x1p1024", infm0, ErrRange},
|
||||||
|
{"+0x1p1024i", inf0p, ErrRange},
|
||||||
|
{"-0x1p1024i", inf0m, ErrRange},
|
||||||
|
{"+0x1p1024+0x1p1024i", infpp, ErrRange},
|
||||||
|
{"+0x1p1024-0x1p1024i", infpm, ErrRange},
|
||||||
|
{"-0x1p1024+0x1p1024i", infmp, ErrRange},
|
||||||
|
{"-0x1p1024-0x1p1024i", infmm, ErrRange},
|
||||||
|
// the border is ...158079
|
||||||
|
// borderline - okay
|
||||||
|
{"+0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
|
||||||
|
{"+0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
|
||||||
|
{"-0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
|
||||||
|
{"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
|
||||||
|
// borderline - too large
|
||||||
|
{"+0x1.fffffffffffff8p1023", infp0, ErrRange},
|
||||||
|
{"-0x1fffffffffffff.8p+971", infm0, ErrRange},
|
||||||
|
{"+0x1.fffffffffffff8p1023i", inf0p, ErrRange},
|
||||||
|
{"-0x1fffffffffffff.8p+971i", inf0m, ErrRange},
|
||||||
|
{"+0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange},
|
||||||
|
{"+0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infpm, ErrRange},
|
||||||
|
{"-0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infmp, ErrRange},
|
||||||
|
{"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", infmm, ErrRange},
|
||||||
|
// a little too large
|
||||||
|
{"1e308+1e308i", 1e+308 + 1e+308i, nil},
|
||||||
|
{"2e308+2e308i", infpp, ErrRange},
|
||||||
|
{"1e309+1e309i", infpp, ErrRange},
|
||||||
|
{"0x1p1025+0x1p1025i", infpp, ErrRange},
|
||||||
|
{"2e308", infp0, ErrRange},
|
||||||
|
{"1e309", infp0, ErrRange},
|
||||||
|
{"0x1p1025", infp0, ErrRange},
|
||||||
|
{"2e308i", inf0p, ErrRange},
|
||||||
|
{"1e309i", inf0p, ErrRange},
|
||||||
|
{"0x1p1025i", inf0p, ErrRange},
|
||||||
|
// way too large
|
||||||
|
{"+1e310+1e310i", infpp, ErrRange},
|
||||||
|
{"+1e310-1e310i", infpm, ErrRange},
|
||||||
|
{"-1e310+1e310i", infmp, ErrRange},
|
||||||
|
{"-1e310-1e310i", infmm, ErrRange},
|
||||||
|
// under/overflow exponent
|
||||||
|
{"1e-4294967296", 0, nil},
|
||||||
|
{"1e-4294967296i", 0, nil},
|
||||||
|
{"1e-4294967296+1i", 1i, nil},
|
||||||
|
{"1+1e-4294967296i", 1, nil},
|
||||||
|
{"1e-4294967296+1e-4294967296i", 0, nil},
|
||||||
|
{"1e+4294967296", infp0, ErrRange},
|
||||||
|
{"1e+4294967296i", inf0p, ErrRange},
|
||||||
|
{"1e+4294967296+1e+4294967296i", infpp, ErrRange},
|
||||||
|
{"1e+4294967296-1e+4294967296i", infpm, ErrRange},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := &tests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{Func: "ParseComplex", Num: test.in, Err: test.err}
|
||||||
|
}
|
||||||
|
got, err := ParseComplex(test.in, 128)
|
||||||
|
if !reflect.DeepEqual(err, test.err) {
|
||||||
|
t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
if !(cmplx.IsNaN(test.out) && cmplx.IsNaN(got)) && got != test.out {
|
||||||
|
t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if complex128(complex64(test.out)) == test.out {
|
||||||
|
got, err := ParseComplex(test.in, 64)
|
||||||
|
if !reflect.DeepEqual(err, test.err) {
|
||||||
|
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
got64 := complex64(got)
|
||||||
|
if complex128(got64) != test.out {
|
||||||
|
t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
|
||||||
|
func TestParseComplexIncorrectBitSize(t *testing.T) {
|
||||||
|
const s = "1.5e308+1.0e307i"
|
||||||
|
const want = 1.5e308 + 1.0e307i
|
||||||
|
|
||||||
|
for _, bitSize := range []int{0, 10, 100, 256} {
|
||||||
|
c, err := ParseComplex(s, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseComplex(%q, %d) gave error %s", s, bitSize, err)
|
||||||
|
}
|
||||||
|
if c != want {
|
||||||
|
t.Fatalf("ParseComplex(%q, %d) = %g (expected %g)", s, bitSize, c, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type atofTest struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var atoftests = []atofTest{
|
||||||
|
{"", "0", ErrSyntax},
|
||||||
|
{"1.25", "1.25", nil},
|
||||||
|
{"+1", "1", nil},
|
||||||
|
{"1x", "0", ErrSyntax},
|
||||||
|
{"1.1.", "0", ErrSyntax},
|
||||||
|
{"1e23", "1e+23", nil},
|
||||||
|
{"1E23", "1e+23", nil},
|
||||||
|
{"0x1fFe2.p0", "131042", nil},
|
||||||
|
{"0x1fFe2.P0", "131042", nil},
|
||||||
|
{"-0x2p3", "-16", nil},
|
||||||
|
{"0x0.fp4", "15", nil},
|
||||||
|
{"0x0.fp0", "0.9375", nil},
|
||||||
|
{"0x1e2", "0", ErrSyntax},
|
||||||
|
{"1p2", "0", ErrSyntax},
|
||||||
|
{"0x1p1024", "+Inf", ErrRange},
|
||||||
|
{"-0x1p1024", "-Inf", ErrRange},
|
||||||
|
{"0x1.fffffffffffff7fffp1023", "1.7976931348623157e+308", nil},
|
||||||
|
{"-0x1.fffffffffffff7fffp1023", "-1.7976931348623157e+308", nil},
|
||||||
|
{"1.797693134862315808e308", "+Inf", ErrRange},
|
||||||
|
{"-1.797693134862315808e308", "-Inf", ErrRange},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// The atof routines return NumErrors wrapping
|
||||||
|
// the error and the string. Convert the table above.
|
||||||
|
for i := range atoftests {
|
||||||
|
test := &atoftests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseFloat", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAtof(t *testing.T) {
|
||||||
|
for i := 0; i < len(atoftests); i++ {
|
||||||
|
test := &atoftests[i]
|
||||||
|
out, err := ParseFloat(test.in, 64)
|
||||||
|
outs := FormatFloat(out, 'g', -1, 64)
|
||||||
|
if outs != test.out || !reflect.DeepEqual(err, test.err) {
|
||||||
|
t.Errorf("ParseFloat(%v, 64) = %v, %v want %v, %v",
|
||||||
|
test.in, out, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if float64(float32(out)) == out {
|
||||||
|
out, err := ParseFloat(test.in, 32)
|
||||||
|
out32 := float32(out)
|
||||||
|
if float64(out32) != out {
|
||||||
|
t.Errorf("ParseFloat(%v, 32) = %v, not a float32 (closest is %v)", test.in, out, float64(out32))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outs := FormatFloat(float64(out32), 'g', -1, 32)
|
||||||
|
if outs != test.out || !reflect.DeepEqual(err, test.err) {
|
||||||
|
t.Errorf("ParseFloat(%v, 32) = %v, %v want %v, %v # %v",
|
||||||
|
test.in, out32, err, test.out, test.err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseUint64Test struct {
|
||||||
|
in string
|
||||||
|
out uint64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseUint64Tests = []parseUint64Test{
|
||||||
|
{"", 0, ErrSyntax},
|
||||||
|
{"0", 0, nil},
|
||||||
|
{"1", 1, nil},
|
||||||
|
{"12345", 12345, nil},
|
||||||
|
{"012345", 12345, nil},
|
||||||
|
{"18446744073709551616", 1<<64 - 1, ErrRange},
|
||||||
|
{"-1", 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseUint64BaseTest struct {
|
||||||
|
in string
|
||||||
|
base int
|
||||||
|
out uint64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseUint64BaseTests = []parseUint64BaseTest{
|
||||||
|
{"", 0, 0, ErrSyntax},
|
||||||
|
{"0", 0, 0, nil},
|
||||||
|
{"1", 0, 1, nil},
|
||||||
|
{"-1", 0, 0, ErrSyntax},
|
||||||
|
{"12345", 0, 12345, nil},
|
||||||
|
{"012345", 0, 012345, nil},
|
||||||
|
{"18446744073709551616", 0, 1<<64 - 1, ErrRange},
|
||||||
|
{"0b", 0, 0, ErrSyntax},
|
||||||
|
{"101", 2, 5, nil},
|
||||||
|
{"101_", 2, 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseInt64Test struct {
|
||||||
|
in string
|
||||||
|
out int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseInt64Tests = []parseInt64Test{
|
||||||
|
{"", 0, ErrSyntax},
|
||||||
|
{"0", 0, nil},
|
||||||
|
{"1", 1, nil},
|
||||||
|
{"-1", -1, nil},
|
||||||
|
{"12345", 12345, nil},
|
||||||
|
{"9223372036854775808", 1<<63 - 1, ErrRange},
|
||||||
|
{"123%45", 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseInt64BaseTest struct {
|
||||||
|
in string
|
||||||
|
base int
|
||||||
|
out int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseInt64BaseTests = []parseInt64BaseTest{
|
||||||
|
{"", 0, 0, ErrSyntax},
|
||||||
|
{"0", 0, 0, nil},
|
||||||
|
{"1", 0, 1, nil},
|
||||||
|
{"-1", 0, -1, nil},
|
||||||
|
{"12345", 0, 12345, nil},
|
||||||
|
{"12345", 9, 8303, nil},
|
||||||
|
{"012345", 0, 012345, nil},
|
||||||
|
{"9223372036854775808", 10, 1<<63 - 1, ErrRange},
|
||||||
|
{"0b", 0, 0, ErrSyntax},
|
||||||
|
{"101", 2, 5, nil},
|
||||||
|
{"101_", 2, 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseUint32Test struct {
|
||||||
|
in string
|
||||||
|
out uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseUint32Tests = []parseUint32Test{
|
||||||
|
{"", 0, ErrSyntax},
|
||||||
|
{"0", 0, nil},
|
||||||
|
{"1", 1, nil},
|
||||||
|
{"12345", 12345, nil},
|
||||||
|
{"12345x", 0, ErrSyntax},
|
||||||
|
{"987654321", 987654321, nil},
|
||||||
|
{"4294967296", 1<<32 - 1, ErrRange},
|
||||||
|
{"1_2_3_4_5", 0, ErrSyntax}, // base=10 so no underscores allowed
|
||||||
|
{"12345_", 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseInt32Test struct {
|
||||||
|
in string
|
||||||
|
out int32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseInt32Tests = []parseInt32Test{
|
||||||
|
{"", 0, ErrSyntax},
|
||||||
|
{"0", 0, nil},
|
||||||
|
{"-0", 0, nil},
|
||||||
|
{"1", 1, nil},
|
||||||
|
{"-1", -1, nil},
|
||||||
|
{"12345", 12345, nil},
|
||||||
|
{"-12345", -12345, nil},
|
||||||
|
{"2147483648", 1<<31 - 1, ErrRange},
|
||||||
|
{"12345_", 0, ErrSyntax},
|
||||||
|
}
|
||||||
|
|
||||||
|
type numErrorTest struct {
|
||||||
|
num, want string
|
||||||
|
}
|
||||||
|
|
||||||
|
var numErrorTests = []numErrorTest{
|
||||||
|
{"0", `strconv.ParseFloat: parsing "0": failed`},
|
||||||
|
{"`", "strconv.ParseFloat: parsing \"`\": failed"},
|
||||||
|
{"1\x00.2", `strconv.ParseFloat: parsing "1\x00.2": failed`},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// The parse routines return NumErrors wrapping
|
||||||
|
// the error and the string. Convert the tables above.
|
||||||
|
for i := range parseUint64Tests {
|
||||||
|
test := &parseUint64Tests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseUint", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range parseUint64BaseTests {
|
||||||
|
test := &parseUint64BaseTests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseUint", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range parseInt64Tests {
|
||||||
|
test := &parseInt64Tests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseInt", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range parseInt64BaseTests {
|
||||||
|
test := &parseInt64BaseTests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseInt", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range parseUint32Tests {
|
||||||
|
test := &parseUint32Tests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseUint", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range parseInt32Tests {
|
||||||
|
test := &parseInt32Tests[i]
|
||||||
|
if test.err != nil {
|
||||||
|
test.err = &NumError{"ParseInt", test.in, test.err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUint32(t *testing.T) {
|
||||||
|
for i := range parseUint32Tests {
|
||||||
|
test := &parseUint32Tests[i]
|
||||||
|
out, err := ParseUint(test.in, 10, 32)
|
||||||
|
if uint64(test.out) != out || !reflect.DeepEqual(test.err, err) {
|
||||||
|
t.Errorf("ParseUint(%q, 10, 32) = %v, %v want %v, %v",
|
||||||
|
test.in, out, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUint64(t *testing.T) {
|
||||||
|
for i := range parseUint64Tests {
|
||||||
|
test := &parseUint64Tests[i]
|
||||||
|
out, err := ParseUint(test.in, 10, 64)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUint64Base(t *testing.T) {
|
||||||
|
for i := range parseUint64BaseTests {
|
||||||
|
test := &parseUint64BaseTests[i]
|
||||||
|
out, err := ParseUint(test.in, test.base, 64)
|
||||||
|
if test.out != out || !reflect.DeepEqual(test.err, err) {
|
||||||
|
t.Errorf("ParseUint(%q, %v, 64) = %v, %v want %v, %v",
|
||||||
|
test.in, test.base, out, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseInt32(t *testing.T) {
|
||||||
|
for i := range parseInt32Tests {
|
||||||
|
test := &parseInt32Tests[i]
|
||||||
|
out, err := ParseInt(test.in, 10, 32)
|
||||||
|
if int64(test.out) != out || !reflect.DeepEqual(test.err, err) {
|
||||||
|
t.Errorf("ParseInt(%q, 10 ,32) = %v, %v want %v, %v",
|
||||||
|
test.in, out, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseInt64(t *testing.T) {
|
||||||
|
for i := range parseInt64Tests {
|
||||||
|
test := &parseInt64Tests[i]
|
||||||
|
out, err := ParseInt(test.in, 10, 64)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseInt64Base(t *testing.T) {
|
||||||
|
for i := range parseInt64BaseTests {
|
||||||
|
test := &parseInt64BaseTests[i]
|
||||||
|
out, err := ParseInt(test.in, test.base, 64)
|
||||||
|
if test.out != out || !reflect.DeepEqual(test.err, err) {
|
||||||
|
t.Errorf("ParseInt(%q, %v, 64) = %v, %v want %v, %v",
|
||||||
|
test.in, test.base, out, err, test.out, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUint(t *testing.T) {
|
||||||
|
switch IntSize {
|
||||||
|
case 32:
|
||||||
|
for i := range parseUint32Tests {
|
||||||
|
test := &parseUint32Tests[i]
|
||||||
|
out, err := ParseUint(test.in, 10, 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, 10, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseInt(t *testing.T) {
|
||||||
|
switch IntSize {
|
||||||
|
case 32:
|
||||||
|
for i := range parseInt32Tests {
|
||||||
|
test := &parseInt32Tests[i]
|
||||||
|
out, err := ParseInt(test.in, 10, 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, 10, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.in, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.in, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitSizeErrStub(name string, bitSize int) error {
|
||||||
|
return bitSizeError(name, "0", bitSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func baseErrStub(name string, base int) error {
|
||||||
|
return baseError(name, "0", base)
|
||||||
|
}
|
||||||
|
|
||||||
|
func noErrStub(name string, arg int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseErrorTest struct {
|
||||||
|
arg int
|
||||||
|
errStub func(name string, arg int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseBitSizeTests = []parseErrorTest{
|
||||||
|
{-1, bitSizeErrStub},
|
||||||
|
{0, noErrStub},
|
||||||
|
{64, noErrStub},
|
||||||
|
{65, bitSizeErrStub},
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseBaseTests = []parseErrorTest{
|
||||||
|
{-1, baseErrStub},
|
||||||
|
{0, noErrStub},
|
||||||
|
{1, baseErrStub},
|
||||||
|
{2, noErrStub},
|
||||||
|
{36, noErrStub},
|
||||||
|
{37, baseErrStub},
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalError(a, b error) bool {
|
||||||
|
if a == nil {
|
||||||
|
return b == nil
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
return a == nil
|
||||||
|
}
|
||||||
|
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", 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", 0, test.arg)
|
||||||
|
if !equalError(testErr, err) {
|
||||||
|
t.Errorf("ParseUint(\"0\", 0, %v) = 0, %v want 0, %v",
|
||||||
|
test.arg, err, testErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseIntBase(t *testing.T) {
|
||||||
|
for i := range parseBaseTests {
|
||||||
|
test := &parseBaseTests[i]
|
||||||
|
testErr := test.errStub("ParseInt", test.arg)
|
||||||
|
_, err := ParseInt("0", test.arg, 0)
|
||||||
|
if !equalError(testErr, err) {
|
||||||
|
t.Errorf("ParseInt(\"0\", %v, 0) = 0, %v want 0, %v",
|
||||||
|
test.arg, err, testErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUintBase(t *testing.T) {
|
||||||
|
for i := range parseBaseTests {
|
||||||
|
test := &parseBaseTests[i]
|
||||||
|
testErr := test.errStub("ParseUint", test.arg)
|
||||||
|
_, err := ParseUint("0", test.arg, 0)
|
||||||
|
if !equalError(testErr, err) {
|
||||||
|
t.Errorf("ParseUint(\"0\", %v, 0) = 0, %v want 0, %v",
|
||||||
|
test.arg, err, testErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumError(t *testing.T) {
|
||||||
|
for _, test := range numErrorTests {
|
||||||
|
err := &NumError{
|
||||||
|
Func: "ParseFloat",
|
||||||
|
Num: test.num,
|
||||||
|
Err: errors.New("failed"),
|
||||||
|
}
|
||||||
|
if got := err.Error(); got != test.want {
|
||||||
|
t.Errorf(`(&NumError{"ParseFloat", %q, "failed"}).Error() = %v, want %v`, test.num, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumErrorUnwrap(t *testing.T) {
|
||||||
|
err := &NumError{Err: ErrSyntax}
|
||||||
|
if !errors.Is(err, ErrSyntax) {
|
||||||
|
t.Error("errors.Is failed, wanted success")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatComplex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
c complex128
|
||||||
|
fmt byte
|
||||||
|
prec int
|
||||||
|
bitSize int
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
// a variety of signs
|
||||||
|
{1 + 2i, 'g', -1, 128, "(1+2i)"},
|
||||||
|
{3 - 4i, 'g', -1, 128, "(3-4i)"},
|
||||||
|
{-5 + 6i, 'g', -1, 128, "(-5+6i)"},
|
||||||
|
{-7 - 8i, 'g', -1, 128, "(-7-8i)"},
|
||||||
|
|
||||||
|
// test that fmt and prec are working
|
||||||
|
{3.14159 + 0.00123i, 'e', 3, 128, "(3.142e+00+1.230e-03i)"},
|
||||||
|
{3.14159 + 0.00123i, 'f', 3, 128, "(3.142+0.001i)"},
|
||||||
|
{3.14159 + 0.00123i, 'g', 3, 128, "(3.14+0.00123i)"},
|
||||||
|
|
||||||
|
// ensure bitSize rounding is working
|
||||||
|
{1.2345678901234567 + 9.876543210987654i, 'f', -1, 128, "(1.2345678901234567+9.876543210987654i)"},
|
||||||
|
{1.2345678901234567 + 9.876543210987654i, 'f', -1, 64, "(1.2345679+9.876543i)"},
|
||||||
|
|
||||||
|
// other cases are handled by FormatFloat tests
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
out := FormatComplex(test.c, test.fmt, test.prec, test.bitSize)
|
||||||
|
if out != test.out {
|
||||||
|
t.Fatalf("FormatComplex(%v, %q, %d, %d) = %q; want %q",
|
||||||
|
test.c, test.fmt, test.prec, test.bitSize, out, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatComplexInvalidBitSize(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Fatalf("expected panic due to invalid bitSize")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
_ = FormatComplex(1+2i, 'g', -1, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
type itob64Test struct {
|
||||||
|
in int64
|
||||||
|
base int
|
||||||
|
out string
|
||||||
|
}
|
||||||
|
|
||||||
|
var itob64tests = []itob64Test{
|
||||||
|
{0, 10, "0"},
|
||||||
|
{1, 10, "1"},
|
||||||
|
{-1, 10, "-1"},
|
||||||
|
{12345678, 10, "12345678"},
|
||||||
|
{-1 << 63, 10, "-9223372036854775808"},
|
||||||
|
{16, 17, "g"},
|
||||||
|
{25, 25, "10"},
|
||||||
|
{(((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36 + 32, 36, "holycow"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestItoa(t *testing.T) {
|
||||||
|
for _, test := range itob64tests {
|
||||||
|
s := FormatInt(test.in, test.base)
|
||||||
|
if s != test.out {
|
||||||
|
t.Errorf("FormatInt(%v, %v) = %v want %v",
|
||||||
|
test.in, test.base, s, test.out)
|
||||||
|
}
|
||||||
|
x := AppendInt([]byte("abc"), test.in, test.base)
|
||||||
|
if string(x) != "abc"+test.out {
|
||||||
|
t.Errorf("AppendInt(%q, %v, %v) = %q want %v",
|
||||||
|
"abc", test.in, test.base, x, test.out)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.in >= 0 {
|
||||||
|
s := FormatUint(uint64(test.in), test.base)
|
||||||
|
if s != test.out {
|
||||||
|
t.Errorf("FormatUint(%v, %v) = %v want %v",
|
||||||
|
test.in, test.base, s, test.out)
|
||||||
|
}
|
||||||
|
x := AppendUint(nil, uint64(test.in), test.base)
|
||||||
|
if string(x) != test.out {
|
||||||
|
t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
|
||||||
|
"abc", uint64(test.in), test.base, x, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.base == 10 && int64(int(test.in)) == test.in {
|
||||||
|
s := Itoa(int(test.in))
|
||||||
|
if s != test.out {
|
||||||
|
t.Errorf("Itoa(%v) = %v want %v",
|
||||||
|
test.in, s, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override when base is illegal
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Fatalf("expected panic due to illegal base")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
FormatUint(12345678, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
type uitob64Test struct {
|
||||||
|
in uint64
|
||||||
|
base int
|
||||||
|
out string
|
||||||
|
}
|
||||||
|
|
||||||
|
var uitob64tests = []uitob64Test{
|
||||||
|
{1<<63 - 1, 10, "9223372036854775807"},
|
||||||
|
{1 << 63, 10, "9223372036854775808"},
|
||||||
|
{1<<63 + 1, 10, "9223372036854775809"},
|
||||||
|
{1<<64 - 2, 10, "18446744073709551614"},
|
||||||
|
{1<<64 - 1, 10, "18446744073709551615"},
|
||||||
|
{1<<64 - 1, 2, "1111111111111111111111111111111111111111111111111111111111111111"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUitoa(t *testing.T) {
|
||||||
|
for _, test := range uitob64tests {
|
||||||
|
s := FormatUint(test.in, test.base)
|
||||||
|
if s != test.out {
|
||||||
|
t.Errorf("FormatUint(%v, %v) = %v want %v",
|
||||||
|
test.in, test.base, s, test.out)
|
||||||
|
}
|
||||||
|
x := AppendUint([]byte("abc"), test.in, test.base)
|
||||||
|
if string(x) != "abc"+test.out {
|
||||||
|
t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
|
||||||
|
"abc", test.in, test.base, x, test.out)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var varlenUints = []struct {
|
||||||
|
in uint64
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{1, "1"},
|
||||||
|
{12, "12"},
|
||||||
|
{123, "123"},
|
||||||
|
{1234, "1234"},
|
||||||
|
{12345, "12345"},
|
||||||
|
{123456, "123456"},
|
||||||
|
{1234567, "1234567"},
|
||||||
|
{12345678, "12345678"},
|
||||||
|
{123456789, "123456789"},
|
||||||
|
{1234567890, "1234567890"},
|
||||||
|
{12345678901, "12345678901"},
|
||||||
|
{123456789012, "123456789012"},
|
||||||
|
{1234567890123, "1234567890123"},
|
||||||
|
{12345678901234, "12345678901234"},
|
||||||
|
{123456789012345, "123456789012345"},
|
||||||
|
{1234567890123456, "1234567890123456"},
|
||||||
|
{12345678901234567, "12345678901234567"},
|
||||||
|
{123456789012345678, "123456789012345678"},
|
||||||
|
{1234567890123456789, "1234567890123456789"},
|
||||||
|
{12345678901234567890, "12345678901234567890"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatUintVarlen(t *testing.T) {
|
||||||
|
for _, test := range varlenUints {
|
||||||
|
s := FormatUint(test.in, 10)
|
||||||
|
if s != test.out {
|
||||||
|
t.Errorf("FormatUint(%v, 10) = %v want %v", test.in, s, test.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue