Codebase list golang-github-dsnet-golib / 94ca2f39-9b02-456a-a97f-4a7c696361e6/main
New upstream release. Debian Janitor 2 years ago
30 changed file(s) with 1736 addition(s) and 34 deletion(s). Raw diff Collapse all Expand all
66 are pulled out and placed here.
77
88
9 ## Installation ##
9 ## Packages ##
1010
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.
1713
1814 | Package | Description |
1915 | :------ | :---------- |
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 module github.com/dsnet/golib/bufpipe
1
2 go 1.12
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 module github.com/dsnet/golib/cron
1
2 go 1.12
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
06 golang-github-dsnet-golib (0.0~git20171103.1ea1667-1) unstable; urgency=medium
17
28 * Initial release (Closes: #902735)
0 module github.com/dsnet/golib
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 module github.com/dsnet/golib/hashmerge
1
2 go 1.12
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 module github.com/dsnet/golib/jsoncs
1
2 go 1.9
3
4 require github.com/google/go-cmp v0.5.1
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 module github.com/dsnet/golib/jsonfmt
1
2 go 1.12
3
4 require github.com/google/go-cmp v0.3.0
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 module github.com/dsnet/golib/memfile
1
2 go 1.12
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 module github.com/dsnet/golib/unitconv
1
2 go 1.12
3
4 require github.com/google/go-cmp v0.3.0
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=
9292
9393 // Prefix factors according to SI standards.
9494 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
114114 )
115115
116116 const (
272272 {"123K", AutoParse, 123 * Kilo, nil},
273273 {"3Mi", AutoParse, 3 * Mebi, nil},
274274 {"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},
277277 }
278278
279279 for _, tt := range tests {