Skip to content

Commit

Permalink
More optimizations
Browse files Browse the repository at this point in the history
* Avoid checkValid and compact as much as possible
* Keep decodeState's in a pool
  • Loading branch information
evanphx committed Jan 12, 2024
1 parent 174e1d7 commit 2a122d1
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 71 deletions.
48 changes: 46 additions & 2 deletions v5/internal/json/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"reflect"
"strconv"
"strings"
"sync"
"unicode"
"unicode/utf16"
"unicode/utf8"
Expand Down Expand Up @@ -98,7 +99,9 @@ func Unmarshal(data []byte, v any) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState
d := ds.Get().(*decodeState)
defer ds.Put(d)
//var d decodeState
d.useNumber = true
err := checkValid(data, &d.scan)
if err != nil {
Expand All @@ -109,11 +112,20 @@ func Unmarshal(data []byte, v any) error {
return d.unmarshal(v)
}

var ds = sync.Pool{
New: func() any {
return new(decodeState)
},
}

func UnmarshalWithKeys(data []byte, v any) ([]string, error) {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState

d := ds.Get().(*decodeState)
defer ds.Put(d)
//var d decodeState
d.useNumber = true
err := checkValid(data, &d.scan)
if err != nil {
Expand All @@ -129,6 +141,38 @@ func UnmarshalWithKeys(data []byte, v any) ([]string, error) {
return d.lastKeys, nil
}

func UnmarshalValid(data []byte, v any) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
d := ds.Get().(*decodeState)
defer ds.Put(d)
//var d decodeState
d.useNumber = true

d.init(data)
return d.unmarshal(v)
}

func UnmarshalValidWithKeys(data []byte, v any) ([]string, error) {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.

d := ds.Get().(*decodeState)
defer ds.Put(d)
//var d decodeState
d.useNumber = true

d.init(data)
err := d.unmarshal(v)
if err != nil {
return nil, err
}

return d.lastKeys, nil
}

// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
Expand Down
60 changes: 58 additions & 2 deletions v5/internal/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,14 @@ type Marshaler interface {
MarshalJSON() ([]byte, error)
}

type RedirectMarshaler interface {
RedirectMarshalJSON() (any, error)
}

type TrustMarshaler interface {
TrustMarshalJSON(b *bytes.Buffer) error
}

// An UnsupportedTypeError is returned by Marshal when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct {
Expand Down Expand Up @@ -406,13 +414,21 @@ func typeEncoder(t reflect.Type) encoderFunc {
}

var (
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
redirMarshalerType = reflect.TypeOf((*RedirectMarshaler)(nil)).Elem()
trustMarshalerType = reflect.TypeOf((*TrustMarshaler)(nil)).Elem()
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)

// newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
if t.Implements(redirMarshalerType) {
return redirMarshalerEncoder
}
if t.Implements(trustMarshalerType) {
return marshalerTrustEncoder
}
// If we have a non-pointer value whose type implements
// Marshaler with a value receiver, then we're better off taking
// the address of the value - otherwise we end up with an
Expand Down Expand Up @@ -464,6 +480,46 @@ func invalidValueEncoder(e *encodeState, v reflect.Value, _ encOpts) {
e.WriteString("null")
}

func redirMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Pointer && v.IsNil() {
e.WriteString("null")
return
}
m, ok := v.Interface().(RedirectMarshaler)
if !ok {
e.WriteString("null")
return
}

iv, err := m.RedirectMarshalJSON()
if err != nil {
e.error(&MarshalerError{v.Type(), err, "RedirectMarshalJSON"})
return
}

e.marshal(iv, opts)
}

func marshalerTrustEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Pointer && v.IsNil() {
e.WriteString("null")
return
}
m, ok := v.Interface().(TrustMarshaler)
if !ok {
e.WriteString("null")
return
}
err := m.TrustMarshalJSON(&e.Buffer)
if err == nil {
//_, err = e.Buffer.Write(b)
// copy JSON into buffer, checking validity.
//err = compact(&e.Buffer, b, opts.escapeHTML)
}
if err != nil {
e.error(&MarshalerError{v.Type(), err, "MarshalJSON"})
}
}
func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Pointer && v.IsNil() {
e.WriteString("null")
Expand Down
14 changes: 8 additions & 6 deletions v5/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,15 @@ func MergePatch(docData, patchData []byte) ([]byte, error) {
func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
doc := &partialDoc{}

docErr := unmarshal(docData, doc)

patch := &partialDoc{
fastKeys: true,
if !json.Valid(docData) || !json.Valid(patchData) {
return nil, ErrInvalid
}

patchErr := unmarshal(patchData, patch)
docErr := doc.UnmarshalJSON(docData)

patch := &partialDoc{}

patchErr := patch.UnmarshalJSON(patchData)

if isSyntaxError(docErr) {
return nil, errBadJSONDoc
Expand Down Expand Up @@ -268,7 +270,7 @@ func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
}

func unmarshal(data []byte, into interface{}) error {
return json.Unmarshal(data, into)
return json.UnmarshalValid(data, into)
}

// createArrayMergePatch will return an array of merge-patch documents capable
Expand Down

0 comments on commit 2a122d1

Please sign in to comment.