You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
4.1 KiB
200 lines
4.1 KiB
3 years ago
|
package jx
|
||
|
|
||
|
import "io"
|
||
|
|
||
|
// Encoder encodes json to underlying buffer.
|
||
|
//
|
||
|
// Zero value is valid.
|
||
|
type Encoder struct {
|
||
|
w Writer // underlying writer
|
||
|
indent int // count of spaces for single indentation level
|
||
|
|
||
|
// first handles state for comma and indentation writing.
|
||
|
//
|
||
|
// New Object or Array appends new level to this slice, and
|
||
|
// last element of this slice denotes whether first element was written.
|
||
|
//
|
||
|
// We write commas only before non-first element of Array or Object.
|
||
|
//
|
||
|
// See comma, begin, end and FieldStart for implementation details.
|
||
|
//
|
||
|
// Note: probably, this can be optimized as bit set to ease memory
|
||
|
// consumption.
|
||
|
//
|
||
|
// See https://yourbasic.org/algorithms/your-basic-int/#simple-sets
|
||
|
first []bool
|
||
|
}
|
||
|
|
||
|
// Write implements io.Writer.
|
||
|
func (e *Encoder) Write(p []byte) (n int, err error) {
|
||
|
return e.w.Write(p)
|
||
|
}
|
||
|
|
||
|
// WriteTo implements io.WriterTo.
|
||
|
func (e *Encoder) WriteTo(w io.Writer) (n int64, err error) {
|
||
|
return e.w.WriteTo(w)
|
||
|
}
|
||
|
|
||
|
// SetIdent sets length of single indentation step.
|
||
|
func (e *Encoder) SetIdent(n int) {
|
||
|
e.indent = n
|
||
|
}
|
||
|
|
||
|
// String returns string of underlying buffer.
|
||
|
func (e Encoder) String() string {
|
||
|
return string(e.Bytes())
|
||
|
}
|
||
|
|
||
|
// Reset resets underlying buffer.
|
||
|
func (e *Encoder) Reset() {
|
||
|
e.w.Buf = e.w.Buf[:0]
|
||
|
e.first = e.first[:0]
|
||
|
}
|
||
|
|
||
|
// Bytes returns underlying buffer.
|
||
|
func (e Encoder) Bytes() []byte { return e.w.Buf }
|
||
|
|
||
|
// SetBytes sets underlying buffer.
|
||
|
func (e *Encoder) SetBytes(buf []byte) { e.w.Buf = buf }
|
||
|
|
||
|
// byte writes a single byte.
|
||
|
func (e *Encoder) byte(c byte) {
|
||
|
e.w.Buf = append(e.w.Buf, c)
|
||
|
}
|
||
|
|
||
|
// RawStr writes string as raw json.
|
||
|
func (e *Encoder) RawStr(v string) {
|
||
|
e.comma()
|
||
|
e.w.RawStr(v)
|
||
|
}
|
||
|
|
||
|
// Raw writes byte slice as raw json.
|
||
|
func (e *Encoder) Raw(b []byte) {
|
||
|
e.comma()
|
||
|
e.w.Raw(b)
|
||
|
}
|
||
|
|
||
|
// Null writes null.
|
||
|
func (e *Encoder) Null() {
|
||
|
e.comma()
|
||
|
e.w.Null()
|
||
|
}
|
||
|
|
||
|
// Bool encodes boolean.
|
||
|
func (e *Encoder) Bool(v bool) {
|
||
|
e.comma()
|
||
|
e.w.Bool(v)
|
||
|
}
|
||
|
|
||
|
// ObjStart writes object start, performing indentation if needed.
|
||
|
//
|
||
|
// Use Obj as convenience helper for writing objects.
|
||
|
func (e *Encoder) ObjStart() {
|
||
|
e.comma()
|
||
|
e.w.ObjStart()
|
||
|
e.begin()
|
||
|
e.writeIndent()
|
||
|
}
|
||
|
|
||
|
// FieldStart encodes field name and writes colon.
|
||
|
//
|
||
|
// For non-zero indentation also writes single space after colon.
|
||
|
//
|
||
|
// Use Field as convenience helper for encoding fields.
|
||
|
func (e *Encoder) FieldStart(field string) {
|
||
|
e.comma()
|
||
|
e.w.FieldStart(field)
|
||
|
if e.indent > 0 {
|
||
|
e.byte(' ')
|
||
|
}
|
||
|
if len(e.first) > 0 {
|
||
|
e.first[e.current()] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Field encodes field start and then invokes callback.
|
||
|
//
|
||
|
// Has ~5ns overhead over FieldStart.
|
||
|
func (e *Encoder) Field(name string, f func(e *Encoder)) {
|
||
|
e.FieldStart(name)
|
||
|
f(e)
|
||
|
}
|
||
|
|
||
|
// ObjEnd writes end of object token, performing indentation if needed.
|
||
|
//
|
||
|
// Use Obj as convenience helper for writing objects.
|
||
|
func (e *Encoder) ObjEnd() {
|
||
|
e.end()
|
||
|
e.writeIndent()
|
||
|
e.w.ObjEnd()
|
||
|
}
|
||
|
|
||
|
// ObjEmpty writes empty object.
|
||
|
func (e *Encoder) ObjEmpty() {
|
||
|
e.comma()
|
||
|
e.w.ObjStart()
|
||
|
e.w.ObjEnd()
|
||
|
}
|
||
|
|
||
|
// Obj writes start of object, invokes callback and writes end of object.
|
||
|
//
|
||
|
// If callback is nil, writes empty object.
|
||
|
func (e *Encoder) Obj(f func(e *Encoder)) {
|
||
|
if f == nil {
|
||
|
e.ObjEmpty()
|
||
|
return
|
||
|
}
|
||
|
e.ObjStart()
|
||
|
f(e)
|
||
|
e.ObjEnd()
|
||
|
}
|
||
|
|
||
|
// ArrStart writes start of array, performing indentation if needed.
|
||
|
//
|
||
|
// Use Arr as convenience helper for writing arrays.
|
||
|
func (e *Encoder) ArrStart() {
|
||
|
e.comma()
|
||
|
e.w.ArrStart()
|
||
|
e.begin()
|
||
|
e.writeIndent()
|
||
|
}
|
||
|
|
||
|
// ArrEmpty writes empty array.
|
||
|
func (e *Encoder) ArrEmpty() {
|
||
|
e.comma()
|
||
|
e.w.ArrStart()
|
||
|
e.w.ArrEnd()
|
||
|
}
|
||
|
|
||
|
// ArrEnd writes end of array, performing indentation if needed.
|
||
|
//
|
||
|
// Use Arr as convenience helper for writing arrays.
|
||
|
func (e *Encoder) ArrEnd() {
|
||
|
e.end()
|
||
|
e.writeIndent()
|
||
|
e.w.ArrEnd()
|
||
|
}
|
||
|
|
||
|
// Arr writes start of array, invokes callback and writes end of array.
|
||
|
//
|
||
|
// If callback is nil, writes empty array.
|
||
|
func (e *Encoder) Arr(f func(e *Encoder)) {
|
||
|
if f == nil {
|
||
|
e.ArrEmpty()
|
||
|
return
|
||
|
}
|
||
|
e.ArrStart()
|
||
|
f(e)
|
||
|
e.ArrEnd()
|
||
|
}
|
||
|
|
||
|
func (e *Encoder) writeIndent() {
|
||
|
if e.indent == 0 {
|
||
|
return
|
||
|
}
|
||
|
e.byte('\n')
|
||
|
for i := 0; i < len(e.first)*e.indent; i++ {
|
||
|
e.w.Buf = append(e.w.Buf, ' ')
|
||
|
}
|
||
|
}
|