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
}
type EncodeOption int
const (
EncodeOptionHTMLEscape EncodeOption = 1 << iota
EncodeOptionIndent
EncodeOptionUnorderedMap
EncodeOptionDebug
)
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
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 {
var opt EncodeOption
initOption(ctx.Option)
if e.enabledHTMLEscape {
opt |= EncodeOptionHTMLEscape
ctx.Option.HTMLEscape = true
}
for _, optFunc := range optFuncs {
opt = optFunc(opt)
optFunc(ctx.Option)
}
var (
buf []byte
err error
)
if e.enabledIndent {
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt)
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr)
} else {
buf, err = encode(ctx, v, opt)
buf, err = encode(ctx, v)
}
if err != nil {
return err
@ -103,10 +94,16 @@ func (e *Encoder) SetIndent(prefix, indent string) {
e.enabledIndent = true
}
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
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 {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -124,10 +121,13 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
return copied, nil
}
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
func marshalNoEscape(v interface{}) ([]byte, error) {
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 {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -145,10 +145,17 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
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()
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 {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -162,7 +169,7 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
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]
if v == nil {
b = encoder.AppendNull(b)
@ -180,7 +187,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
p := uintptr(header.ptr)
var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 {
if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode
} else {
code = codeSet.NoescapeKeyCode
@ -188,7 +195,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
ctx.Init(code, p, codeSet.CodeLength)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil {
return nil, err
}
@ -196,7 +203,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
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]
if v == nil {
b = encoder.AppendNull(b)
@ -213,14 +220,14 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
}
var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 {
if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode
} else {
code = codeSet.NoescapeKeyCode
}
p := uintptr(header.ptr)
ctx.Init(code, p, codeSet.CodeLength)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil {
return nil, err
}
@ -229,7 +236,7 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
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]
if v == nil {
b = encoder.AppendNull(b)
@ -246,14 +253,14 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
}
var code *encoder.Opcode
if (opt & EncodeOptionHTMLEscape) != 0 {
if ctx.Option.HTMLEscape {
code = codeSet.EscapeKeyCode
} else {
code = codeSet.NoescapeKeyCode
}
p := uintptr(header.ptr)
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)
@ -265,38 +272,45 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
return buf, nil
}
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
if (opt & EncodeOptionDebug) != 0 {
return encodeDebugRunCode(ctx, b, codeSet, opt)
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if ctx.Option.Debug {
return encodeDebugRunCode(ctx, b, codeSet)
}
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped.Run(ctx, b, codeSet, encoder.Option(opt))
if ctx.Option.HTMLEscape {
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) {
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped.DebugRun(ctx, b, codeSet, encoder.Option(opt))
func encodeDebugRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if ctx.Option.HTMLEscape {
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.IndentStr = []byte(indent)
if (opt & EncodeOptionDebug) != 0 {
return encodeDebugRunIndentCode(ctx, b, codeSet, opt)
if ctx.Option.Debug {
return encodeDebugRunIndentCode(ctx, b, codeSet)
}
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped_indent.Run(ctx, b, codeSet, encoder.Option(opt))
if ctx.Option.HTMLEscape {
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) {
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped_indent.DebugRun(ctx, b, codeSet, encoder.Option(opt))
func encodeDebugRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if ctx.Option.HTMLEscape {
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"
)
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
ptrOffset := uintptr(0)
ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil {
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.Length, uintptr(mlen))
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.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
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)
length := load(ctxptr, code.Length)
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)

View File

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

View File

@ -17,14 +17,6 @@ import (
"github.com/goccy/go-json/internal/runtime"
)
type Option int
const (
HTMLEscapeOption Option = 1 << iota
IndentOption
UnorderedMapOption
)
func (t OpType) IsMultipleOpHead() bool {
switch t {
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"
)
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() {
if err := recover(); err != nil {
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
}
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))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
@ -148,7 +148,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
ctx.Ptrs = newPtrs
ctx.Code = ifaceCodeSet.NoescapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt)
bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil {
return nil, err
}

View File

@ -9,7 +9,7 @@ import (
"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
ptrOffset := uintptr(0)
ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil {
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.Length, uintptr(mlen))
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.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
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)
length := load(ctxptr, code.Length)
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"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() {
if err := recover(); err != nil {
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
}
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))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
@ -148,7 +148,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
ctx.Ptrs = newPtrs
ctx.Code = ifaceCodeSet.EscapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt)
bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil {
return nil, err
}

View File

@ -9,7 +9,7 @@ import (
"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
ptrOffset := uintptr(0)
ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil {
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.Length, uintptr(mlen))
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.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
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)
length := load(ctxptr, code.Length)
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"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() {
if err := recover(); err != nil {
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, ':', ' ')
}
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))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
@ -138,7 +138,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
ctx.Code = ifaceCodeSet.EscapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt)
bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil {
return nil, err
}

View File

@ -9,7 +9,7 @@ import (
"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
ptrOffset := uintptr(0)
ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil {
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.Length, uintptr(mlen))
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.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
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)
length := load(ctxptr, code.Length)
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)

View File

@ -6,7 +6,7 @@ import (
"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() {
if err := recover(); err != nil {
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, ':', ' ')
}
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))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
@ -138,7 +138,7 @@ func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, op
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
ctx.Code = ifaceCodeSet.NoescapeKeyCode
bb, err := Run(ctx, b, ifaceCodeSet, opt)
bb, err := Run(ctx, b, ifaceCodeSet)
if err != nil {
return nil, err
}

View File

@ -9,7 +9,7 @@ import (
"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
ptrOffset := uintptr(0)
ctxptr := ctx.Ptr()
@ -185,7 +185,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
bb, err := appendInterface(ctx, codeSet, code, b, iface, ptrOffset)
if err != nil {
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.Length, uintptr(mlen))
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.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
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)
length := load(ctxptr, code.Length)
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
if ctx.Option.UnorderedMap {
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)

16
json.go
View File

@ -160,16 +160,12 @@ func Marshal(v interface{}) ([]byte, error) {
// MarshalNoEscape
func MarshalNoEscape(v interface{}) ([]byte, error) {
return marshalNoEscape(v, EncodeOptionHTMLEscape)
return marshalNoEscape(v)
}
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshal(v, opt)
return marshal(v, optFuncs...)
}
// 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.
func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape | EncodeOptionIndent
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshalIndent(v, prefix, indent, opt)
return marshalIndent(v, prefix, indent, optFuncs...)
}
// 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 {
return
}
buf, _ := marshal(v, EncodeOptionHTMLEscape)
buf, _ := marshal(v)
dst.Write(buf)
}

View File

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