Refactor encode option

This commit is contained in:
Masaaki Goshima 2021-05-31 22:25:33 +09:00
parent ca4e811a0b
commit a56c080959
19 changed files with 141 additions and 127 deletions

112
encode.go
View File

@ -20,15 +20,6 @@ type Encoder struct {
indentStr string indentStr string
} }
type EncodeOption int
const (
EncodeOptionHTMLEscape EncodeOption = 1 << iota
EncodeOptionIndent
EncodeOptionUnorderedMap
EncodeOptionDebug
)
// NewEncoder returns a new encoder that writes to w. // NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder { func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, enabledHTMLEscape: true} return &Encoder{w: w, enabledHTMLEscape: true}
@ -52,21 +43,21 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
} }
func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error { func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
var opt EncodeOption initOption(ctx.Option)
if e.enabledHTMLEscape { if e.enabledHTMLEscape {
opt |= EncodeOptionHTMLEscape ctx.Option.HTMLEscape = true
} }
for _, optFunc := range optFuncs { for _, optFunc := range optFuncs {
opt = optFunc(opt) optFunc(ctx.Option)
} }
var ( var (
buf []byte buf []byte
err error err error
) )
if e.enabledIndent { if e.enabledIndent {
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt) buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr)
} else { } else {
buf, err = encode(ctx, v, opt) buf, err = encode(ctx, v)
} }
if err != nil { if err != nil {
return err return err
@ -103,10 +94,16 @@ func (e *Encoder) SetIndent(prefix, indent string) {
e.enabledIndent = true e.enabledIndent = true
} }
func marshal(v interface{}, opt EncodeOption) ([]byte, error) { func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
ctx := encoder.TakeRuntimeContext() ctx := encoder.TakeRuntimeContext()
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape) initOption(ctx.Option)
ctx.Option.HTMLEscape = true
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
buf, err := encode(ctx, v)
if err != nil { if err != nil {
encoder.ReleaseRuntimeContext(ctx) encoder.ReleaseRuntimeContext(ctx)
return nil, err return nil, err
@ -124,10 +121,13 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
return copied, nil return copied, nil
} }
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) { func marshalNoEscape(v interface{}) ([]byte, error) {
ctx := encoder.TakeRuntimeContext() ctx := encoder.TakeRuntimeContext()
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape) initOption(ctx.Option)
ctx.Option.HTMLEscape = true
buf, err := encodeNoEscape(ctx, v)
if err != nil { if err != nil {
encoder.ReleaseRuntimeContext(ctx) encoder.ReleaseRuntimeContext(ctx)
return nil, err return nil, err
@ -145,10 +145,17 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
return copied, nil return copied, nil
} }
func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) { func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
ctx := encoder.TakeRuntimeContext() ctx := encoder.TakeRuntimeContext()
buf, err := encodeIndent(ctx, v, prefix, indent, opt|EncodeOptionHTMLEscape) initOption(ctx.Option)
ctx.Option.HTMLEscape = true
ctx.Option.Indent = true
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
buf, err := encodeIndent(ctx, v, prefix, indent)
if err != nil { if err != nil {
encoder.ReleaseRuntimeContext(ctx) encoder.ReleaseRuntimeContext(ctx)
return nil, err return nil, err
@ -162,7 +169,7 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
return copied, nil return copied, nil
} }
func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) { func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
b := ctx.Buf[:0] b := ctx.Buf[:0]
if v == nil { if v == nil {
b = encoder.AppendNull(b) b = encoder.AppendNull(b)
@ -180,7 +187,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
p := uintptr(header.ptr) p := uintptr(header.ptr)
var code *encoder.Opcode var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode code = codeSet.EscapeKeyCode
} else { } else {
code = codeSet.NoescapeKeyCode code = codeSet.NoescapeKeyCode
@ -188,7 +195,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
ctx.Init(code, p, codeSet.CodeLength) ctx.Init(code, p, codeSet.CodeLength)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr) ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
buf, err := encodeRunCode(ctx, b, codeSet, opt) buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -196,7 +203,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
return buf, nil return buf, nil
} }
func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) { func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
b := ctx.Buf[:0] b := ctx.Buf[:0]
if v == nil { if v == nil {
b = encoder.AppendNull(b) b = encoder.AppendNull(b)
@ -213,14 +220,14 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
} }
var code *encoder.Opcode var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode code = codeSet.EscapeKeyCode
} else { } else {
code = codeSet.NoescapeKeyCode code = codeSet.NoescapeKeyCode
} }
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.Init(code, p, codeSet.CodeLength) ctx.Init(code, p, codeSet.CodeLength)
buf, err := encodeRunCode(ctx, b, codeSet, opt) buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -229,7 +236,7 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
return buf, nil return buf, nil
} }
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) { func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string) ([]byte, error) {
b := ctx.Buf[:0] b := ctx.Buf[:0]
if v == nil { if v == nil {
b = encoder.AppendNull(b) b = encoder.AppendNull(b)
@ -246,14 +253,14 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
} }
var code *encoder.Opcode var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode code = codeSet.EscapeKeyCode
} else { } else {
code = codeSet.NoescapeKeyCode code = codeSet.NoescapeKeyCode
} }
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.Init(code, p, codeSet.CodeLength) ctx.Init(code, p, codeSet.CodeLength)
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt) buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr) ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
@ -265,38 +272,45 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
return buf, nil return buf, nil
} }
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) { func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if (opt & EncodeOptionDebug) != 0 { if ctx.Option.Debug {
return encodeDebugRunCode(ctx, b, codeSet, opt) return encodeDebugRunCode(ctx, b, codeSet)
} }
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
return vm_escaped.Run(ctx, b, codeSet, encoder.Option(opt)) return vm_escaped.Run(ctx, b, codeSet)
} }
return vm.Run(ctx, b, codeSet, encoder.Option(opt)) return vm.Run(ctx, b, codeSet)
} }
func encodeDebugRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) { func encodeDebugRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
return vm_escaped.DebugRun(ctx, b, codeSet, encoder.Option(opt)) return vm_escaped.DebugRun(ctx, b, codeSet)
} }
return vm.DebugRun(ctx, b, codeSet, encoder.Option(opt)) return vm.DebugRun(ctx, b, codeSet)
} }
func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) { func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string) ([]byte, error) {
ctx.Prefix = []byte(prefix) ctx.Prefix = []byte(prefix)
ctx.IndentStr = []byte(indent) ctx.IndentStr = []byte(indent)
if (opt & EncodeOptionDebug) != 0 { if ctx.Option.Debug {
return encodeDebugRunIndentCode(ctx, b, codeSet, opt) return encodeDebugRunIndentCode(ctx, b, codeSet)
} }
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
return vm_escaped_indent.Run(ctx, b, codeSet, encoder.Option(opt)) return vm_escaped_indent.Run(ctx, b, codeSet)
} }
return vm_indent.Run(ctx, b, codeSet, encoder.Option(opt)) return vm_indent.Run(ctx, b, codeSet)
} }
func encodeDebugRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) { func encodeDebugRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if (opt & EncodeOptionHTMLEscape) != 0 { if ctx.Option.HTMLEscape {
return vm_escaped_indent.DebugRun(ctx, b, codeSet, encoder.Option(opt)) return vm_escaped_indent.DebugRun(ctx, b, codeSet)
} }
return vm_indent.DebugRun(ctx, b, codeSet, encoder.Option(opt)) return vm_indent.DebugRun(ctx, b, codeSet)
}
func initOption(opt *EncodeOption) {
opt.HTMLEscape = false
opt.Indent = false
opt.UnorderedMap = false
opt.Debug = false
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.Ptr() ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset) bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,13 +365,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen)) store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter)) store(ctxptr, code.MapIter, uintptr(iter))
if (opt & encoder.UnorderedMapOption) == 0 { if ctx.Option.UnorderedMap {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen) mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -380,7 +380,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
b = appendColon(b) b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)

