// // 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(v) // : static_cast(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 }