a3bf13711c
I had to run `make generate`. Some API functions got additional parameters `Options` and `Context`. I used empty options and `context.TODO()` for now. Signed-off-by: leonnicolas <leonloechner@gmx.de>
239 lines
5.8 KiB
Go
239 lines
5.8 KiB
Go
/*
|
|
Copyright 2019 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package fieldpath
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"unsafe"
|
|
|
|
jsoniter "github.com/json-iterator/go"
|
|
)
|
|
|
|
func (s *Set) ToJSON() ([]byte, error) {
|
|
buf := bytes.Buffer{}
|
|
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()
|
|
err := s.emitContentsV1(false, stream, &r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectEnd()
|
|
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
|
|
}
|
|
|
|
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) emitContentsV1(includeSelf bool, stream *jsoniter.Stream, r *reusableBuilder) error {
|
|
mi, ci := 0, 0
|
|
first := true
|
|
preWrite := func() {
|
|
if first {
|
|
first = false
|
|
return
|
|
}
|
|
stream.WriteMore()
|
|
}
|
|
|
|
if includeSelf && !(len(s.Members.members) == 0 && len(s.Children.members) == 0) {
|
|
preWrite()
|
|
stream.WriteObjectField(".")
|
|
stream.WriteEmptyObject()
|
|
}
|
|
|
|
for mi < len(s.Members.members) && ci < len(s.Children.members) {
|
|
mpe := s.Members.members[mi]
|
|
cpe := s.Children.members[ci].pathElement
|
|
|
|
if c := mpe.Compare(cpe); c < 0 {
|
|
preWrite()
|
|
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectField(r.unsafeString())
|
|
stream.WriteEmptyObject()
|
|
mi++
|
|
} else if c > 0 {
|
|
preWrite()
|
|
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectField(r.unsafeString())
|
|
stream.WriteObjectStart()
|
|
if err := s.Children.members[ci].set.emitContentsV1(false, stream, r); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectEnd()
|
|
ci++
|
|
} else {
|
|
preWrite()
|
|
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectField(r.unsafeString())
|
|
stream.WriteObjectStart()
|
|
if err := s.Children.members[ci].set.emitContentsV1(true, stream, r); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectEnd()
|
|
mi++
|
|
ci++
|
|
}
|
|
}
|
|
|
|
for mi < len(s.Members.members) {
|
|
mpe := s.Members.members[mi]
|
|
|
|
preWrite()
|
|
if err := serializePathElementToWriter(r.reset(), mpe); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectField(r.unsafeString())
|
|
stream.WriteEmptyObject()
|
|
mi++
|
|
}
|
|
|
|
for ci < len(s.Children.members) {
|
|
cpe := s.Children.members[ci].pathElement
|
|
|
|
preWrite()
|
|
if err := serializePathElementToWriter(r.reset(), cpe); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectField(r.unsafeString())
|
|
stream.WriteObjectStart()
|
|
if err := s.Children.members[ci].set.emitContentsV1(false, stream, r); err != nil {
|
|
return err
|
|
}
|
|
stream.WriteObjectEnd()
|
|
ci++
|
|
}
|
|
|
|
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, _ := readIterV1(iter)
|
|
if found == nil {
|
|
*s = Set{}
|
|
} else {
|
|
*s = *found
|
|
}
|
|
return iter.Error
|
|
}
|
|
|
|
// returns true if this subtree is also (or only) a member of parent; s is nil
|
|
// if there are no further children.
|
|
func readIterV1(iter *jsoniter.Iterator) (children *Set, isMember bool) {
|
|
iter.ReadMapCB(func(iter *jsoniter.Iterator, key string) bool {
|
|
if key == "." {
|
|
isMember = true
|
|
iter.Skip()
|
|
return true
|
|
}
|
|
pe, err := DeserializePathElement(key)
|
|
if err == ErrUnknownPathElementType {
|
|
// Ignore these-- a future version maybe knows what
|
|
// they are. We drop these completely rather than try
|
|
// to preserve things we don't understand.
|
|
iter.Skip()
|
|
return true
|
|
} else if err != nil {
|
|
iter.ReportError("parsing key as path element", err.Error())
|
|
iter.Skip()
|
|
return true
|
|
}
|
|
grandchildren, childIsMember := readIterV1(iter)
|
|
if childIsMember {
|
|
if children == nil {
|
|
children = &Set{}
|
|
}
|
|
m := &children.Members.members
|
|
// Since we expect that most of the time these will have been
|
|
// serialized in the right order, we just verify that and append.
|
|
appendOK := len(*m) == 0 || (*m)[len(*m)-1].Less(pe)
|
|
if appendOK {
|
|
*m = append(*m, pe)
|
|
} else {
|
|
children.Members.Insert(pe)
|
|
}
|
|
}
|
|
if grandchildren != nil {
|
|
if children == nil {
|
|
children = &Set{}
|
|
}
|
|
// Since we expect that most of the time these will have been
|
|
// serialized in the right order, we just verify that and append.
|
|
m := &children.Children.members
|
|
appendOK := len(*m) == 0 || (*m)[len(*m)-1].pathElement.Less(pe)
|
|
if appendOK {
|
|
*m = append(*m, setNode{pe, grandchildren})
|
|
} else {
|
|
*children.Children.Descend(pe) = *grandchildren
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
if children == nil {
|
|
isMember = true
|
|
}
|
|
|
|
return children, isMember
|
|
}
|