writer.go (107 lines of code) (raw):

// Copyright 2018 Elasticsearch BV // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fastjson import ( "strconv" "time" "unicode/utf8" ) // Writer is a JSON writer, appending to an internal buffer. // // Writer is not safe for concurrent use. A Writer can be // reset and reused, which will reuse the underlying buffer. type Writer struct { buf []byte } // Bytes returns the internal buffer. The result is invalidated when Reset is called. func (w *Writer) Bytes() []byte { return w.buf } // Size returns the current size of the buffer. Size is typically used in conjunction // with Rewind, to mark a position to which the writer may later be rewound. func (w *Writer) Size() int { return len(w.buf) } // Rewind rewinds the buffer such that it has size bytes, dropping everything proceeding. func (w *Writer) Rewind(size int) { w.buf = w.buf[:size] } // Reset resets the internal []byte buffer to empty. func (w *Writer) Reset() { w.buf = w.buf[:0] } // RawByte appends c to the buffer. func (w *Writer) RawByte(c byte) { w.buf = append(w.buf, c) } // RawBytes appends data, unmodified, to the buffer. func (w *Writer) RawBytes(data []byte) { w.buf = append(w.buf, data...) } // RawString appends s to the buffer. func (w *Writer) RawString(s string) { w.buf = append(w.buf, s...) } // Uint64 appends n to the buffer. func (w *Writer) Uint64(n uint64) { w.buf = strconv.AppendUint(w.buf, uint64(n), 10) } // Int64 appends n to the buffer. func (w *Writer) Int64(n int64) { w.buf = strconv.AppendInt(w.buf, int64(n), 10) } // Float32 appends n to the buffer. func (w *Writer) Float32(n float32) { w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 32) } // Float64 appends n to the buffer. func (w *Writer) Float64(n float64) { w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 64) } // Bool appends v to the buffer. func (w *Writer) Bool(v bool) { w.buf = strconv.AppendBool(w.buf, v) } // Time appends t to the buffer, formatted according to layout. // // The encoded time is not surrounded by quotes; it is the // responsibility of the caller to ensure the formatted time is // quoted as necessary. func (w *Writer) Time(t time.Time, layout string) { w.buf = t.AppendFormat(w.buf, layout) } // String appends s, quoted and escaped, to the buffer. func (w *Writer) String(s string) { w.RawByte('"') w.StringContents(s) w.RawByte('"') } // Note: code below taken from mailru/easyjson, adapted to use Writer. const chars = "0123456789abcdef" // StringContents is the same as String, but without the surrounding quotes. func (w *Writer) StringContents(s string) { // Portions of the string that contain no escapes are appended as byte slices. p := 0 // last non-escape symbol for i := 0; i < len(s); { if c := s[i]; c < utf8.RuneSelf { if htmlSafeSet[c] { i++ continue } // single-with character, need to escape w.RawString(s[p:i]) switch c { case '\\': w.RawString(`\\`) case '"': w.RawString(`\"`) case '\b': w.RawString(`\b`) case '\f': w.RawString(`\f`) case '\n': w.RawString(`\n`) case '\r': w.RawString(`\r`) case '\t': w.RawString(`\t`) default: w.RawString(`\u00`) w.RawByte(chars[c>>4]) w.RawByte(chars[c&0xf]) } i++ p = i continue } // broken utf runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) if runeValue == utf8.RuneError && runeWidth == 1 { w.RawString(s[p:i]) w.RawString(`\ufffd`) i++ p = i continue } // jsonp stuff - tab separator and line separator if runeValue == '\u2028' || runeValue == '\u2029' { w.RawString(s[p:i]) w.RawString(`\u202`) w.RawByte(chars[runeValue&0xf]) i += runeWidth p = i continue } i += runeWidth } w.RawString(s[p:]) }