package json
import (
)
type kind uint8
const (
_ kind = (1 << iota) / 2
name
scalar
objectOpen
objectClose
arrayOpen
arrayClose
)
type Encoder struct {
indent string
lastKind kind
indents []byte
out []byte
}
func ( string) (*Encoder, error) {
:= &Encoder{}
if len() > 0 {
if strings.Trim(, " \t") != "" {
return nil, errors.New("indent may only be composed of space or tab characters")
}
.indent =
}
return , nil
}
func ( *Encoder) () []byte {
return .out
}
func ( *Encoder) () {
.prepareNext(scalar)
.out = append(.out, "null"...)
}
func ( *Encoder) ( bool) {
.prepareNext(scalar)
if {
.out = append(.out, "true"...)
} else {
.out = append(.out, "false"...)
}
}
func ( *Encoder) ( string) error {
.prepareNext(scalar)
var error
if .out, = appendString(.out, ); != nil {
return
}
return nil
}
var errInvalidUTF8 = errors.New("invalid UTF-8")
func ( []byte, string) ([]byte, error) {
= append(, '"')
:= indexNeedEscapeInString()
, = [:], append(, [:]...)
for len() > 0 {
switch , := utf8.DecodeRuneInString(); {
case == utf8.RuneError && == 1:
return , errInvalidUTF8
case < ' ' || == '"' || == '\\':
= append(, '\\')
switch {
case '"', '\\':
= append(, byte())
case '\b':
= append(, 'b')
case '\f':
= append(, 'f')
case '\n':
= append(, 'n')
case '\r':
= append(, 'r')
case '\t':
= append(, 't')
default:
= append(, 'u')
= append(, "0000"[1+(bits.Len32(uint32())-1)/4:]...)
= strconv.AppendUint(, uint64(), 16)
}
= [:]
default:
:= indexNeedEscapeInString([:])
, = [+:], append(, [:+]...)
}
}
= append(, '"')
return , nil
}
func ( string) int {
for , := range {
if < ' ' || == '\\' || == '"' || == utf8.RuneError {
return
}
}
return len()
}
func ( *Encoder) ( float64, int) {
.prepareNext(scalar)
.out = appendFloat(.out, , )
}
func ( []byte, float64, int) []byte {
switch {
case math.IsNaN():
return append(, `"NaN"`...)
case math.IsInf(, +1):
return append(, `"Infinity"`...)
case math.IsInf(, -1):
return append(, `"-Infinity"`...)
}
:= byte('f')
if := math.Abs(); != 0 {
if == 64 && ( < 1e-6 || >= 1e21) ||
== 32 && (float32() < 1e-6 || float32() >= 1e21) {
= 'e'
}
}
= strconv.AppendFloat(, , , -1, )
if == 'e' {
:= len()
if >= 4 && [-4] == 'e' && [-3] == '-' && [-2] == '0' {
[-2] = [-1]
= [:-1]
}
}
return
}
func ( *Encoder) ( int64) {
.prepareNext(scalar)
.out = append(.out, strconv.FormatInt(, 10)...)
}
func ( *Encoder) ( uint64) {
.prepareNext(scalar)
.out = append(.out, strconv.FormatUint(, 10)...)
}
func ( *Encoder) () {
.prepareNext(objectOpen)
.out = append(.out, '{')
}
func ( *Encoder) () {
.prepareNext(objectClose)
.out = append(.out, '}')
}
func ( *Encoder) ( string) error {
.prepareNext(name)
var error
.out, = appendString(.out, )
.out = append(.out, ':')
return
}
func ( *Encoder) () {
.prepareNext(arrayOpen)
.out = append(.out, '[')
}
func ( *Encoder) () {
.prepareNext(arrayClose)
.out = append(.out, ']')
}
func ( *Encoder) ( kind) {
defer func() {
.lastKind =
}()
if len(.indent) == 0 {
if .lastKind&(scalar|objectClose|arrayClose) != 0 &&
&(name|scalar|objectOpen|arrayOpen) != 0 {
.out = append(.out, ',')
if detrand.Bool() {
.out = append(.out, ' ')
}
}
return
}
switch {
case .lastKind&(objectOpen|arrayOpen) != 0:
if &(objectClose|arrayClose) == 0 {
.indents = append(.indents, .indent...)
.out = append(.out, '\n')
.out = append(.out, .indents...)
}
case .lastKind&(scalar|objectClose|arrayClose) != 0:
switch {
case &(name|scalar|objectOpen|arrayOpen) != 0:
.out = append(.out, ',', '\n')
case &(objectClose|arrayClose) != 0:
.indents = .indents[:len(.indents)-len(.indent)]
.out = append(.out, '\n')
}
.out = append(.out, .indents...)
case .lastKind&name != 0:
.out = append(.out, ' ')
if detrand.Bool() {
.out = append(.out, ' ')
}
}
}