diff --git a/encode.go b/encode.go index 9dad513..3da7b4f 100644 --- a/encode.go +++ b/encode.go @@ -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 } diff --git a/internal/cmd/generator/vm.go.tmpl b/internal/cmd/generator/vm.go.tmpl index 7af9d3a..b68a306 100644 --- a/internal/cmd/generator/vm.go.tmpl +++ b/internal/cmd/generator/vm.go.tmpl @@ -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) diff --git a/internal/encoder/context.go b/internal/encoder/context.go index 2a81e7b..e917134 100644 --- a/internal/encoder/context.go +++ b/internal/encoder/context.go @@ -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) { diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index 2f9b6a7..4ab4c4d 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -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: diff --git a/internal/encoder/option.go b/internal/encoder/option.go new file mode 100644 index 0000000..0e61a1f --- /dev/null +++ b/internal/encoder/option.go @@ -0,0 +1,8 @@ +package encoder + +type Option struct { + HTMLEscape bool + Indent bool + UnorderedMap bool + Debug bool +} diff --git a/internal/encoder/vm/debug_vm.go b/internal/encoder/vm/debug_vm.go index 1a49c0d..5891c55 100644 --- a/internal/encoder/vm/debug_vm.go +++ b/internal/encoder/vm/debug_vm.go @@ -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) } diff --git a/internal/encoder/vm/util.go b/internal/encoder/vm/util.go index 2887827..f0d29c2 100644 --- a/internal/encoder/vm/util.go +++ b/internal/encoder/vm/util.go @@ -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 } diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 7af9d3a..b68a306 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -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) diff --git a/internal/encoder/vm_escaped/debug_vm.go b/internal/encoder/vm_escaped/debug_vm.go index a4e773c..b10f588 100644 --- a/internal/encoder/vm_escaped/debug_vm.go +++ b/internal/encoder/vm_escaped/debug_vm.go @@ -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) } diff --git a/internal/encoder/vm_escaped/util.go b/internal/encoder/vm_escaped/util.go index 13fd3e3..5f1a77c 100644 --- a/internal/encoder/vm_escaped/util.go +++ b/internal/encoder/vm_escaped/util.go @@ -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 } diff --git a/internal/encoder/vm_escaped/vm.go b/internal/encoder/vm_escaped/vm.go index eb6a923..ae8ca01 100644 --- a/internal/encoder/vm_escaped/vm.go +++ b/internal/encoder/vm_escaped/vm.go @@ -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) diff --git a/internal/encoder/vm_escaped_indent/debug_vm.go b/internal/encoder/vm_escaped_indent/debug_vm.go index 2654630..dcbdb10 100644 --- a/internal/encoder/vm_escaped_indent/debug_vm.go +++ b/internal/encoder/vm_escaped_indent/debug_vm.go @@ -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) } diff --git a/internal/encoder/vm_escaped_indent/util.go b/internal/encoder/vm_escaped_indent/util.go index b1458ae..48354f5 100644 --- a/internal/encoder/vm_escaped_indent/util.go +++ b/internal/encoder/vm_escaped_indent/util.go @@ -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 } diff --git a/internal/encoder/vm_escaped_indent/vm.go b/internal/encoder/vm_escaped_indent/vm.go index 11ad8e6..fa9ff75 100644 --- a/internal/encoder/vm_escaped_indent/vm.go +++ b/internal/encoder/vm_escaped_indent/vm.go @@ -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) diff --git a/internal/encoder/vm_indent/debug_vm.go b/internal/encoder/vm_indent/debug_vm.go index 4dc9ee8..348d6ff 100644 --- a/internal/encoder/vm_indent/debug_vm.go +++ b/internal/encoder/vm_indent/debug_vm.go @@ -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) } diff --git a/internal/encoder/vm_indent/util.go b/internal/encoder/vm_indent/util.go index ae358f2..799f99c 100644 --- a/internal/encoder/vm_indent/util.go +++ b/internal/encoder/vm_indent/util.go @@ -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 } diff --git a/internal/encoder/vm_indent/vm.go b/internal/encoder/vm_indent/vm.go index b165f7b..9223c45 100644 --- a/internal/encoder/vm_indent/vm.go +++ b/internal/encoder/vm_indent/vm.go @@ -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) diff --git a/json.go b/json.go index 05756ef..4ac8536 100644 --- a/json.go +++ b/json.go @@ -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) } diff --git a/option.go b/option.go index 6837a4b..cd2bdfe 100644 --- a/option.go +++ b/option.go @@ -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 } }