github.com/m3db/m3@v1.5.0/src/query/util/json/writer.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package json contains logic for writing JSON. 22 package json 23 24 import ( 25 "bufio" 26 "bytes" 27 "errors" 28 "fmt" 29 "io" 30 "math" 31 "strconv" 32 ) 33 34 var ( 35 errContainerMismatch = errors.New("container mismatch") 36 errNotInContainer = errors.New("not in container") 37 errFieldNotAllowed = errors.New("field not allowed") 38 errValueNotAllowed = errors.New("value not allowed") 39 errContainerStillOpen = errors.New("container still open") 40 ) 41 42 type ( 43 writeState int 44 containerType int 45 ) 46 47 const ( 48 object containerType = iota 49 array 50 ) 51 52 const ( 53 writeStart writeState = iota 54 writeBeforeFirstField 55 writeBeforeNthField 56 writeBeforeFieldValue 57 writeBeforeFirstArrayElement 58 writeBeforeNthArrayElement 59 writeEnd 60 ) 61 62 var writeValueAllowed = map[writeState]struct{}{ 63 writeStart: {}, 64 writeBeforeFieldValue: {}, 65 writeBeforeFirstArrayElement: {}, 66 writeBeforeNthArrayElement: {}, 67 } 68 69 func (s writeState) isValueAllowed() bool { 70 _, allowed := writeValueAllowed[s] 71 return allowed 72 } 73 74 // A Writer can be used to directly stream JSON results without going through 75 // an intermediate object layer. 76 type Writer interface { 77 BeginObject() 78 BeginObjectField(name string) 79 BeginObjectBytesField(name []byte) 80 EndObject() 81 BeginArray() 82 EndArray() 83 WriteBool(b bool) 84 WriteNull() 85 WriteFloat64(n float64) 86 WriteInt(n int) 87 WriteString(s string) 88 WriteBytesString(s []byte) 89 Flush() error 90 Close() error 91 } 92 93 type writer struct { 94 w *bufio.Writer 95 buff *bytes.Reader 96 state writeState 97 containers []containerType 98 err error 99 } 100 101 // NewWriter creates a new JSON token writer 102 func NewWriter(w io.Writer) Writer { 103 return &writer{ 104 w: bufio.NewWriter(w), 105 buff: bytes.NewReader(nil), 106 containers: make([]containerType, 0, 5), 107 } 108 } 109 110 // BeginObject begins a new object 111 func (w *writer) BeginObject() { 112 if !w.beginValue() { 113 return 114 } 115 116 w.containers = append(w.containers, object) 117 _, w.err = w.w.WriteRune('{') 118 w.state = writeBeforeFirstField 119 } 120 121 // BeginObjectField begins a new object field with the given name 122 func (w *writer) BeginObjectField(name string) { 123 w.err = w.beginObjectFieldStart() 124 if w.err != nil { 125 return 126 } 127 128 w.writeString(name) 129 if w.err != nil { 130 return 131 } 132 133 w.err = w.beginObjectFieldEnd() 134 } 135 136 func (w *writer) beginObjectFieldStart() error { 137 if w.err != nil { 138 return w.err 139 } 140 141 if w.state != writeBeforeFirstField && w.state != writeBeforeNthField { 142 return errFieldNotAllowed 143 } 144 145 if w.state == writeBeforeNthField { 146 if _, err := w.w.WriteRune(','); err != nil { 147 return err 148 } 149 } 150 151 return nil 152 } 153 154 func (w *writer) beginObjectFieldEnd() error { 155 if _, err := w.w.WriteRune(':'); err != nil { 156 return err 157 } 158 w.state = writeBeforeFieldValue 159 return nil 160 } 161 162 func (w *writer) BeginObjectBytesField(name []byte) { 163 w.err = w.beginObjectFieldStart() 164 if w.err != nil { 165 return 166 } 167 168 w.writeBytesString(name) 169 if w.err != nil { 170 return 171 } 172 173 w.err = w.beginObjectFieldEnd() 174 } 175 176 // EndObject finishes an open object 177 func (w *writer) EndObject() { 178 if !w.endContainer(object) { 179 return 180 } 181 182 _, w.err = w.w.WriteRune('}') 183 } 184 185 // BeginArray begins a new array value 186 func (w *writer) BeginArray() { 187 if !w.beginValue() { 188 return 189 } 190 191 w.containers = append(w.containers, array) 192 _, w.err = w.w.WriteRune('[') 193 w.state = writeBeforeFirstArrayElement 194 } 195 196 // EndArray finishes an array value 197 func (w *writer) EndArray() { 198 if !w.endContainer(array) { 199 return 200 } 201 202 _, w.err = w.w.WriteRune(']') 203 } 204 205 // endContainer finishes a container of the given type 206 func (w *writer) endContainer(expected containerType) bool { 207 if w.err != nil { 208 return false 209 } 210 211 if len(w.containers) == 0 { 212 w.err = errNotInContainer 213 return false 214 } 215 216 container := w.containers[len(w.containers)-1] 217 w.containers = w.containers[:len(w.containers)-1] 218 if container != expected { 219 w.err = errContainerMismatch 220 return false 221 } 222 223 w.endValue() 224 return true 225 } 226 227 // WriteBool writes a boolean value 228 func (w *writer) WriteBool(b bool) { 229 if !w.beginValue() { 230 return 231 } 232 233 if b { 234 _, w.err = w.w.WriteString("true") 235 } else { 236 _, w.err = w.w.WriteString("false") 237 } 238 w.endValue() 239 } 240 241 func (w *writer) writeNull() { 242 _, w.err = w.w.WriteString("null") 243 } 244 245 // WriteNull writes a null value 246 func (w *writer) WriteNull() { 247 if !w.beginValue() { 248 return 249 } 250 251 w.writeNull() 252 w.endValue() 253 } 254 255 // WriteFloat64 writes a float value 256 func (w *writer) WriteFloat64(n float64) { 257 if !w.beginValue() { 258 return 259 } 260 261 // JSON does not support NaNs or infinity 262 if math.IsNaN(n) || math.IsInf(n, 0) { 263 w.writeNull() 264 } else { 265 _, w.err = fmt.Fprintf(w.w, "%f", n) 266 } 267 268 w.endValue() 269 } 270 271 // WriteInt writes an int value 272 func (w *writer) WriteInt(n int) { 273 if !w.beginValue() { 274 return 275 } 276 277 _, w.err = w.w.WriteString(strconv.Itoa(n)) 278 w.endValue() 279 } 280 281 // WriteString writes a string value 282 func (w *writer) WriteString(s string) { 283 if !w.beginValue() { 284 return 285 } 286 287 w.writeString(s) 288 289 w.endValue() 290 } 291 292 func (w *writer) writeString(s string) { 293 if _, w.err = w.w.WriteRune('"'); w.err != nil { 294 return 295 } 296 297 for _, c := range s { 298 w.writeRune(c) 299 if w.err != nil { 300 return 301 } 302 } 303 304 _, w.err = w.w.WriteRune('"') 305 } 306 307 func (w *writer) WriteBytesString(s []byte) { 308 if !w.beginValue() { 309 return 310 } 311 312 w.writeBytesString(s) 313 314 w.endValue() 315 } 316 317 func (w *writer) writeBytesString(s []byte) { 318 if _, w.err = w.w.WriteRune('"'); w.err != nil { 319 return 320 } 321 322 w.buff.Reset(s) 323 defer w.buff.Reset(nil) // Free holding onto byte slice. 324 325 for { 326 c, _, err := w.buff.ReadRune() 327 if errors.Is(err, io.EOF) { 328 break 329 } 330 if err != nil { 331 w.err = err 332 return 333 } 334 w.writeRune(c) 335 } 336 337 _, w.err = w.w.WriteRune('"') 338 } 339 340 func (w *writer) writeRune(r rune) { 341 if r <= 31 || r == '"' || r == '\\' { 342 if _, w.err = w.w.WriteRune('\\'); w.err != nil { 343 return 344 } 345 346 switch r { 347 case '"', '\\': 348 if _, w.err = w.w.WriteRune(r); w.err != nil { 349 return 350 } 351 case '\n': 352 if _, w.err = w.w.WriteRune('n'); w.err != nil { 353 return 354 } 355 case '\r': 356 if _, w.err = w.w.WriteRune('r'); w.err != nil { 357 return 358 } 359 case '\t': 360 if _, w.err = w.w.WriteRune('t'); w.err != nil { 361 return 362 } 363 default: 364 codePoint := fmt.Sprintf("%U", r) 365 if _, w.err = w.w.WriteRune('u'); w.err != nil { 366 return 367 } 368 if _, w.err = w.w.WriteString(codePoint[2:]); w.err != nil { 369 return 370 } 371 } 372 373 return 374 } 375 376 if _, w.err = w.w.WriteRune(r); w.err != nil { 377 return 378 } 379 } 380 381 // beginValue begins a new value, confirming that the current position of the 382 // writer allows a value 383 func (w *writer) beginValue() bool { 384 if w.err != nil { 385 return false 386 } 387 388 if !w.state.isValueAllowed() { 389 w.err = errValueNotAllowed 390 return false 391 } 392 393 if w.state == writeBeforeNthArrayElement { 394 if _, w.err = w.w.WriteRune(','); w.err != nil { 395 return false 396 } 397 } 398 399 return true 400 } 401 402 // endValue marks as value as being complete 403 func (w *writer) endValue() { 404 if len(w.containers) == 0 { 405 // End of top level object 406 w.state = writeEnd 407 return 408 } 409 410 c := w.containers[len(w.containers)-1] 411 switch c { 412 case object: 413 w.state = writeBeforeNthField 414 case array: 415 w.state = writeBeforeNthArrayElement 416 default: 417 panic(fmt.Sprintf("unknown container type %d", c)) 418 } 419 } 420 421 // Flush flushes the writer 422 func (w *writer) Flush() error { 423 if w.err != nil { 424 return w.err 425 } 426 427 w.err = w.w.Flush() 428 return w.err 429 } 430 431 // Close closes the writer, returning any write errors that have occurred 432 func (w *writer) Close() error { 433 if w.err != nil { 434 return w.err 435 } 436 437 if len(w.containers) > 0 { 438 w.err = errContainerStillOpen 439 return w.err 440 } 441 442 w.err = w.w.Flush() 443 return w.err 444 }