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