| // 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 bytesconv |
| |
| import ( |
| "errors" |
| "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') |
| } |
| |
| // 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 (e.g. ErrRange, ErrSyntax, etc.) |
| } |
| |
| func (e *NumError) Error() string { |
| return "strconv." + e.Func + ": " + "parsing " + strconv.Quote(e.Num) + ": " + e.Err.Error() |
| } |
| |
| func syntaxError(fn string, str []byte) *NumError { |
| return &NumError{fn, string(str), ErrSyntax} |
| } |
| |
| func rangeError(fn string, str []byte) *NumError { |
| return &NumError{fn, string(str), ErrRange} |
| } |
| |
| func baseError(fn string, str []byte, base int) *NumError { |
| return &NumError{fn, string(str), errors.New("invalid base " + strconv.Itoa(base))} |
| } |
| |
| func bitSizeError(fn string, str []byte, bitSize int) *NumError { |
| return &NumError{fn, string(str), errors.New("invalid bit size " + strconv.Itoa(bitSize))} |
| } |
| |
| const intSize = 32 << (^uint(0) >> 63) |
| |
| // IntSize is the size in bits of an int or uint value. |
| const IntSize = intSize |
| |
| const maxUint64 = 1<<64 - 1 |
| |
| // ParseUint is like ParseInt but for unsigned numbers. |
| func ParseUint(s []byte, base int, bitSize int) (uint64, error) { |
| const fnParseUint = "ParseUint" |
| |
| if len(s) == 0 || !underscoreOK(s) { |
| return 0, syntaxError(fnParseUint, s) |
| } |
| |
| 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, baseError(fnParseUint, s0, base) |
| } |
| |
| if bitSize == 0 { |
| bitSize = int(IntSize) |
| } else if bitSize < 0 || bitSize > 64 { |
| return 0, bitSizeError(fnParseUint, s0, bitSize) |
| } |
| |
| // Cutoff is the smallest number such that cutoff*base > maxUint64. |
| // Use compile-time constants for common cases. |
| 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 |
| |
| var n uint64 |
| for _, c := range s { |
| var d byte |
| switch { |
| case c == '_' && base0: |
| // underscoreOK already called |
| continue |
| case '0' <= c && c <= '9': |
| d = c - '0' |
| case 'a' <= lower(c) && lower(c) <= 'z': |
| d = lower(c) - 'a' + 10 |
| default: |
| return 0, syntaxError(fnParseUint, s0) |
| } |
| |
| if d >= byte(base) { |
| return 0, syntaxError(fnParseUint, s0) |
| } |
| |
| if n >= cutoff { |
| // n*base overflows |
| return maxVal, rangeError(fnParseUint, s0) |
| } |
| n *= uint64(base) |
| |
| n1 := n + uint64(d) |
| if n1 < n || n1 > maxVal { |
| // n+v overflows |
| return maxVal, rangeError(fnParseUint, s0) |
| } |
| n = n1 |
| } |
| |
| 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. |
| // |
| // If base == 0, the base is implied by the string's prefix: |
| // base 2 for "0b", base 8 for "0" or "0o", base 16 for "0x", |
| // and base 10 otherwise. Also, for base == 0 only, underscore |
| // characters are permitted per the Go integer literal syntax. |
| // If base is below 0, is 1, or is above 36, an error is returned. |
| // |
| // 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. |
| func ParseInt(s []byte, base int, bitSize int) (i int64, err error) { |
| const fnParseInt = "ParseInt" |
| |
| 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 = string(s0) |
| return 0, err |
| } |
| |
| if bitSize == 0 { |
| bitSize = int(IntSize) |
| } |
| |
| 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 equivalent to ParseInt(s, 10, 0), converted to type int. |
| func Atoi(s []byte) (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, &NumError{fnAtoi, string(s0), ErrSyntax} |
| } |
| } |
| |
| n := 0 |
| for _, ch := range []byte(s) { |
| ch -= '0' |
| if ch > 9 { |
| return 0, &NumError{fnAtoi, string(s0), 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) |
| if nerr, ok := err.(*NumError); ok { |
| nerr.Func = fnAtoi |
| } |
| 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 []byte) 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 != '_' |
| } |