@@ -19,22 +19,72 @@ package fieldpath
19
19
import (
20
20
"bytes"
21
21
"io"
22
+ "strings"
23
+ "unsafe"
22
24
23
25
jsoniter "github.com/json-iterator/go"
24
26
)
25
27
26
28
func (s * Set ) ToJSON () ([]byte , error ) {
27
29
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
29
42
30
43
stream .WriteObjectStart ()
31
- s .emitContents_v1 (false , stream )
44
+ err := s .emitContents_v1 (false , stream , & r )
45
+ if err != nil {
46
+ return err
47
+ }
32
48
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
+ // Help jsoniter manage its buffers--without this, it does a bunch of
54
+ // alloctaions that are not necessary. They were probably optimizing
55
+ // for folks using the buffer directly.
56
+ b := stream .Buffer ()
57
+ if len (b ) > 4096 || cap (b )- len (b ) < 2048 {
58
+ if err := stream .Flush (); err != nil {
59
+ return err
60
+ }
61
+ stream .SetBuffer (b [:0 ])
62
+ }
63
+ return nil
64
+ }
65
+
66
+ // reusableBuilder is a copy of strings.StringBuilder, which supports copy-free
67
+ // []byte -> string transformation. This offers a reset() method which mutates
68
+ // internal data, permitting us to reuse the same memory over and over. This
69
+ // only makes sense if the string is going to be copied first;, otherwise we'd
70
+ // be mutating the string, which is very bad.
71
+ //
72
+ // We could remove this completely if we modified jsoniter to permit writing a
73
+ // string from a []byte.
74
+ //
75
+ // This MUST match the strings.Builder definition EXACTLY, otherwise reset()
76
+ // will do something very bad to the heap.
77
+ type reusableBuilder struct {
78
+ addr * strings.Builder
79
+ buf []byte
80
+ }
81
+
82
+ func (r * reusableBuilder ) reset () * strings.Builder {
83
+ r .buf = r .buf [:0 ]
84
+ return (* strings .Builder )(unsafe .Pointer (r ))
35
85
}
36
86
37
- func (s * Set ) emitContents_v1 (includeSelf bool , stream * jsoniter.Stream ) {
87
+ func (s * Set ) emitContents_v1 (includeSelf bool , stream * jsoniter.Stream , r * reusableBuilder ) error {
38
88
mi , ci := 0 , 0
39
89
first := true
40
90
preWrite := func () {
@@ -51,24 +101,28 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
51
101
52
102
if mpe .Less (cpe ) {
53
103
preWrite ()
54
- str , _ := SerializePathElement ( mpe )
104
+ str , _ := serializePathElementUsingSB ( r . reset (), mpe )
55
105
stream .WriteObjectField (str )
56
106
stream .WriteEmptyObject ()
57
107
mi ++
58
108
} else if cpe .Less (mpe ) {
59
109
preWrite ()
60
- str , _ := SerializePathElement ( cpe )
110
+ str , _ := serializePathElementUsingSB ( r . reset (), cpe )
61
111
stream .WriteObjectField (str )
62
112
stream .WriteObjectStart ()
63
- s .Children .members [ci ].set .emitContents_v1 (false , stream )
113
+ if err := s .Children .members [ci ].set .emitContents_v1 (false , stream , r ); err != nil {
114
+ return err
115
+ }
64
116
stream .WriteObjectEnd ()
65
117
ci ++
66
118
} else {
67
119
preWrite ()
68
- str , _ := SerializePathElement ( cpe )
120
+ str , _ := serializePathElementUsingSB ( r . reset (), cpe )
69
121
stream .WriteObjectField (str )
70
122
stream .WriteObjectStart ()
71
- s .Children .members [ci ].set .emitContents_v1 (true , stream )
123
+ if err := s .Children .members [ci ].set .emitContents_v1 (true , stream , r ); err != nil {
124
+ return err
125
+ }
72
126
stream .WriteObjectEnd ()
73
127
mi ++
74
128
ci ++
@@ -79,7 +133,7 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
79
133
mpe := s .Members .members [mi ]
80
134
81
135
preWrite ()
82
- str , _ := SerializePathElement ( mpe )
136
+ str , _ := serializePathElementUsingSB ( r . reset (), mpe )
83
137
stream .WriteObjectField (str )
84
138
stream .WriteEmptyObject ()
85
139
mi ++
@@ -89,10 +143,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
89
143
cpe := s .Children .members [ci ].pathElement
90
144
91
145
preWrite ()
92
- str , _ := SerializePathElement ( cpe )
146
+ str , _ := serializePathElementUsingSB ( r . reset (), cpe )
93
147
stream .WriteObjectField (str )
94
148
stream .WriteObjectStart ()
95
- s .Children .members [ci ].set .emitContents_v1 (false , stream )
149
+ if err := s .Children .members [ci ].set .emitContents_v1 (false , stream , r ); err != nil {
150
+ return err
151
+ }
96
152
stream .WriteObjectEnd ()
97
153
ci ++
98
154
}
@@ -102,10 +158,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
102
158
stream .WriteObjectField ("." )
103
159
stream .WriteEmptyObject ()
104
160
}
161
+ return manageMemory (stream )
105
162
}
106
163
107
164
// FromJSON clears s and reads a JSON formatted set structure.
108
165
func (s * Set ) FromJSON (r io.Reader ) error {
166
+ // The iterator pool is completely useless for memory management, grrr.
109
167
iter := jsoniter .Parse (jsoniter .ConfigCompatibleWithStandardLibrary , r , 4096 )
110
168
111
169
found , _ := readIter_v1 (iter )
0 commit comments