package text
import (
)
type encType uint8
const (
_ encType = (1 << iota) / 2
name
scalar
messageOpen
messageClose
)
type Encoder struct {
encoderState
indent string
delims [2]byte
outputASCII bool
}
type encoderState struct {
lastType encType
indents []byte
out []byte
}
func ( string, [2]byte, bool) (*Encoder, error) {
:= &Encoder{}
if len() > 0 {
if strings.Trim(, " \t") != "" {
return nil, errors.New("indent may only be composed of space and tab characters")
}
.indent =
}
switch {
case [2]byte{0, 0}:
.delims = [2]byte{'{', '}'}
case [2]byte{'{', '}'}, [2]byte{'<', '>'}:
.delims =
default:
return nil, errors.New("delimiters may only be \"{}\" or \"<>\"")
}
.outputASCII =
return , nil
}
func ( *Encoder) () []byte {
return .out
}
func ( *Encoder) () {
.prepareNext(messageOpen)
.out = append(.out, .delims[0])
}
func ( *Encoder) () {
.prepareNext(messageClose)
.out = append(.out, .delims[1])
}
func ( *Encoder) ( string) {
.prepareNext(name)
.out = append(.out, ...)
.out = append(.out, ':')
}
func ( *Encoder) ( bool) {
if {
.WriteLiteral("true")
} else {
.WriteLiteral("false")
}
}
func ( *Encoder) ( string) {
.prepareNext(scalar)
.out = appendString(.out, , .outputASCII)
}
func ( []byte, string, bool) []byte {
= append(, '"')
:= indexNeedEscapeInString()
, = [:], append(, [:]...)
for len() > 0 {
switch , := utf8.DecodeRuneInString(); {
case == utf8.RuneError && == 1:
= rune([0])
fallthrough
case < ' ' || == '"' || == '\\' || == 0x7f:
= append(, '\\')
switch {
case '"', '\\':
= append(, byte())
case '\n':
= append(, 'n')
case '\r':
= append(, 'r')
case '\t':
= append(, 't')
default:
= append(, 'x')
= append(, "00"[1+(bits.Len32(uint32())-1)/4:]...)
= strconv.AppendUint(, uint64(), 16)
}
= [:]
case >= utf8.RuneSelf && ( || <= 0x009f):
= append(, '\\')
if <= math.MaxUint16 {
= append(, 'u')
= append(, "0000"[1+(bits.Len32(uint32())-1)/4:]...)
= strconv.AppendUint(, uint64(), 16)
} else {
= append(, 'U')
= append(, "00000000"[1+(bits.Len32(uint32())-1)/4:]...)
= strconv.AppendUint(, uint64(), 16)
}
= [:]
default:
:= indexNeedEscapeInString([:])
, = [+:], append(, [:+]...)
}
}
= append(, '"')
return
}
func ( string) int {
for := 0; < len(); ++ {
if := []; < ' ' || == '"' || == '\'' || == '\\' || >= 0x7f {
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(, "inf"...)
case math.IsInf(, -1):
return append(, "-inf"...)
default:
return strconv.AppendFloat(, , 'g', -1, )
}
}
func ( *Encoder) ( int64) {
.prepareNext(scalar)
.out = append(.out, strconv.FormatInt(, 10)...)
}
func ( *Encoder) ( uint64) {
.prepareNext(scalar)
.out = append(.out, strconv.FormatUint(, 10)...)
}
func ( *Encoder) ( string) {
.prepareNext(scalar)
.out = append(.out, ...)
}
func ( *Encoder) ( encType) {
defer func() {
.lastType =
}()
if len(.indent) == 0 {
if .lastType&(scalar|messageClose) != 0 && == name {
.out = append(.out, ' ')
if detrand.Bool() {
.out = append(.out, ' ')
}
}
return
}
switch {
case .lastType == name:
.out = append(.out, ' ')
if detrand.Bool() {
.out = append(.out, ' ')
}
case .lastType == messageOpen && != messageClose:
.indents = append(.indents, .indent...)
.out = append(.out, '\n')
.out = append(.out, .indents...)
case .lastType&(scalar|messageClose) != 0:
if == messageClose {
.indents = .indents[:len(.indents)-len(.indent)]
}
.out = append(.out, '\n')
.out = append(.out, .indents...)
}
}
func ( *Encoder) () encoderState {
return .encoderState
}
func ( *Encoder) ( encoderState) {
.encoderState =
}
func ( []byte, string) []byte {
return appendString(, , false)
}