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.
199 lines
4.1 KiB
199 lines
4.1 KiB
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, ' ') |
|
} |
|
}
|
|
|