1
0
Fork 0
arangodb/3rdParty/velocypack/include/velocypack/Builder.h

549 lines
18 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Library to build up VPack documents.
///
/// DISCLAIMER
///
/// Copyright 2015 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 Max Neunhoeffer
/// @author Jan Steemann
/// @author Copyright 2015, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#ifndef VELOCYPACK_BUILDER_H
#define VELOCYPACK_BUILDER_H
#include <vector>
#include <cstring>
#include <cstdint>
#include <algorithm>
#include <memory>
#include "velocypack/velocypack-common.h"
#include "velocypack/AttributeTranslator.h"
#include "velocypack/Buffer.h"
#include "velocypack/Exception.h"
#include "velocypack/Options.h"
#include "velocypack/Slice.h"
#include "velocypack/Value.h"
#include "velocypack/ValueType.h"
namespace arangodb {
namespace velocypack {
class Builder {
friend class Parser; // The parser needs access to internals.
public:
// A struct for sorting index tables for objects:
struct SortEntry {
uint8_t const* nameStart;
uint64_t nameSize;
uint64_t offset;
};
void reserve (ValueLength len) {
reserveSpace(len);
}
private:
std::shared_ptr<Buffer<uint8_t>> _buffer; // Here we collect the result
uint8_t* _start; // Always points to the start of _buffer
ValueLength _size; // Always contains the size of _buffer
ValueLength _pos; // the append position, always <= _size
std::vector<ValueLength> _stack; // Start positions of
// open objects/arrays
std::vector<std::vector<ValueLength>> _index; // Indices for starts
// of subindex
// Here are the mechanics of how this building process works:
// The whole VPack being built starts at where _start points to
// and uses at most _size bytes. The variable _pos keeps the
// current write position. The method "set" simply writes a new
// VPack subobject at the current write position and advances
// it. Whenever one makes an array or object, a ValueLength for
// the beginning of the value is pushed onto the _stack, which
// remembers that we are in the process of building an array or
// object. The _index vectors are used to collect information
// for the index tables of arrays and objects, which are written
// behind the subvalues. The add methods are used to keep track
// of the new subvalue in _index followed by a set, and are
// what the user from the outside calls. The close method seals
// the innermost array or object that is currently being built
// and pops a ValueLength off the _stack. The vectors in _index
// stay until the next clearTemporary() is called to minimize
// allocations. In the beginning, the _stack is empty, which
// allows to build a sequence of unrelated VPack objects in the
// buffer. Whenever the stack is empty, one can use the start,
// size and slice methods to get out the ready built VPack
// object(s).
void reserveSpace (ValueLength len) {
// Reserves len bytes at pos of the current state (top of stack)
// or throws an exception
if (_pos + len <= _size) {
return; // All OK, we can just increase tos->pos by len
}
checkValueLength(_pos + len);
_buffer->prealloc(len);
_start = _buffer->data();
_size = _buffer->size();
}
// Sort the indices by attribute name:
static void doActualSort (std::vector<SortEntry>& entries);
// Find the actual bytes of the attribute name of the VPack value
// at position base, also determine the length len of the attribute.
// This takes into account the different possibilities for the format
// of attribute names:
static uint8_t const* findAttrName (uint8_t const* base, uint64_t& len);
static void sortObjectIndexShort (uint8_t* objBase,
std::vector<ValueLength>& offsets);
static void sortObjectIndexLong (uint8_t* objBase,
std::vector<ValueLength>& offsets);
static void sortObjectIndex (uint8_t* objBase,
std::vector<ValueLength>& offsets);
public:
Options const* options;
// Constructor and destructor:
explicit Builder (std::shared_ptr<Buffer<uint8_t>>& buffer, Options const* options = &Options::Defaults)
: _buffer(buffer),
_pos(0),
options(options) {
_start = _buffer->data();
_size = _buffer->size();
VELOCYPACK_ASSERT(options != nullptr);
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
}
explicit Builder (Options const* options = &Options::Defaults)
: _buffer(new Buffer<uint8_t>()),
_pos(0),
options(options) {
_start = _buffer->data();
_size = _buffer->size();
VELOCYPACK_ASSERT(options != nullptr);
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
}
// The rule of five:
~Builder () {
}
Builder (Builder const& that)
: _buffer(that._buffer), _start(_buffer->data()), _size(_buffer->size()), _pos(that._pos),
_stack(that._stack), _index(that._index), options(that.options) {
VELOCYPACK_ASSERT(options != nullptr);
if (that._buffer == nullptr) {
throw Exception(Exception::InternalError, "Buffer of Builder is already gone");
}
}
Builder& operator= (Builder const& that) {
if (that._buffer == nullptr) {
throw Exception(Exception::InternalError, "Buffer of Builder is already gone");
}
_buffer = that._buffer;
_start = _buffer->data();
_size = _buffer->size();
_pos = that._pos;
_stack = that._stack;
_index = that._index;
options = that.options;
VELOCYPACK_ASSERT(options != nullptr);
return *this;
}
Builder (Builder&& that) {
if (that._buffer == nullptr) {
throw Exception(Exception::InternalError, "Buffer of Builder is already gone");
}
_buffer = that._buffer;
that._buffer.reset();
_start = _buffer->data();
_size = _buffer->size();
_pos = that._pos;
_stack.clear();
_stack.swap(that._stack);
_index.clear();
_index.swap(that._index);
options = that.options;
VELOCYPACK_ASSERT(options != nullptr);
that._start = nullptr;
that._size = 0;
that._pos = 0;
}
Builder& operator= (Builder&& that) {
if (that._buffer == nullptr) {
throw Exception(Exception::InternalError, "Buffer of Builder is already gone");
}
_buffer = that._buffer;
that._buffer.reset();
_start = _buffer->data();
_size = _buffer->size();
_pos = that._pos;
_stack.clear();
_stack.swap(that._stack);
_index.clear();
_index.swap(that._index);
options = that.options;
VELOCYPACK_ASSERT(options != nullptr);
that._start = nullptr;
that._size = 0;
that._pos = 0;
return *this;
}
// get a const reference to the Builder's Buffer object
std::shared_ptr<Buffer<uint8_t>> const& buffer () const {
return _buffer;
}
// get a non-const reference to the Builder's Buffer object
std::shared_ptr<Buffer<uint8_t>>& buffer () {
return _buffer;
}
static Builder clone (Slice const& slice, Options const* options = &Options::Defaults) {
VELOCYPACK_ASSERT(options != nullptr);
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
Builder b;
b.options = options;
b.add(slice);
return std::move(b);
}
// Clear and start from scratch:
void clear () {
_pos = 0;
_stack.clear();
}
// Return a pointer to the start of the result:
uint8_t* start () const {
if (! isClosed()) {
throw Exception(Exception::BuilderNotSealed);
}
return _start;
}
// Return a Slice of the result:
Slice slice () const {
return Slice(start(), options);
}
// Compute the actual size here, but only when sealed
ValueLength size () const {
if (! isClosed()) {
throw Exception(Exception::BuilderNotSealed);
}
return _pos;
}
bool isClosed () const throw() {
return _stack.empty();
}
// Add a subvalue into an object from a Value:
uint8_t* add (std::string const& attrName, Value const& sub);
// Add a subvalue into an object from a Slice:
uint8_t* add (std::string const& attrName, Slice const& sub);
// Add a subvalue into an object from a ValuePair:
uint8_t* add (std::string const& attrName, ValuePair const& sub);
// Add a subvalue into an array from a Value:
uint8_t* add (Value const& sub);
// Add a slice to an array
uint8_t* add (Slice const& sub);
// Add a subvalue into an array from a ValuePair:
uint8_t* add (ValuePair const& sub);
// Seal the innermost array or object:
void close ();
// Remove last subvalue written to an (unclosed) object or array:
// Throws if an error occurs.
void removeLast ();
// whether or not a specific key is present in an Object value
bool hasKey (std::string const& key) const;
// Syntactic sugar for add:
Builder& operator() (std::string const& attrName, Value sub) {
add(attrName, sub);
return *this;
}
// Syntactic sugar for add:
Builder& operator() (std::string const& attrName, ValuePair sub) {
add(attrName, sub);
return *this;
}
// Syntactic sugar for add:
Builder& operator() (Value sub) {
add(sub);
return *this;
}
// Syntactic sugar for add:
Builder& operator() (ValuePair sub) {
add(sub);
return *this;
}
// Syntactic sugar for close:
Builder& operator() () {
close();
return *this;
}
void addNull () {
reserveSpace(1);
_start[_pos++] = 0x18;
}
void addFalse () {
reserveSpace(1);
_start[_pos++] = 0x19;
}
void addTrue () {
reserveSpace(1);
_start[_pos++] = 0x1a;
}
void addDouble (double v) {
uint64_t dv;
memcpy(&dv, &v, sizeof(double));
ValueLength vSize = sizeof(double);
reserveSpace(1 + vSize);
_start[_pos++] = 0x1b;
for (uint64_t x = dv; vSize > 0; vSize--) {
_start[_pos++] = x & 0xff;
x >>= 8;
}
}
void addInt (int64_t v) {
if (v >= 0 && v <= 9) {
reserveSpace(1);
_start[_pos++] = static_cast<uint8_t>(0x30 + v);
}
else if (v < 0 && v >= -6) {
reserveSpace(1);
_start[_pos++] = static_cast<uint8_t>(0x40 + v);
}
else {
appendInt(v, 0x1f);
}
}
void addUInt (uint64_t v) {
if (v <= 9) {
reserveSpace(1);
_start[_pos++] = static_cast<uint8_t>(0x30 + v);
}
else {
appendUInt(v, 0x27);
}
}
void addUTCDate (int64_t v) {
uint8_t vSize = sizeof(int64_t); // is always 8
uint64_t x = toUInt64(v);
reserveSpace(1 + vSize);
_start[_pos++] = 0x1c;
appendLength(x, 8);
}
uint8_t* addString (uint64_t strLen) {
uint8_t* target;
if (strLen > 126) {
// long string
_start[_pos++] = 0xbf;
// write string length
appendLength(strLen, 8);
}
else {
// short string
_start[_pos++] = static_cast<uint8_t>(0x40 + strLen);
}
target = _start + _pos;
_pos += strLen;
return target;
}
inline void addArray (bool unindexed = false) {
addCompoundValue(unindexed ? 0x13 : 0x06);
}
inline void addObject (bool unindexed = false) {
addCompoundValue(unindexed ? 0x14 : 0x0b);
}
private:
template<typename T>
uint8_t* addInternal (T const& sub) {
if (! _stack.empty()) {
ValueLength& tos = _stack.back();
if (_start[tos] != 0x06 && _start[tos] != 0x13) {
throw Exception(Exception::BuilderNeedOpenArray);
}
reportAdd(tos);
}
return set(sub);
}
template<typename T>
uint8_t* addInternal (std::string const& attrName, T const& sub) {
if (! _stack.empty()) {
ValueLength& tos = _stack.back();
if (_start[tos] != 0x0b && _start[tos] != 0x14) {
throw Exception(Exception::BuilderNeedOpenObject);
}
reportAdd(tos);
}
if (options->attributeTranslator != nullptr) {
// check if a translation for the attribute name exists
uint8_t const* translated = options->attributeTranslator->translate(attrName);
if (translated != nullptr) {
set(Slice(options->attributeTranslator->translate(attrName)));
return set(sub);
}
// otherwise fall through to regular behavior
}
set(Value(attrName, ValueType::String));
return set(sub);
}
void addCompoundValue (uint8_t type) {
reserveSpace(9);
// an Array or Object is started:
_stack.push_back(_pos);
while (_stack.size() > _index.size()) {
_index.emplace_back();
}
_index[_stack.size() - 1].clear();
_start[_pos++] = type;
memset(_start + _pos, 0, 8);
_pos += 8; // Will be filled later with bytelength and nr subs
}
uint8_t* set (Value const& item);
uint8_t* set (ValuePair const& pair);
uint8_t* set (Slice const& item);
void reportAdd (ValueLength base) {
size_t depth = _stack.size() - 1;
_index[depth].push_back(_pos - base);
}
void appendLength (ValueLength v, uint64_t n) {
reserveSpace(n);
for (uint64_t i = 0; i < n; ++i) {
_start[_pos++] = v & 0xff;
v >>= 8;
}
}
void appendUInt (uint64_t v, uint8_t base) {
reserveSpace(9);
ValueLength save = _pos++;
uint8_t vSize = 0;
do {
vSize++;
_start[_pos++] = static_cast<uint8_t>(v & 0xff);
v >>= 8;
}
while (v != 0);
_start[save] = base + vSize;
}
// returns number of bytes required to store the value in 2s-complement
static inline uint8_t intLength (int64_t value) {
if (value >= -0x80 && value <= 0x7f) {
// shortcut for the common case
return 1;
}
uint64_t x = value >= 0 ? static_cast<uint64_t>(value)
: static_cast<uint64_t>(-(value + 1));
uint8_t xSize = 0;
do {
xSize++;
x >>= 8;
}
while (x >= 0x80);
return xSize + 1;
}
void appendInt (int64_t v, uint8_t base) {
uint8_t vSize = intLength(v);
uint64_t x;
if (vSize == 8) {
x = toUInt64(v);
}
else {
int64_t shift = 1LL << (vSize * 8 - 1); // will never overflow!
x = v >= 0 ? static_cast<uint64_t>(v)
: static_cast<uint64_t>(v + shift) + shift;
}
reserveSpace(1 + vSize);
_start[_pos++] = base + vSize;
while (vSize-- > 0) {
_start[_pos++] = x & 0xff;
x >>= 8;
}
}
void checkAttributeUniqueness (Slice const& obj) const;
};
} // namespace arangodb::velocypack
} // namespace arangodb
#endif