ALL

Solo para leer y verificar. No puedes copiar, usar, modificar ni redistribuir este código. Aviso completo en la página principal.

tunnel/jsonparse.go

SHA-256: 10d9a104e6eeda517aad020ee28809430a1902065fa0333cb2aa54027fdf1107
187 líneas · 4266 bytes · Ver en GitHub

1package main
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "io"
8 "math"
9 "sort"
10 "strconv"
11 "strings"
12 "unicode/utf8"
13)
14
15// The tokens and bodies of the contracts are JSON that every implementation must read the same way, so the node reads it strictly: valid
16// UTF-8, no repeated key (a body with a repeated key means different things to different parsers), no non-integral number, nothing after the value.
17
18// parseJSON parses one JSON value into maps, slices, strings, bools, nil and float64 (integers up to 2^53 are exact).
19func parseJSON(text string) (any, error) {
20 if !utf8.ValidString(text) {
21 return nil, errors.New("utf-8")
22 }
23 dec := json.NewDecoder(strings.NewReader(text))
24 dec.UseNumber()
25 value, err := decodeValue(dec)
26 if err != nil {
27 return nil, err
28 }
29 if _, err := dec.Token(); err != io.EOF {
30 return nil, errors.New("trailing data")
31 }
32 if err := noFractions(value); err != nil {
33 return nil, err
34 }
35 return value, nil
36}
37
38func decodeValue(dec *json.Decoder) (any, error) {
39 token, err := dec.Token()
40 if err != nil {
41 return nil, err
42 }
43 switch t := token.(type) {
44 case json.Delim:
45 switch t {
46 case '{':
47 out := map[string]any{}
48 for dec.More() {
49 keyToken, err := dec.Token()
50 if err != nil {
51 return nil, err
52 }
53 key, ok := keyToken.(string)
54 if !ok {
55 return nil, errors.New("key")
56 }
57 if _, dup := out[key]; dup {
58 return nil, errors.New("duplicate key")
59 }
60 value, err := decodeValue(dec)
61 if err != nil {
62 return nil, err
63 }
64 out[key] = value
65 }
66 if _, err := dec.Token(); err != nil {
67 return nil, err
68 }
69 return out, nil
70 case '[':
71 out := []any{}
72 for dec.More() {
73 value, err := decodeValue(dec)
74 if err != nil {
75 return nil, err
76 }
77 out = append(out, value)
78 }
79 if _, err := dec.Token(); err != nil {
80 return nil, err
81 }
82 return out, nil
83 }
84 return nil, errors.New("delimiter")
85 case json.Number:
86 f, err := strconv.ParseFloat(string(t), 64)
87 if err != nil || math.IsInf(f, 0) {
88 return nil, errors.New("number")
89 }
90 return f, nil
91 default:
92 return token, nil // string, bool, nil
93 }
94}
95
96// noFractions refuses a non-integral number anywhere in a value (the contracts' bodies hold integers, never fractions).
97func noFractions(value any) error {
98 switch v := value.(type) {
99 case float64:
100 if v != math.Trunc(v) {
101 return errors.New("fraction")
102 }
103 case map[string]any:
104 for _, item := range v {
105 if err := noFractions(item); err != nil {
106 return err
107 }
108 }
109 case []any:
110 for _, item := range v {
111 if err := noFractions(item); err != nil {
112 return err
113 }
114 }
115 }
116 return nil
117}
118
119func escapeJSON(s string) string {
120 var b strings.Builder
121 b.WriteByte('"')
122 for _, r := range s {
123 switch {
124 case r == '"':
125 b.WriteString(`\"`)
126 case r == '\\':
127 b.WriteString(`\\`)
128 case r == '\n':
129 b.WriteString(`\n`)
130 case r == '\r':
131 b.WriteString(`\r`)
132 case r == '\t':
133 b.WriteString(`\t`)
134 case r == '\b':
135 b.WriteString(`\b`)
136 case r == '\f':
137 b.WriteString(`\f`)
138 case r < 0x20 || r > 0x7e:
139 if r > 0xffff {
140 r -= 0x10000
141 fmt.Fprintf(&b, `\u%04x\u%04x`, 0xd800+(r>>10), 0xdc00+(r&0x3ff))
142 } else {
143 fmt.Fprintf(&b, `\u%04x`, r)
144 }
145 default:
146 b.WriteRune(r)
147 }
148 }
149 b.WriteByte('"')
150 return b.String()
151}
152
153// canonicalJSON is the form of a JSON body on a wire line: keys sorted, no whitespace, ASCII only, integers without a fraction.
154func canonicalJSON(value any) string {
155 switch v := value.(type) {
156 case nil:
157 return "null"
158 case bool:
159 return strconv.FormatBool(v)
160 case int:
161 return strconv.Itoa(v)
162 case int64:
163 return strconv.FormatInt(v, 10)
164 case float64:
165 return strconv.FormatInt(int64(v), 10)
166 case string:
167 return escapeJSON(v)
168 case []any:
169 parts := make([]string, len(v))
170 for i, item := range v {
171 parts[i] = canonicalJSON(item)
172 }
173 return "[" + strings.Join(parts, ",") + "]"
174 case map[string]any:
175 keys := make([]string, 0, len(v))
176 for key := range v {
177 keys = append(keys, key)
178 }
179 sort.Strings(keys)
180 parts := make([]string, len(keys))
181 for i, key := range keys {
182 parts[i] = escapeJSON(key) + ":" + canonicalJSON(v[key])
183 }
184 return "{" + strings.Join(parts, ",") + "}"
185 }
186 panic(fmt.Sprintf("canonical json: %T", value))
187}