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

//
// 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
}