Skip to content

Commit 1cc8391

Browse files
committed
codec: do conditional compilation for json/v2 experiment
Signed-off-by: Hank Donnay <[email protected]>
1 parent 6295627 commit 1cc8391

File tree

4 files changed

+132
-48
lines changed

4 files changed

+132
-48
lines changed

internal/codec/codec.go

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,37 @@ package codec
44

55
import (
66
"io"
7-
"sync"
8-
9-
"github.com/ugorji/go/codec"
107
)
118

12-
var jsonHandle codec.JsonHandle
13-
14-
func init() {
15-
// This is documented to cause "smart buffering".
16-
jsonHandle.WriterBufferSize = 4096
17-
jsonHandle.ReaderBufferSize = 4096
18-
// Force calling time.Time's Marshal function. This causes an allocation on
19-
// every time.Time value, but is the same behavior as the stdlib json
20-
// encoder. If we decide nulls are OK, this should get removed.
21-
jsonHandle.TimeNotBuiltin = true
9+
// Encoder encodes.
10+
type Encoder interface {
11+
Encode(in any) error
2212
}
2313

24-
// Encoder and decoder pools, to reuse if possible.
25-
var (
26-
encPool = sync.Pool{
27-
New: func() interface{} {
28-
return codec.NewEncoder(nil, &jsonHandle)
29-
},
30-
}
31-
decPool = sync.Pool{
32-
New: func() interface{} {
33-
return codec.NewDecoder(nil, &jsonHandle)
34-
},
35-
}
36-
)
14+
// Decoder decodes.
15+
type Decoder interface {
16+
Decode(out any) error
17+
}
3718

38-
// Encoder encodes.
39-
type Encoder = codec.Encoder
19+
// All the exported functions delegate to an unexported version, which is
20+
// provided by whichever implementation is selected at compile time.
4021

4122
// GetEncoder returns an encoder configured to write to w.
42-
func GetEncoder(w io.Writer) *Encoder {
43-
e := encPool.Get().(*Encoder)
44-
e.Reset(w)
45-
return e
23+
func GetEncoder(w io.Writer) Encoder {
24+
return getEncoder(w)
4625
}
4726

4827
// PutEncoder returns an encoder to the pool.
49-
func PutEncoder(e *Encoder) {
50-
e.Reset(nil)
51-
encPool.Put(e)
28+
func PutEncoder(v Encoder) {
29+
putEncoder(v)
5230
}
5331

54-
// Decoder decodes.
55-
type Decoder = codec.Decoder
56-
5732
// GetDecoder returns a decoder configured to read from r.
58-
func GetDecoder(r io.Reader) *Decoder {
59-
d := decPool.Get().(*Decoder)
60-
d.Reset(r)
61-
return d
33+
func GetDecoder(r io.Reader) Decoder {
34+
return getDecoder(r)
6235
}
6336

6437
// PutDecoder returns a decoder to the pool.
65-
func PutDecoder(d *Decoder) {
66-
d.Reset(nil)
67-
decPool.Put(d)
38+
func PutDecoder(v Decoder) {
39+
putDecoder(v)
6840
}

internal/codec/codec_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import (
1515
func Example() {
1616
enc := GetEncoder(os.Stdout)
1717
defer PutEncoder(enc)
18-
enc.MustEncode([]string{"a", "slice", "of", "strings"})
18+
enc.Encode([]string{"a", "slice", "of", "strings"})
1919
fmt.Fprintln(os.Stdout)
20-
enc.MustEncode(nil)
20+
enc.Encode(nil)
2121
fmt.Fprintln(os.Stdout)
22-
enc.MustEncode(map[string]string{})
22+
enc.Encode(map[string]string{})
2323
fmt.Fprintln(os.Stdout)
2424
// Output: ["a","slice","of","strings"]
2525
// null

internal/codec/jsonv2.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//go:build go1.25 && goexperiment.jsonv2
2+
3+
package codec
4+
5+
import (
6+
"encoding/json/jsontext"
7+
"encoding/json/v2"
8+
"io"
9+
)
10+
11+
// The interface built on json/v2 does not use its own pool and instead relies
12+
// on the json package's pooling.
13+
14+
var options = json.JoinOptions(
15+
json.DefaultOptionsV2(),
16+
jsontext.Multiline(false),
17+
jsontext.SpaceAfterColon(false),
18+
jsontext.SpaceAfterComma(false),
19+
json.OmitZeroStructFields(true),
20+
json.FormatNilMapAsNull(true),
21+
json.FormatNilSliceAsNull(true),
22+
)
23+
24+
type fwdWriter struct {
25+
io.Writer
26+
}
27+
28+
func (w fwdWriter) Encode(in any) error {
29+
return json.MarshalWrite(w.Writer, in, options)
30+
}
31+
32+
type fwdReader struct {
33+
io.Reader
34+
}
35+
36+
func (r fwdReader) Decode(out any) error {
37+
return json.UnmarshalRead(r.Reader, out, options)
38+
}
39+
40+
// GetEncoder returns an encoder configured to write to w.
41+
func getEncoder(w io.Writer) Encoder { return fwdWriter{w} }
42+
43+
func putEncoder(v Encoder) {}
44+
45+
// GetDecoder returns a decoder configured to read from r.
46+
func getDecoder(r io.Reader) Decoder { return fwdReader{r} }
47+
48+
func putDecoder(v Decoder) {}

internal/codec/ugorji.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//go:build !go1.25 || !goexperiment.jsonv2
2+
3+
package codec
4+
5+
import (
6+
"io"
7+
"sync"
8+
9+
"github.com/ugorji/go/codec"
10+
)
11+
12+
var jsonHandle codec.JsonHandle
13+
14+
func init() {
15+
// This is documented to cause "smart buffering".
16+
jsonHandle.WriterBufferSize = 4096
17+
jsonHandle.ReaderBufferSize = 4096
18+
// Force calling time.Time's Marshal function. This causes an allocation on
19+
// every time.Time value, but is the same behavior as the stdlib json
20+
// encoder. If we decide nulls are OK, this should get removed.
21+
jsonHandle.TimeNotBuiltin = true
22+
}
23+
24+
// Encoder and decoder pools, to reuse if possible.
25+
var (
26+
encPool = sync.Pool{
27+
New: func() any {
28+
return codec.NewEncoder(nil, &jsonHandle)
29+
},
30+
}
31+
decPool = sync.Pool{
32+
New: func() any {
33+
return codec.NewDecoder(nil, &jsonHandle)
34+
},
35+
}
36+
)
37+
38+
// GetEncoder returns an encoder configured to write to w.
39+
func getEncoder(w io.Writer) Encoder {
40+
e := encPool.Get().(*codec.Encoder)
41+
e.Reset(w)
42+
return e
43+
}
44+
45+
// PutEncoder returns an encoder to the pool.
46+
func putEncoder(v Encoder) {
47+
e := v.(*codec.Encoder)
48+
e.Reset(nil)
49+
encPool.Put(e)
50+
}
51+
52+
// GetDecoder returns a decoder configured to read from r.
53+
func getDecoder(r io.Reader) Decoder {
54+
d := decPool.Get().(*codec.Decoder)
55+
d.Reset(r)
56+
return d
57+
}
58+
59+
// PutDecoder returns a decoder to the pool.
60+
func putDecoder(v Decoder) {
61+
d := v.(*codec.Decoder)
62+
d.Reset(nil)
63+
decPool.Put(d)
64+
}

0 commit comments

Comments
 (0)