| // 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 | |
| // lower(c) is a lower-case letter if and only if | |
| // 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'. | |
| // Note that lower of non-letters can produce other non-letters. | |
| func lower(c byte) byte { | |
| return c | ('x' - 'X') | |
| } | |
| type Error int | |
| const ( | |
| _ Error = iota | |
| ErrRange | |
| ErrSyntax | |
| ErrBase | |
| ErrBitSize | |
| ) | |
| func (e Error) Error() string { | |
| switch e { | |
| case ErrRange: | |
| return "value out of range" | |
| case ErrSyntax: | |
| return "invalid syntax" | |
| case ErrBase: | |
| return "invalid base" | |
| case ErrBitSize: | |
| return "invalid bit size" | |
| } | |
| return "unknown error" | |
| } | |
| const intSize = 32 << (^uint(0) >> 63) | |
| // IntSize is the size in bits of an int or uint value. | |
| const IntSize = intSize | |
| // ParseUint is like [ParseInt] but for unsigned numbers. | |
| // | |
| // A sign prefix is not permitted. | |
| func ParseUint(s string, base int, bitSize int) (uint64, error) { | |
| const fnParseUint = "ParseUint" | |
| if s == "" { | |
| return 0, ErrSyntax | |
| } | |
| base0 := base == 0 | |
| s0 := s | |
| switch { | |
| case 2 <= base && base <= 36: | |
| // valid base; nothing to do | |
| case base == 0: | |
| // Look for octal, hex prefix. | |
| base = 10 | |
| if s[0] == '0' { | |
| switch { | |
| case len(s) >= 3 && lower(s[1]) == 'b': | |
| base = 2 | |
| s = s[2:] | |
| case len(s) >= 3 && lower(s[1]) == 'o': | |
| base = 8 | |
| s = s[2:] | |
| case len(s) >= 3 && lower(s[1]) == 'x': | |
| base = 16 | |
| s = s[2:] | |
| default: | |
| base = 8 | |
| s = s[1:] | |
| } | |
| } | |
| default: | |
| return 0, ErrBase | |
| } | |
| if bitSize == 0 { | |
| bitSize = IntSize | |
| } else if bitSize < 0 || bitSize > 64 { | |
| return 0, ErrBitSize | |
| } | |
| // Cutoff is the smallest number such that cutoff*base > maxUint64. | |
| // Use compile-time constants for common cases. | |
| const maxUint64 = 1<<64 - 1 | |
| var cutoff uint64 | |
| switch base { | |
| case 10: | |
| cutoff = maxUint64/10 + 1 | |
| case 16: | |
| cutoff = maxUint64/16 + 1 | |
| default: | |
| cutoff = maxUint64/uint64(base) + 1 | |
| } | |
| maxVal := uint64(1)<<uint(bitSize) - 1 | |
| underscores := false | |
| var n uint64 | |
| for _, c := range []byte(s) { | |
| var d byte | |
| switch { | |
| case c == '_' && base0: | |
| underscores = true | |
| continue | |
| case '0' <= c && c <= '9': | |
| d = c - '0' | |
| case 'a' <= lower(c) && lower(c) <= 'z': | |
| d = lower(c) - 'a' + 10 | |
| default: | |
| return 0, ErrSyntax | |
| } | |
| if d >= byte(base) { | |
| return 0, ErrSyntax | |
| } | |
| if n >= cutoff { | |
| // n*base overflows | |
| return maxVal, ErrRange | |
| } | |
| n *= uint64(base) | |
| n1 := n + uint64(d) | |
| if n1 < n || n1 > maxVal { | |
| // n+d overflows | |
| return maxVal, ErrRange | |
| } | |
| n = n1 | |
| } | |
| if underscores && !underscoreOK(s0) { | |
| return 0, ErrSyntax | |
| } | |
| return n, 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) { | |
| const fnParseInt = "ParseInt" | |
| if s == "" { | |
| return 0, ErrSyntax | |
| } | |
| // Pick off leading sign. | |
| neg := false | |
| switch s[0] { | |
| case '+': | |
| s = s[1:] | |
| case '-': | |
| s = s[1:] | |
| neg = true | |
| } | |
| // Convert unsigned and check range. | |
| var un uint64 | |
| un, err = ParseUint(s, base, bitSize) | |
| if err != nil && err != ErrRange { | |
| return 0, err | |
| } | |
| if bitSize == 0 { | |
| bitSize = IntSize | |
| } | |
| cutoff := uint64(1 << uint(bitSize-1)) | |
| if !neg && un >= cutoff { | |
| return int64(cutoff - 1), ErrRange | |
| } | |
| if neg && un > cutoff { | |
| return -int64(cutoff), ErrRange | |
| } | |
| n := int64(un) | |
| if neg { | |
| n = -n | |
| } | |
| return n, nil | |
| } | |
| // Atoi is equivalent to ParseInt(s, 10, 0), converted to type int. | |
| func Atoi(s string) (int, error) { | |
| const fnAtoi = "Atoi" | |
| sLen := len(s) | |
| if intSize == 32 && (0 < sLen && sLen < 10) || | |
| intSize == 64 && (0 < sLen && sLen < 19) { | |
| // Fast path for small integers that fit int type. | |
| s0 := s | |
| if s[0] == '-' || s[0] == '+' { | |
| s = s[1:] | |
| if len(s) < 1 { | |
| return 0, ErrSyntax | |
| } | |
| } | |
| n := 0 | |
| for _, ch := range []byte(s) { | |
| ch -= '0' | |
| if ch > 9 { | |
| return 0, ErrSyntax | |
| } | |
| n = n*10 + int(ch) | |
| } | |
| if s0[0] == '-' { | |
| n = -n | |
| } | |
| return n, nil | |
| } | |
| // Slow path for invalid, big, or underscored integers. | |
| i64, err := ParseInt(s, 10, 0) | |
| return int(i64), err | |
| } | |
| // underscoreOK reports whether the underscores in s are allowed. | |
| // Checking them in this one function lets all the parsers skip over them simply. | |
| // Underscore must appear only between digits or between a base prefix and a digit. | |
| func underscoreOK(s string) bool { | |
| // saw tracks the last character (class) we saw: | |
| // ^ for beginning of number, | |
| // 0 for a digit or base prefix, | |
| // _ for an underscore, | |
| // ! for none of the above. | |
| saw := '^' | |
| i := 0 | |
| // Optional sign. | |
| if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { | |
| s = s[1:] | |
| } | |
| // Optional base prefix. | |
| hex := false | |
| if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { | |
| i = 2 | |
| saw = '0' // base prefix counts as a digit for "underscore as digit separator" | |
| hex = lower(s[1]) == 'x' | |
| } | |
| // Number proper. | |
| for ; i < len(s); i++ { | |
| // Digits are always okay. | |
| if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { | |
| saw = '0' | |
| continue | |
| } | |
| // Underscore must follow digit. | |
| if s[i] == '_' { | |
| if saw != '0' { | |
| return false | |
| } | |
| saw = '_' | |
| continue | |
| } | |
| // Underscore must also be followed by digit. | |
| if saw == '_' { | |
| return false | |
| } | |
| // Saw non-digit, non-underscore. | |
| saw = '!' | |
| } | |
| return saw != '_' | |
| } |