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  }