View File

@ -97,6 +97,7 @@ var (
Buf: make([]byte, 0, bufSize), Buf: make([]byte, 0, bufSize),
Ptrs: make([]uintptr, 128), Ptrs: make([]uintptr, 128),
KeepRefs: make([]unsafe.Pointer, 0, 8), KeepRefs: make([]unsafe.Pointer, 0, 8),
Option: &Option{},
} }
}, },
} }
@ -112,6 +113,7 @@ type RuntimeContext struct {
Prefix []byte Prefix []byte
IndentStr []byte IndentStr []byte
Code *Opcode Code *Opcode
Option *Option
} }
func (c *RuntimeContext) Init(code *Opcode, p uintptr, codelen int) { func (c *RuntimeContext) Init(code *Opcode, p uintptr, codelen int) {

View File

@ -17,14 +17,6 @@ import (
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
type Option int
const (
HTMLEscapeOption Option = 1 << iota
IndentOption
UnorderedMapOption
)
func (t OpType) IsMultipleOpHead() bool { func (t OpType) IsMultipleOpHead() bool {
switch t { switch t {
case OpStructHead: case OpStructHead:

View File

@ -0,0 +1,8 @@
package encoder
type Option struct {
HTMLEscape bool
Indent bool
UnorderedMap bool
Debug bool
}

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
fmt.Println("=============[DEBUG]===============") fmt.Println("=============[DEBUG]===============")
@ -23,5 +23,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
} }
}() }()
return Run(ctx, b, codeSet, opt) return Run(ctx, b, codeSet)
} }

View File

@ -123,7 +123,7 @@ func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte
return b return b
} }
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) { func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
@ -148,7 +148,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
ctx.Ptrs = newPtrs ctx.Ptrs = newPtrs
ctx.Code = ifaceCodeSet.NoescapeKeyCode ctx.Code = ifaceCodeSet.NoescapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt) bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.Ptr() ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset) bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,13 +365,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen)) store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter)) store(ctxptr, code.MapIter, uintptr(iter))
if (opt & encoder.UnorderedMapOption) == 0 { if ctx.Option.UnorderedMap {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen) mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -380,7 +380,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
b = appendColon(b) b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
fmt.Println("=============[DEBUG]===============") fmt.Println("=============[DEBUG]===============")
@ -23,5 +23,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
} }
}() }()
return Run(ctx, b, codeSet, opt) return Run(ctx, b, codeSet)
} }

