Skip to content

Commit 0a60131

Browse files
committed
reduce allocations by cheating
1 parent b48d312 commit 0a60131

File tree

2 files changed

+65
-15
lines changed

2 files changed

+65
-15
lines changed

fieldpath/serialize-pe.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ var (
111111
// SerializePathElement serializes a path element
112112
func SerializePathElement(pe PathElement) (string, error) {
113113
buf := strings.Builder{}
114-
stream := writePool.BorrowStream(&buf)
114+
return serializePathElementUsingSB(&buf, pe)
115+
}
116+
117+
func serializePathElementUsingSB(sb *strings.Builder, pe PathElement) (string, error) {
118+
stream := writePool.BorrowStream(sb)
115119
defer writePool.ReturnStream(stream)
116120
switch {
117121
case pe.FieldName != nil:
@@ -138,6 +142,8 @@ func SerializePathElement(pe PathElement) (string, error) {
138142
default:
139143
return "", errors.New("invalid PathElement")
140144
}
145+
b := stream.Buffer()
141146
err := stream.Flush()
142-
return buf.String(), err
147+
stream.SetBuffer(b[:0])
148+
return sb.String(), err
143149
}

fieldpath/serialize.go

+57-13
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,58 @@ package fieldpath
1919
import (
2020
"bytes"
2121
"io"
22+
"strings"
23+
"unsafe"
2224

2325
jsoniter "github.com/json-iterator/go"
2426
)
2527

2628
func (s *Set) ToJSON() ([]byte, error) {
2729
buf := bytes.Buffer{}
28-
stream := jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, &buf, 4096)
30+
err := s.ToJSONStream(&buf)
31+
if err != nil {
32+
return nil, err
33+
}
34+
return buf.Bytes(), nil
35+
}
36+
37+
func (s *Set) ToJSONStream(w io.Writer) error {
38+
stream := writePool.BorrowStream(w)
39+
defer writePool.ReturnStream(stream)
40+
41+
var r reusableBuilder
2942

3043
stream.WriteObjectStart()
31-
s.emitContents_v1(false, stream)
44+
err := s.emitContents_v1(false, stream, &r)
45+
if err != nil {
46+
return err
47+
}
3248
stream.WriteObjectEnd()
33-
err := stream.Flush()
34-
return buf.Bytes(), err
49+
return stream.Flush()
50+
}
51+
52+
func manageMemory(stream *jsoniter.Stream) error {
53+
b := stream.Buffer()
54+
if len(b) > 4096 || cap(b)-len(b) < 2048 {
55+
if err := stream.Flush(); err != nil {
56+
return err
57+
}
58+
stream.SetBuffer(b[:0])
59+
}
60+
return nil
61+
}
62+
63+
type reusableBuilder struct {
64+
addr *strings.Builder // of receiver, to detect copies by value
65+
buf []byte
66+
}
67+
68+
func (r *reusableBuilder) reset() *strings.Builder {
69+
r.buf = r.buf[:0]
70+
return (*strings.Builder)(unsafe.Pointer(r))
3571
}
3672

37-
func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
73+
func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream, r *reusableBuilder) error {
3874
mi, ci := 0, 0
3975
first := true
4076
preWrite := func() {
@@ -51,24 +87,28 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
5187

5288
if mpe.Less(cpe) {
5389
preWrite()
54-
str, _ := SerializePathElement(mpe)
90+
str, _ := serializePathElementUsingSB(r.reset(), mpe)
5591
stream.WriteObjectField(str)
5692
stream.WriteEmptyObject()
5793
mi++
5894
} else if cpe.Less(mpe) {
5995
preWrite()
60-
str, _ := SerializePathElement(cpe)
96+
str, _ := serializePathElementUsingSB(r.reset(), cpe)
6197
stream.WriteObjectField(str)
6298
stream.WriteObjectStart()
63-
s.Children.members[ci].set.emitContents_v1(false, stream)
99+
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
100+
return err
101+
}
64102
stream.WriteObjectEnd()
65103
ci++
66104
} else {
67105
preWrite()
68-
str, _ := SerializePathElement(cpe)
106+
str, _ := serializePathElementUsingSB(r.reset(), cpe)
69107
stream.WriteObjectField(str)
70108
stream.WriteObjectStart()
71-
s.Children.members[ci].set.emitContents_v1(true, stream)
109+
if err := s.Children.members[ci].set.emitContents_v1(true, stream, r); err != nil {
110+
return err
111+
}
72112
stream.WriteObjectEnd()
73113
mi++
74114
ci++
@@ -79,7 +119,7 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
79119
mpe := s.Members.members[mi]
80120

81121
preWrite()
82-
str, _ := SerializePathElement(mpe)
122+
str, _ := serializePathElementUsingSB(r.reset(), mpe)
83123
stream.WriteObjectField(str)
84124
stream.WriteEmptyObject()
85125
mi++
@@ -89,10 +129,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
89129
cpe := s.Children.members[ci].pathElement
90130

91131
preWrite()
92-
str, _ := SerializePathElement(cpe)
132+
str, _ := serializePathElementUsingSB(r.reset(), cpe)
93133
stream.WriteObjectField(str)
94134
stream.WriteObjectStart()
95-
s.Children.members[ci].set.emitContents_v1(false, stream)
135+
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
136+
return err
137+
}
96138
stream.WriteObjectEnd()
97139
ci++
98140
}
@@ -102,10 +144,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
102144
stream.WriteObjectField(".")
103145
stream.WriteEmptyObject()
104146
}
147+
return manageMemory(stream)
105148
}
106149

107150
// FromJSON clears s and reads a JSON formatted set structure.
108151
func (s *Set) FromJSON(r io.Reader) error {
152+
// The iterator pool is completely useless for memory management, grrr.
109153
iter := jsoniter.Parse(jsoniter.ConfigCompatibleWithStandardLibrary, r, 4096)
110154

111155
found, _ := readIter_v1(iter)

0 commit comments

Comments
 (0)