Skip to content

Commit 2a122d1

Browse files
committed
More optimizations
* Avoid checkValid and compact as much as possible * Keep decodeState's in a pool
1 parent 174e1d7 commit 2a122d1

File tree

4 files changed

+161
-71
lines changed

4 files changed

+161
-71
lines changed

v5/internal/json/decode.go

+46-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"reflect"
1515
"strconv"
1616
"strings"
17+
"sync"
1718
"unicode"
1819
"unicode/utf16"
1920
"unicode/utf8"
@@ -98,7 +99,9 @@ func Unmarshal(data []byte, v any) error {
9899
// Check for well-formedness.
99100
// Avoids filling out half a data structure
100101
// before discovering a JSON syntax error.
101-
var d decodeState
102+
d := ds.Get().(*decodeState)
103+
defer ds.Put(d)
104+
//var d decodeState
102105
d.useNumber = true
103106
err := checkValid(data, &d.scan)
104107
if err != nil {
@@ -109,11 +112,20 @@ func Unmarshal(data []byte, v any) error {
109112
return d.unmarshal(v)
110113
}
111114

115+
var ds = sync.Pool{
116+
New: func() any {
117+
return new(decodeState)
118+
},
119+
}
120+
112121
func UnmarshalWithKeys(data []byte, v any) ([]string, error) {
113122
// Check for well-formedness.
114123
// Avoids filling out half a data structure
115124
// before discovering a JSON syntax error.
116-
var d decodeState
125+
126+
d := ds.Get().(*decodeState)
127+
defer ds.Put(d)
128+
//var d decodeState
117129
d.useNumber = true
118130
err := checkValid(data, &d.scan)
119131
if err != nil {
@@ -129,6 +141,38 @@ func UnmarshalWithKeys(data []byte, v any) ([]string, error) {
129141
return d.lastKeys, nil
130142
}
131143

144+
func UnmarshalValid(data []byte, v any) error {
145+
// Check for well-formedness.
146+
// Avoids filling out half a data structure
147+
// before discovering a JSON syntax error.
148+
d := ds.Get().(*decodeState)
149+
defer ds.Put(d)
150+
//var d decodeState
151+
d.useNumber = true
152+
153+
d.init(data)
154+
return d.unmarshal(v)
155+
}
156+
157+
func UnmarshalValidWithKeys(data []byte, v any) ([]string, error) {
158+
// Check for well-formedness.
159+
// Avoids filling out half a data structure
160+
// before discovering a JSON syntax error.
161+
162+
d := ds.Get().(*decodeState)
163+
defer ds.Put(d)
164+
//var d decodeState
165+
d.useNumber = true
166+
167+
d.init(data)
168+
err := d.unmarshal(v)
169+
if err != nil {
170+
return nil, err
171+
}
172+
173+
return d.lastKeys, nil
174+
}
175+
132176
// Unmarshaler is the interface implemented by types
133177
// that can unmarshal a JSON description of themselves.
134178
// The input can be assumed to be a valid encoding of

v5/internal/json/encode.go

+58-2
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,14 @@ type Marshaler interface {
224224
MarshalJSON() ([]byte, error)
225225
}
226226

227+
type RedirectMarshaler interface {
228+
RedirectMarshalJSON() (any, error)
229+
}
230+
231+
type TrustMarshaler interface {
232+
TrustMarshalJSON(b *bytes.Buffer) error
233+
}
234+
227235
// An UnsupportedTypeError is returned by Marshal when attempting
228236
// to encode an unsupported value type.
229237
type UnsupportedTypeError struct {
@@ -406,13 +414,21 @@ func typeEncoder(t reflect.Type) encoderFunc {
406414
}
407415

408416
var (
409-
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
410-
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
417+
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
418+
redirMarshalerType = reflect.TypeOf((*RedirectMarshaler)(nil)).Elem()
419+
trustMarshalerType = reflect.TypeOf((*TrustMarshaler)(nil)).Elem()
420+
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
411421
)
412422

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

483+
func redirMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
484+
if v.Kind() == reflect.Pointer && v.IsNil() {
485+
e.WriteString("null")
486+
return
487+
}
488+
m, ok := v.Interface().(RedirectMarshaler)
489+
if !ok {
490+
e.WriteString("null")
491+
return
492+
}
493+
494+
iv, err := m.RedirectMarshalJSON()
495+
if err != nil {
496+
e.error(&MarshalerError{v.Type(), err, "RedirectMarshalJSON"})
497+
return
498+
}
499+
500+
e.marshal(iv, opts)
501+
}
502+
503+
func marshalerTrustEncoder(e *encodeState, v reflect.Value, opts encOpts) {
504+
if v.Kind() == reflect.Pointer && v.IsNil() {
505+
e.WriteString("null")
506+
return
507+
}
508+
m, ok := v.Interface().(TrustMarshaler)
509+
if !ok {
510+
e.WriteString("null")
511+
return
512+
}
513+
err := m.TrustMarshalJSON(&e.Buffer)
514+
if err == nil {
515+
//_, err = e.Buffer.Write(b)
516+
// copy JSON into buffer, checking validity.
517+
//err = compact(&e.Buffer, b, opts.escapeHTML)
518+
}
519+
if err != nil {
520+
e.error(&MarshalerError{v.Type(), err, "MarshalJSON"})
521+
}
522+
}
467523
func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
468524
if v.Kind() == reflect.Pointer && v.IsNil() {
469525
e.WriteString("null")

v5/merge.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,15 @@ func MergePatch(docData, patchData []byte) ([]byte, error) {
122122
func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) {
123123
doc := &partialDoc{}
124124

125-
docErr := unmarshal(docData, doc)
126-
127-
patch := &partialDoc{
128-
fastKeys: true,
125+
if !json.Valid(docData) || !json.Valid(patchData) {
126+
return nil, ErrInvalid
129127
}
130128

131-
patchErr := unmarshal(patchData, patch)
129+
docErr := doc.UnmarshalJSON(docData)
130+
131+
patch := &partialDoc{}
132+
133+
patchErr := patch.UnmarshalJSON(patchData)
132134

133135
if isSyntaxError(docErr) {
134136
return nil, errBadJSONDoc
@@ -268,7 +270,7 @@ func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) {
268270
}
269271

270272
func unmarshal(data []byte, into interface{}) error {
271-
return json.Unmarshal(data, into)
273+
return json.UnmarshalValid(data, into)
272274
}
273275

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

0 commit comments

Comments
 (0)