Skip to content

Commit dabc507

Browse files
json: internal uses of Append now use new encoder.appendAny
This is necessary for cycle detection to work, which had been implemented, yet was broken. Also introduce cachedCodec helper function.
1 parent e960845 commit dabc507

File tree

3 files changed

+37
-30
lines changed

3 files changed

+37
-30
lines changed

json/codec.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ type (
6363
// lookup time for simple types like bool, int, etc..
6464
var cache atomic.Pointer[map[unsafe.Pointer]codec]
6565

66+
func cachedCodec(t reflect.Type) codec {
67+
cache := cacheLoad()
68+
69+
c, found := cache[typeid(t)]
70+
if !found {
71+
c = constructCachedCodec(t, cache)
72+
}
73+
74+
return c
75+
}
76+
6677
func cacheLoad() map[unsafe.Pointer]codec {
6778
p := cache.Load()
6879
if p == nil {

json/encode.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math"
77
"reflect"
8+
"runtime"
89
"sort"
910
"strconv"
1011
"sync"
@@ -17,6 +18,23 @@ import (
1718

1819
const hex = "0123456789abcdef"
1920

21+
func (e encoder) appendAny(b []byte, x any) ([]byte, error) {
22+
if x == nil {
23+
// Special case for nil values because it makes the rest of the code
24+
// simpler to assume that it won't be seeing nil pointers.
25+
return e.encodeNull(b, nil)
26+
}
27+
28+
t := reflect.TypeOf(x)
29+
p := (*iface)(unsafe.Pointer(&x)).ptr
30+
c := cachedCodec(t)
31+
32+
b, err := c.encode(e, b, p)
33+
runtime.KeepAlive(x)
34+
35+
return b, err
36+
}
37+
2038
func (e encoder) encodeNull(b []byte, p unsafe.Pointer) ([]byte, error) {
2139
return append(b, "null"...), nil
2240
}
@@ -383,7 +401,7 @@ func (e encoder) encodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, e
383401
b, _ = e.encodeString(b, unsafe.Pointer(&k))
384402
b = append(b, ':')
385403

386-
b, err = Append(b, v, e.flags)
404+
b, err = e.appendAny(b, v)
387405
if err != nil {
388406
return b, err
389407
}
@@ -417,7 +435,7 @@ func (e encoder) encodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, e
417435
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
418436
b = append(b, ':')
419437

420-
b, err = Append(b, elem.val, e.flags)
438+
b, err = e.appendAny(b, elem.val)
421439
if err != nil {
422440
break
423441
}
@@ -813,11 +831,11 @@ func (e encoder) encodePointer(b []byte, p unsafe.Pointer, t reflect.Type, encod
813831
}
814832

815833
func (e encoder) encodeInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
816-
return Append(b, *(*any)(p), e.flags)
834+
return e.appendAny(b, *(*any)(p))
817835
}
818836

819837
func (e encoder) encodeMaybeEmptyInterface(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
820-
return Append(b, reflect.NewAt(t, p).Elem().Interface(), e.flags)
838+
return e.appendAny(b, reflect.NewAt(t, p).Elem().Interface())
821839
}
822840

823841
func (e encoder) encodeUnsupportedTypeError(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {

json/json.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"io"
77
"math/bits"
88
"reflect"
9-
"runtime"
109
"sync"
1110
"unsafe"
1211
)
@@ -194,25 +193,9 @@ func (k Kind) Class() Kind { return Kind(1 << uint(bits.Len(uint(k))-1)) }
194193
// Append acts like Marshal but appends the json representation to b instead of
195194
// always reallocating a new slice.
196195
func Append(b []byte, x any, flags AppendFlags) ([]byte, error) {
197-
if x == nil {
198-
// Special case for nil values because it makes the rest of the code
199-
// simpler to assume that it won't be seeing nil pointers.
200-
return append(b, "null"...), nil
201-
}
202-
203-
t := reflect.TypeOf(x)
204-
p := (*iface)(unsafe.Pointer(&x)).ptr
205-
206-
cache := cacheLoad()
207-
c, found := cache[typeid(t)]
208-
209-
if !found {
210-
c = constructCachedCodec(t, cache)
211-
}
196+
e := encoder{flags: flags}
212197

213-
b, err := c.encode(encoder{flags: flags}, b, p)
214-
runtime.KeepAlive(x)
215-
return b, err
198+
return e.appendAny(b, x)
216199
}
217200

218201
// Escape is a convenience helper to construct an escaped JSON string from s.
@@ -330,14 +313,9 @@ func Parse(b []byte, x any, flags ParseFlags) ([]byte, error) {
330313
}
331314
return r, &InvalidUnmarshalError{Type: t}
332315
}
333-
t = t.Elem()
334316

335-
cache := cacheLoad()
336-
c, found := cache[typeid(t)]
337-
338-
if !found {
339-
c = constructCachedCodec(t, cache)
340-
}
317+
t = t.Elem()
318+
c := cachedCodec(t)
341319

342320
r, err := c.decode(d, b, p)
343321
return skipSpaces(r), err

0 commit comments

Comments
 (0)