Skip to content

Commit 0c1d754

Browse files
authored
Merge pull request #91 from lavalamp/unsafe
Unsafe
2 parents 0eb2853 + 41a38d8 commit 0c1d754

File tree

2 files changed

+97
-25
lines changed

2 files changed

+97
-25
lines changed

fieldpath/serialize-pe.go

+19-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package fieldpath
1919
import (
2020
"errors"
2121
"fmt"
22+
"io"
2223
"strconv"
2324
"strings"
2425

@@ -111,33 +112,44 @@ var (
111112
// SerializePathElement serializes a path element
112113
func SerializePathElement(pe PathElement) (string, error) {
113114
buf := strings.Builder{}
114-
stream := writePool.BorrowStream(&buf)
115+
err := serializePathElementToWriter(&buf, pe)
116+
return buf.String(), err
117+
}
118+
119+
func serializePathElementToWriter(w io.Writer, pe PathElement) error {
120+
stream := writePool.BorrowStream(w)
115121
defer writePool.ReturnStream(stream)
116122
switch {
117123
case pe.FieldName != nil:
118124
if _, err := stream.Write(peFieldSepBytes); err != nil {
119-
return "", err
125+
return err
120126
}
121127
stream.WriteRaw(*pe.FieldName)
122128
case pe.Key != nil:
123129
if _, err := stream.Write(peKeySepBytes); err != nil {
124-
return "", err
130+
return err
125131
}
126132
v := value.Value{MapValue: pe.Key}
127133
v.WriteJSONStream(stream)
128134
case pe.Value != nil:
129135
if _, err := stream.Write(peValueSepBytes); err != nil {
130-
return "", err
136+
return err
131137
}
132138
pe.Value.WriteJSONStream(stream)
133139
case pe.Index != nil:
134140
if _, err := stream.Write(peIndexSepBytes); err != nil {
135-
return "", err
141+
return err
136142
}
137143
stream.WriteInt(*pe.Index)
138144
default:
139-
return "", errors.New("invalid PathElement")
145+
return errors.New("invalid PathElement")
140146
}
147+
b := stream.Buffer()
141148
err := stream.Flush()
142-
return buf.String(), err
149+
// Help jsoniter manage its buffers--without this, the next
150+
// use of the stream is likely to require an allocation. Look
151+
// at the jsoniter stream code to understand why. They were probably
152+
// optimizing for folks using the buffer directly.
153+
stream.SetBuffer(b[:0])
154+
return err
143155
}

fieldpath/serialize.go

+78-18
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,64 @@ package fieldpath
1919
import (
2020
"bytes"
2121
"io"
22+
"unsafe"
2223

2324
jsoniter "github.com/json-iterator/go"
2425
)
2526

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

3042
stream.WriteObjectStart()
31-
s.emitContents_v1(false, stream)
43+
err := s.emitContents_v1(false, stream, &r)
44+
if err != nil {
45+
return err
46+
}
3247
stream.WriteObjectEnd()
33-
err := stream.Flush()
34-
return buf.Bytes(), err
48+
return stream.Flush()
49+
}
50+
51+
func manageMemory(stream *jsoniter.Stream) error {
52+
// Help jsoniter manage its buffers--without this, it does a bunch of
53+
// alloctaions that are not necessary. They were probably optimizing
54+
// for folks using the buffer directly.
55+
b := stream.Buffer()
56+
if len(b) > 4096 || cap(b)-len(b) < 2048 {
57+
if err := stream.Flush(); err != nil {
58+
return err
59+
}
60+
stream.SetBuffer(b[:0])
61+
}
62+
return nil
3563
}
3664

37-
func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
65+
type reusableBuilder struct {
66+
bytes.Buffer
67+
}
68+
69+
func (r *reusableBuilder) unsafeString() string {
70+
b := r.Bytes()
71+
return *(*string)(unsafe.Pointer(&b))
72+
}
73+
74+
func (r *reusableBuilder) reset() *bytes.Buffer {
75+
r.Reset()
76+
return &r.Buffer
77+
}
78+
79+
func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream, r *reusableBuilder) error {
3880
mi, ci := 0, 0
3981
first := true
4082
preWrite := func() {
@@ -51,24 +93,34 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
5193

5294
if mpe.Less(cpe) {
5395
preWrite()
54-
str, _ := SerializePathElement(mpe)
55-
stream.WriteObjectField(str)
96+
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
97+
return err
98+
}
99+
stream.WriteObjectField(r.unsafeString())
56100
stream.WriteEmptyObject()
57101
mi++
58102
} else if cpe.Less(mpe) {
59103
preWrite()
60-
str, _ := SerializePathElement(cpe)
61-
stream.WriteObjectField(str)
104+
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
105+
return err
106+
}
107+
stream.WriteObjectField(r.unsafeString())
62108
stream.WriteObjectStart()
63-
s.Children.members[ci].set.emitContents_v1(false, stream)
109+
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
110+
return err
111+
}
64112
stream.WriteObjectEnd()
65113
ci++
66114
} else {
67115
preWrite()
68-
str, _ := SerializePathElement(cpe)
69-
stream.WriteObjectField(str)
116+
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
117+
return err
118+
}
119+
stream.WriteObjectField(r.unsafeString())
70120
stream.WriteObjectStart()
71-
s.Children.members[ci].set.emitContents_v1(true, stream)
121+
if err := s.Children.members[ci].set.emitContents_v1(true, stream, r); err != nil {
122+
return err
123+
}
72124
stream.WriteObjectEnd()
73125
mi++
74126
ci++
@@ -79,8 +131,10 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
79131
mpe := s.Members.members[mi]
80132

81133
preWrite()
82-
str, _ := SerializePathElement(mpe)
83-
stream.WriteObjectField(str)
134+
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
135+
return err
136+
}
137+
stream.WriteObjectField(r.unsafeString())
84138
stream.WriteEmptyObject()
85139
mi++
86140
}
@@ -89,10 +143,14 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
89143
cpe := s.Children.members[ci].pathElement
90144

91145
preWrite()
92-
str, _ := SerializePathElement(cpe)
93-
stream.WriteObjectField(str)
146+
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
147+
return err
148+
}
149+
stream.WriteObjectField(r.unsafeString())
94150
stream.WriteObjectStart()
95-
s.Children.members[ci].set.emitContents_v1(false, stream)
151+
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
152+
return err
153+
}
96154
stream.WriteObjectEnd()
97155
ci++
98156
}
@@ -102,10 +160,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
102160
stream.WriteObjectField(".")
103161
stream.WriteEmptyObject()
104162
}
163+
return manageMemory(stream)
105164
}
106165

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

111171
found, _ := readIter_v1(iter)

0 commit comments

Comments
 (0)