atoi.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /**
  2. * Copyright 2014 Paul Querna
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. /* Portions of this file are on Go stdlib's strconv/atoi.go */
  18. // Copyright 2009 The Go Authors. All rights reserved.
  19. // Use of this source code is governed by a BSD-style
  20. // license that can be found in the LICENSE file.
  21. package internal
  22. import (
  23. "errors"
  24. "strconv"
  25. )
  26. // ErrRange indicates that a value is out of range for the target type.
  27. var ErrRange = errors.New("value out of range")
  28. // ErrSyntax indicates that a value does not have the right syntax for the target type.
  29. var ErrSyntax = errors.New("invalid syntax")
  30. // A NumError records a failed conversion.
  31. type NumError struct {
  32. Func string // the failing function (ParseBool, ParseInt, ParseUint, ParseFloat)
  33. Num string // the input
  34. Err error // the reason the conversion failed (ErrRange, ErrSyntax)
  35. }
  36. func (e *NumError) Error() string {
  37. return "strconv." + e.Func + ": " + "parsing " + strconv.Quote(e.Num) + ": " + e.Err.Error()
  38. }
  39. func syntaxError(fn, str string) *NumError {
  40. return &NumError{fn, str, ErrSyntax}
  41. }
  42. func rangeError(fn, str string) *NumError {
  43. return &NumError{fn, str, ErrRange}
  44. }
  45. const intSize = 32 << uint(^uint(0)>>63)
  46. // IntSize is the size in bits of an int or uint value.
  47. const IntSize = intSize
  48. // Return the first number n such that n*base >= 1<<64.
  49. func cutoff64(base int) uint64 {
  50. if base < 2 {
  51. return 0
  52. }
  53. return (1<<64-1)/uint64(base) + 1
  54. }
  55. // ParseUint is like ParseInt but for unsigned numbers, and oeprating on []byte
  56. func ParseUint(s []byte, base int, bitSize int) (n uint64, err error) {
  57. var cutoff, maxVal uint64
  58. if bitSize == 0 {
  59. bitSize = int(IntSize)
  60. }
  61. s0 := s
  62. switch {
  63. case len(s) < 1:
  64. err = ErrSyntax
  65. goto Error
  66. case 2 <= base && base <= 36:
  67. // valid base; nothing to do
  68. case base == 0:
  69. // Look for octal, hex prefix.
  70. switch {
  71. case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
  72. base = 16
  73. s = s[2:]
  74. if len(s) < 1 {
  75. err = ErrSyntax
  76. goto Error
  77. }
  78. case s[0] == '0':
  79. base = 8
  80. default:
  81. base = 10
  82. }
  83. default:
  84. err = errors.New("invalid base " + strconv.Itoa(base))
  85. goto Error
  86. }
  87. n = 0
  88. cutoff = cutoff64(base)
  89. maxVal = 1<<uint(bitSize) - 1
  90. for i := 0; i < len(s); i++ {
  91. var v byte
  92. d := s[i]
  93. switch {
  94. case '0' <= d && d <= '9':
  95. v = d - '0'
  96. case 'a' <= d && d <= 'z':
  97. v = d - 'a' + 10
  98. case 'A' <= d && d <= 'Z':
  99. v = d - 'A' + 10
  100. default:
  101. n = 0
  102. err = ErrSyntax
  103. goto Error
  104. }
  105. if int(v) >= base {
  106. n = 0
  107. err = ErrSyntax
  108. goto Error
  109. }
  110. if n >= cutoff {
  111. // n*base overflows
  112. n = 1<<64 - 1
  113. err = ErrRange
  114. goto Error
  115. }
  116. n *= uint64(base)
  117. n1 := n + uint64(v)
  118. if n1 < n || n1 > maxVal {
  119. // n+v overflows
  120. n = 1<<64 - 1
  121. err = ErrRange
  122. goto Error
  123. }
  124. n = n1
  125. }
  126. return n, nil
  127. Error:
  128. return n, &NumError{"ParseUint", string(s0), err}
  129. }
  130. // ParseInt interprets a string s in the given base (2 to 36) and
  131. // returns the corresponding value i. If base == 0, the base is
  132. // implied by the string's prefix: base 16 for "0x", base 8 for
  133. // "0", and base 10 otherwise.
  134. //
  135. // The bitSize argument specifies the integer type
  136. // that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
  137. // correspond to int, int8, int16, int32, and int64.
  138. //
  139. // The errors that ParseInt returns have concrete type *NumError
  140. // and include err.Num = s. If s is empty or contains invalid
  141. // digits, err.Err = ErrSyntax and the returned value is 0;
  142. // if the value corresponding to s cannot be represented by a
  143. // signed integer of the given size, err.Err = ErrRange and the
  144. // returned value is the maximum magnitude integer of the
  145. // appropriate bitSize and sign.
  146. func ParseInt(s []byte, base int, bitSize int) (i int64, err error) {
  147. const fnParseInt = "ParseInt"
  148. if bitSize == 0 {
  149. bitSize = int(IntSize)
  150. }
  151. // Empty string bad.
  152. if len(s) == 0 {
  153. return 0, syntaxError(fnParseInt, string(s))
  154. }
  155. // Pick off leading sign.
  156. s0 := s
  157. neg := false
  158. if s[0] == '+' {
  159. s = s[1:]
  160. } else if s[0] == '-' {
  161. neg = true
  162. s = s[1:]
  163. }
  164. // Convert unsigned and check range.
  165. var un uint64
  166. un, err = ParseUint(s, base, bitSize)
  167. if err != nil && err.(*NumError).Err != ErrRange {
  168. err.(*NumError).Func = fnParseInt
  169. err.(*NumError).Num = string(s0)
  170. return 0, err
  171. }
  172. cutoff := uint64(1 << uint(bitSize-1))
  173. if !neg && un >= cutoff {
  174. return int64(cutoff - 1), rangeError(fnParseInt, string(s0))
  175. }
  176. if neg && un > cutoff {
  177. return -int64(cutoff), rangeError(fnParseInt, string(s0))
  178. }
  179. n := int64(un)
  180. if neg {
  181. n = -n
  182. }
  183. return n, nil
  184. }