github.com/urso/go-structform@v0.0.2/json/visitor.go (about) 1 package json 2 3 import ( 4 "fmt" 5 "io" 6 "math" 7 "strconv" 8 "unicode/utf8" 9 10 structform "github.com/urso/go-structform" 11 ) 12 13 // Visitor implements the structform.Visitor interface, json encoding the 14 // structure being visited 15 type Visitor struct { 16 w writer 17 18 scratch [64]byte 19 20 first boolStack 21 inArray boolStack 22 } 23 24 type boolStack struct { 25 stack []bool 26 stack0 [32]bool 27 current bool 28 } 29 30 var _ structform.Visitor = &Visitor{} 31 32 type writer struct { 33 out io.Writer 34 } 35 36 func (w writer) write(b []byte) error { 37 _, err := w.out.Write(b) 38 return err 39 } 40 41 func NewVisitor(out io.Writer) *Visitor { 42 v := &Visitor{w: writer{out}} 43 return v 44 } 45 46 func (vs *Visitor) writeByte(b byte) error { 47 vs.scratch[0] = b 48 return vs.w.write(vs.scratch[:1]) 49 } 50 51 func (vs *Visitor) writeString(s string) error { 52 return vs.w.write(str2Bytes(s)) 53 } 54 55 func (vs *Visitor) OnObjectStart(_ int, _ structform.BaseType) error { 56 if err := vs.tryElemNext(); err != nil { 57 return err 58 } 59 60 vs.first.push(true) 61 vs.inArray.push(false) 62 return vs.writeByte('{') 63 } 64 65 func (vs *Visitor) OnObjectFinished() error { 66 vs.first.pop() 67 vs.inArray.pop() 68 return vs.writeByte('}') 69 } 70 71 func (vs *Visitor) OnKeyRef(s []byte) error { 72 if err := vs.onFieldNext(); err != nil { 73 return err 74 } 75 76 err := vs.OnStringRef(s) 77 if err == nil { 78 err = vs.writeByte(':') 79 } 80 return err 81 } 82 83 func (vs *Visitor) OnKey(s string) error { 84 if err := vs.onFieldNext(); err != nil { 85 return err 86 } 87 88 err := vs.OnString(s) 89 if err == nil { 90 err = vs.writeByte(':') 91 } 92 return err 93 } 94 95 func (vs *Visitor) onFieldNext() error { 96 if vs.first.current { 97 vs.first.current = false 98 return nil 99 } 100 return vs.writeByte(',') 101 } 102 103 func (vs *Visitor) OnArrayStart(_ int, _ structform.BaseType) error { 104 if err := vs.tryElemNext(); err != nil { 105 return err 106 } 107 108 vs.first.push(true) 109 vs.inArray.push(true) 110 return vs.writeByte('[') 111 } 112 113 func (vs *Visitor) OnArrayFinished() error { 114 vs.first.pop() 115 vs.inArray.pop() 116 return vs.writeByte(']') 117 } 118 119 func (vs *Visitor) tryElemNext() error { 120 if !vs.inArray.current { 121 return nil 122 } 123 124 if vs.first.current { 125 vs.first.current = false 126 return nil 127 } 128 return vs.w.write(commaSymbol) 129 } 130 131 var hex = "0123456789abcdef" 132 133 func (vs *Visitor) OnStringRef(s []byte) error { 134 return vs.OnString(bytes2Str(s)) 135 } 136 137 func (vs *Visitor) OnString(s string) error { 138 if err := vs.tryElemNext(); err != nil { 139 return err 140 } 141 142 vs.writeByte('"') 143 start := 0 144 for i := 0; i < len(s); { 145 if b := s[i]; b < utf8.RuneSelf { 146 if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { 147 i++ 148 continue 149 } 150 if start < i { 151 vs.writeString(s[start:i]) 152 } 153 switch b { 154 case '\\', '"': 155 vs.scratch[0], vs.scratch[1] = '\\', b 156 vs.w.write(vs.scratch[:2]) 157 case '\n': 158 vs.scratch[0], vs.scratch[1] = '\\', 'n' 159 vs.w.write(vs.scratch[:2]) 160 case '\r': 161 vs.scratch[0], vs.scratch[1] = '\\', 'r' 162 vs.w.write(vs.scratch[:2]) 163 case '\t': 164 vs.scratch[0], vs.scratch[1] = '\\', 't' 165 vs.w.write(vs.scratch[:2]) 166 default: 167 // This vsodes bytes < 0x20 except for \n and \r, 168 // as well as <, > and &. The latter are escaped because they 169 // can lead to security holes when user-controlled strings 170 // are rendered into JSON and served to some browsers. 171 vs.scratch[0], vs.scratch[1], vs.scratch[2], vs.scratch[3] = '\\', 'u', '0', '0' 172 vs.scratch[4] = hex[b>>4] 173 vs.scratch[5] = hex[b&0xF] 174 vs.w.write(vs.scratch[:6]) 175 } 176 i++ 177 start = i 178 continue 179 } 180 c, size := utf8.DecodeRuneInString(s[i:]) 181 if c == utf8.RuneError && size == 1 { 182 if start < i { 183 vs.writeString(s[start:i]) 184 } 185 vs.w.write(invalidCharSym) 186 i += size 187 start = i 188 continue 189 } 190 // U+2028 is LINE SEPARATOR. 191 // U+2029 is PARAGRAPH SEPARATOR. 192 // They are both technically valid characters in JSON strings, 193 // but don't work in JSONP, which has to be evaluated as JavaScript, 194 // and can lead to security holes there. It is valid JSON to 195 // escape them, so we do so unconditionally. 196 // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. 197 if c == '\u2028' || c == '\u2029' { 198 if start < i { 199 vs.writeString(s[start:i]) 200 } 201 vs.writeString(`\u202`) 202 vs.writeByte(hex[c&0xF]) 203 i += size 204 start = i 205 continue 206 } 207 i += size 208 } 209 if start < len(s) { 210 vs.writeString(s[start:]) 211 } 212 vs.writeByte('"') 213 return nil 214 } 215 216 func (vs *Visitor) OnBool(b bool) error { 217 if err := vs.tryElemNext(); err != nil { 218 return err 219 } 220 221 var err error 222 if b { 223 err = vs.w.write(trueSymbol) 224 } else { 225 err = vs.w.write(falseSymbol) 226 } 227 return err 228 } 229 230 func (vs *Visitor) OnNil() error { 231 if err := vs.tryElemNext(); err != nil { 232 return err 233 } 234 235 err := vs.w.write(nullSymbol) 236 return err 237 } 238 239 func (vs *Visitor) OnInt8(i int8) error { 240 return vs.onInt(int64(i)) 241 } 242 243 func (vs *Visitor) OnInt16(i int16) error { 244 return vs.onInt(int64(i)) 245 } 246 247 func (vs *Visitor) OnInt32(i int32) error { 248 return vs.onInt(int64(i)) 249 } 250 251 func (vs *Visitor) OnInt64(i int64) error { 252 return vs.onInt(i) 253 } 254 255 func (vs *Visitor) OnInt(i int) error { 256 return vs.onInt(int64(i)) 257 } 258 259 func (vs *Visitor) onInt(v int64) error { 260 if err := vs.tryElemNext(); err != nil { 261 return err 262 } 263 264 /* 265 b := strconv.AppendInt(vs.scratch[:0], i, 10) 266 _, err := vs.w.Write(b) 267 */ 268 vs.onNumber(v < 0, uint64(v)) 269 return nil 270 } 271 272 func (vs *Visitor) OnUint8(u uint8) error { 273 return vs.onUint(uint64(u)) 274 } 275 276 func (vs *Visitor) OnByte(b byte) error { 277 return vs.onUint(uint64(b)) 278 } 279 280 func (vs *Visitor) OnUint16(u uint16) error { 281 return vs.onUint(uint64(u)) 282 } 283 284 func (vs *Visitor) OnUint32(u uint32) error { 285 return vs.onUint(uint64(u)) 286 } 287 288 func (vs *Visitor) OnUint64(u uint64) error { 289 return vs.onUint(u) 290 } 291 292 func (vs *Visitor) OnUint(u uint) error { 293 return vs.onUint(uint64(u)) 294 } 295 296 func (vs *Visitor) onUint(u uint64) error { 297 if err := vs.tryElemNext(); err != nil { 298 return err 299 } 300 301 return vs.onNumber(false, u) 302 /* 303 b := strconv.AppendUint(vs.scratch[:0], u, 10) 304 _, err := vs.w.Write(b) 305 return err 306 */ 307 } 308 309 func (vs *Visitor) onNumber(neg bool, u uint64) error { 310 if neg { 311 u = -u 312 } 313 i := len(vs.scratch) 314 315 // common case: use constants for / because 316 // the compiler can optimize it into a multiply+shift 317 if ^uintptr(0)>>32 == 0 { 318 for u > uint64(^uintptr(0)) { 319 q := u / 1e9 320 us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr 321 for j := 9; j > 0; j-- { 322 i-- 323 qs := us / 10 324 vs.scratch[i] = byte(us - qs*10 + '0') 325 us = qs 326 } 327 u = q 328 } 329 } 330 331 // u guaranteed to fit into a uintptr 332 us := uintptr(u) 333 for us >= 10 { 334 i-- 335 q := us / 10 336 vs.scratch[i] = byte(us - q*10 + '0') 337 us = q 338 } 339 // u < 10 340 i-- 341 vs.scratch[i] = byte(us + '0') 342 343 if neg { 344 i-- 345 vs.scratch[i] = '-' 346 } 347 return vs.w.write(vs.scratch[i:]) 348 } 349 350 func (vs *Visitor) OnFloat32(f float32) error { 351 return vs.onFloat(float64(f), 32) 352 } 353 354 func (vs *Visitor) OnFloat64(f float64) error { 355 return vs.onFloat(f, 64) 356 } 357 358 func (vs *Visitor) onFloat(f float64, bits int) error { 359 if err := vs.tryElemNext(); err != nil { 360 return err 361 } 362 363 if math.IsInf(f, 0) || math.IsNaN(f) { 364 return fmt.Errorf("unsupported float value: %v", f) 365 } 366 367 b := strconv.AppendFloat(vs.scratch[:0], f, 'g', -1, bits) 368 err := vs.w.write(b) 369 return err 370 } 371 372 func (s *boolStack) init() { 373 s.stack = s.stack0[:0] 374 } 375 376 func (s *boolStack) push(b bool) { 377 s.stack = append(s.stack, s.current) 378 s.current = b 379 } 380 381 func (s *boolStack) pop() { 382 if len(s.stack) == 0 { 383 panic("pop from empty stack") 384 } 385 386 last := len(s.stack) - 1 387 s.current = s.stack[last] 388 s.stack = s.stack[:last] 389 }