1 package extra
2
3 import (
4 "github.com/json-iterator/go"
5 "github.com/modern-go/reflect2"
6 "unicode/utf8"
7 "unsafe"
8 )
9
10
11
12
13
14
15
16 var safeSet = [utf8.RuneSelf]bool{
17 ' ': true,
18 '!': true,
19 '"': false,
20 '#': true,
21 '$': true,
22 '%': true,
23 '&': true,
24 '\'': true,
25 '(': true,
26 ')': true,
27 '*': true,
28 '+': true,
29 ',': true,
30 '-': true,
31 '.': true,
32 '/': true,
33 '0': true,
34 '1': true,
35 '2': true,
36 '3': true,
37 '4': true,
38 '5': true,
39 '6': true,
40 '7': true,
41 '8': true,
42 '9': true,
43 ':': true,
44 ';': true,
45 '<': true,
46 '=': true,
47 '>': true,
48 '?': true,
49 '@': true,
50 'A': true,
51 'B': true,
52 'C': true,
53 'D': true,
54 'E': true,
55 'F': true,
56 'G': true,
57 'H': true,
58 'I': true,
59 'J': true,
60 'K': true,
61 'L': true,
62 'M': true,
63 'N': true,
64 'O': true,
65 'P': true,
66 'Q': true,
67 'R': true,
68 'S': true,
69 'T': true,
70 'U': true,
71 'V': true,
72 'W': true,
73 'X': true,
74 'Y': true,
75 'Z': true,
76 '[': true,
77 '\\': false,
78 ']': true,
79 '^': true,
80 '_': true,
81 '`': true,
82 'a': true,
83 'b': true,
84 'c': true,
85 'd': true,
86 'e': true,
87 'f': true,
88 'g': true,
89 'h': true,
90 'i': true,
91 'j': true,
92 'k': true,
93 'l': true,
94 'm': true,
95 'n': true,
96 'o': true,
97 'p': true,
98 'q': true,
99 'r': true,
100 's': true,
101 't': true,
102 'u': true,
103 'v': true,
104 'w': true,
105 'x': true,
106 'y': true,
107 'z': true,
108 '{': true,
109 '|': true,
110 '}': true,
111 '~': true,
112 '\u007f': true,
113 }
114
115 var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
116
117 type BinaryAsStringExtension struct {
118 jsoniter.DummyExtension
119 }
120
121 func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
122 if typ == binaryType {
123 return &binaryAsStringCodec{}
124 }
125 return nil
126 }
127
128 func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
129 if typ == binaryType {
130 return &binaryAsStringCodec{}
131 }
132 return nil
133 }
134
135 type binaryAsStringCodec struct {
136 }
137
138 func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
139 rawBytes := iter.ReadStringAsSlice()
140 bytes := make([]byte, 0, len(rawBytes))
141 for i := 0; i < len(rawBytes); i++ {
142 b := rawBytes[i]
143 if b == '\\' {
144 b2 := rawBytes[i+1]
145 if b2 != '\\' {
146 iter.ReportError("decode binary as string", `\\x is only supported escape`)
147 return
148 }
149 b3 := rawBytes[i+2]
150 if b3 != 'x' {
151 iter.ReportError("decode binary as string", `\\x is only supported escape`)
152 return
153 }
154 b4 := rawBytes[i+3]
155 b5 := rawBytes[i+4]
156 i += 4
157 b = readHex(iter, b4, b5)
158 }
159 bytes = append(bytes, b)
160 }
161 *(*[]byte)(ptr) = bytes
162 }
163 func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
164 return len(*((*[]byte)(ptr))) == 0
165 }
166 func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
167 newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
168 stream.SetBuffer(newBuffer)
169 }
170
171 func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
172 var ret byte
173 if b1 >= '0' && b1 <= '9' {
174 ret = b1 - '0'
175 } else if b1 >= 'a' && b1 <= 'f' {
176 ret = b1 - 'a' + 10
177 } else {
178 iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
179 return 0
180 }
181 ret *= 16
182 if b2 >= '0' && b2 <= '9' {
183 ret += b2 - '0'
184 } else if b2 >= 'a' && b2 <= 'f' {
185 ret += b2 - 'a' + 10
186 } else {
187 iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
188 return 0
189 }
190 return ret
191 }
192
193 var hex = "0123456789abcdef"
194
195 func writeBytes(space []byte, s []byte) []byte {
196 space = append(space, '"')
197
198 var i int
199 var c byte
200 for i, c = range s {
201 if c < utf8.RuneSelf && safeSet[c] {
202 space = append(space, c)
203 } else {
204 break
205 }
206 }
207 if i == len(s)-1 {
208 space = append(space, '"')
209 return space
210 }
211 return writeBytesSlowPath(space, s[i:])
212 }
213
214 func writeBytesSlowPath(space []byte, s []byte) []byte {
215 start := 0
216
217 var i int
218 var b byte
219 for i, b = range s {
220 if b >= utf8.RuneSelf {
221 space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
222 start = i + 1
223 continue
224 }
225 if safeSet[b] {
226 continue
227 }
228 if start < i {
229 space = append(space, s[start:i]...)
230 }
231 space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
232 start = i + 1
233 }
234 if start < len(s) {
235 space = append(space, s[start:]...)
236 }
237 return append(space, '"')
238 }
239
View as plain text