123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- // Package jwriter contains a JSON writer.
- package jwriter
- import (
- "io"
- "strconv"
- "unicode/utf8"
- "github.com/mailru/easyjson/buffer"
- )
- // Flags describe various encoding options. The behavior may be actually implemented in the encoder, but
- // Flags field in Writer is used to set and pass them around.
- type Flags int
- const (
- NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'.
- NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'.
- )
- // Writer is a JSON writer.
- type Writer struct {
- Flags Flags
- Error error
- Buffer buffer.Buffer
- NoEscapeHTML bool
- }
- // Size returns the size of the data that was written out.
- func (w *Writer) Size() int {
- return w.Buffer.Size()
- }
- // DumpTo outputs the data to given io.Writer, resetting the buffer.
- func (w *Writer) DumpTo(out io.Writer) (written int, err error) {
- return w.Buffer.DumpTo(out)
- }
- // BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice
- // as argument that it will try to reuse.
- func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) {
- if w.Error != nil {
- return nil, w.Error
- }
- return w.Buffer.BuildBytes(reuse...), nil
- }
- // ReadCloser returns an io.ReadCloser that can be used to read the data.
- // ReadCloser also resets the buffer.
- func (w *Writer) ReadCloser() (io.ReadCloser, error) {
- if w.Error != nil {
- return nil, w.Error
- }
- return w.Buffer.ReadCloser(), nil
- }
- // RawByte appends raw binary data to the buffer.
- func (w *Writer) RawByte(c byte) {
- w.Buffer.AppendByte(c)
- }
- // RawByte appends raw binary data to the buffer.
- func (w *Writer) RawString(s string) {
- w.Buffer.AppendString(s)
- }
- // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for
- // calling with results of MarshalJSON-like functions.
- func (w *Writer) Raw(data []byte, err error) {
- switch {
- case w.Error != nil:
- return
- case err != nil:
- w.Error = err
- case len(data) > 0:
- w.Buffer.AppendBytes(data)
- default:
- w.RawString("null")
- }
- }
- // RawText encloses raw binary data in quotes and appends in to the buffer.
- // Useful for calling with results of MarshalText-like functions.
- func (w *Writer) RawText(data []byte, err error) {
- switch {
- case w.Error != nil:
- return
- case err != nil:
- w.Error = err
- case len(data) > 0:
- w.String(string(data))
- default:
- w.RawString("null")
- }
- }
- // Base64Bytes appends data to the buffer after base64 encoding it
- func (w *Writer) Base64Bytes(data []byte) {
- if data == nil {
- w.Buffer.AppendString("null")
- return
- }
- w.Buffer.AppendByte('"')
- w.base64(data)
- w.Buffer.AppendByte('"')
- }
- func (w *Writer) Uint8(n uint8) {
- w.Buffer.EnsureSpace(3)
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- }
- func (w *Writer) Uint16(n uint16) {
- w.Buffer.EnsureSpace(5)
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- }
- func (w *Writer) Uint32(n uint32) {
- w.Buffer.EnsureSpace(10)
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- }
- func (w *Writer) Uint(n uint) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- }
- func (w *Writer) Uint64(n uint64) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
- }
- func (w *Writer) Int8(n int8) {
- w.Buffer.EnsureSpace(4)
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- }
- func (w *Writer) Int16(n int16) {
- w.Buffer.EnsureSpace(6)
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- }
- func (w *Writer) Int32(n int32) {
- w.Buffer.EnsureSpace(11)
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- }
- func (w *Writer) Int(n int) {
- w.Buffer.EnsureSpace(21)
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- }
- func (w *Writer) Int64(n int64) {
- w.Buffer.EnsureSpace(21)
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
- }
- func (w *Writer) Uint8Str(n uint8) {
- w.Buffer.EnsureSpace(3)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Uint16Str(n uint16) {
- w.Buffer.EnsureSpace(5)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Uint32Str(n uint32) {
- w.Buffer.EnsureSpace(10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) UintStr(n uint) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Uint64Str(n uint64) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) UintptrStr(n uintptr) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Int8Str(n int8) {
- w.Buffer.EnsureSpace(4)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Int16Str(n int16) {
- w.Buffer.EnsureSpace(6)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Int32Str(n int32) {
- w.Buffer.EnsureSpace(11)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) IntStr(n int) {
- w.Buffer.EnsureSpace(21)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Int64Str(n int64) {
- w.Buffer.EnsureSpace(21)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Float32(n float32) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
- }
- func (w *Writer) Float32Str(n float32) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Float64(n float64) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64)
- }
- func (w *Writer) Float64Str(n float64) {
- w.Buffer.EnsureSpace(20)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64)
- w.Buffer.Buf = append(w.Buffer.Buf, '"')
- }
- func (w *Writer) Bool(v bool) {
- w.Buffer.EnsureSpace(5)
- if v {
- w.Buffer.Buf = append(w.Buffer.Buf, "true"...)
- } else {
- w.Buffer.Buf = append(w.Buffer.Buf, "false"...)
- }
- }
- const chars = "0123456789abcdef"
- func isNotEscapedSingleChar(c byte, escapeHTML bool) bool {
- // Note: might make sense to use a table if there are more chars to escape. With 4 chars
- // it benchmarks the same.
- if escapeHTML {
- return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
- } else {
- return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
- }
- }
- func (w *Writer) String(s string) {
- w.Buffer.AppendByte('"')
- // 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); {
- c := s[i]
- if isNotEscapedSingleChar(c, !w.NoEscapeHTML) {
- // single-width character, no escaping is required
- i++
- continue
- } else if c < utf8.RuneSelf {
- // single-with character, need to escape
- w.Buffer.AppendString(s[p:i])
- switch c {
- case '\t':
- w.Buffer.AppendString(`\t`)
- case '\r':
- w.Buffer.AppendString(`\r`)
- case '\n':
- w.Buffer.AppendString(`\n`)
- case '\\':
- w.Buffer.AppendString(`\\`)
- case '"':
- w.Buffer.AppendString(`\"`)
- default:
- w.Buffer.AppendString(`\u00`)
- w.Buffer.AppendByte(chars[c>>4])
- w.Buffer.AppendByte(chars[c&0xf])
- }
- i++
- p = i
- continue
- }
- // broken utf
- runeValue, runeWidth := utf8.DecodeRuneInString(s[i:])
- if runeValue == utf8.RuneError && runeWidth == 1 {
- w.Buffer.AppendString(s[p:i])
- w.Buffer.AppendString(`\ufffd`)
- i++
- p = i
- continue
- }
- // jsonp stuff - tab separator and line separator
- if runeValue == '\u2028' || runeValue == '\u2029' {
- w.Buffer.AppendString(s[p:i])
- w.Buffer.AppendString(`\u202`)
- w.Buffer.AppendByte(chars[runeValue&0xf])
- i += runeWidth
- p = i
- continue
- }
- i += runeWidth
- }
- w.Buffer.AppendString(s[p:])
- w.Buffer.AppendByte('"')
- }
- const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- const padChar = '='
- func (w *Writer) base64(in []byte) {
- if len(in) == 0 {
- return
- }
- w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4)
- si := 0
- n := (len(in) / 3) * 3
- for si < n {
- // Convert 3x 8bit source bytes into 4 bytes
- val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2])
- w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F])
- si += 3
- }
- remain := len(in) - si
- if remain == 0 {
- return
- }
- // Add the remaining small block
- val := uint(in[si+0]) << 16
- if remain == 2 {
- val |= uint(in[si+1]) << 8
- }
- w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F])
- switch remain {
- case 2:
- w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar))
- case 1:
- w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar))
- }
- }
|