Skip to content

Commit 2878d48

Browse files
committed
encoding/json: periodically flush decodeState’s buffer to Encoder’s io.Writer (addresses golang#7872)
1 parent ae01846 commit 2878d48

File tree

3 files changed

+62
-14
lines changed

3 files changed

+62
-14
lines changed

src/encoding/json/encode.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"bytes"
1515
"encoding"
1616
"encoding/base64"
17+
"io"
1718
"math"
1819
"reflect"
1920
"runtime"
@@ -141,7 +142,7 @@ func Marshal(v interface{}) ([]byte, error) {
141142
if err != nil {
142143
return nil, err
143144
}
144-
return e.Bytes(), nil
145+
return e.buf.Bytes(), nil
145146
}
146147

147148
// MarshalIndent is like Marshal but applies Indent to format the output.
@@ -243,23 +244,68 @@ func (e *MarshalerError) Error() string {
243244

244245
var hex = "0123456789abcdef"
245246

246-
// An encodeState encodes JSON into a bytes.Buffer.
247+
// An encodeState encodes JSON into a bytes.Buffer,
248+
// optionally flushing to an io.Writer.
247249
type encodeState struct {
248-
bytes.Buffer // accumulated output
249-
scratch [64]byte
250+
buf bytes.Buffer // accumulated output
251+
w io.Writer // optional
252+
scratch [64]byte
250253
}
251254

252255
var encodeStatePool sync.Pool
253256

254257
func newEncodeState() *encodeState {
255258
if v := encodeStatePool.Get(); v != nil {
256259
e := v.(*encodeState)
257-
e.Reset()
260+
e.buf.Reset()
258261
return e
259262
}
260263
return new(encodeState)
261264
}
262265

266+
const maxBuffer = 256 // bytes
267+
268+
func (e *encodeState) flush(force bool) error {
269+
// @ydnar: Non-streaming encoders don’t have a writer.
270+
if e.w == nil {
271+
return nil
272+
}
273+
if force || e.buf.Len() > maxBuffer {
274+
b := e.buf.Bytes()
275+
n, err := e.w.Write(b)
276+
e.buf.Reset()
277+
if n != len(b) {
278+
e.buf.Write(b[n:]) // Keep remaining bytes in the buffer
279+
}
280+
return err
281+
}
282+
return nil
283+
}
284+
285+
func (e *encodeState) Write(p []byte) (int, error) {
286+
n, err := e.buf.Write(p)
287+
if err == nil {
288+
err = e.flush(false)
289+
}
290+
return n, err
291+
}
292+
293+
func (e *encodeState) WriteString(s string) (int, error) {
294+
n, err := e.buf.WriteString(s)
295+
if err == nil {
296+
err = e.flush(false)
297+
}
298+
return n, err
299+
}
300+
301+
func (e *encodeState) WriteByte(c byte) error {
302+
err := e.buf.WriteByte(c)
303+
if err == nil {
304+
err = e.flush(false)
305+
}
306+
return err
307+
}
308+
263309
func (e *encodeState) marshal(v interface{}) (err error) {
264310
defer func() {
265311
if r := recover(); r != nil {
@@ -423,7 +469,7 @@ func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
423469
b, err := m.MarshalJSON()
424470
if err == nil {
425471
// copy JSON into buffer, checking validity.
426-
err = compact(&e.Buffer, b, true)
472+
err = compact(&e.buf, b, true)
427473
}
428474
if err != nil {
429475
e.error(&MarshalerError{v.Type(), err})
@@ -440,7 +486,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
440486
b, err := m.MarshalJSON()
441487
if err == nil {
442488
// copy JSON into buffer, checking validity.
443-
err = compact(&e.Buffer, b, true)
489+
err = compact(&e.buf, b, true)
444490
}
445491
if err != nil {
446492
e.error(&MarshalerError{v.Type(), err})
@@ -818,7 +864,7 @@ func (sv stringValues) get(i int) string { return sv[i].String() }
818864

819865
// NOTE: keep in sync with stringBytes below.
820866
func (e *encodeState) string(s string) (int, error) {
821-
len0 := e.Len()
867+
len0 := e.buf.Len()
822868
e.WriteByte('"')
823869
start := 0
824870
for i := 0; i < len(s); {
@@ -889,12 +935,12 @@ func (e *encodeState) string(s string) (int, error) {
889935
e.WriteString(s[start:])
890936
}
891937
e.WriteByte('"')
892-
return e.Len() - len0, nil
938+
return e.buf.Len() - len0, nil
893939
}
894940

895941
// NOTE: keep in sync with string above.
896942
func (e *encodeState) stringBytes(s []byte) (int, error) {
897-
len0 := e.Len()
943+
len0 := e.buf.Len()
898944
e.WriteByte('"')
899945
start := 0
900946
for i := 0; i < len(s); {
@@ -965,7 +1011,7 @@ func (e *encodeState) stringBytes(s []byte) (int, error) {
9651011
e.Write(s[start:])
9661012
}
9671013
e.WriteByte('"')
968-
return e.Len() - len0, nil
1014+
return e.buf.Len() - len0, nil
9691015
}
9701016

9711017
// A field represents a single field found in a struct.

src/encoding/json/encode_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,8 @@ func TestStringBytes(t *testing.T) {
392392
t.Fatal(err)
393393
}
394394

395-
enc := es.Buffer.String()
396-
encBytes := esBytes.Buffer.String()
395+
enc := es.buf.String()
396+
encBytes := esBytes.buf.String()
397397
if enc != encBytes {
398398
i := 0
399399
for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {

src/encoding/json/stream.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ func (enc *Encoder) Encode(v interface{}) error {
157157
return enc.err
158158
}
159159
e := newEncodeState()
160+
e.w = enc.w
160161
err := e.marshal(v)
161162
if err != nil {
162163
return err
@@ -170,7 +171,8 @@ func (enc *Encoder) Encode(v interface{}) error {
170171
// digits coming.
171172
e.WriteByte('\n')
172173

173-
if _, err = enc.w.Write(e.Bytes()); err != nil {
174+
// @ydnar: Flush any bytes remaining.
175+
if err = e.flush(true); err != nil {
174176
enc.err = err
175177
}
176178
encodeStatePool.Put(e)

0 commit comments

Comments
 (0)