You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1186 lines
29 KiB
1186 lines
29 KiB
//
|
|
// DISCLAIMER
|
|
//
|
|
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
|
//
|
|
// 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.
|
|
//
|
|
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
//
|
|
// Author Ewout Prangsma
|
|
//
|
|
|
|
package velocypack
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"reflect"
|
|
)
|
|
|
|
// BuilderOptions contains options that influence how Builder builds slices.
|
|
type BuilderOptions struct {
|
|
BuildUnindexedArrays bool
|
|
BuildUnindexedObjects bool
|
|
CheckAttributeUniqueness bool
|
|
}
|
|
|
|
// Builder is used to build VPack structures.
|
|
type Builder struct {
|
|
BuilderOptions
|
|
buf builderBuffer
|
|
stack builderStack
|
|
index []indexVector
|
|
keyWritten bool
|
|
}
|
|
|
|
func NewBuilder(capacity uint) *Builder {
|
|
b := &Builder{
|
|
buf: make(builderBuffer, 0, capacity),
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Clear and start from scratch:
|
|
func (b *Builder) Clear() {
|
|
b.buf = nil
|
|
b.stack.Clear()
|
|
b.keyWritten = false
|
|
}
|
|
|
|
// Bytes return the generated bytes.
|
|
// The returned slice is shared with the builder itself, so you must not modify it.
|
|
// When the builder is not closed, an error is returned.
|
|
func (b *Builder) Bytes() ([]byte, error) {
|
|
if !b.IsClosed() {
|
|
return nil, WithStack(BuilderNotClosedError)
|
|
}
|
|
return b.buf, nil
|
|
}
|
|
|
|
// Slice returns a slice of the result.
|
|
func (b *Builder) Slice() (Slice, error) {
|
|
if b.buf.IsEmpty() {
|
|
return Slice{}, nil
|
|
}
|
|
bytes, err := b.Bytes()
|
|
return bytes, WithStack(err)
|
|
}
|
|
|
|
// WriteTo writes the generated bytes to the given writer.
|
|
// When the builder is not closed, an error is returned.
|
|
func (b *Builder) WriteTo(w io.Writer) (int64, error) {
|
|
if !b.IsClosed() {
|
|
return 0, WithStack(BuilderNotClosedError)
|
|
}
|
|
if n, err := w.Write(b.buf); err != nil {
|
|
return 0, WithStack(err)
|
|
} else {
|
|
return int64(n), nil
|
|
}
|
|
}
|
|
|
|
// Size returns the actual size of the generated slice.
|
|
// Returns an error when builder is not closed.
|
|
func (b *Builder) Size() (ValueLength, error) {
|
|
if !b.IsClosed() {
|
|
return 0, WithStack(BuilderNotClosedError)
|
|
}
|
|
return b.buf.Len(), nil
|
|
}
|
|
|
|
// IsEmpty returns true when no bytes have been generated yet.
|
|
func (b *Builder) IsEmpty() bool {
|
|
return b.buf.IsEmpty()
|
|
}
|
|
|
|
// IsOpenObject returns true when the builder has an open object at the top of the stack.
|
|
func (b *Builder) IsOpenObject() bool {
|
|
if b.stack.IsEmpty() {
|
|
return false
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
return h == 0x0b || h == 0x014
|
|
}
|
|
|
|
// IsOpenArray returns true when the builder has an open array at the top of the stack.
|
|
func (b *Builder) IsOpenArray() bool {
|
|
if b.stack.IsEmpty() {
|
|
return false
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
return h == 0x06 || h == 0x013
|
|
}
|
|
|
|
// OpenObject starts a new object.
|
|
// This must be closed using Close.
|
|
func (b *Builder) OpenObject(unindexed ...bool) error {
|
|
var vType byte
|
|
if optionalBool(unindexed, false) {
|
|
vType = 0x14
|
|
} else {
|
|
vType = 0x0b
|
|
}
|
|
return WithStack(b.openCompoundValue(vType))
|
|
}
|
|
|
|
// OpenArray starts a new array.
|
|
// This must be closed using Close.
|
|
func (b *Builder) OpenArray(unindexed ...bool) error {
|
|
var vType byte
|
|
if optionalBool(unindexed, false) {
|
|
vType = 0x13
|
|
} else {
|
|
vType = 0x06
|
|
}
|
|
return WithStack(b.openCompoundValue(vType))
|
|
}
|
|
|
|
// Close ends an open object or array.
|
|
func (b *Builder) Close() error {
|
|
if b.IsClosed() {
|
|
return WithStack(BuilderNeedOpenCompoundError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
head := b.buf[tos]
|
|
|
|
vpackAssert(head == 0x06 || head == 0x0b || head == 0x13 || head == 0x14)
|
|
|
|
isArray := (head == 0x06 || head == 0x13)
|
|
index := b.index[b.stack.Len()-1]
|
|
|
|
if index.IsEmpty() {
|
|
b.closeEmptyArrayOrObject(tos, isArray)
|
|
return nil
|
|
}
|
|
|
|
// From now on index.size() > 0
|
|
vpackAssert(len(index) > 0)
|
|
|
|
// check if we can use the compact Array / Object format
|
|
if head == 0x13 || head == 0x14 ||
|
|
(head == 0x06 && b.BuilderOptions.BuildUnindexedArrays) ||
|
|
(head == 0x0b && (b.BuilderOptions.BuildUnindexedObjects || len(index) == 1)) {
|
|
if b.closeCompactArrayOrObject(tos, isArray, index) {
|
|
return nil
|
|
}
|
|
// This might fall through, if closeCompactArrayOrObject gave up!
|
|
}
|
|
|
|
if isArray {
|
|
b.closeArray(tos, index)
|
|
return nil
|
|
}
|
|
|
|
// From now on we're closing an object
|
|
|
|
// fix head byte in case a compact Array / Object was originally requested
|
|
b.buf[tos] = 0x0b
|
|
|
|
// First determine byte length and its format:
|
|
offsetSize := uint(8)
|
|
// can be 1, 2, 4 or 8 for the byte width of the offsets,
|
|
// the byte length and the number of subvalues:
|
|
if b.buf.Len()-tos+ValueLength(len(index))-6 <= 0xff {
|
|
// We have so far used _pos - tos bytes, including the reserved 8
|
|
// bytes for byte length and number of subvalues. In the 1-byte number
|
|
// case we would win back 6 bytes but would need one byte per subvalue
|
|
// for the index table
|
|
offsetSize = 1
|
|
|
|
// Maybe we need to move down data:
|
|
targetPos := ValueLength(3)
|
|
if b.buf.Len() > (tos + 9) {
|
|
_len := ValueLength(b.buf.Len() - (tos + 9))
|
|
checkOverflow(_len)
|
|
src := b.buf[tos+9:]
|
|
copy(b.buf[tos+targetPos:], src[:_len])
|
|
}
|
|
diff := ValueLength(9 - targetPos)
|
|
b.buf.Shrink(uint(diff))
|
|
n := len(index)
|
|
for i := 0; i < n; i++ {
|
|
index[i] -= diff
|
|
}
|
|
|
|
// One could move down things in the offsetSize == 2 case as well,
|
|
// since we only need 4 bytes in the beginning. However, saving these
|
|
// 4 bytes has been sacrificed on the Altar of Performance.
|
|
} else if b.buf.Len()-tos+2*ValueLength(len(index)) <= 0xffff {
|
|
offsetSize = 2
|
|
} else if b.buf.Len()-tos+4*ValueLength(len(index)) <= 0xffffffff {
|
|
offsetSize = 4
|
|
}
|
|
|
|
// Now build the table:
|
|
extraSpace := offsetSize * uint(len(index))
|
|
if offsetSize == 8 {
|
|
extraSpace += 8
|
|
}
|
|
b.buf.ReserveSpace(extraSpace)
|
|
tableBase := b.buf.Len()
|
|
b.buf.Grow(offsetSize * uint(len(index)))
|
|
// Object
|
|
if len(index) >= 2 {
|
|
if err := b.sortObjectIndex(b.buf[tos:], index); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
}
|
|
for i := uint(0); i < uint(len(index)); i++ {
|
|
indexBase := tableBase + ValueLength(offsetSize*i)
|
|
x := uint64(index[i])
|
|
for j := uint(0); j < offsetSize; j++ {
|
|
b.buf[indexBase+ValueLength(j)] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
}
|
|
// Finally fix the byte width in the type byte:
|
|
if offsetSize > 1 {
|
|
if offsetSize == 2 {
|
|
b.buf[tos] += 1
|
|
} else if offsetSize == 4 {
|
|
b.buf[tos] += 2
|
|
} else { // offsetSize == 8
|
|
b.buf[tos] += 3
|
|
b.appendLength(ValueLength(len(index)), 8)
|
|
}
|
|
}
|
|
|
|
// Fix the byte length in the beginning:
|
|
x := ValueLength(b.buf.Len() - tos)
|
|
for i := uint(1); i <= offsetSize; i++ {
|
|
b.buf[tos+ValueLength(i)] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
|
|
if offsetSize < 8 {
|
|
x := len(index)
|
|
for i := uint(offsetSize + 1); i <= 2*offsetSize; i++ {
|
|
b.buf[tos+ValueLength(i)] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
}
|
|
|
|
// And, if desired, check attribute uniqueness:
|
|
if b.BuilderOptions.CheckAttributeUniqueness && len(index) > 1 {
|
|
// check uniqueness of attribute names
|
|
if err := b.checkAttributeUniqueness(Slice(b.buf[tos:])); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
}
|
|
|
|
// Now the array or object is complete, we pop a ValueLength off the _stack:
|
|
b.stack.Pop()
|
|
// Intentionally leave _index[depth] intact to avoid future allocs!
|
|
return nil
|
|
}
|
|
|
|
// IsClosed returns true if there are no more open objects or arrays.
|
|
func (b *Builder) IsClosed() bool {
|
|
return b.stack.IsEmpty()
|
|
}
|
|
|
|
// HasKey checks whether an Object value has a specific key attribute.
|
|
func (b *Builder) HasKey(key string) (bool, error) {
|
|
if b.stack.IsEmpty() {
|
|
return false, WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
if h != 0x0b && h != 0x14 {
|
|
return false, WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
index := b.index[b.stack.Len()-1]
|
|
if index.IsEmpty() {
|
|
return false, nil
|
|
}
|
|
for _, idx := range index {
|
|
s := Slice(b.buf[tos+idx:])
|
|
k, err := s.makeKey()
|
|
if err != nil {
|
|
return false, WithStack(err)
|
|
}
|
|
if eq, err := k.IsEqualString(key); err != nil {
|
|
return false, WithStack(err)
|
|
} else if eq {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// GetKey returns the value for a specific key of an Object value.
|
|
// Returns Slice of type None when key is not found.
|
|
func (b *Builder) GetKey(key string) (Slice, error) {
|
|
if b.stack.IsEmpty() {
|
|
return nil, WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
if h != 0x0b && h != 0x14 {
|
|
return nil, WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
index := b.index[b.stack.Len()-1]
|
|
if index.IsEmpty() {
|
|
return nil, nil
|
|
}
|
|
for _, idx := range index {
|
|
s := Slice(b.buf[tos+idx:])
|
|
k, err := s.makeKey()
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
if eq, err := k.IsEqualString(key); err != nil {
|
|
return nil, WithStack(err)
|
|
} else if eq {
|
|
value, err := s.Next()
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
return value, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// RemoveLast removes last subvalue written to an (unclosed) object or array.
|
|
func (b *Builder) RemoveLast() error {
|
|
if b.stack.IsEmpty() {
|
|
return WithStack(BuilderNeedOpenCompoundError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
index := &b.index[b.stack.Len()-1]
|
|
if index.IsEmpty() {
|
|
return WithStack(BuilderNeedSubValueError)
|
|
}
|
|
newLength := tos + (*index)[len(*index)-1]
|
|
lastSize := b.buf.Len() - newLength
|
|
b.buf.Shrink(uint(lastSize))
|
|
index.RemoveLast()
|
|
return nil
|
|
}
|
|
|
|
// addNull adds a null value to the buffer.
|
|
func (b *Builder) addNull() {
|
|
b.buf.WriteByte(0x18)
|
|
}
|
|
|
|
// addFalse adds a bool false value to the buffer.
|
|
func (b *Builder) addFalse() {
|
|
b.buf.WriteByte(0x19)
|
|
}
|
|
|
|
// addTrue adds a bool true value to the buffer.
|
|
func (b *Builder) addTrue() {
|
|
b.buf.WriteByte(0x1a)
|
|
}
|
|
|
|
// addBool adds a bool value to the buffer.
|
|
func (b *Builder) addBool(v bool) {
|
|
if v {
|
|
b.addTrue()
|
|
} else {
|
|
b.addFalse()
|
|
}
|
|
}
|
|
|
|
// addDouble adds a double value to the buffer.
|
|
func (b *Builder) addDouble(v float64) {
|
|
bits := math.Float64bits(v)
|
|
b.buf.ReserveSpace(9)
|
|
b.buf.WriteByte(0x1b)
|
|
binary.LittleEndian.PutUint64(b.buf.Grow(8), bits)
|
|
}
|
|
|
|
// addInt adds an int value to the buffer.
|
|
func (b *Builder) addInt(v int64) {
|
|
if v >= 0 && v <= 9 {
|
|
b.buf.WriteByte(0x30 + byte(v))
|
|
} else if v < 0 && v >= -6 {
|
|
b.buf.WriteByte(byte(0x40 + int(v)))
|
|
} else {
|
|
b.appendInt(v, 0x1f)
|
|
}
|
|
}
|
|
|
|
// addUInt adds an uint value to the buffer.
|
|
func (b *Builder) addUInt(v uint64) {
|
|
if v <= 9 {
|
|
b.buf.WriteByte(0x30 + byte(v))
|
|
} else {
|
|
b.appendUInt(v, 0x27)
|
|
}
|
|
}
|
|
|
|
// addUTCDate adds an UTC date value to the buffer.
|
|
func (b *Builder) addUTCDate(v int64) {
|
|
x := toUInt64(v)
|
|
dst := b.buf.Grow(9)
|
|
dst[0] = 0x1c
|
|
setLength(dst[1:], ValueLength(x), 8)
|
|
}
|
|
|
|
// addString adds a string value to the buffer.
|
|
func (b *Builder) addString(v string) {
|
|
strLen := uint(len(v))
|
|
if strLen > 126 {
|
|
// long string
|
|
dst := b.buf.Grow(1 + 8 + strLen)
|
|
dst[0] = 0xbf
|
|
setLength(dst[1:], ValueLength(strLen), 8) // string length
|
|
copy(dst[9:], v) // string data
|
|
} else {
|
|
dst := b.buf.Grow(1 + strLen)
|
|
dst[0] = byte(0x40 + strLen) // short string (with length)
|
|
copy(dst[1:], v) // string data
|
|
}
|
|
}
|
|
|
|
// addBinary adds a binary value to the buffer.
|
|
func (b *Builder) addBinary(v []byte) {
|
|
l := uint(len(v))
|
|
b.buf.ReserveSpace(1 + 8 + l)
|
|
b.appendUInt(uint64(l), 0xbf) // data length
|
|
b.buf.Write(v) // data
|
|
}
|
|
|
|
// addIllegal adds an Illegal value to the buffer.
|
|
func (b *Builder) addIllegal() {
|
|
b.buf.WriteByte(0x17)
|
|
}
|
|
|
|
// addMinKey adds a MinKey value to the buffer.
|
|
func (b *Builder) addMinKey() {
|
|
b.buf.WriteByte(0x1e)
|
|
}
|
|
|
|
// addMaxKey adds a MaxKey value to the buffer.
|
|
func (b *Builder) addMaxKey() {
|
|
b.buf.WriteByte(0x1f)
|
|
}
|
|
|
|
// Add adds a raw go value value to an array/raw value/object.
|
|
func (b *Builder) Add(v interface{}) error {
|
|
if it, ok := v.(*ObjectIterator); ok {
|
|
return WithStack(b.AddKeyValuesFromIterator(it))
|
|
}
|
|
if it, ok := v.(*ArrayIterator); ok {
|
|
return WithStack(b.AddValuesFromIterator(it))
|
|
}
|
|
value := NewValue(v)
|
|
if value.IsIllegal() {
|
|
return WithStack(BuilderUnexpectedTypeError{fmt.Sprintf("Cannot convert value of type %s", reflect.TypeOf(v).Name())})
|
|
}
|
|
if err := b.addInternal(value); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddValue adds a value to an array/raw value/object.
|
|
func (b *Builder) AddValue(v Value) error {
|
|
if err := b.addInternal(v); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddKeyValue adds a key+value to an open object.
|
|
func (b *Builder) AddKeyValue(key string, v Value) error {
|
|
if err := b.addInternalKeyValue(key, v); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddValuesFromIterator adds values to an array from the given iterator.
|
|
// The array must be opened before a call to this function and the array is left open Intentionally.
|
|
func (b *Builder) AddValuesFromIterator(it *ArrayIterator) error {
|
|
if b.stack.IsEmpty() {
|
|
return WithStack(BuilderNeedOpenArrayError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
if h != 0x06 && h != 0x13 {
|
|
return WithStack(BuilderNeedOpenArrayError)
|
|
}
|
|
for it.IsValid() {
|
|
v, err := it.Value()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := b.addInternal(NewSliceValue(v)); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := it.Next(); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddKeyValuesFromIterator adds values to an object from the given iterator.
|
|
// The object must be opened before a call to this function and the object is left open Intentionally.
|
|
func (b *Builder) AddKeyValuesFromIterator(it *ObjectIterator) error {
|
|
if b.stack.IsEmpty() {
|
|
return WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
tos, _ := b.stack.Tos()
|
|
h := b.buf[tos]
|
|
if h != 0x0b && h != 0x14 {
|
|
return WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
if b.keyWritten {
|
|
return WithStack(BuilderKeyAlreadyWrittenError)
|
|
}
|
|
for it.IsValid() {
|
|
k, err := it.Key(true)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
key, err := k.GetString()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
v, err := it.Value()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := b.addInternalKeyValue(key, NewSliceValue(v)); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := it.Next(); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// returns number of bytes required to store the value in 2s-complement
|
|
func intLength(value int64) uint {
|
|
if value >= -0x80 && value <= 0x7f {
|
|
// shortcut for the common case
|
|
return 1
|
|
}
|
|
var x uint64
|
|
if value >= 0 {
|
|
x = uint64(value)
|
|
} else {
|
|
x = uint64(-(value + 1))
|
|
}
|
|
xSize := uint(0)
|
|
for {
|
|
xSize++
|
|
x >>= 8
|
|
if x < 0x80 {
|
|
return xSize + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Builder) appendInt(v int64, base uint) {
|
|
vSize := intLength(v)
|
|
var x uint64
|
|
if vSize == 8 {
|
|
x = toUInt64(v)
|
|
} else {
|
|
shift := int64(1) << (vSize*8 - 1) // will never overflow!
|
|
if v >= 0 {
|
|
x = uint64(v)
|
|
} else {
|
|
x = uint64(v+shift) + uint64(shift)
|
|
}
|
|
// x = v >= 0 ? static_cast<uint64_t>(v)
|
|
// : static_cast<uint64_t>(v + shift) + shift;
|
|
}
|
|
dst := b.buf.Grow(1 + vSize)
|
|
dst[0] = byte(base + vSize)
|
|
off := 1
|
|
for ; vSize > 0; vSize-- {
|
|
dst[off] = byte(x & 0xff)
|
|
x >>= 8
|
|
off++
|
|
}
|
|
}
|
|
|
|
func (b *Builder) appendUInt(v uint64, base uint) {
|
|
b.buf.ReserveSpace(9)
|
|
save := b.buf.Len()
|
|
b.buf.WriteByte(0) // Will be overwritten at end of function.
|
|
vSize := uint(0)
|
|
for {
|
|
vSize++
|
|
b.buf.WriteByte(byte(v & 0xff))
|
|
v >>= 8
|
|
if v == 0 {
|
|
break
|
|
}
|
|
}
|
|
b.buf[save] = byte(base + vSize)
|
|
}
|
|
|
|
func (b *Builder) appendLength(v ValueLength, n uint) {
|
|
dst := b.buf.Grow(n)
|
|
setLength(dst, v, n)
|
|
}
|
|
|
|
func setLength(dst []byte, v ValueLength, n uint) {
|
|
for i := uint(0); i < n; i++ {
|
|
dst[i] = byte(v & 0xff)
|
|
v >>= 8
|
|
}
|
|
}
|
|
|
|
// openCompoundValue opens an array/object, checking the context.
|
|
func (b *Builder) openCompoundValue(vType byte) error {
|
|
//haveReported := false
|
|
tos, stackLen := b.stack.Tos()
|
|
if stackLen > 0 {
|
|
h := b.buf[tos]
|
|
if !b.keyWritten {
|
|
if h != 0x06 && h != 0x13 {
|
|
return WithStack(BuilderNeedOpenArrayError)
|
|
}
|
|
b.reportAdd()
|
|
//haveReported = true
|
|
} else {
|
|
b.keyWritten = false
|
|
}
|
|
}
|
|
b.addCompoundValue(vType)
|
|
// if err && haveReported { b.cleanupAdd() }
|
|
return nil
|
|
}
|
|
|
|
// addCompoundValue adds the start of a component value to the stream & stack.
|
|
func (b *Builder) addCompoundValue(vType byte) {
|
|
pos := b.buf.Len()
|
|
b.stack.Push(pos)
|
|
stackLen := b.stack.Len()
|
|
toAdd := stackLen - len(b.index)
|
|
for toAdd > 0 {
|
|
newIndex := make(indexVector, 0, 16) // Pre-allocate 16 entries so we don't have to allocate memory for the first 16 entries
|
|
b.index = append(b.index, newIndex)
|
|
toAdd--
|
|
}
|
|
b.index[stackLen-1].Clear()
|
|
b.buf.Write([]byte{vType, 0, 0, 0, 0, 0, 0, 0, 0})
|
|
}
|
|
|
|
// closeEmptyArrayOrObject closes an empty array/object, removing the pre-allocated length space.
|
|
func (b *Builder) closeEmptyArrayOrObject(tos ValueLength, isArray bool) {
|
|
// empty Array or Object
|
|
if isArray {
|
|
b.buf[tos] = 0x01
|
|
} else {
|
|
b.buf[tos] = 0x0a
|
|
}
|
|
vpackAssert(b.buf.Len() == tos+9)
|
|
b.buf.Shrink(8)
|
|
b.stack.Pop()
|
|
}
|
|
|
|
// closeCompactArrayOrObject tries to close an array/object using compact notation.
|
|
// Returns true when a compact notation was possible, false otherwise.
|
|
func (b *Builder) closeCompactArrayOrObject(tos ValueLength, isArray bool, index indexVector) bool {
|
|
// use compact notation
|
|
nrItems := len(index)
|
|
nrItemsLen := getVariableValueLength(ValueLength(nrItems))
|
|
vpackAssert(nrItemsLen > 0)
|
|
|
|
byteSize := b.buf.Len() - (tos + 8) + nrItemsLen
|
|
vpackAssert(byteSize > 0)
|
|
|
|
byteSizeLen := getVariableValueLength(byteSize)
|
|
byteSize += byteSizeLen
|
|
if getVariableValueLength(byteSize) != byteSizeLen {
|
|
byteSize++
|
|
byteSizeLen++
|
|
}
|
|
|
|
if byteSizeLen < 9 {
|
|
// can only use compact notation if total byte length is at most 8 bytes long
|
|
if isArray {
|
|
b.buf[tos] = 0x13
|
|
} else {
|
|
b.buf[tos] = 0x14
|
|
}
|
|
|
|
valuesLen := b.buf.Len() - (tos + 9) // Amount of bytes taken up by array/object values.
|
|
if valuesLen > 0 && byteSizeLen < 8 {
|
|
// We have array/object values and our byteSize needs less than the pre-allocated 8 bytes.
|
|
// So we move the array/object values back.
|
|
checkOverflow(valuesLen)
|
|
src := b.buf[tos+9:]
|
|
copy(b.buf[tos+1+byteSizeLen:], src[:valuesLen])
|
|
}
|
|
// Shrink buffer, removing unused space allocated for byteSize.
|
|
b.buf.Shrink(uint(8 - byteSizeLen))
|
|
|
|
// store byte length
|
|
vpackAssert(byteSize > 0)
|
|
storeVariableValueLength(b.buf, tos+1, byteSize, false)
|
|
|
|
// store nrItems
|
|
b.buf.Grow(uint(nrItemsLen))
|
|
storeVariableValueLength(b.buf, tos+byteSize-1, ValueLength(len(index)), true)
|
|
|
|
b.stack.Pop()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// checkAttributeUniqueness checks the given slice for duplicate keys.
|
|
// It returns an error when duplicate keys are found, nil otherwise.
|
|
func (b *Builder) checkAttributeUniqueness(obj Slice) error {
|
|
vpackAssert(b.BuilderOptions.CheckAttributeUniqueness)
|
|
n, err := obj.Length()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
|
|
if obj.IsSorted() {
|
|
// object attributes are sorted
|
|
previous, err := obj.KeyAt(0)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
p, err := previous.GetString()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
|
|
// compare each two adjacent attribute names
|
|
for i := ValueLength(1); i < n; i++ {
|
|
current, err := obj.KeyAt(i)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
// keyAt() guarantees a string as returned type
|
|
vpackAssert(current.IsString())
|
|
|
|
q, err := current.GetString()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
|
|
if p == q {
|
|
// identical key
|
|
return WithStack(DuplicateAttributeNameError)
|
|
}
|
|
// re-use already calculated values for next round
|
|
p = q
|
|
}
|
|
} else {
|
|
keys := make(map[string]struct{})
|
|
|
|
for i := ValueLength(0); i < n; i++ {
|
|
// note: keyAt() already translates integer attributes
|
|
key, err := obj.KeyAt(i)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
// keyAt() guarantees a string as returned type
|
|
vpackAssert(key.IsString())
|
|
|
|
k, err := key.GetString()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if _, found := keys[k]; found {
|
|
return WithStack(DuplicateAttributeNameError)
|
|
}
|
|
keys[k] = struct{}{}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func findAttrName(base []byte) ([]byte, error) {
|
|
b := base[0]
|
|
if b >= 0x40 && b <= 0xbe {
|
|
// short UTF-8 string
|
|
l := b - 0x40
|
|
return base[1 : 1+l], nil
|
|
}
|
|
if b == 0xbf {
|
|
// long UTF-8 string
|
|
l := uint(0)
|
|
// read string length
|
|
for i := 8; i >= 1; i-- {
|
|
l = (l << 8) + uint(base[i])
|
|
}
|
|
return base[1+8 : 1+8+l], nil
|
|
}
|
|
|
|
// translate attribute name
|
|
key, err := Slice(base).makeKey()
|
|
if err != nil {
|
|
return nil, WithStack(err)
|
|
}
|
|
return findAttrName(key)
|
|
}
|
|
|
|
func (b *Builder) sortObjectIndex(objBase []byte, offsets []ValueLength) error {
|
|
list := make(sortEntries, len(offsets))
|
|
for i, off := range offsets {
|
|
name, err := findAttrName(objBase[off:])
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
list[i] = sortEntry{
|
|
Offset: off,
|
|
Name: name,
|
|
}
|
|
}
|
|
list.Sort()
|
|
//sort.Sort(list)
|
|
for i, entry := range list {
|
|
offsets[i] = entry.Offset
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) closeArray(tos ValueLength, index []ValueLength) {
|
|
// fix head byte in case a compact Array was originally requested:
|
|
b.buf[tos] = 0x06
|
|
|
|
needIndexTable := true
|
|
needNrSubs := true
|
|
if len(index) == 1 {
|
|
needIndexTable = false
|
|
needNrSubs = false
|
|
} else if (b.buf.Len()-tos)-index[0] == ValueLength(len(index))*(index[1]-index[0]) {
|
|
// In this case it could be that all entries have the same length
|
|
// and we do not need an offset table at all:
|
|
noTable := true
|
|
subLen := index[1] - index[0]
|
|
if (b.buf.Len()-tos)-index[len(index)-1] != subLen {
|
|
noTable = false
|
|
} else {
|
|
for i := 1; i < len(index)-1; i++ {
|
|
if index[i+1]-index[i] != subLen {
|
|
noTable = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if noTable {
|
|
needIndexTable = false
|
|
needNrSubs = false
|
|
}
|
|
}
|
|
|
|
// First determine byte length and its format:
|
|
var offsetSize uint
|
|
// can be 1, 2, 4 or 8 for the byte width of the offsets,
|
|
// the byte length and the number of subvalues:
|
|
var indexLenIfNeeded ValueLength
|
|
if needIndexTable {
|
|
indexLenIfNeeded = ValueLength(len(index))
|
|
}
|
|
nrSubsLenIfNeeded := ValueLength(7)
|
|
if needNrSubs {
|
|
nrSubsLenIfNeeded = 6
|
|
}
|
|
if b.buf.Len()-tos+(indexLenIfNeeded)-(nrSubsLenIfNeeded) <= 0xff {
|
|
// We have so far used _pos - tos bytes, including the reserved 8
|
|
// bytes for byte length and number of subvalues. In the 1-byte number
|
|
// case we would win back 6 bytes but would need one byte per subvalue
|
|
// for the index table
|
|
offsetSize = 1
|
|
} else if b.buf.Len()-tos+(indexLenIfNeeded*2) <= 0xffff {
|
|
offsetSize = 2
|
|
} else if b.buf.Len()-tos+(indexLenIfNeeded*4) <= 0xffffffff {
|
|
offsetSize = 4
|
|
} else {
|
|
offsetSize = 8
|
|
}
|
|
|
|
// Maybe we need to move down data:
|
|
if offsetSize == 1 {
|
|
targetPos := ValueLength(3)
|
|
if !needIndexTable {
|
|
targetPos = 2
|
|
}
|
|
if b.buf.Len() > (tos + 9) {
|
|
_len := ValueLength(b.buf.Len() - (tos + 9))
|
|
checkOverflow(_len)
|
|
src := b.buf[tos+9:]
|
|
copy(b.buf[tos+targetPos:], src[:_len])
|
|
}
|
|
diff := ValueLength(9 - targetPos)
|
|
b.buf.Shrink(uint(diff))
|
|
if needIndexTable {
|
|
n := len(index)
|
|
for i := 0; i < n; i++ {
|
|
index[i] -= diff
|
|
}
|
|
} // Note: if !needIndexTable the index array is now wrong!
|
|
}
|
|
// One could move down things in the offsetSize == 2 case as well,
|
|
// since we only need 4 bytes in the beginning. However, saving these
|
|
// 4 bytes has been sacrificed on the Altar of Performance.
|
|
|
|
// Now build the table:
|
|
if needIndexTable {
|
|
extraSpaceNeeded := offsetSize * uint(len(index))
|
|
if offsetSize == 8 {
|
|
extraSpaceNeeded += 8
|
|
}
|
|
b.buf.ReserveSpace(extraSpaceNeeded)
|
|
tableBase := b.buf.Grow(offsetSize * uint(len(index)))
|
|
for i := uint(0); i < uint(len(index)); i++ {
|
|
x := uint64(index[i])
|
|
for j := uint(0); j < offsetSize; j++ {
|
|
tableBase[offsetSize*i+j] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
}
|
|
} else { // no index table
|
|
b.buf[tos] = 0x02
|
|
}
|
|
// Finally fix the byte width in the type byte:
|
|
if offsetSize > 1 {
|
|
if offsetSize == 2 {
|
|
b.buf[tos] += 1
|
|
} else if offsetSize == 4 {
|
|
b.buf[tos] += 2
|
|
} else { // offsetSize == 8
|
|
b.buf[tos] += 3
|
|
if needNrSubs {
|
|
b.appendLength(ValueLength(len(index)), 8)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fix the byte length in the beginning:
|
|
x := ValueLength(b.buf.Len() - tos)
|
|
for i := uint(1); i <= offsetSize; i++ {
|
|
b.buf[tos+ValueLength(i)] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
|
|
if offsetSize < 8 && needNrSubs {
|
|
x = ValueLength(len(index))
|
|
for i := offsetSize + 1; i <= 2*offsetSize; i++ {
|
|
b.buf[tos+ValueLength(i)] = byte(x & 0xff)
|
|
x >>= 8
|
|
}
|
|
}
|
|
|
|
// Now the array or object is complete, we pop a ValueLength
|
|
// off the _stack:
|
|
b.stack.Pop()
|
|
// Intentionally leave _index[depth] intact to avoid future allocs!
|
|
}
|
|
|
|
func (b *Builder) cleanupAdd() {
|
|
depth := b.stack.Len() - 1
|
|
b.index[depth].RemoveLast()
|
|
}
|
|
|
|
func (b *Builder) reportAdd() {
|
|
tos, stackLen := b.stack.Tos()
|
|
depth := stackLen - 1
|
|
b.index[depth].Add(b.buf.Len() - tos)
|
|
}
|
|
|
|
func (b *Builder) addArray(unindexed ...bool) {
|
|
h := byte(0x06)
|
|
if optionalBool(unindexed, false) {
|
|
h = 0x13
|
|
}
|
|
b.addCompoundValue(h)
|
|
}
|
|
|
|
func (b *Builder) addObject(unindexed ...bool) {
|
|
h := byte(0x0b)
|
|
if optionalBool(unindexed, false) {
|
|
h = 0x14
|
|
}
|
|
b.addCompoundValue(h)
|
|
}
|
|
|
|
func (b *Builder) addInternal(v Value) error {
|
|
haveReported := false
|
|
if !b.stack.IsEmpty() {
|
|
if !b.keyWritten {
|
|
b.reportAdd()
|
|
haveReported = true
|
|
}
|
|
}
|
|
if err := b.set(v); err != nil {
|
|
if haveReported {
|
|
b.cleanupAdd()
|
|
}
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) addInternalKeyValue(attrName string, v Value) error {
|
|
haveReported, err := b.addInternalKey(attrName)
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
if err := b.set(v); err != nil {
|
|
if haveReported {
|
|
b.cleanupAdd()
|
|
}
|
|
return WithStack(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) addInternalKey(attrName string) (haveReported bool, err error) {
|
|
haveReported = false
|
|
tos, stackLen := b.stack.Tos()
|
|
if stackLen > 0 {
|
|
h := b.buf[tos]
|
|
if h != 0x0b && h != 0x14 {
|
|
return haveReported, WithStack(BuilderNeedOpenObjectError)
|
|
}
|
|
if b.keyWritten {
|
|
return haveReported, WithStack(BuilderKeyAlreadyWrittenError)
|
|
}
|
|
b.reportAdd()
|
|
haveReported = true
|
|
}
|
|
|
|
onError := func() {
|
|
if haveReported {
|
|
b.cleanupAdd()
|
|
haveReported = false
|
|
}
|
|
}
|
|
|
|
if err := b.set(NewStringValue(attrName)); err != nil {
|
|
onError()
|
|
return haveReported, WithStack(err)
|
|
}
|
|
b.keyWritten = true
|
|
return haveReported, nil
|
|
}
|
|
|
|
func (b *Builder) checkKeyIsString(isString bool) error {
|
|
tos, stackLen := b.stack.Tos()
|
|
if stackLen > 0 {
|
|
h := b.buf[tos]
|
|
if h == 0x0b || h == 0x14 {
|
|
if !b.keyWritten {
|
|
if isString {
|
|
b.keyWritten = true
|
|
} else {
|
|
return WithStack(BuilderKeyMustBeStringError)
|
|
}
|
|
} else {
|
|
b.keyWritten = false
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) set(item Value) error {
|
|
//oldPos := b.buf.Len()
|
|
//ctype := item.vt
|
|
|
|
if err := b.checkKeyIsString(item.vt == String); err != nil {
|
|
return WithStack(err)
|
|
}
|
|
|
|
if item.IsSlice() {
|
|
switch item.vt {
|
|
case None:
|
|
return WithStack(BuilderUnexpectedTypeError{"Cannot set a ValueType::None"})
|
|
case External:
|
|
return fmt.Errorf("External not supported")
|
|
case Custom:
|
|
return WithStack(fmt.Errorf("Cannot set a ValueType::Custom with this method"))
|
|
}
|
|
s := item.sliceValue()
|
|
// Determine length of slice
|
|
l, err := s.ByteSize()
|
|
if err != nil {
|
|
return WithStack(err)
|
|
}
|
|
b.buf.Write(s[:l])
|
|
return nil
|
|
}
|
|
|
|
// This method builds a single further VPack item at the current
|
|
// append position. If this is an array or object, then an index
|
|
// table is created and a new ValueLength is pushed onto the stack.
|
|
switch item.vt {
|
|
case None:
|
|
return WithStack(BuilderUnexpectedTypeError{"Cannot set a ValueType::None"})
|
|
case Null:
|
|
b.addNull()
|
|
case Bool:
|
|
b.addBool(item.boolValue())
|
|
case Double:
|
|
b.addDouble(item.doubleValue())
|
|
case External:
|
|
return fmt.Errorf("External not supported")
|
|
/*if (options->disallowExternals) {
|
|
// External values explicitly disallowed as a security
|
|
// precaution
|
|
throw Exception(Exception::BuilderExternalsDisallowed);
|
|
}
|
|
if (ctype != Value::CType::VoidPtr) {
|
|
throw Exception(Exception::BuilderUnexpectedValue,
|
|
"Must give void pointer for ValueType::External");
|
|
}
|
|
reserveSpace(1 + sizeof(void*));
|
|
// store pointer. this doesn't need to be portable
|
|
_start[_pos++] = 0x1d;
|
|
void const* value = item.getExternal();
|
|
memcpy(_start + _pos, &value, sizeof(void*));
|
|
_pos += sizeof(void*);
|
|
break;
|
|
}*/
|
|
case SmallInt:
|
|
b.addInt(item.intValue())
|
|
case Int:
|
|
b.addInt(item.intValue())
|
|
case UInt:
|
|
b.addUInt(item.uintValue())
|
|
case UTCDate:
|
|
b.addUTCDate(item.utcDateValue())
|
|
case String:
|
|
b.addString(item.stringValue())
|
|
case Array:
|
|
b.addArray(item.unindexed)
|
|
case Object:
|
|
b.addObject(item.unindexed)
|
|
case Binary:
|
|
b.addBinary(item.binaryValue())
|
|
case Illegal:
|
|
b.addIllegal()
|
|
case MinKey:
|
|
b.addMinKey()
|
|
case MaxKey:
|
|
b.addMaxKey()
|
|
case BCD:
|
|
return WithStack(fmt.Errorf("Not implemented"))
|
|
case Custom:
|
|
return WithStack(fmt.Errorf("Cannot set a ValueType::Custom with this method"))
|
|
}
|
|
return nil
|
|
}
|
|
|