Skip to content

Unsafe #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions fieldpath/serialize-pe.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package fieldpath
import (
"errors"
"fmt"
"io"
"strconv"
"strings"

Expand Down Expand Up @@ -111,33 +112,44 @@ var (
// SerializePathElement serializes a path element
func SerializePathElement(pe PathElement) (string, error) {
buf := strings.Builder{}
stream := writePool.BorrowStream(&buf)
err := serializePathElementToWriter(&buf, pe)
return buf.String(), err
}

func serializePathElementToWriter(w io.Writer, pe PathElement) error {
stream := writePool.BorrowStream(w)
defer writePool.ReturnStream(stream)
switch {
case pe.FieldName != nil:
if _, err := stream.Write(peFieldSepBytes); err != nil {
return "", err
return err
}
stream.WriteRaw(*pe.FieldName)
case pe.Key != nil:
if _, err := stream.Write(peKeySepBytes); err != nil {
return "", err
return err
}
v := value.Value{MapValue: pe.Key}
v.WriteJSONStream(stream)
case pe.Value != nil:
if _, err := stream.Write(peValueSepBytes); err != nil {
return "", err
return err
}
pe.Value.WriteJSONStream(stream)
case pe.Index != nil:
if _, err := stream.Write(peIndexSepBytes); err != nil {
return "", err
return err
}
stream.WriteInt(*pe.Index)
default:
return "", errors.New("invalid PathElement")
return errors.New("invalid PathElement")
}
b := stream.Buffer()
err := stream.Flush()
return buf.String(), err
// Help jsoniter manage its buffers--without this, the next
// use of the stream is likely to require an allocation. Look
// at the jsoniter stream code to understand why. They were probably
// optimizing for folks using the buffer directly.
stream.SetBuffer(b[:0])
return err
}
96 changes: 78 additions & 18 deletions fieldpath/serialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,64 @@ package fieldpath
import (
"bytes"
"io"
"unsafe"

jsoniter "github.com/json-iterator/go"
)

func (s *Set) ToJSON() ([]byte, error) {
buf := bytes.Buffer{}
stream := jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, &buf, 4096)
err := s.ToJSONStream(&buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func (s *Set) ToJSONStream(w io.Writer) error {
stream := writePool.BorrowStream(w)
defer writePool.ReturnStream(stream)

var r reusableBuilder

stream.WriteObjectStart()
s.emitContents_v1(false, stream)
err := s.emitContents_v1(false, stream, &r)
if err != nil {
return err
}
stream.WriteObjectEnd()
err := stream.Flush()
return buf.Bytes(), err
return stream.Flush()
}

func manageMemory(stream *jsoniter.Stream) error {
// Help jsoniter manage its buffers--without this, it does a bunch of
// alloctaions that are not necessary. They were probably optimizing
// for folks using the buffer directly.
b := stream.Buffer()
if len(b) > 4096 || cap(b)-len(b) < 2048 {
if err := stream.Flush(); err != nil {
return err
}
stream.SetBuffer(b[:0])
}
return nil
}

func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
type reusableBuilder struct {
bytes.Buffer
}

func (r *reusableBuilder) unsafeString() string {
b := r.Bytes()
return *(*string)(unsafe.Pointer(&b))
}

func (r *reusableBuilder) reset() *bytes.Buffer {
r.Reset()
return &r.Buffer
}

func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream, r *reusableBuilder) error {
mi, ci := 0, 0
first := true
preWrite := func() {
Expand All @@ -51,24 +93,34 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {

if mpe.Less(cpe) {
preWrite()
str, _ := SerializePathElement(mpe)
stream.WriteObjectField(str)
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
return err
}
stream.WriteObjectField(r.unsafeString())
stream.WriteEmptyObject()
mi++
} else if cpe.Less(mpe) {
preWrite()
str, _ := SerializePathElement(cpe)
stream.WriteObjectField(str)
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
return err
}
stream.WriteObjectField(r.unsafeString())
stream.WriteObjectStart()
s.Children.members[ci].set.emitContents_v1(false, stream)
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
return err
}
stream.WriteObjectEnd()
ci++
} else {
preWrite()
str, _ := SerializePathElement(cpe)
stream.WriteObjectField(str)
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
return err
}
stream.WriteObjectField(r.unsafeString())
stream.WriteObjectStart()
s.Children.members[ci].set.emitContents_v1(true, stream)
if err := s.Children.members[ci].set.emitContents_v1(true, stream, r); err != nil {
return err
}
stream.WriteObjectEnd()
mi++
ci++
Expand All @@ -79,8 +131,10 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
mpe := s.Members.members[mi]

preWrite()
str, _ := SerializePathElement(mpe)
stream.WriteObjectField(str)
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
return err
}
stream.WriteObjectField(r.unsafeString())
stream.WriteEmptyObject()
mi++
}
Expand All @@ -89,10 +143,14 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
cpe := s.Children.members[ci].pathElement

preWrite()
str, _ := SerializePathElement(cpe)
stream.WriteObjectField(str)
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
return err
}
stream.WriteObjectField(r.unsafeString())
stream.WriteObjectStart()
s.Children.members[ci].set.emitContents_v1(false, stream)
if err := s.Children.members[ci].set.emitContents_v1(false, stream, r); err != nil {
return err
}
stream.WriteObjectEnd()
ci++
}
Expand All @@ -102,10 +160,12 @@ func (s *Set) emitContents_v1(includeSelf bool, stream *jsoniter.Stream) {
stream.WriteObjectField(".")
stream.WriteEmptyObject()
}
return manageMemory(stream)
}

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

found, _ := readIter_v1(iter)
Expand Down