podman

Форк
0
254 строки · 5.2 Кб
1
// Copyright 2018 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4

5
package json
6

7
import (
8
	"bytes"
9
	"strconv"
10
)
11

12
// parseNumber reads the given []byte for a valid JSON number. If it is valid,
13
// it returns the number of bytes.  Parsing logic follows the definition in
14
// https://tools.ietf.org/html/rfc7159#section-6, and is based off
15
// encoding/json.isValidNumber function.
16
func parseNumber(input []byte) (int, bool) {
17
	var n int
18

19
	s := input
20
	if len(s) == 0 {
21
		return 0, false
22
	}
23

24
	// Optional -
25
	if s[0] == '-' {
26
		s = s[1:]
27
		n++
28
		if len(s) == 0 {
29
			return 0, false
30
		}
31
	}
32

33
	// Digits
34
	switch {
35
	case s[0] == '0':
36
		s = s[1:]
37
		n++
38

39
	case '1' <= s[0] && s[0] <= '9':
40
		s = s[1:]
41
		n++
42
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
43
			s = s[1:]
44
			n++
45
		}
46

47
	default:
48
		return 0, false
49
	}
50

51
	// . followed by 1 or more digits.
52
	if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
53
		s = s[2:]
54
		n += 2
55
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
56
			s = s[1:]
57
			n++
58
		}
59
	}
60

61
	// e or E followed by an optional - or + and
62
	// 1 or more digits.
63
	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
64
		s = s[1:]
65
		n++
66
		if s[0] == '+' || s[0] == '-' {
67
			s = s[1:]
68
			n++
69
			if len(s) == 0 {
70
				return 0, false
71
			}
72
		}
73
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
74
			s = s[1:]
75
			n++
76
		}
77
	}
78

79
	// Check that next byte is a delimiter or it is at the end.
80
	if n < len(input) && isNotDelim(input[n]) {
81
		return 0, false
82
	}
83

84
	return n, true
85
}
86

87
// numberParts is the result of parsing out a valid JSON number. It contains
88
// the parts of a number. The parts are used for integer conversion.
89
type numberParts struct {
90
	neg  bool
91
	intp []byte
92
	frac []byte
93
	exp  []byte
94
}
95

96
// parseNumber constructs numberParts from given []byte. The logic here is
97
// similar to consumeNumber above with the difference of having to construct
98
// numberParts. The slice fields in numberParts are subslices of the input.
99
func parseNumberParts(input []byte) (numberParts, bool) {
100
	var neg bool
101
	var intp []byte
102
	var frac []byte
103
	var exp []byte
104

105
	s := input
106
	if len(s) == 0 {
107
		return numberParts{}, false
108
	}
109

110
	// Optional -
111
	if s[0] == '-' {
112
		neg = true
113
		s = s[1:]
114
		if len(s) == 0 {
115
			return numberParts{}, false
116
		}
117
	}
118

119
	// Digits
120
	switch {
121
	case s[0] == '0':
122
		// Skip first 0 and no need to store.
123
		s = s[1:]
124

125
	case '1' <= s[0] && s[0] <= '9':
126
		intp = s
127
		n := 1
128
		s = s[1:]
129
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
130
			s = s[1:]
131
			n++
132
		}
133
		intp = intp[:n]
134

135
	default:
136
		return numberParts{}, false
137
	}
138

139
	// . followed by 1 or more digits.
140
	if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
141
		frac = s[1:]
142
		n := 1
143
		s = s[2:]
144
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
145
			s = s[1:]
146
			n++
147
		}
148
		frac = frac[:n]
149
	}
150

151
	// e or E followed by an optional - or + and
152
	// 1 or more digits.
153
	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
154
		s = s[1:]
155
		exp = s
156
		n := 0
157
		if s[0] == '+' || s[0] == '-' {
158
			s = s[1:]
159
			n++
160
			if len(s) == 0 {
161
				return numberParts{}, false
162
			}
163
		}
164
		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
165
			s = s[1:]
166
			n++
167
		}
168
		exp = exp[:n]
169
	}
170

171
	return numberParts{
172
		neg:  neg,
173
		intp: intp,
174
		frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
175
		exp:  exp,
176
	}, true
177
}
178

179
// normalizeToIntString returns an integer string in normal form without the
180
// E-notation for given numberParts. It will return false if it is not an
181
// integer or if the exponent exceeds than max/min int value.
182
func normalizeToIntString(n numberParts) (string, bool) {
183
	intpSize := len(n.intp)
184
	fracSize := len(n.frac)
185

186
	if intpSize == 0 && fracSize == 0 {
187
		return "0", true
188
	}
189

190
	var exp int
191
	if len(n.exp) > 0 {
192
		i, err := strconv.ParseInt(string(n.exp), 10, 32)
193
		if err != nil {
194
			return "", false
195
		}
196
		exp = int(i)
197
	}
198

199
	var num []byte
200
	if exp >= 0 {
201
		// For positive E, shift fraction digits into integer part and also pad
202
		// with zeroes as needed.
203

204
		// If there are more digits in fraction than the E value, then the
205
		// number is not an integer.
206
		if fracSize > exp {
207
			return "", false
208
		}
209

210
		// Make sure resulting digits are within max value limit to avoid
211
		// unnecessarily constructing a large byte slice that may simply fail
212
		// later on.
213
		const maxDigits = 20 // Max uint64 value has 20 decimal digits.
214
		if intpSize+exp > maxDigits {
215
			return "", false
216
		}
217

218
		// Set cap to make a copy of integer part when appended.
219
		num = n.intp[:len(n.intp):len(n.intp)]
220
		num = append(num, n.frac...)
221
		for i := 0; i < exp-fracSize; i++ {
222
			num = append(num, '0')
223
		}
224
	} else {
225
		// For negative E, shift digits in integer part out.
226

227
		// If there are fractions, then the number is not an integer.
228
		if fracSize > 0 {
229
			return "", false
230
		}
231

232
		// index is where the decimal point will be after adjusting for negative
233
		// exponent.
234
		index := intpSize + exp
235
		if index < 0 {
236
			return "", false
237
		}
238

239
		num = n.intp
240
		// If any of the digits being shifted to the right of the decimal point
241
		// is non-zero, then the number is not an integer.
242
		for i := index; i < intpSize; i++ {
243
			if num[i] != '0' {
244
				return "", false
245
			}
246
		}
247
		num = num[:index]
248
	}
249

250
	if n.neg {
251
		return "-" + string(num), true
252
	}
253
	return string(num), true
254
}
255

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.