View File

@ -123,7 +123,7 @@ func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte
return b return b
} }
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) { func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
@ -148,7 +148,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
ctx.Ptrs = newPtrs ctx.Ptrs = newPtrs
ctx.Code = ifaceCodeSet.EscapeKeyCode ctx.Code = ifaceCodeSet.EscapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt) bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.Ptr() ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset) bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,13 +365,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen)) store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter)) store(ctxptr, code.MapIter, uintptr(iter))
if (opt & encoder.UnorderedMapOption) == 0 { if ctx.Option.UnorderedMap {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen) mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -380,7 +380,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
b = appendColon(b) b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
fmt.Println("=============[DEBUG]===============") fmt.Println("=============[DEBUG]===============")
@ -23,5 +23,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
} }
}() }()
return Run(ctx, b, codeSet, opt) return Run(ctx, b, codeSet)
} }

View File

@ -111,7 +111,7 @@ func appendColon(b []byte) []byte {
return append(b, ':', ' ') return append(b, ':', ' ')
} }
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) { func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
@ -138,7 +138,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
oldBaseIndent := ctx.BaseIndent oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent ctx.BaseIndent = code.Indent
ctx.Code = ifaceCodeSet.EscapeKeyCode ctx.Code = ifaceCodeSet.EscapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt) bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.Ptr() ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset) bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,13 +365,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen)) store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter)) store(ctxptr, code.MapIter, uintptr(iter))
if (opt & encoder.UnorderedMapOption) == 0 { if ctx.Option.UnorderedMap {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen) mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -380,7 +380,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
b = appendColon(b) b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
fmt.Println("=============[DEBUG]===============") fmt.Println("=============[DEBUG]===============")
@ -23,5 +23,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
} }
}() }()
return Run(ctx, b, codeSet, opt) return Run(ctx, b, codeSet)
} }

View File

@ -111,7 +111,7 @@ func appendColon(b []byte) []byte {
return append(b, ':', ' ') return append(b, ':', ' ')
} }
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) { func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
@ -138,7 +138,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
oldBaseIndent := ctx.BaseIndent oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent ctx.BaseIndent = code.Indent
ctx.Code = ifaceCodeSet.NoescapeKeyCode ctx.Code = ifaceCodeSet.NoescapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt) bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -9,7 +9,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
) )
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.Ptr() ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset) bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -365,13 +365,13 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(mlen)) store(ctxptr, code.Length, uintptr(mlen))
store(ctxptr, code.MapIter, uintptr(iter)) store(ctxptr, code.MapIter, uintptr(iter))
if (opt & encoder.UnorderedMapOption) == 0 { if ctx.Option.UnorderedMap {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx := encoder.NewMapContext(mlen) mapCtx := encoder.NewMapContext(mlen)
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -380,7 +380,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if ctx.Option.UnorderedMap {
b = appendColon(b) b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)

16
json.go
View File

@ -160,16 +160,12 @@ func Marshal(v interface{}) ([]byte, error) {
// MarshalNoEscape // MarshalNoEscape
func MarshalNoEscape(v interface{}) ([]byte, error) { func MarshalNoEscape(v interface{}) ([]byte, error) {
return marshalNoEscape(v, EncodeOptionHTMLEscape) return marshalNoEscape(v)
} }
// MarshalWithOption returns the JSON encoding of v with EncodeOption. // MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) { func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape return marshal(v, optFuncs...)
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshal(v, opt)
} }
// MarshalIndent is like Marshal but applies Indent to format the output. // MarshalIndent is like Marshal but applies Indent to format the output.
@ -181,11 +177,7 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption. // MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) { func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape | EncodeOptionIndent return marshalIndent(v, prefix, indent, optFuncs...)
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshalIndent(v, prefix, indent, opt)
} }
// Unmarshal parses the JSON-encoded data and stores the result // Unmarshal parses the JSON-encoded data and stores the result
@ -326,7 +318,7 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
if err := dec.Decode(&v); err != nil { if err := dec.Decode(&v); err != nil {
return return
} }
buf, _ := marshal(v, EncodeOptionHTMLEscape) buf, _ := marshal(v)
dst.Write(buf) dst.Write(buf)
} }

View File

@ -1,15 +1,21 @@
package json package json
type EncodeOptionFunc func(EncodeOption) EncodeOption import (
"github.com/goccy/go-json/internal/encoder"
)
func UnorderedMap() func(EncodeOption) EncodeOption { type EncodeOption = encoder.Option
return func(opt EncodeOption) EncodeOption {
return opt | EncodeOptionUnorderedMap type EncodeOptionFunc func(*EncodeOption)
func UnorderedMap() EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.UnorderedMap = true
} }
} }
func Debug() func(EncodeOption) EncodeOption { func Debug() EncodeOptionFunc {
return func(opt EncodeOption) EncodeOption { return func(opt *EncodeOption) {
return opt | EncodeOptionDebug opt.Debug = true
} }
} }