diff --git a/decode.go b/decode.go index eedbe05..e77a676 100644 --- a/decode.go +++ b/decode.go @@ -37,14 +37,21 @@ func unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { if err != nil { return err } - cursor, err := dec.Decode(src, 0, 0, header.ptr) + ctx := decoder.TakeRuntimeContext() + ctx.Buf = src + for _, optFunc := range optFuncs { + optFunc(ctx.Option) + } + cursor, err := dec.Decode(ctx, 0, 0, header.ptr) if err != nil { + decoder.ReleaseRuntimeContext(ctx) return err } + decoder.ReleaseRuntimeContext(ctx) return validateEndBuf(src, cursor) } -func unmarshalNoEscape(data []byte, v interface{}) error { +func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { src := make([]byte, len(data)+1) // append nul byte to the end copy(src, data) @@ -57,10 +64,18 @@ func unmarshalNoEscape(data []byte, v interface{}) error { if err != nil { return err } - cursor, err := dec.Decode(src, 0, 0, noescape(header.ptr)) + + ctx := decoder.TakeRuntimeContext() + ctx.Buf = src + for _, optFunc := range optFuncs { + optFunc(ctx.Option) + } + cursor, err := dec.Decode(ctx, 0, 0, noescape(header.ptr)) if err != nil { + decoder.ReleaseRuntimeContext(ctx) return err } + decoder.ReleaseRuntimeContext(ctx) return validateEndBuf(src, cursor) } diff --git a/internal/decoder/anonymous_field.go b/internal/decoder/anonymous_field.go index 0f557cb..030cb7a 100644 --- a/internal/decoder/anonymous_field.go +++ b/internal/decoder/anonymous_field.go @@ -28,10 +28,10 @@ func (d *anonymousFieldDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Po return d.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+d.offset)) } -func (d *anonymousFieldDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { if *(*unsafe.Pointer)(p) == nil { *(*unsafe.Pointer)(p) = unsafe_New(d.structType) } p = *(*unsafe.Pointer)(p) - return d.dec.Decode(buf, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset)) + return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset)) } diff --git a/internal/decoder/array.go b/internal/decoder/array.go index bc8d971..7f9f1eb 100644 --- a/internal/decoder/array.go +++ b/internal/decoder/array.go @@ -91,7 +91,8 @@ ERROR: return errors.ErrUnexpectedEndOfJSON("array", s.totalOffset()) } -func (d *arrayDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf depth++ if depth > maxDecodeNestingDepth { return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) @@ -113,7 +114,7 @@ func (d *arrayDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) for { cursor++ if idx < d.alen { - c, err := d.valueDecoder.Decode(buf, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)) + c, err := d.valueDecoder.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)) if err != nil { return 0, err } diff --git a/internal/decoder/bool.go b/internal/decoder/bool.go index 870d3df..455042a 100644 --- a/internal/decoder/bool.go +++ b/internal/decoder/bool.go @@ -49,7 +49,8 @@ ERROR: return errors.ErrUnexpectedEndOfJSON("bool", s.totalOffset()) } -func (d *boolDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *boolDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf cursor = skipWhiteSpace(buf, cursor) switch buf[cursor] { case 't': diff --git a/internal/decoder/bytes.go b/internal/decoder/bytes.go index da7535f..e0619be 100644 --- a/internal/decoder/bytes.go +++ b/internal/decoder/bytes.go @@ -57,8 +57,8 @@ func (d *bytesDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er return nil } -func (d *bytesDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.decodeBinary(buf, cursor, depth, p) +func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.decodeBinary(ctx, cursor, depth, p) if err != nil { return 0, err } @@ -131,7 +131,8 @@ func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Point return nil, errors.ErrNotAtBeginningOfValue(s.totalOffset()) } -func (d *bytesDecoder) decodeBinary(buf []byte, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) { +func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) { + buf := ctx.Buf for { switch buf[cursor] { case ' ', '\n', '\t', '\r': @@ -157,7 +158,7 @@ func (d *bytesDecoder) decodeBinary(buf []byte, cursor, depth int64, p unsafe.Po Offset: cursor, } } - c, err := d.sliceDecoder.Decode(buf, cursor, depth, p) + c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p) if err != nil { return nil, 0, err } diff --git a/internal/decoder/context.go b/internal/decoder/context.go index 380d73e..cb2ffda 100644 --- a/internal/decoder/context.go +++ b/internal/decoder/context.go @@ -1,11 +1,35 @@ package decoder import ( + "sync" "unsafe" "github.com/goccy/go-json/internal/errors" ) +type RuntimeContext struct { + Buf []byte + Option *Option +} + +var ( + runtimeContextPool = sync.Pool{ + New: func() interface{} { + return &RuntimeContext{ + Option: &Option{}, + } + }, + } +) + +func TakeRuntimeContext() *RuntimeContext { + return runtimeContextPool.Get().(*RuntimeContext) +} + +func ReleaseRuntimeContext(ctx *RuntimeContext) { + runtimeContextPool.Put(ctx) +} + var ( isWhiteSpace = [256]bool{} ) diff --git a/internal/decoder/float.go b/internal/decoder/float.go index b53befb..dfb7168 100644 --- a/internal/decoder/float.go +++ b/internal/decoder/float.go @@ -135,7 +135,8 @@ func (d *floatDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er return nil } -func (d *floatDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *floatDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf bytes, c, err := d.decodeByte(buf, cursor) if err != nil { return 0, err diff --git a/internal/decoder/int.go b/internal/decoder/int.go index 18cbd2a..7edfb04 100644 --- a/internal/decoder/int.go +++ b/internal/decoder/int.go @@ -209,8 +209,8 @@ func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro return nil } -func (d *intDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.decodeByte(buf, cursor) +func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.decodeByte(ctx.Buf, cursor) if err != nil { return 0, err } diff --git a/internal/decoder/interface.go b/internal/decoder/interface.go index 9b59455..182653c 100644 --- a/internal/decoder/interface.go +++ b/internal/decoder/interface.go @@ -309,7 +309,8 @@ func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *err } } -func (d *interfaceDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *interfaceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&emptyInterface{ typ: d.typ, ptr: p, @@ -339,10 +340,10 @@ func (d *interfaceDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Poin typ := ifaceHeader.typ if ifaceHeader.ptr == nil || d.typ == typ || typ == nil { // concrete type is empty interface - return d.decodeEmptyInterface(buf, cursor, depth, p) + return d.decodeEmptyInterface(ctx, cursor, depth, p) } if typ.Kind() == reflect.Ptr && typ.Elem() == d.typ || typ.Kind() != reflect.Ptr { - return d.decodeEmptyInterface(buf, cursor, depth, p) + return d.decodeEmptyInterface(ctx, cursor, depth, p) } cursor = skipWhiteSpace(buf, cursor) if buf[cursor] == 'n' { @@ -357,16 +358,17 @@ func (d *interfaceDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Poin if err != nil { return 0, err } - return decoder.Decode(buf, cursor, depth, ifaceHeader.ptr) + return decoder.Decode(ctx, cursor, depth, ifaceHeader.ptr) } -func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf cursor = skipWhiteSpace(buf, cursor) switch buf[cursor] { case '{': var v map[string]interface{} ptr := unsafe.Pointer(&v) - cursor, err := d.mapDecoder.Decode(buf, cursor, depth, ptr) + cursor, err := d.mapDecoder.Decode(ctx, cursor, depth, ptr) if err != nil { return 0, err } @@ -375,18 +377,18 @@ func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64, case '[': var v []interface{} ptr := unsafe.Pointer(&v) - cursor, err := d.sliceDecoder.Decode(buf, cursor, depth, ptr) + cursor, err := d.sliceDecoder.Decode(ctx, cursor, depth, ptr) if err != nil { return 0, err } **(**interface{})(unsafe.Pointer(&p)) = v return cursor, nil case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return d.floatDecoder.Decode(buf, cursor, depth, p) + return d.floatDecoder.Decode(ctx, cursor, depth, p) case '"': var v string ptr := unsafe.Pointer(&v) - cursor, err := d.stringDecoder.Decode(buf, cursor, depth, ptr) + cursor, err := d.stringDecoder.Decode(ctx, cursor, depth, ptr) if err != nil { return 0, err } diff --git a/internal/decoder/map.go b/internal/decoder/map.go index b1b3437..367a8a1 100644 --- a/internal/decoder/map.go +++ b/internal/decoder/map.go @@ -90,7 +90,8 @@ func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro } } -func (d *mapDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf depth++ if depth > maxDecodeNestingDepth { return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) @@ -126,7 +127,7 @@ func (d *mapDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) ( } for { k := unsafe_New(d.keyType) - keyCursor, err := d.keyDecoder.Decode(buf, cursor, depth, k) + keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k) if err != nil { return 0, err } @@ -136,7 +137,7 @@ func (d *mapDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) ( } cursor++ v := unsafe_New(d.valueType) - valueCursor, err := d.valueDecoder.Decode(buf, cursor, depth, v) + valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v) if err != nil { return 0, err } diff --git a/internal/decoder/number.go b/internal/decoder/number.go index 20e5fe7..c50d62b 100644 --- a/internal/decoder/number.go +++ b/internal/decoder/number.go @@ -37,8 +37,8 @@ func (d *numberDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e return nil } -func (d *numberDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.decodeByte(buf, cursor) +func (d *numberDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.decodeByte(ctx.Buf, cursor) if err != nil { return 0, err } diff --git a/internal/decoder/ptr.go b/internal/decoder/ptr.go index 9eb6014..2c83b9c 100644 --- a/internal/decoder/ptr.go +++ b/internal/decoder/ptr.go @@ -58,7 +58,8 @@ func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro return nil } -func (d *ptrDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf cursor = skipWhiteSpace(buf, cursor) if buf[cursor] == 'n' { if err := validateNull(buf, cursor); err != nil { @@ -77,7 +78,7 @@ func (d *ptrDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) ( } else { newptr = *(*unsafe.Pointer)(p) } - c, err := d.dec.Decode(buf, cursor, depth, newptr) + c, err := d.dec.Decode(ctx, cursor, depth, newptr) if err != nil { return 0, err } diff --git a/internal/decoder/slice.go b/internal/decoder/slice.go index e17b158..853a555 100644 --- a/internal/decoder/slice.go +++ b/internal/decoder/slice.go @@ -199,7 +199,8 @@ ERROR: return errors.ErrUnexpectedEndOfJSON("slice", s.totalOffset()) } -func (d *sliceDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf depth++ if depth > maxDecodeNestingDepth { return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) @@ -253,7 +254,7 @@ func (d *sliceDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) typedmemmove(d.elemType, ep, unsafe_New(d.elemType)) } } - c, err := d.valueDecoder.Decode(buf, cursor, depth, ep) + c, err := d.valueDecoder.Decode(ctx, cursor, depth, ep) if err != nil { return 0, err } diff --git a/internal/decoder/string.go b/internal/decoder/string.go index d470806..352dc42 100644 --- a/internal/decoder/string.go +++ b/internal/decoder/string.go @@ -45,8 +45,8 @@ func (d *stringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e return nil } -func (d *stringDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.decodeByte(buf, cursor) +func (d *stringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.decodeByte(ctx.Buf, cursor) if err != nil { return 0, err } diff --git a/internal/decoder/struct.go b/internal/decoder/struct.go index 102db78..c5de021 100644 --- a/internal/decoder/struct.go +++ b/internal/decoder/struct.go @@ -680,7 +680,8 @@ func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e } } -func (d *structDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf depth++ if depth > maxDecodeNestingDepth { return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) @@ -722,7 +723,7 @@ func (d *structDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer if field.err != nil { return 0, field.err } - c, err := field.dec.Decode(buf, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset)) + c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset)) if err != nil { return 0, err } diff --git a/internal/decoder/type.go b/internal/decoder/type.go index fd9ffb9..419ad4a 100644 --- a/internal/decoder/type.go +++ b/internal/decoder/type.go @@ -8,7 +8,7 @@ import ( ) type Decoder interface { - Decode([]byte, int64, int64, unsafe.Pointer) (int64, error) + Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error) DecodeStream(*Stream, int64, unsafe.Pointer) error } diff --git a/internal/decoder/uint.go b/internal/decoder/uint.go index 064c196..a62c514 100644 --- a/internal/decoder/uint.go +++ b/internal/decoder/uint.go @@ -158,8 +158,8 @@ func (d *uintDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) err return nil } -func (d *uintDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.decodeByte(buf, cursor) +func (d *uintDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.decodeByte(ctx.Buf, cursor) if err != nil { return 0, err } diff --git a/internal/decoder/unmarshal_json.go b/internal/decoder/unmarshal_json.go index 3676eab..a5e4fa6 100644 --- a/internal/decoder/unmarshal_json.go +++ b/internal/decoder/unmarshal_json.go @@ -53,7 +53,8 @@ func (d *unmarshalJSONDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Poi return nil } -func (d *unmarshalJSONDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf cursor = skipWhiteSpace(buf, cursor) start := cursor end, err := skipValue(buf, cursor, depth) diff --git a/internal/decoder/unmarshal_text.go b/internal/decoder/unmarshal_text.go index 709ae56..1ef2877 100644 --- a/internal/decoder/unmarshal_text.go +++ b/internal/decoder/unmarshal_text.go @@ -91,7 +91,8 @@ func (d *unmarshalTextDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Poi return nil } -func (d *unmarshalTextDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { +func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + buf := ctx.Buf cursor = skipWhiteSpace(buf, cursor) start := cursor end, err := skipValue(buf, cursor, depth) diff --git a/internal/decoder/wrapped_string.go b/internal/decoder/wrapped_string.go index 8dbdb3a..a8d36bb 100644 --- a/internal/decoder/wrapped_string.go +++ b/internal/decoder/wrapped_string.go @@ -40,14 +40,14 @@ func (d *wrappedStringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Poi } b := make([]byte, len(bytes)+1) copy(b, bytes) - if _, err := d.dec.Decode(b, 0, depth, p); err != nil { + if _, err := d.dec.Decode(&RuntimeContext{Buf: b}, 0, depth, p); err != nil { return err } return nil } -func (d *wrappedStringDecoder) Decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) { - bytes, c, err := d.stringDecoder.decodeByte(buf, cursor) +func (d *wrappedStringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { + bytes, c, err := d.stringDecoder.decodeByte(ctx.Buf, cursor) if err != nil { return 0, err } @@ -58,7 +58,7 @@ func (d *wrappedStringDecoder) Decode(buf []byte, cursor, depth int64, p unsafe. return c, nil } bytes = append(bytes, nul) - if _, err := d.dec.Decode(bytes, 0, depth, p); err != nil { + if _, err := d.dec.Decode(ctx, 0, depth, p); err != nil { return 0, err } return c, nil diff --git a/json.go b/json.go index 2517368..601e164 100644 --- a/json.go +++ b/json.go @@ -262,8 +262,8 @@ func UnmarshalWithOption(data []byte, v interface{}, optFuncs ...DecodeOptionFun return unmarshal(data, v, optFuncs...) } -func UnmarshalNoEscape(data []byte, v interface{}) error { - return unmarshalNoEscape(data, v) +func UnmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { + return unmarshalNoEscape(data, v, optFuncs...) } // A Token holds a value of one of these types: