// Package jwriter contains a JSON writer.
package jwriter import ( ) // 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 ( *Writer) () int { return .Buffer.Size() } // DumpTo outputs the data to given io.Writer, resetting the buffer. func ( *Writer) ( io.Writer) ( int, error) { return .Buffer.DumpTo() } // 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 ( *Writer) ( ...[]byte) ([]byte, error) { if .Error != nil { return nil, .Error } return .Buffer.BuildBytes(...), nil } // ReadCloser returns an io.ReadCloser that can be used to read the data. // ReadCloser also resets the buffer. func ( *Writer) () (io.ReadCloser, error) { if .Error != nil { return nil, .Error } return .Buffer.ReadCloser(), nil } // RawByte appends raw binary data to the buffer. func ( *Writer) ( byte) { .Buffer.AppendByte() } // RawByte appends raw binary data to the buffer. func ( *Writer) ( string) { .Buffer.AppendString() } // 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 ( *Writer) ( []byte, error) { switch { case .Error != nil: return case != nil: .Error = case len() > 0: .Buffer.AppendBytes() default: .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 ( *Writer) ( []byte, error) { switch { case .Error != nil: return case != nil: .Error = case len() > 0: .String(string()) default: .RawString("null") } } // Base64Bytes appends data to the buffer after base64 encoding it func ( *Writer) ( []byte) { if == nil { .Buffer.AppendString("null") return } .Buffer.AppendByte('"') .base64() .Buffer.AppendByte('"') } func ( *Writer) ( uint8) { .Buffer.EnsureSpace(3) .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) } func ( *Writer) ( uint16) { .Buffer.EnsureSpace(5) .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) } func ( *Writer) ( uint32) { .Buffer.EnsureSpace(10) .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) } func ( *Writer) ( uint) { .Buffer.EnsureSpace(20) .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) } func ( *Writer) ( uint64) { .Buffer.EnsureSpace(20) .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, , 10) } func ( *Writer) ( int8) { .Buffer.EnsureSpace(4) .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) } func ( *Writer) ( int16) { .Buffer.EnsureSpace(6) .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) } func ( *Writer) ( int32) { .Buffer.EnsureSpace(11) .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) } func ( *Writer) ( int) { .Buffer.EnsureSpace(21) .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) } func ( *Writer) ( int64) { .Buffer.EnsureSpace(21) .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, , 10) } func ( *Writer) ( uint8) { .Buffer.EnsureSpace(3) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( uint16) { .Buffer.EnsureSpace(5) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( uint32) { .Buffer.EnsureSpace(10) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( uint) { .Buffer.EnsureSpace(20) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( uint64) { .Buffer.EnsureSpace(20) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, , 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( uintptr) { .Buffer.EnsureSpace(20) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendUint(.Buffer.Buf, uint64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( int8) { .Buffer.EnsureSpace(4) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( int16) { .Buffer.EnsureSpace(6) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( int32) { .Buffer.EnsureSpace(11) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( int) { .Buffer.EnsureSpace(21) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, int64(), 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( int64) { .Buffer.EnsureSpace(21) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendInt(.Buffer.Buf, , 10) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( float32) { .Buffer.EnsureSpace(20) .Buffer.Buf = strconv.AppendFloat(.Buffer.Buf, float64(), 'g', -1, 32) } func ( *Writer) ( float32) { .Buffer.EnsureSpace(20) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendFloat(.Buffer.Buf, float64(), 'g', -1, 32) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( float64) { .Buffer.EnsureSpace(20) .Buffer.Buf = strconv.AppendFloat(.Buffer.Buf, , 'g', -1, 64) } func ( *Writer) ( float64) { .Buffer.EnsureSpace(20) .Buffer.Buf = append(.Buffer.Buf, '"') .Buffer.Buf = strconv.AppendFloat(.Buffer.Buf, float64(), 'g', -1, 64) .Buffer.Buf = append(.Buffer.Buf, '"') } func ( *Writer) ( bool) { .Buffer.EnsureSpace(5) if { .Buffer.Buf = append(.Buffer.Buf, "true"...) } else { .Buffer.Buf = append(.Buffer.Buf, "false"...) } } const chars = "0123456789abcdef" func getTable( ...int) [128]bool { := [128]bool{} for := 0; < 128; ++ { [] = true } for , := range { [] = false } return } var ( htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\') htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\') ) func ( *Writer) ( string) { .Buffer.AppendByte('"') // Portions of the string that contain no escapes are appended as // byte slices. := 0 // last non-escape symbol := &htmlEscapeTable if .NoEscapeHTML { = &htmlNoEscapeTable } for := 0; < len(); { := [] if < utf8.RuneSelf { if [] { // single-width character, no escaping is required ++ continue } .Buffer.AppendString([:]) switch { case '\t': .Buffer.AppendString(`\t`) case '\r': .Buffer.AppendString(`\r`) case '\n': .Buffer.AppendString(`\n`) case '\\': .Buffer.AppendString(`\\`) case '"': .Buffer.AppendString(`\"`) default: .Buffer.AppendString(`\u00`) .Buffer.AppendByte(chars[>>4]) .Buffer.AppendByte(chars[&0xf]) } ++ = continue } // broken utf , := utf8.DecodeRuneInString([:]) if == utf8.RuneError && == 1 { .Buffer.AppendString([:]) .Buffer.AppendString(`\ufffd`) ++ = continue } // jsonp stuff - tab separator and line separator if == '\u2028' || == '\u2029' { .Buffer.AppendString([:]) .Buffer.AppendString(`\u202`) .Buffer.AppendByte(chars[&0xf]) += = continue } += } .Buffer.AppendString([:]) .Buffer.AppendByte('"') } const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const padChar = '=' func ( *Writer) ( []byte) { if len() == 0 { return } .Buffer.EnsureSpace(((len()-1)/3 + 1) * 4) := 0 := (len() / 3) * 3 for < { // Convert 3x 8bit source bytes into 4 bytes := uint([+0])<<16 | uint([+1])<<8 | uint([+2]) .Buffer.Buf = append(.Buffer.Buf, encode[>>18&0x3F], encode[>>12&0x3F], encode[>>6&0x3F], encode[&0x3F]) += 3 } := len() - if == 0 { return } // Add the remaining small block := uint([+0]) << 16 if == 2 { |= uint([+1]) << 8 } .Buffer.Buf = append(.Buffer.Buf, encode[>>18&0x3F], encode[>>12&0x3F]) switch { case 2: .Buffer.Buf = append(.Buffer.Buf, encode[>>6&0x3F], byte(padChar)) case 1: .Buffer.Buf = append(.Buffer.Buf, byte(padChar), byte(padChar)) } }