New upstream release.
Debian Janitor
2 years ago
6 | 6 | are pulled out and placed here. |
7 | 7 | |
8 | 8 | |
9 | ## Installation ## | |
9 | ## Packages ## | |
10 | 10 | |
11 | Run the command: | |
12 | ||
13 | ```go get -u github.com/dsnet/golib``` | |
14 | ||
15 | ||
16 | ## Packages ## | |
11 | Each top-level package is a sub-module so that the individual packages | |
12 | can be versioned separately from each other. | |
17 | 13 | |
18 | 14 | | Package | Description | |
19 | 15 | | :------ | :---------- | |
20 | | [bufpipe](http://godoc.org/github.com/dsnet/golib/bufpipe) | Package bufpipe implements a buffered pipe. | | |
21 | | [cron](http://godoc.org/github.com/dsnet/golib/cron) | Package cron parses and runs cron schedules. | | |
22 | | [hashmerge](http://godoc.org/github.com/dsnet/golib/hashmerge) | Package hashmerge merges hash checksums. | | |
23 | | [jsonfmt](http://godoc.org/github.com/dsnet/golib/jsonfmt) | Package jsonfmt implements a JSON formatter. | | |
24 | | [memfile](http://godoc.org/github.com/dsnet/golib/memfile) | Package memfile implements an in-memory emulation of os.File. | | |
25 | | [unitconv](http://godoc.org/github.com/dsnet/golib/unitconv) | Package unitconv implements string conversion functionality for unit prefixes. | | |
16 | | [bufpipe](https://pkg.go.dev/github.com/dsnet/golib/bufpipe) | Package bufpipe implements a buffered pipe. | | |
17 | | [cron](https://pkg.go.dev/github.com/dsnet/golib/cron) | Package cron parses and runs cron schedules. | | |
18 | | [hashmerge](https://pkg.go.dev/github.com/dsnet/golib/hashmerge) | Package hashmerge merges hash checksums. | | |
19 | | [jsoncs](https://pkg.go.dev/github.com/dsnet/golib/jsoncs) | Package jsoncs implements JSON Canonicalization Scheme (JCS) as specified in RFC 8785. | | |
20 | | [jsonfmt](https://pkg.go.dev/github.com/dsnet/golib/jsonfmt) | Package jsonfmt implements a JSON formatter. | | |
21 | | [memfile](https://pkg.go.dev/github.com/dsnet/golib/memfile) | Package memfile implements an in-memory emulation of os.File. | | |
22 | | [unitconv](https://pkg.go.dev/github.com/dsnet/golib/unitconv) | Package unitconv implements string conversion functionality for unit prefixes. | |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | golang-github-dsnet-golib (1.0.2-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream release. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Fri, 20 Aug 2021 09:10:47 -0000 | |
5 | ||
0 | 6 | golang-github-dsnet-golib (0.0~git20171103.1ea1667-1) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * Initial release (Closes: #902735) |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "bytes" | |
8 | "errors" | |
9 | "fmt" | |
10 | "io" | |
11 | "strconv" | |
12 | "unicode" | |
13 | "unicode/utf16" | |
14 | "unicode/utf8" | |
15 | ) | |
16 | ||
17 | type ( | |
18 | jsonValue = interface{} | |
19 | jsonArray = []jsonValue | |
20 | jsonObject = map[string]jsonValue | |
21 | ) | |
22 | ||
23 | var ( | |
24 | nullLiteral = []byte("null") | |
25 | trueLiteral = []byte("true") | |
26 | falseLiteral = []byte("false") | |
27 | ) | |
28 | ||
29 | // decode unmarshals the JSON input as a JSON value. | |
30 | // | |
31 | // It is similar to: | |
32 | // | |
33 | // var v interface{} | |
34 | // err := json.Unmarshal(b, &v) | |
35 | // return v, err | |
36 | // | |
37 | // However, it is stricter than the standard library implementation in that | |
38 | // it rejects JSON objects with duplicate keys and invalid UTF-8 | |
39 | // in adherence of RFC 7493. | |
40 | func decode(b []byte) (jsonValue, error) { | |
41 | v, b, err := decodeValue(b) | |
42 | if err != nil { | |
43 | return nil, err | |
44 | } | |
45 | if len(trimSpace(b)) > 0 { | |
46 | return nil, errors.New("invalid data after top-level value") | |
47 | } | |
48 | return v, nil | |
49 | } | |
50 | ||
51 | // decodeValue unmarshals the next JSON value per RFC 7159, section 3. | |
52 | // It consume the leading value, and returns the remaining bytes. | |
53 | func decodeValue(b []byte) (jsonValue, []byte, error) { | |
54 | b = trimSpace(b) | |
55 | switch { | |
56 | case len(b) == 0: | |
57 | return nil, nil, io.ErrUnexpectedEOF | |
58 | case b[0] == '{': | |
59 | return decodeObject(b) | |
60 | case b[0] == '[': | |
61 | return decodeArray(b) | |
62 | case b[0] == '"': | |
63 | return decodeString(b) | |
64 | case (b[0] == '-' || ('0' <= b[0] && b[0] <= '9')): | |
65 | return decodeNumber(b) | |
66 | case bytes.HasPrefix(b, nullLiteral): | |
67 | return nil, b[len(nullLiteral):], nil | |
68 | case bytes.HasPrefix(b, trueLiteral): | |
69 | return true, b[len(trueLiteral):], nil | |
70 | case bytes.HasPrefix(b, falseLiteral): | |
71 | return false, b[len(falseLiteral):], nil | |
72 | default: | |
73 | return nil, b, errors.New("expected next JSON value") | |
74 | } | |
75 | } | |
76 | ||
77 | // decodeObject unmarshals the next JSON object per RFC 7159, section 4, | |
78 | // with special attention paid to RFC 7493, section 2.3 | |
79 | // regarding rejection of duplicate entry names. | |
80 | // It consume the leading value, and returns the remaining bytes. | |
81 | func decodeObject(b []byte) (jsonObject, []byte, error) { | |
82 | b = trimSpace(b) | |
83 | switch { | |
84 | case len(b) == 0: | |
85 | return nil, b, io.ErrUnexpectedEOF | |
86 | case b[0] == '{': | |
87 | b = b[1:] | |
88 | default: | |
89 | return nil, b, errors.New("expected '{' character in JSON object") | |
90 | } | |
91 | ||
92 | var init bool | |
93 | var obj = make(jsonObject) | |
94 | var err error | |
95 | for { | |
96 | b = trimSpace(b) | |
97 | if len(b) > 0 && b[0] == '}' { | |
98 | return obj, b[1:], nil | |
99 | } | |
100 | ||
101 | if init { | |
102 | b = trimSpace(b) | |
103 | switch { | |
104 | case len(b) == 0: | |
105 | return nil, b, io.ErrUnexpectedEOF | |
106 | case b[0] == ',': | |
107 | b = b[1:] | |
108 | default: | |
109 | return nil, b, errors.New("expected ',' character in JSON object") | |
110 | } | |
111 | } | |
112 | ||
113 | var k string | |
114 | k, b, err = decodeString(b) | |
115 | if err != nil { | |
116 | return nil, b, err | |
117 | } | |
118 | if _, ok := obj[k]; ok { | |
119 | return nil, b, fmt.Errorf("duplicate key %q in JSON object", k) | |
120 | } | |
121 | ||
122 | b = trimSpace(b) | |
123 | switch { | |
124 | case len(b) == 0: | |
125 | return nil, b, io.ErrUnexpectedEOF | |
126 | case b[0] == ':': | |
127 | b = b[1:] | |
128 | default: | |
129 | return nil, b, errors.New("expected ':' character in JSON object") | |
130 | } | |
131 | ||
132 | var v jsonValue | |
133 | v, b, err = decodeValue(b) | |
134 | if err != nil { | |
135 | return nil, b, err | |
136 | } | |
137 | ||
138 | obj[k] = v | |
139 | init = true | |
140 | } | |
141 | } | |
142 | ||
143 | // decodeArray unmarshals the next JSON object per RFC 7159, section 5. | |
144 | // It consume the leading value, and returns the remaining bytes. | |
145 | func decodeArray(b []byte) (jsonArray, []byte, error) { | |
146 | b = trimSpace(b) | |
147 | switch { | |
148 | case len(b) == 0: | |
149 | return nil, b, io.ErrUnexpectedEOF | |
150 | case b[0] == '[': | |
151 | b = b[1:] | |
152 | default: | |
153 | return nil, b, errors.New("expected '[' character in JSON array") | |
154 | } | |
155 | ||
156 | var init bool | |
157 | var arr jsonArray | |
158 | var err error | |
159 | for { | |
160 | b = trimSpace(b) | |
161 | if len(b) > 0 && b[0] == ']' { | |
162 | return arr, b[1:], nil | |
163 | } | |
164 | ||
165 | if init { | |
166 | b = trimSpace(b) | |
167 | switch { | |
168 | case len(b) == 0: | |
169 | return nil, b, io.ErrUnexpectedEOF | |
170 | case b[0] == ',': | |
171 | b = b[1:] | |
172 | default: | |
173 | return nil, b, errors.New("expected ',' character in JSON array") | |
174 | } | |
175 | } | |
176 | ||
177 | var v jsonValue | |
178 | v, b, err = decodeValue(b) | |
179 | if err != nil { | |
180 | return nil, b, err | |
181 | } | |
182 | ||
183 | arr = append(arr, v) | |
184 | init = true | |
185 | } | |
186 | } | |
187 | ||
188 | // decodeString unmarshals the next JSON string per RFC 7159, section 7, | |
189 | // with special attention paid to RFC 7493, section 2.1 | |
190 | // regarding rejection of unpaired surrogate halves. | |
191 | // It consume the leading value, and returns the remaining bytes. | |
192 | func decodeString(b []byte) (string, []byte, error) { | |
193 | // indexNeedEscape returns the index of the character that needs escaping. | |
194 | indexNeedEscape := func(b []byte) int { | |
195 | for i, r := range string(b) { | |
196 | if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError { | |
197 | return i | |
198 | } | |
199 | } | |
200 | return len(b) | |
201 | } | |
202 | ||
203 | b = trimSpace(b) | |
204 | switch { | |
205 | case len(b) == 0: | |
206 | return "", b, io.ErrUnexpectedEOF | |
207 | case b[0] == '"': | |
208 | b = b[1:] | |
209 | default: | |
210 | return "", b, errors.New("expected '\"' character in JSON string") | |
211 | } | |
212 | ||
213 | var s []byte | |
214 | i := indexNeedEscape(b) | |
215 | b, s = b[i:], append(s, b[:i]...) | |
216 | for len(b) > 0 { | |
217 | switch r, n := utf8.DecodeRune(b); { | |
218 | case r == utf8.RuneError && n == 1: | |
219 | return "", b, errors.New("invalid UTF-8 in JSON string") | |
220 | case r < ' ': | |
221 | return "", b, fmt.Errorf("invalid character %q in string", r) | |
222 | case r == '"': | |
223 | b = b[1:] | |
224 | return string(s), b, nil | |
225 | case r == '\\': | |
226 | if len(b) < 2 { | |
227 | return "", b, io.ErrUnexpectedEOF | |
228 | } | |
229 | switch r := b[1]; r { | |
230 | case '"', '\\', '/': | |
231 | b, s = b[2:], append(s, r) | |
232 | case 'b': | |
233 | b, s = b[2:], append(s, '\b') | |
234 | case 'f': | |
235 | b, s = b[2:], append(s, '\f') | |
236 | case 'n': | |
237 | b, s = b[2:], append(s, '\n') | |
238 | case 'r': | |
239 | b, s = b[2:], append(s, '\r') | |
240 | case 't': | |
241 | b, s = b[2:], append(s, '\t') | |
242 | case 'u': | |
243 | if len(b) < 6 { | |
244 | return "", b, io.ErrUnexpectedEOF | |
245 | } | |
246 | v, err := strconv.ParseUint(string(b[2:6]), 16, 16) | |
247 | if err != nil { | |
248 | return "", b, fmt.Errorf("invalid escape code %q in string", b[:6]) | |
249 | } | |
250 | b = b[6:] | |
251 | ||
252 | r := rune(v) | |
253 | if utf16.IsSurrogate(r) { | |
254 | if len(b) < 6 { | |
255 | return "", b, io.ErrUnexpectedEOF | |
256 | } | |
257 | v, err := strconv.ParseUint(string(b[2:6]), 16, 16) | |
258 | r = utf16.DecodeRune(r, rune(v)) | |
259 | if b[0] != '\\' || b[1] != 'u' || r == unicode.ReplacementChar || err != nil { | |
260 | return "", b, fmt.Errorf("invalid escape code %q in string", b[:6]) | |
261 | } | |
262 | b = b[6:] | |
263 | } | |
264 | s = append(s, string(r)...) | |
265 | default: | |
266 | return "", b, fmt.Errorf("invalid escape code %q in string", b[:2]) | |
267 | } | |
268 | default: | |
269 | i := indexNeedEscape(b[n:]) | |
270 | b, s = b[n+i:], append(s, b[:n+i]...) | |
271 | } | |
272 | } | |
273 | return "", b, io.ErrUnexpectedEOF | |
274 | } | |
275 | ||
276 | // decodeNumber unmarshals the next JSON number per RFC 7159, section 6. | |
277 | // It consume the leading value, and returns the remaining bytes. | |
278 | func decodeNumber(b []byte) (float64, []byte, error) { | |
279 | b = trimSpace(b) | |
280 | b0 := b | |
281 | if len(b) > 0 && b[0] == '-' { | |
282 | b = b[1:] | |
283 | } | |
284 | switch { | |
285 | case len(b) == 0: | |
286 | return 0, b, io.ErrUnexpectedEOF | |
287 | case b[0] == '0': | |
288 | b = b[1:] | |
289 | case '1' <= b[0] && b[0] <= '9': | |
290 | b = b[1:] | |
291 | for len(b) > 0 && ('0' <= b[0] && b[0] <= '9') { | |
292 | b = b[1:] | |
293 | } | |
294 | default: | |
295 | return 0, nil, errors.New("expected digit character in JSON number") | |
296 | } | |
297 | if len(b) > 0 && b[0] == '.' { | |
298 | b = b[1:] | |
299 | switch { | |
300 | case len(b) == 0: | |
301 | return 0, b, io.ErrUnexpectedEOF | |
302 | case '0' <= b[0] && b[0] <= '9': | |
303 | b = b[1:] | |
304 | default: | |
305 | return 0, nil, errors.New("expected digit character in JSON number") | |
306 | } | |
307 | for len(b) > 0 && ('0' <= b[0] && b[0] <= '9') { | |
308 | b = b[1:] | |
309 | } | |
310 | } | |
311 | if len(b) > 0 && (b[0] == 'e' || b[0] == 'E') { | |
312 | b = b[1:] | |
313 | if len(b) > 0 && (b[0] == '-' || b[0] == '+') { | |
314 | b = b[1:] | |
315 | } | |
316 | switch { | |
317 | case len(b) == 0: | |
318 | return 0, b, io.ErrUnexpectedEOF | |
319 | case '0' <= b[0] && b[0] <= '9': | |
320 | b = b[1:] | |
321 | default: | |
322 | return 0, nil, errors.New("expected digit character in JSON number") | |
323 | } | |
324 | for len(b) > 0 && ('0' <= b[0] && b[0] <= '9') { | |
325 | b = b[1:] | |
326 | } | |
327 | } | |
328 | ||
329 | f, err := strconv.ParseFloat(string(b0[:len(b0)-len(b)]), 64) | |
330 | if err != nil { | |
331 | return 0, b, fmt.Errorf("invalid JSON number: %s", b0[:len(b0)-len(b)]) | |
332 | } | |
333 | return f, b, nil | |
334 | } | |
335 | ||
336 | // trimSpace strips leading whitespace per RFC 7159, section 2. | |
337 | func trimSpace(b []byte) []byte { | |
338 | return bytes.TrimLeft(b, " \t\n\r") | |
339 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "bytes" | |
8 | "math" | |
9 | "strconv" | |
10 | ) | |
11 | ||
12 | // BUG(https://golang.org/issue/29491): On Go 1.12 and earlier, float formatting | |
13 | // produces incorrect results in some rare cases due to a rounding error. | |
14 | ||
15 | // formatES6 appends a canonically formatted float according to ES6, | |
16 | // formally defined in ECMA-262, 6th edition, section 7.1.12.1. | |
17 | // This implements the formatting specified in RFC 8785, section 3.2.2.3, | |
18 | // except that this handles NaNs and Infinity. | |
19 | func formatES6(b []byte, m float64) []byte { | |
20 | switch { | |
21 | // 1. If m is NaN, return the String "NaN". | |
22 | case math.IsNaN(m): | |
23 | return append(b, "NaN"...) | |
24 | // 2. If m is +0 or -0, return the String "0". | |
25 | case m == 0: | |
26 | return append(b, "0"...) | |
27 | // 3. If m is less than zero, return the String concatenation of the String "-" and ToString(-m). | |
28 | case m < 0: | |
29 | return formatES6(append(b, '-'), -m) | |
30 | // 4. If m is +∞, return the String "Infinity". | |
31 | case math.IsInf(m, +1): | |
32 | return append(b, "Infinity"...) | |
33 | } | |
34 | ||
35 | // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10^(k-1) ≤ s < 10^k, | |
36 | // the Number value for s ⨯ 10^(n-k) is m, and k is as small as possible. | |
37 | // If there are multiple possibilities for s, choose the value of s for which s ⨯ 10^(n-k) | |
38 | // is closest in value to m. If there are two such possible values of s, choose the one that is even. | |
39 | // Note that k is the number of digits in the decimal representation of s | |
40 | // and that s is not divisible by 10. | |
41 | var n, k int64 | |
42 | var s []byte // decimal representation of s | |
43 | { | |
44 | // Unfortunately strconv.FormatFloat does not directly expose n, k, s, | |
45 | // nor is the output defined as stable in any way. | |
46 | // However, it's implementation is guaranteed to produce precise n, k, s values. | |
47 | // Format a float with the 'e' format and derive n, k, s from the output. | |
48 | var arr [32]byte | |
49 | b := strconv.AppendFloat(arr[:0], m, 'e', -1, 64) // e.g., "d.dddde±dd" | |
50 | ||
51 | // Parse the exponent. | |
52 | i := bytes.IndexByte(b, 'e') | |
53 | nk, err := strconv.ParseInt(string(b[i+len("e"):]), 10, 64) // i.e., n-k | |
54 | if err != nil { | |
55 | panic("BUG: unexpected strconv.ParseInt error: " + err.Error()) | |
56 | } | |
57 | ||
58 | // Format the significand. | |
59 | s = b[:i] | |
60 | if len(b) > 1 && b[1] == '.' { | |
61 | s[1], s = s[0], s[1:] // e.g., "d.dddd" => "ddddd" | |
62 | for len(s) > 1 && s[len(s)-1] == '0' { | |
63 | s = s[:len(s)-1] // trim trailing zeros | |
64 | } | |
65 | nk -= int64(len(s) - 1) | |
66 | } | |
67 | ||
68 | k = int64(len(s)) // k is the number of digits in the decimal representation of s | |
69 | n = nk + k // nk=n-k => n=nk+k | |
70 | } | |
71 | ||
72 | const zeros = "000000000000000000000" | |
73 | switch { | |
74 | // 6. If k ≤ n ≤ 21, … | |
75 | case k <= n && n <= 21: | |
76 | // … return the String consisting of the code units of the k digits of | |
77 | // the decimal representation of s (in order, with no leading zeros), … | |
78 | b = append(b, s...) | |
79 | // … followed by n-k occurrences of the code unit 0x0030 (DIGIT ZERO). | |
80 | b = append(b, zeros[:n-k]...) | |
81 | ||
82 | // 7. If 0 < n ≤ 21, … | |
83 | case 0 < n && n <= 21: | |
84 | // … return the String consisting of the code units of the | |
85 | // most significant n digits of the decimal representation of s, … | |
86 | b = append(b, s[:n]...) | |
87 | // … followed by the code unit 0x002E (FULL STOP), … | |
88 | b = append(b, '.') | |
89 | // … followed by the code units of the remaining k-n digits of the decimal representation of s. | |
90 | b = append(b, s[n:]...) | |
91 | ||
92 | // 8. If -6 < n ≤ 0, … | |
93 | case -6 < n && n <= 0: | |
94 | // … return the String consisting of the code unit 0x0030 (DIGIT ZERO), … | |
95 | b = append(b, '0') | |
96 | // … followed by the code unit 0x002E (FULL STOP), … | |
97 | b = append(b, '.') | |
98 | // … followed by -n occurrences of the code unit 0x0030 (DIGIT ZERO), … | |
99 | b = append(b, zeros[:-n]...) | |
100 | // … followed by the code units of the k digits of the decimal representation of s. | |
101 | b = append(b, s...) | |
102 | ||
103 | // 9. If k = 1, … | |
104 | case k == 1: | |
105 | // … return the String consisting of the code unit of the single digit of s, … | |
106 | b = append(b, s...) | |
107 | // … followed by code unit 0x0065 (LATIN SMALL LETTER E), … | |
108 | b = append(b, 'e') | |
109 | // … followed by code unit 0x002B (PLUS SIGN) or the code unit 0x002D (HYPHEN-MINUS) according to whether n-1 is positive or negative, … | |
110 | // … followed by the code units of the decimal representation of the integer abs(n-1) (with no leading zeroes). | |
111 | switch { | |
112 | case n-1 > 0: | |
113 | b = strconv.AppendInt(append(b, '+'), n-1, 10) | |
114 | case n-1 < 0: | |
115 | b = strconv.AppendInt(append(b, '-'), 1-n, 10) | |
116 | } | |
117 | ||
118 | // 10. Otherwise … | |
119 | default: | |
120 | // … return the String consisting of the code units of the | |
121 | // most significant digit of the decimal representation of s, … | |
122 | b = append(b, s[0]) | |
123 | // … followed by code unit 0x002E (FULL STOP), … | |
124 | b = append(b, '.') | |
125 | // … followed by the code units of the remaining k-1 digits of the decimal representation of s, … | |
126 | b = append(b, s[1:]...) | |
127 | // … followed by code unit 0x0065 (LATIN SMALL LETTER E), … | |
128 | b = append(b, 'e') | |
129 | // … followed by code unit 0x002B (PLUS SIGN) or the code unit 0x002D (HYPHEN-MINUS) according to whether n-1 is positive or negative, … | |
130 | // … followed by the code units of the decimal representation of the integer abs(n-1) (with no leading zeroes). | |
131 | switch { | |
132 | case n-1 > 0: | |
133 | b = strconv.AppendInt(append(b, '+'), n-1, 10) | |
134 | case n-1 < 0: | |
135 | b = strconv.AppendInt(append(b, '-'), 1-n, 10) | |
136 | } | |
137 | } | |
138 | return b | |
139 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "math" | |
8 | "testing" | |
9 | ) | |
10 | ||
11 | func TestFormatES6(t *testing.T) { | |
12 | tests := []struct { | |
13 | in float64 | |
14 | want string | |
15 | }{ | |
16 | {math.NaN(), "NaN"}, | |
17 | {math.Inf(-1), "-Infinity"}, | |
18 | {math.Inf(+1), "Infinity"}, | |
19 | {math.Copysign(0, -1), "0"}, | |
20 | {math.Copysign(0, +1), "0"}, | |
21 | ||
22 | // The following cases are from RFC 8785, Appendix B. | |
23 | {math.Float64frombits(0x0000000000000000), "0"}, | |
24 | {math.Float64frombits(0x8000000000000000), "0"}, | |
25 | {math.Float64frombits(0x0000000000000001), "5e-324"}, | |
26 | {math.Float64frombits(0x8000000000000001), "-5e-324"}, | |
27 | {math.Float64frombits(0x7fefffffffffffff), "1.7976931348623157e+308"}, | |
28 | {math.Float64frombits(0xffefffffffffffff), "-1.7976931348623157e+308"}, | |
29 | {math.Float64frombits(0x4340000000000000), "9007199254740992"}, | |
30 | {math.Float64frombits(0xc340000000000000), "-9007199254740992"}, | |
31 | {math.Float64frombits(0x4430000000000000), "295147905179352830000"}, | |
32 | {math.Float64frombits(0x7fffffffffffffff), "NaN"}, | |
33 | {math.Float64frombits(0x7ff0000000000000), "Infinity"}, | |
34 | {math.Float64frombits(0x44b52d02c7e14af5), "9.999999999999997e+22"}, | |
35 | {math.Float64frombits(0x44b52d02c7e14af6), "1e+23"}, | |
36 | {math.Float64frombits(0x44b52d02c7e14af7), "1.0000000000000001e+23"}, | |
37 | {math.Float64frombits(0x444b1ae4d6e2ef4e), "999999999999999700000"}, | |
38 | {math.Float64frombits(0x444b1ae4d6e2ef4f), "999999999999999900000"}, | |
39 | {math.Float64frombits(0x444b1ae4d6e2ef50), "1e+21"}, | |
40 | {math.Float64frombits(0x3eb0c6f7a0b5ed8c), "9.999999999999997e-7"}, | |
41 | {math.Float64frombits(0x3eb0c6f7a0b5ed8d), "0.000001"}, | |
42 | {math.Float64frombits(0x41b3de4355555553), "333333333.3333332"}, | |
43 | {math.Float64frombits(0x41b3de4355555554), "333333333.33333325"}, | |
44 | {math.Float64frombits(0x41b3de4355555555), "333333333.3333333"}, | |
45 | {math.Float64frombits(0x41b3de4355555556), "333333333.3333334"}, | |
46 | {math.Float64frombits(0x41b3de4355555557), "333333333.33333343"}, | |
47 | {math.Float64frombits(0xbecbf647612f3696), "-0.0000033333333333333333"}, | |
48 | {math.Float64frombits(0x43143ff3c1cb0959), "1424953923781206.2"}, | |
49 | } | |
50 | ||
51 | for _, tt := range tests { | |
52 | got := string(formatES6(nil, tt.in)) | |
53 | if got != tt.want { | |
54 | t.Errorf("formatES6(%016x) = %v, want %v", math.Float64bits(tt.in), got, tt.want) | |
55 | } | |
56 | } | |
57 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | // Package jsoncs implements of the JSON Canonicalization Scheme (JCS) | |
5 | // as specified in RFC 8785. | |
6 | package jsoncs | |
7 | ||
8 | import ( | |
9 | "bytes" | |
10 | "errors" | |
11 | "fmt" | |
12 | "math" | |
13 | "math/bits" | |
14 | "sort" | |
15 | "strconv" | |
16 | "unicode/utf8" | |
17 | ) | |
18 | ||
19 | // Format transforms the JSON input to its canonical form. | |
20 | // The input must comply with RFC 7493. | |
21 | // It reuses the provided input buffer. | |
22 | func Format(b []byte) ([]byte, error) { | |
23 | if Valid(b) { | |
24 | return b, nil | |
25 | } | |
26 | v, err := decode(b) | |
27 | if err != nil { | |
28 | return nil, err | |
29 | } | |
30 | return formatValue(b[:0], v) | |
31 | } | |
32 | ||
33 | // formatValue canonically marshals a JSON value. | |
34 | func formatValue(b []byte, v jsonValue) ([]byte, error) { | |
35 | switch v := v.(type) { | |
36 | case jsonObject: | |
37 | return formatObject(b, v) | |
38 | case jsonArray: | |
39 | return formatArray(b, v) | |
40 | case string: | |
41 | return formatString(b, v) | |
42 | case float64: | |
43 | return formatNumber(b, v) | |
44 | case nil: | |
45 | return append(b, nullLiteral...), nil | |
46 | case bool: | |
47 | switch v { | |
48 | case true: | |
49 | return append(b, trueLiteral...), nil | |
50 | case false: | |
51 | return append(b, falseLiteral...), nil | |
52 | } | |
53 | } | |
54 | return nil, fmt.Errorf("invalid type: %T", v) | |
55 | } | |
56 | ||
57 | // formatObject canonically marshals a JSON object per RFC 8785, section 3.2.3. | |
58 | func formatObject(b []byte, obj jsonObject) ([]byte, error) { | |
59 | var ks []string | |
60 | for k := range obj { | |
61 | ks = append(ks, k) | |
62 | } | |
63 | sort.Slice(ks, func(i, j int) bool { | |
64 | return lessUTF16(ks[i], ks[j]) | |
65 | }) | |
66 | ||
67 | var err error | |
68 | b = append(b, '{') | |
69 | for _, k := range ks { | |
70 | b, err = formatString(b, k) | |
71 | if err != nil { | |
72 | return nil, err | |
73 | } | |
74 | b = append(b, ':') | |
75 | b, err = formatValue(b, obj[k]) | |
76 | if err != nil { | |
77 | return nil, err | |
78 | } | |
79 | b = append(b, ',') | |
80 | } | |
81 | b = bytes.TrimRight(b, ",") | |
82 | b = append(b, '}') | |
83 | return b, nil | |
84 | } | |
85 | ||
86 | // formatArray canonically marshals a JSON array. | |
87 | func formatArray(b []byte, arr jsonArray) ([]byte, error) { | |
88 | var err error | |
89 | b = append(b, '[') | |
90 | for _, v := range arr { | |
91 | b, err = formatValue(b, v) | |
92 | if err != nil { | |
93 | return nil, err | |
94 | } | |
95 | b = append(b, ',') | |
96 | } | |
97 | b = bytes.TrimRight(b, ",") | |
98 | b = append(b, ']') | |
99 | return b, nil | |
100 | } | |
101 | ||
102 | // formatArray canonically marshals a JSON string per RFC 8785, section 3.2.2.2. | |
103 | func formatString(b []byte, s string) ([]byte, error) { | |
104 | // indexNeedEscape returns the index of the character that needs escaping. | |
105 | indexNeedEscape := func(s string) int { | |
106 | for i, r := range s { | |
107 | if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError { | |
108 | return i | |
109 | } | |
110 | } | |
111 | return len(s) | |
112 | } | |
113 | ||
114 | b = append(b, '"') | |
115 | i := indexNeedEscape(s) | |
116 | s, b = s[i:], append(b, s[:i]...) | |
117 | for len(s) > 0 { | |
118 | switch r, n := utf8.DecodeRuneInString(s); { | |
119 | case r == utf8.RuneError && n == 1: | |
120 | return nil, errors.New("invalid UTF-8 in string") | |
121 | case r < ' ' || r == '"' || r == '\\': | |
122 | b = append(b, '\\') | |
123 | switch r { | |
124 | case '"', '\\': | |
125 | b = append(b, byte(r)) | |
126 | case '\b': | |
127 | b = append(b, 'b') | |
128 | case '\f': | |
129 | b = append(b, 'f') | |
130 | case '\n': | |
131 | b = append(b, 'n') | |
132 | case '\r': | |
133 | b = append(b, 'r') | |
134 | case '\t': | |
135 | b = append(b, 't') | |
136 | default: | |
137 | b = append(b, 'u') | |
138 | b = append(b, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...) | |
139 | b = strconv.AppendUint(b, uint64(r), 16) | |
140 | } | |
141 | s = s[n:] | |
142 | default: | |
143 | i := indexNeedEscape(s[n:]) | |
144 | s, b = s[n+i:], append(b, s[:n+i]...) | |
145 | } | |
146 | } | |
147 | b = append(b, '"') | |
148 | return b, nil | |
149 | } | |
150 | ||
151 | // formatNumber canonically marshals a JSON number per RFC 8785, section 3.2.2.3. | |
152 | func formatNumber(b []byte, f float64) ([]byte, error) { | |
153 | if math.IsNaN(f) || math.IsInf(f, 0) { | |
154 | return nil, fmt.Errorf("invalid float value: %v", f) | |
155 | } | |
156 | return formatES6(b, f), nil | |
157 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "io" | |
8 | "strings" | |
9 | "testing" | |
10 | "unicode/utf8" | |
11 | ||
12 | "github.com/google/go-cmp/cmp" | |
13 | "github.com/google/go-cmp/cmp/cmpopts" | |
14 | ) | |
15 | ||
16 | type containsError string | |
17 | ||
18 | func (e containsError) Error() string { | |
19 | return string(e) | |
20 | } | |
21 | func (e containsError) Is(e2 error) bool { | |
22 | if e2 == nil { | |
23 | return false | |
24 | } | |
25 | return strings.Contains(e2.Error(), string(e)) | |
26 | } | |
27 | ||
28 | func TestFormat(t *testing.T) { | |
29 | tests := []struct { | |
30 | inJSON string | |
31 | wantJSON string | |
32 | wantErr error | |
33 | }{{ | |
34 | inJSON: "", | |
35 | wantErr: io.ErrUnexpectedEOF, | |
36 | }, { | |
37 | inJSON: "?", | |
38 | wantErr: containsError("expected next JSON value"), | |
39 | }, { | |
40 | inJSON: "null", | |
41 | wantJSON: "null", | |
42 | }, { | |
43 | inJSON: " \n\r\tnull \n\r\t", | |
44 | wantJSON: "null", | |
45 | }, { | |
46 | inJSON: "nullnull", | |
47 | wantErr: containsError("invalid data"), | |
48 | }, { | |
49 | inJSON: "true", | |
50 | wantJSON: "true", | |
51 | }, { | |
52 | inJSON: " \n\r\ttrue \n\r\t", | |
53 | wantJSON: "true", | |
54 | }, { | |
55 | inJSON: "truetrue", | |
56 | wantErr: containsError("invalid data"), | |
57 | }, { | |
58 | inJSON: "false", | |
59 | wantJSON: "false", | |
60 | }, { | |
61 | inJSON: " \n\r\tfalse \n\r\t", | |
62 | wantJSON: "false", | |
63 | }, { | |
64 | inJSON: "falsefalse", | |
65 | wantErr: containsError("invalid data"), | |
66 | }, { | |
67 | inJSON: "0", | |
68 | wantJSON: "0", | |
69 | }, { | |
70 | inJSON: "-0", | |
71 | wantJSON: "0", | |
72 | }, { | |
73 | inJSON: "1", | |
74 | wantJSON: "1", | |
75 | }, { | |
76 | inJSON: "-1", | |
77 | wantJSON: "-1", | |
78 | }, { | |
79 | inJSON: "0.1", | |
80 | wantJSON: "0.1", | |
81 | }, { | |
82 | inJSON: "-0.1", | |
83 | wantJSON: "-0.1", | |
84 | }, { | |
85 | inJSON: "1234", | |
86 | wantJSON: "1234", | |
87 | }, { | |
88 | inJSON: "-1234", | |
89 | wantJSON: "-1234", | |
90 | }, { | |
91 | inJSON: "12.34", | |
92 | wantJSON: "12.34", | |
93 | }, { | |
94 | inJSON: "-12.34", | |
95 | wantJSON: "-12.34", | |
96 | }, { | |
97 | inJSON: "12E0", | |
98 | wantJSON: "12", | |
99 | }, { | |
100 | inJSON: "12E1", | |
101 | wantJSON: "120", | |
102 | }, { | |
103 | inJSON: "12e34", | |
104 | wantJSON: "1.2e+35", | |
105 | }, { | |
106 | inJSON: "12E-0", | |
107 | wantJSON: "12", | |
108 | }, { | |
109 | inJSON: "12e+1", | |
110 | wantJSON: "120", | |
111 | }, { | |
112 | inJSON: "12e-34", | |
113 | wantJSON: "1.2e-33", | |
114 | }, { | |
115 | inJSON: "-12E0", | |
116 | wantJSON: "-12", | |
117 | }, { | |
118 | inJSON: "-12E1", | |
119 | wantJSON: "-120", | |
120 | }, { | |
121 | inJSON: "-12e34", | |
122 | wantJSON: "-1.2e+35", | |
123 | }, { | |
124 | inJSON: "-12E-0", | |
125 | wantJSON: "-12", | |
126 | }, { | |
127 | inJSON: "-12e+1", | |
128 | wantJSON: "-120", | |
129 | }, { | |
130 | inJSON: "-12e-34", | |
131 | wantJSON: "-1.2e-33", | |
132 | }, { | |
133 | inJSON: "1.2E0", | |
134 | wantJSON: "1.2", | |
135 | }, { | |
136 | inJSON: "1.2E1", | |
137 | wantJSON: "12", | |
138 | }, { | |
139 | inJSON: "1.2e34", | |
140 | wantJSON: "1.2e+34", | |
141 | }, { | |
142 | inJSON: "1.2E-0", | |
143 | wantJSON: "1.2", | |
144 | }, { | |
145 | inJSON: "1.2e+1", | |
146 | wantJSON: "12", | |
147 | }, { | |
148 | inJSON: "1.2e-34", | |
149 | wantJSON: "1.2e-34", | |
150 | }, { | |
151 | inJSON: "-1.2E0", | |
152 | wantJSON: "-1.2", | |
153 | }, { | |
154 | inJSON: "-1.2E1", | |
155 | wantJSON: "-12", | |
156 | }, { | |
157 | inJSON: "-1.2e34", | |
158 | wantJSON: "-1.2e+34", | |
159 | }, { | |
160 | inJSON: "-1.2E-0", | |
161 | wantJSON: "-1.2", | |
162 | }, { | |
163 | inJSON: "-1.2e+1", | |
164 | wantJSON: "-12", | |
165 | }, { | |
166 | inJSON: "-1.2e-34", | |
167 | wantJSON: "-1.2e-34", | |
168 | }, { | |
169 | inJSON: "0E0", | |
170 | wantJSON: "0", | |
171 | }, { | |
172 | inJSON: "0E1", | |
173 | wantJSON: "0", | |
174 | }, { | |
175 | inJSON: "0e34", | |
176 | wantJSON: "0", | |
177 | }, { | |
178 | inJSON: "0E-0", | |
179 | wantJSON: "0", | |
180 | }, { | |
181 | inJSON: "0e+1", | |
182 | wantJSON: "0", | |
183 | }, { | |
184 | inJSON: "0e-34", | |
185 | wantJSON: "0", | |
186 | }, { | |
187 | inJSON: "-0E0", | |
188 | wantJSON: "0", | |
189 | }, { | |
190 | inJSON: "-0E1", | |
191 | wantJSON: "0", | |
192 | }, { | |
193 | inJSON: "-0e34", | |
194 | wantJSON: "0", | |
195 | }, { | |
196 | inJSON: "-0E-0", | |
197 | wantJSON: "0", | |
198 | }, { | |
199 | inJSON: "-0e+1", | |
200 | wantJSON: "0", | |
201 | }, { | |
202 | inJSON: "-0e-34", | |
203 | wantJSON: "0", | |
204 | }, { | |
205 | inJSON: "12345678901234567890", | |
206 | wantJSON: "12345678901234567000", | |
207 | }, { | |
208 | inJSON: "-123456789.0123456789e+0123", | |
209 | wantJSON: "-1.2345678901234568e+131", | |
210 | }, { | |
211 | inJSON: " \n\r\t-123456789.0123456789e+0123 \n\r\t", | |
212 | wantJSON: "-1.2345678901234568e+131", | |
213 | }, { | |
214 | inJSON: "- 123456789.0123456789e+0123", | |
215 | wantErr: containsError("expected digit character in JSON number"), | |
216 | }, { | |
217 | inJSON: "-123456789 .0123456789e+0123", | |
218 | wantErr: containsError("invalid data"), | |
219 | }, { | |
220 | inJSON: "-123456789. 0123456789e+0123", | |
221 | wantErr: containsError("expected digit character in JSON number"), | |
222 | }, { | |
223 | inJSON: "-123456789.0123456789 e+0123", | |
224 | wantErr: containsError("invalid data"), | |
225 | }, { | |
226 | inJSON: "-123456789.0123456789e +0123", | |
227 | wantErr: containsError("expected digit character in JSON number"), | |
228 | }, { | |
229 | inJSON: "-123456789.0123456789e+ 0123", | |
230 | wantErr: containsError("expected digit character in JSON number"), | |
231 | }, { | |
232 | inJSON: "+0", | |
233 | wantErr: containsError("expected next JSON value"), | |
234 | }, { | |
235 | inJSON: "00", | |
236 | wantErr: containsError("invalid data"), | |
237 | }, { | |
238 | inJSON: "01", | |
239 | wantErr: containsError("invalid data"), | |
240 | }, { | |
241 | inJSON: "0f", | |
242 | wantErr: containsError("invalid data"), | |
243 | }, { | |
244 | inJSON: "0.e0", | |
245 | wantErr: containsError("expected digit character in JSON number"), | |
246 | }, { | |
247 | inJSON: "0.0E0", | |
248 | wantJSON: "0", | |
249 | }, { | |
250 | inJSON: "0.0E+0", | |
251 | wantJSON: "0", | |
252 | }, { | |
253 | inJSON: "0.0E-0", | |
254 | wantJSON: "0", | |
255 | }, { | |
256 | inJSON: "0.0E+0", | |
257 | wantJSON: "0", | |
258 | }, { | |
259 | inJSON: "0.0E-0", | |
260 | wantJSON: "0", | |
261 | }, { | |
262 | inJSON: "0.0E0.", | |
263 | wantErr: containsError("invalid data"), | |
264 | }, { | |
265 | inJSON: `""`, | |
266 | wantJSON: `""`, | |
267 | }, { | |
268 | inJSON: " \n\r\t\"\" \n\r\t", | |
269 | wantJSON: `""`, | |
270 | }, { | |
271 | inJSON: " \n\r\t\"hello\" \n\r\t", | |
272 | wantJSON: `"hello"`, | |
273 | }, { | |
274 | inJSON: "\"\x00\"", | |
275 | wantErr: containsError("invalid character"), | |
276 | }, { | |
277 | inJSON: "\"\xff\"", | |
278 | wantErr: containsError("invalid UTF-8"), | |
279 | }, { | |
280 | inJSON: `"` + string(utf8.RuneError) + `"`, | |
281 | wantJSON: `"` + string(utf8.RuneError) + `"`, | |
282 | }, { | |
283 | inJSON: `"\uFFFD"`, | |
284 | wantJSON: "\"\uFFFD\"", | |
285 | }, { | |
286 | inJSON: `"\x"`, | |
287 | wantErr: containsError("invalid escape code"), | |
288 | }, { | |
289 | inJSON: `"\uXXXX"`, | |
290 | wantErr: containsError("invalid escape code"), | |
291 | }, { | |
292 | inJSON: `"\uDEAD"`, // unmatched surrogate pair | |
293 | wantErr: io.ErrUnexpectedEOF, | |
294 | }, { | |
295 | inJSON: `"\uDEAD______"`, // unmatched surrogate pair | |
296 | wantErr: containsError("invalid escape code"), | |
297 | }, { | |
298 | inJSON: `"\uDEAD\uBEEF"`, // invalid surrogate half | |
299 | wantErr: containsError("invalid escape code"), | |
300 | }, { | |
301 | inJSON: `"\uD800\udead"`, // valid surrogate pair | |
302 | wantJSON: `"𐊭"`, | |
303 | }, { | |
304 | inJSON: `"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f"`, | |
305 | wantJSON: `"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f"`, | |
306 | }, { | |
307 | inJSON: `"\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"`, | |
308 | wantJSON: `"\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"`, | |
309 | }, { | |
310 | inJSON: `"\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f"`, | |
311 | wantJSON: `" !\"#$%&'()*+,-./"`, | |
312 | }, { | |
313 | inJSON: `"\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f"`, | |
314 | wantJSON: `"0123456789:;<=>?"`, | |
315 | }, { | |
316 | inJSON: `"\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f"`, | |
317 | wantJSON: `"@ABCDEFGHIJKLMNO"`, | |
318 | }, { | |
319 | inJSON: `"\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005a\u005b\u005c\u005d\u005e\u005f"`, | |
320 | wantJSON: `"PQRSTUVWXYZ[\\]^_"`, | |
321 | }, { | |
322 | inJSON: `"\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f"`, | |
323 | wantJSON: "\"`abcdefghijklmno\"", | |
324 | }, { | |
325 | inJSON: `"\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u007f"`, | |
326 | wantJSON: "\"pqrstuvwxyz{|}~\u007f\"", | |
327 | }, { | |
328 | inJSON: `"\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f"`, | |
329 | wantJSON: "\"\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f\"", | |
330 | }, { | |
331 | inJSON: `"\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f"`, | |
332 | wantJSON: "\"\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f\"", | |
333 | }, { | |
334 | inJSON: `"\u00a0\u00a1\u00a2\u00a3\u00a4\u00a5\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ad\u00ae\u00af"`, | |
335 | wantJSON: "\"\u00a0¡¢£¤¥¦§¨©ª«¬\u00ad®¯\"", | |
336 | }, { | |
337 | inJSON: `"\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf"`, | |
338 | wantJSON: `"°±²³´µ¶·¸¹º»¼½¾¿"`, | |
339 | }, { | |
340 | inJSON: `"\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf"`, | |
341 | wantJSON: `"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"`, | |
342 | }, { | |
343 | inJSON: `"\u00d0\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d7\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00de\u00df"`, | |
344 | wantJSON: `"ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß"`, | |
345 | }, { | |
346 | inJSON: `"\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef"`, | |
347 | wantJSON: `"àáâãäåæçèéêëìíîï"`, | |
348 | }, { | |
349 | inJSON: `"\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00fe\u00ff"`, | |
350 | wantJSON: `"ðñòóôõö÷øùúûüýþÿ"`, | |
351 | }, { | |
352 | inJSON: `"בְּרֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃"`, | |
353 | wantJSON: `"בְּרֵאשִׁ֖ית בָּרָ֣א אֱלֹהִ֑ים אֵ֥ת הַשָּׁמַ֖יִם וְאֵ֥ת הָאָֽרֶץ׃"`, | |
354 | }, { | |
355 | inJSON: `"ἐν ἀρχῇ ἐποίησεν ὁ θεὸς τὸν οὐρανὸν καὶ τὴν γῆν"`, | |
356 | wantJSON: `"ἐν ἀρχῇ ἐποίησεν ὁ θεὸς τὸν οὐρανὸν καὶ τὴν γῆν"`, | |
357 | }, { | |
358 | inJSON: `"في البدء خلق الله السموات والارض."`, | |
359 | wantJSON: `"في البدء خلق الله السموات والارض."`, | |
360 | }, { | |
361 | inJSON: `"起 初 , 神 创 造 天 地 。"`, | |
362 | wantJSON: `"起 初 , 神 创 造 天 地 。"`, | |
363 | }, { | |
364 | inJSON: `"ในเริ่มแรกนั้นพระเจ้าทรงเนรมิตสร้างฟ้าและแผ่นดินโลก"`, | |
365 | wantJSON: `"ในเริ่มแรกนั้นพระเจ้าทรงเนรมิตสร้างฟ้าและแผ่นดินโลก"`, | |
366 | }, { | |
367 | inJSON: "[]", | |
368 | wantJSON: "[]", | |
369 | }, { | |
370 | inJSON: " \n\r\t[ \n\r\t] \n\r\t", | |
371 | wantJSON: "[]", | |
372 | }, { | |
373 | inJSON: " \n\r\t[ \n\r\t0 \n\r\t] \n\r\t", | |
374 | wantJSON: "[0]", | |
375 | }, { | |
376 | inJSON: " \n\r\t[ \n\r\t0 \n\r\t, \n\r\t1 \n\r\t] \n\r\t", | |
377 | wantJSON: "[0,1]", | |
378 | }, { | |
379 | inJSON: "[0{}]", | |
380 | wantErr: containsError("expected ',' character in JSON array"), | |
381 | }, { | |
382 | inJSON: "[][]", | |
383 | wantErr: containsError("invalid data after top-level value"), | |
384 | }, { | |
385 | inJSON: "{}", | |
386 | wantJSON: "{}", | |
387 | }, { | |
388 | inJSON: " \n\r\t{ \n\r\t} \n\r\t", | |
389 | wantJSON: "{}", | |
390 | }, { | |
391 | inJSON: " \n\r\t{ \n\r\t\"0\" \n\r\t: \n\r\t0 \n\r\t} \n\r\t", | |
392 | wantJSON: `{"0":0}`, | |
393 | }, { | |
394 | inJSON: " \n\r\t{ \n\r\t\"0\" \n\r\t: \n\r\t0 \n\r\t, \n\r\t\"1\" \n\r\t: \n\r\t1 \n\r\t} \n\r\t", | |
395 | wantJSON: `{"0":0,"1":1}`, | |
396 | }, { | |
397 | inJSON: `{"k"}`, | |
398 | wantErr: containsError("expected ':' character in JSON object"), | |
399 | }, { | |
400 | inJSON: `{"k":0,}`, | |
401 | wantErr: containsError("expected '\"' character in JSON string"), | |
402 | }, { | |
403 | inJSON: `{"k":0[]}`, | |
404 | wantErr: containsError("expected ',' character in JSON object"), | |
405 | }, { | |
406 | inJSON: `{"k":0,"\u006b":0}`, | |
407 | wantErr: containsError("duplicate key \"k\" in JSON object"), | |
408 | }, { | |
409 | inJSON: "{}{}", | |
410 | wantErr: containsError("invalid data after top-level value"), | |
411 | }, { | |
412 | inJSON: `[ | |
413 | 56, | |
414 | { | |
415 | "d": true, | |
416 | "10": null, | |
417 | "1": [ ] | |
418 | } | |
419 | ]`, | |
420 | wantJSON: `[56,{"1":[],"10":null,"d":true}]`, | |
421 | }, { | |
422 | inJSON: `{ | |
423 | "peach": "This sorting order", | |
424 | "péché": "is wrong according to French", | |
425 | "pêche": "but canonicalization MUST", | |
426 | "sin": "ignore locale" | |
427 | }`, | |
428 | wantJSON: `{"peach":"This sorting order","péché":"is wrong according to French","pêche":"but canonicalization MUST","sin":"ignore locale"}`, | |
429 | }, { | |
430 | inJSON: `{ | |
431 | "1": {"f": {"f": "hi","F": 5} ,"\n": 56.0}, | |
432 | "10": { }, | |
433 | "": "empty", | |
434 | "a": { }, | |
435 | "111": [ {"e": "yes","E": "no" } ], | |
436 | "A": { } | |
437 | }`, | |
438 | wantJSON: `{"":"empty","1":{"\n":56,"f":{"F":5,"f":"hi"}},"10":{},"111":[{"E":"no","e":"yes"}],"A":{},"a":{}}`, | |
439 | }, { | |
440 | inJSON: `{ | |
441 | "Unnormalized Unicode":"A\u030a" | |
442 | }`, | |
443 | wantJSON: `{"Unnormalized Unicode":"Å"}`, | |
444 | }, { | |
445 | inJSON: `{ | |
446 | "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001], | |
447 | "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", | |
448 | "literals": [null, true, false] | |
449 | }`, | |
450 | wantJSON: `{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}`, | |
451 | }, { | |
452 | inJSON: `{ | |
453 | "\u20ac": "Euro Sign", | |
454 | "\r": "Carriage Return", | |
455 | "\u000a": "Newline", | |
456 | "1": "One", | |
457 | "\u0080": "Control\u007f", | |
458 | "\ud83d\ude02": "Smiley", | |
459 | "\u00f6": "Latin Small Letter O With Diaeresis", | |
460 | "\ufb33": "Hebrew Letter Dalet With Dagesh", | |
461 | "</script>": "Browser Challenge" | |
462 | }`, | |
463 | wantJSON: `{"\n":"Newline","\r":"Carriage Return","1":"One","</script>":"Browser Challenge","` + "\u0080" + `":"Control` + "\u007f" + `","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😂":"Smiley","דּ":"Hebrew Letter Dalet With Dagesh"}`, | |
464 | }} | |
465 | ||
466 | for _, tt := range tests { | |
467 | t.Run("", func(t *testing.T) { | |
468 | t.Logf("Input Data: %q", tt.inJSON) | |
469 | gotJSON, gotErr := Format([]byte(tt.inJSON)) | |
470 | if diff := cmp.Diff(string(tt.wantJSON), string(gotJSON)); diff != "" { | |
471 | t.Errorf("Format output mismatch (-want +got):\n%s", diff) | |
472 | } | |
473 | if diff := cmp.Diff(tt.wantErr, gotErr, cmpopts.EquateErrors()); diff != "" { | |
474 | t.Errorf("Format output mismatch (-want +got):\n%s", diff) | |
475 | } | |
476 | ||
477 | gotValid := Valid([]byte(tt.inJSON)) | |
478 | wantValid := string(tt.inJSON) == string(gotJSON) && gotErr == nil | |
479 | if gotValid != wantValid { | |
480 | t.Errorf("Valid() = %v, want %v", gotValid, wantValid) | |
481 | } | |
482 | }) | |
483 | } | |
484 | } |
0 | github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= | |
1 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |
2 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | |
3 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "unicode/utf16" | |
8 | "unicode/utf8" | |
9 | ) | |
10 | ||
11 | // lessUTF16 reports whether x is lexicographically less than y according | |
12 | // to the UTF-16 codepoints of the UTF-8 encoded input strings. | |
13 | // This implements the ordering specified in RFC 8785, section 3.2.3. | |
14 | func lessUTF16(x, y string) bool { | |
15 | for { | |
16 | if len(x) == 0 || len(y) == 0 { | |
17 | return len(x) < len(y) | |
18 | } | |
19 | ||
20 | // ASCII fast-path. | |
21 | if x[0] < utf8.RuneSelf || y[0] < utf8.RuneSelf { | |
22 | if x[0] != y[0] { | |
23 | return x[0] < y[0] | |
24 | } | |
25 | x, y = x[1:], y[1:] | |
26 | continue | |
27 | } | |
28 | ||
29 | // Decode next pair of runes as UTF-8. | |
30 | rx, nx := utf8.DecodeRuneInString(x) | |
31 | ry, ny := utf8.DecodeRuneInString(y) | |
32 | switch { | |
33 | ||
34 | // Both runes encode as either a single or surrogate pair | |
35 | // of UTF-16 codepoints. | |
36 | case isUTF16Self(rx) == isUTF16Self(ry): | |
37 | if rx != ry { | |
38 | return rx < ry | |
39 | } | |
40 | ||
41 | // The x rune is a single UTF-16 codepoint, while | |
42 | // the y rune is a surrogate pair of UTF-16 codepoints. | |
43 | case isUTF16Self(rx): | |
44 | ry, _ := utf16.EncodeRune(ry) | |
45 | if rx != ry { | |
46 | return rx < ry | |
47 | } | |
48 | panic("invalid UTF-8") // implies rx is an unpaired surrogate half | |
49 | ||
50 | // The y rune is a single UTF-16 codepoint, while | |
51 | // the x rune is a surrogate pair of UTF-16 codepoints. | |
52 | case isUTF16Self(ry): | |
53 | rx, _ := utf16.EncodeRune(rx) | |
54 | if rx != ry { | |
55 | return rx < ry | |
56 | } | |
57 | panic("invalid UTF-8") // implies ry is an unpaired surrogate half | |
58 | } | |
59 | x, y = x[nx:], y[ny:] | |
60 | } | |
61 | } | |
62 | ||
63 | func isUTF16Self(r rune) bool { | |
64 | return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF') | |
65 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "math/rand" | |
8 | "sort" | |
9 | "testing" | |
10 | "unicode/utf16" | |
11 | ||
12 | "github.com/google/go-cmp/cmp" | |
13 | ) | |
14 | ||
15 | func TestLessUTF16(t *testing.T) { | |
16 | want := []string{"", "\r", "1", "\u0080", "\u00f6", "\u20ac", "\U0001f600", "\ufb33"} | |
17 | ||
18 | got1 := append([]string(nil), want...) | |
19 | got2 := append([]string(nil), want...) | |
20 | for i, j := range rand.Perm(len(want)) { | |
21 | got1[i], got1[j] = got1[j], got1[i] | |
22 | got2[i], got2[j] = got2[j], got2[i] | |
23 | } | |
24 | ||
25 | // Sort using optimized lessUTF16 implementation. | |
26 | sort.Slice(got1, func(i, j int) bool { | |
27 | return lessUTF16(got1[i], got1[j]) | |
28 | }) | |
29 | if diff := cmp.Diff(want, got1); diff != "" { | |
30 | t.Errorf("sort.Slice(LessUTF16.Optimized) mismatch (-want +got)\n%s", diff) | |
31 | } | |
32 | ||
33 | // Sort using simple, but slow lessUTF16 implementation. | |
34 | lessUTF16 := func(x, y string) bool { | |
35 | ux := utf16.Encode([]rune(x)) | |
36 | uy := utf16.Encode([]rune(y)) | |
37 | for { | |
38 | if len(ux) == 0 || len(uy) == 0 { | |
39 | return len(ux) < len(uy) | |
40 | } | |
41 | if ux[0] != uy[0] { | |
42 | return ux[0] < uy[0] | |
43 | } | |
44 | ux, uy = ux[1:], uy[1:] | |
45 | } | |
46 | } | |
47 | sort.Slice(got2, func(i, j int) bool { | |
48 | return lessUTF16(got2[i], got2[j]) | |
49 | }) | |
50 | if diff := cmp.Diff(want, got2); diff != "" { | |
51 | t.Errorf("sort.Slice(LessUTF16.Simplified) mismatch (-want +got)\n%s", diff) | |
52 | } | |
53 | } |
0 | // Copyright 2020 The Go Authors. All rights reserved. | |
1 | // Use of this source code is governed by a BSD-style | |
2 | // license that can be found in the LICENSE.md file. | |
3 | ||
4 | package jsoncs | |
5 | ||
6 | import ( | |
7 | "bytes" | |
8 | ) | |
9 | ||
10 | // Valid reports whether the JSON input is in canonical form. | |
11 | // Invalid JSON input is reported as false. | |
12 | func Valid(b []byte) bool { | |
13 | b, ok := validValue(b) | |
14 | return ok && len(b) == 0 | |
15 | } | |
16 | ||
17 | // validValue reports whether the next JSON value is in its canonical form. | |
18 | // It consume the leading value, and returns the remaining bytes. | |
19 | func validValue(b []byte) ([]byte, bool) { | |
20 | switch { | |
21 | case len(b) > 0 && b[0] == '{': | |
22 | return validObject(b) | |
23 | case len(b) > 0 && b[0] == '[': | |
24 | return validArray(b) | |
25 | case len(b) > 0 && b[0] == '"': | |
26 | return validString(b) | |
27 | case len(b) > 0 && (b[0] == '-' || ('0' <= b[0] && b[0] <= '9')): | |
28 | return validNumber(b) | |
29 | case bytes.HasPrefix(b, nullLiteral): | |
30 | return b[len(nullLiteral):], true | |
31 | case bytes.HasPrefix(b, trueLiteral): | |
32 | return b[len(trueLiteral):], true | |
33 | case bytes.HasPrefix(b, falseLiteral): | |
34 | return b[len(falseLiteral):], true | |
35 | default: | |
36 | return b, false | |
37 | } | |
38 | } | |
39 | ||
40 | // validObject reports whether the next JSON object is in its canonical form | |
41 | // per RFC 8785, section 3.2.3 regarding object name ordering. | |
42 | // It consume the leading value, and returns the remaining bytes. | |
43 | func validObject(b []byte) ([]byte, bool) { | |
44 | if len(b) == 0 || b[0] != '{' { | |
45 | return b, false | |
46 | } | |
47 | b = b[1:] | |
48 | ||
49 | var init, ok bool | |
50 | var prevKey string | |
51 | for { | |
52 | if len(b) > 0 && b[0] == '}' { | |
53 | return b[1:], true | |
54 | } | |
55 | ||
56 | if init { | |
57 | if len(b) == 0 || b[0] != ',' { | |
58 | return b, false | |
59 | } | |
60 | b = b[1:] | |
61 | } | |
62 | ||
63 | currKey, _, _ := decodeString(b) | |
64 | b, ok = validString(b) | |
65 | if !ok { | |
66 | return b, ok | |
67 | } | |
68 | if init && !lessUTF16(prevKey, currKey) { | |
69 | return b, ok | |
70 | } | |
71 | prevKey = currKey | |
72 | ||
73 | if len(b) == 0 || b[0] != ':' { | |
74 | return b, false | |
75 | } | |
76 | b = b[1:] | |
77 | ||
78 | b, ok = validValue(b) | |
79 | if !ok { | |
80 | return b, ok | |
81 | } | |
82 | ||
83 | init = true | |
84 | } | |
85 | } | |
86 | ||
87 | // validArray reports whether the next JSON array is in its canonical form. | |
88 | // It consume the leading value, and returns the remaining bytes. | |
89 | func validArray(b []byte) ([]byte, bool) { | |
90 | if len(b) == 0 || b[0] != '[' { | |
91 | return b, false | |
92 | } | |
93 | b = b[1:] | |
94 | ||
95 | var init, ok bool | |
96 | for { | |
97 | if len(b) > 0 && b[0] == ']' { | |
98 | return b[1:], true | |
99 | } | |
100 | ||
101 | if init { | |
102 | if len(b) == 0 || b[0] != ',' { | |
103 | return b, false | |
104 | } | |
105 | b = b[1:] | |
106 | } | |
107 | ||
108 | b, ok = validValue(b) | |
109 | if !ok { | |
110 | return b, ok | |
111 | } | |
112 | ||
113 | init = true | |
114 | } | |
115 | } | |
116 | ||
117 | // validString reports whether the next JSON string is in its canonical form | |
118 | // per RFC 8785, section 3.2.2.2. | |
119 | // It consume the leading value, and returns the remaining bytes. | |
120 | func validString(b []byte) ([]byte, bool) { | |
121 | if len(b) == 0 || b[0] != '"' { | |
122 | return b, false | |
123 | } | |
124 | ||
125 | // Fast-path optimization for unescaped ASCII. | |
126 | for b := b[1:]; len(b) > 0; b = b[1:] { | |
127 | if b[0] == '"' { | |
128 | return b[1:], true | |
129 | } | |
130 | if !(0x20 <= b[0] && b[0] < 0x80 && b[0] != '"' && b[0] != '\\') { | |
131 | break | |
132 | } | |
133 | } | |
134 | ||
135 | s, b2, err := decodeString(b) | |
136 | got := b[:len(b)-len(b2)] | |
137 | want, _ := formatString(nil, s) | |
138 | return b2, bytes.Equal(got, want) && err == nil | |
139 | } | |
140 | ||
141 | // validNumber reports whether the next JSON number is in its canonical form | |
142 | // per RFC 8785, section 3.2.2.3. | |
143 | // It consume the leading value, and returns the remaining bytes. | |
144 | func validNumber(b []byte) ([]byte, bool) { | |
145 | if len(b) == 0 || !(b[0] == '-' || ('0' <= b[0] && b[0] <= '9')) { | |
146 | return b, false | |
147 | } | |
148 | ||
149 | // Fast-path optimization for integers. | |
150 | // Integer values in the range of ±2⁵³ are represented in decimal, | |
151 | // which is encoded using up to 16 digits (excluding the sign). | |
152 | { | |
153 | b := b | |
154 | var neg bool | |
155 | if len(b) > 0 && b[0] == '-' { | |
156 | b = b[1:] | |
157 | neg = true | |
158 | } | |
159 | switch { | |
160 | case len(b) == 0: | |
161 | break | |
162 | case b[0] == '0': | |
163 | b = b[1:] | |
164 | if neg { | |
165 | break // -0 is not permitted | |
166 | } | |
167 | if len(b) > 0 && (b[0] == '.' || b[0] == 'e' || b[0] == 'E') { | |
168 | break // number is not yet terminated | |
169 | } | |
170 | return b, true | |
171 | case '1' <= b[0] && b[0] <= '9': | |
172 | var n int | |
173 | b = b[1:] | |
174 | n++ | |
175 | for len(b) > 0 && ('0' <= b[0] && b[0] <= '9') { | |
176 | b = b[1:] | |
177 | n++ | |
178 | } | |
179 | if n >= 16 { | |
180 | break // possibly exceeds ±2⁵³ | |
181 | } | |
182 | if len(b) > 0 && (b[0] == '.' || b[0] == 'e' || b[0] == 'E') { | |
183 | break // number is not yet terminated | |
184 | } | |
185 | return b, true | |
186 | } | |
187 | } | |
188 | ||
189 | f, b2, err := decodeNumber(b) | |
190 | got := b[:len(b)-len(b2)] | |
191 | want, _ := formatNumber(nil, f) | |
192 | return b2, bytes.Equal(got, want) && err == nil | |
193 | } |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | |
1 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | Copyright © 2014, Joe Tsai and The Go Authors. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without | |
3 | modification, are permitted provided that the following conditions are met: | |
4 | ||
5 | * Redistributions of source code must retain the above copyright notice, this | |
6 | list of conditions and the following disclaimer. | |
7 | * Redistributions in binary form must reproduce the above copyright notice, | |
8 | this list of conditions and the following disclaimer in the documentation and/or | |
9 | other materials provided with the distribution. | |
10 | * Neither the copyright holder nor the names of its contributors may be used to | |
11 | endorse or promote products derived from this software without specific prior | |
12 | written permission. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | |
18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
0 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | |
1 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= |
92 | 92 | |
93 | 93 | // Prefix factors according to SI standards. |
94 | 94 | const ( |
95 | Yocto = 1E-24 | |
96 | Zepto = 1E-21 | |
97 | Atto = 1E-18 | |
98 | Femto = 1E-15 | |
99 | Pico = 1E-12 | |
100 | Nano = 1E-9 | |
101 | Micro = 1E-6 | |
102 | Milli = 1E-3 | |
103 | ||
104 | Unit = 1E0 // Not a standard SI prefix. | |
105 | ||
106 | Kilo = 1E+3 | |
107 | Mega = 1E+6 | |
108 | Giga = 1E+9 | |
109 | Tera = 1E+12 | |
110 | Peta = 1E+15 | |
111 | Exa = 1E+18 | |
112 | Zetta = 1E+21 | |
113 | Yotta = 1E+24 | |
95 | Yocto = 1e-24 | |
96 | Zepto = 1e-21 | |
97 | Atto = 1e-18 | |
98 | Femto = 1e-15 | |
99 | Pico = 1e-12 | |
100 | Nano = 1e-9 | |
101 | Micro = 1e-6 | |
102 | Milli = 1e-3 | |
103 | ||
104 | Unit = 1e0 // Not a standard SI prefix. | |
105 | ||
106 | Kilo = 1e+3 | |
107 | Mega = 1e+6 | |
108 | Giga = 1e+9 | |
109 | Tera = 1e+12 | |
110 | Peta = 1e+15 | |
111 | Exa = 1e+18 | |
112 | Zetta = 1e+21 | |
113 | Yotta = 1e+24 | |
114 | 114 | ) |
115 | 115 | |
116 | 116 | const ( |
272 | 272 | {"123K", AutoParse, 123 * Kilo, nil}, |
273 | 273 | {"3Mi", AutoParse, 3 * Mebi, nil}, |
274 | 274 | {"3M", AutoParse, 3 * Mega, nil}, |
275 | {"3E-3", AutoParse, 3E-3, nil}, | |
276 | {"2E2", AutoParse, 2E2, nil}, | |
275 | {"3E-3", AutoParse, 3e-3, nil}, | |
276 | {"2E2", AutoParse, 2e2, nil}, | |
277 | 277 | } |
278 | 278 | |
279 | 279 | for _, tt := range tests { |