Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
198 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Copyright 2009 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package strconv | ||
|
||
import "errors" | ||
|
||
// 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) | ||
Num string // the input | ||
Err error // the reason the conversion failed (ErrRange, ErrSyntax) | ||
} | ||
|
||
func (e *NumError) Error() string { | ||
return "strconv." + e.Func + ": " + `parsing "` + e.Num + `": ` + e.Err.Error() | ||
} | ||
|
||
func syntaxError(fn, str string) *NumError { | ||
return &NumError{fn, str, ErrSyntax} | ||
} | ||
|
||
func rangeError(fn, str string) *NumError { | ||
return &NumError{fn, str, ErrRange} | ||
} | ||
|
||
const intSize = 32 << uint(^uint(0)>>63) | ||
|
||
const IntSize = intSize // number of bits in int, uint (32 or 64) | ||
|
||
// Return the first number n such that n*base >= 1<<64. | ||
func cutoff64(base int) uint64 { | ||
if base < 2 { | ||
return 0 | ||
} | ||
return (1<<64-1)/uint64(base) + 1 | ||
} | ||
|
||
// ParseUint is like ParseInt but for unsigned numbers. | ||
func ParseUint(s string, base int, bitSize int) (n uint64, err error) { | ||
var cutoff, maxVal uint64 | ||
|
||
if bitSize == 0 { | ||
bitSize = int(IntSize) | ||
} | ||
|
||
s0 := s | ||
switch { | ||
case len(s) < 1: | ||
err = ErrSyntax | ||
goto Error | ||
|
||
case 2 <= base && base <= 36: | ||
// valid base; nothing to do | ||
|
||
case base == 0: | ||
// Look for octal, hex prefix. | ||
switch { | ||
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): | ||
base = 16 | ||
s = s[2:] | ||
if len(s) < 1 { | ||
err = ErrSyntax | ||
goto Error | ||
} | ||
case s[0] == '0': | ||
base = 8 | ||
default: | ||
base = 10 | ||
} | ||
|
||
default: | ||
err = errors.New("invalid base " + Itoa(base)) | ||
goto Error | ||
} | ||
|
||
n = 0 | ||
cutoff = cutoff64(base) | ||
maxVal = 1<<uint(bitSize) - 1 | ||
|
||
for i := 0; i < len(s); i++ { | ||
var v byte | ||
d := s[i] | ||
switch { | ||
case '0' <= d && d <= '9': | ||
v = d - '0' | ||
case 'a' <= d && d <= 'z': | ||
v = d - 'a' + 10 | ||
case 'A' <= d && d <= 'Z': | ||
v = d - 'A' + 10 | ||
default: | ||
n = 0 | ||
err = ErrSyntax | ||
goto Error | ||
} | ||
if int(v) >= base { | ||
n = 0 | ||
err = ErrSyntax | ||
goto Error | ||
} | ||
|
||
if n >= cutoff { | ||
// n*base overflows | ||
n = 1<<64 - 1 | ||
err = ErrRange | ||
goto Error | ||
} | ||
n *= uint64(base) | ||
|
||
n1 := n + uint64(v) | ||
if n1 < n || n1 > maxVal { | ||
// n+v overflows | ||
n = 1<<64 - 1 | ||
err = ErrRange | ||
goto Error | ||
} | ||
n = n1 | ||
} | ||
|
||
return n, nil | ||
|
||
Error: | ||
return n, &NumError{"ParseUint", s0, err} | ||
} | ||
|
||
// ParseInt interprets a string s in the given base (2 to 36) and | ||
// returns the corresponding value i. If base == 0, the base is | ||
// implied by the string's prefix: base 16 for "0x", base 8 for | ||
// "0", and base 10 otherwise. | ||
// | ||
// 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. | ||
// | ||
// The errors that ParseInt returns have concrete type *NumError | ||
// and include err.Num = s. If s is empty or contains invalid | ||
// digits, err.Error = ErrSyntax; if the value corresponding | ||
// to s cannot be represented by a signed integer of the | ||
// given size, err.Error = ErrRange. | ||
func ParseInt(s string, base int, bitSize int) (i int64, err error) { | ||
const fnParseInt = "ParseInt" | ||
|
||
if bitSize == 0 { | ||
bitSize = int(IntSize) | ||
} | ||
|
||
// Empty string bad. | ||
if len(s) == 0 { | ||
return 0, syntaxError(fnParseInt, s) | ||
} | ||
|
||
// Pick off leading sign. | ||
s0 := s | ||
neg := false | ||
if s[0] == '+' { | ||
s = s[1:] | ||
} else if s[0] == '-' { | ||
neg = true | ||
s = s[1:] | ||
} | ||
|
||
// Convert unsigned and check range. | ||
var un uint64 | ||
un, err = ParseUint(s, base, bitSize) | ||
if err != nil && err.(*NumError).Err != ErrRange { | ||
err.(*NumError).Func = fnParseInt | ||
err.(*NumError).Num = s0 | ||
return 0, err | ||
} | ||
cutoff := uint64(1 << uint(bitSize-1)) | ||
if !neg && un >= cutoff { | ||
return int64(cutoff - 1), rangeError(fnParseInt, s0) | ||
} | ||
if neg && un > cutoff { | ||
return -int64(cutoff), rangeError(fnParseInt, s0) | ||
} | ||
n := int64(un) | ||
if neg { | ||
n = -n | ||
} | ||
return n, nil | ||
} | ||
|
||
// Atoi is shorthand for ParseInt(s, 10, 0). | ||
func Atoi(s string) (i int, err error) { | ||
i64, err := ParseInt(s, 10, 0) | ||
return int(i64), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters