1
0
Fork 0

updated vpack library

This commit is contained in:
Jan Steemann 2015-11-13 12:58:17 +01:00
parent e54527a037
commit dca8efa82f
18 changed files with 940 additions and 517 deletions

View File

@ -27,11 +27,11 @@
#ifndef VELOCYPACK_BUILDER_H
#define VELOCYPACK_BUILDER_H
#include <ostream>
#include <vector>
#include <cstring>
#include <cstdint>
#include <algorithm>
#include <memory>
#include "velocypack/velocypack-common.h"
#include "velocypack/Buffer.h"
@ -62,7 +62,7 @@ namespace arangodb {
private:
Buffer<uint8_t> _buffer; // Here we collect the result
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
@ -99,11 +99,11 @@ namespace arangodb {
if (_pos + len <= _size) {
return; // All OK, we can just increase tos->pos by len
}
CheckValueLength(_pos + len);
checkValueLength(_pos + len);
_buffer.prealloc(len);
_start = _buffer.data();
_size = _buffer.size();
_buffer->prealloc(len);
_start = _buffer->data();
_size = _buffer->size();
}
// Sort the indices by attribute name:
@ -126,16 +126,33 @@ namespace arangodb {
public:
Options options;
Options const* options;
// Constructor and destructor:
Builder (Options const& options = Options::Defaults)
: _buffer({ 0 }),
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();
_start = _buffer->data();
_size = _buffer->size();
VELOCYPACK_ASSERT(options != nullptr);
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
}
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:
@ -144,61 +161,89 @@ namespace arangodb {
}
Builder (Builder const& that)
: _buffer(that._buffer), _start(_buffer.data()), _size(_buffer.size()), _pos(that._pos),
: _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();
_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) {
_buffer.reset();
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();
_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) {
_buffer.reset();
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();
_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;
}
static Builder clone (Slice const& slice, Options const& options = Options::Defaults) {
// 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 b;
return std::move(b);
}
// Clear and start from scratch:
@ -217,7 +262,7 @@ namespace arangodb {
// Return a Slice of the result:
Slice slice () const {
return Slice(start(), options.customTypeHandler);
return Slice(start(), options);
}
// Compute the actual size here, but only when sealed
@ -343,7 +388,7 @@ namespace arangodb {
void addUTCDate (int64_t v) {
uint8_t vSize = sizeof(int64_t); // is always 8
uint64_t x = ToUInt64(v);
uint64_t x = toUInt64(v);
reserveSpace(1 + vSize);
_start[_pos++] = 0x1c;
appendLength(x, 8);
@ -366,12 +411,12 @@ namespace arangodb {
return target;
}
void addArray () {
addCompoundValue(0x06);
inline void addArray (bool unindexed = false) {
addCompoundValue(unindexed ? 0x13 : 0x06);
}
void addObject () {
addCompoundValue(0x0b);
inline void addObject (bool unindexed = false) {
addCompoundValue(unindexed ? 0x14 : 0x0b);
}
private:
@ -380,7 +425,7 @@ namespace arangodb {
uint8_t* addInternal (T const& sub) {
if (! _stack.empty()) {
ValueLength& tos = _stack.back();
if (_start[tos] != 0x06) {
if (_start[tos] != 0x06 && _start[tos] != 0x13) {
throw Exception(Exception::BuilderNeedOpenArray);
}
reportAdd(tos);
@ -392,7 +437,7 @@ namespace arangodb {
uint8_t* addInternal (std::string const& attrName, T const& sub) {
if (! _stack.empty()) {
ValueLength& tos = _stack.back();
if (_start[tos] != 0x0b) {
if (_start[tos] != 0x0b && _start[tos] != 0x14) {
throw Exception(Exception::BuilderNeedOpenObject);
}
reportAdd(tos);
@ -403,7 +448,7 @@ namespace arangodb {
void addCompoundValue (uint8_t type) {
reserveSpace(9);
// an array is started:
// an Array or Object is started:
_stack.push_back(_pos);
while (_stack.size() > _index.size()) {
_index.emplace_back();
@ -466,7 +511,7 @@ namespace arangodb {
uint8_t vSize = intLength(v);
uint64_t x;
if (vSize == 8) {
x = ToUInt64(v);
x = toUInt64(v);
}
else {
int64_t shift = 1LL << (vSize * 8 - 1); // will never overflow!
@ -481,7 +526,7 @@ namespace arangodb {
}
}
void checkAttributeUniqueness (Slice const obj) const;
void checkAttributeUniqueness (Slice const& obj) const;
};
} // namespace arangodb::velocypack

View File

@ -43,18 +43,26 @@ namespace arangodb {
public:
Options options;
Options const* options;
Dumper (Dumper const&) = delete;
Dumper& operator= (Dumper const&) = delete;
Dumper (Sink* sink, Options const& options = Options::Defaults)
Dumper (Sink* sink, Options const* options = &Options::Defaults)
: options(options), _sink(sink), _indentation(0) {
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
}
~Dumper () {
}
Sink* sink () const {
return _sink;
}
void dump (Slice const& slice) {
_indentation = 0;
_sink->reserve(slice.byteSize());
@ -65,22 +73,23 @@ namespace arangodb {
dump(*slice);
}
static void dump (Slice const& slice, Sink* sink, Options const& options = Options::Defaults) {
static void dump (Slice const& slice, Sink* sink, Options const* options = &Options::Defaults) {
Dumper dumper(sink, options);
dumper.dump(slice);
}
static void dump (Slice const* slice, Sink* sink, Options const& options = Options::Defaults) {
static void dump (Slice const* slice, Sink* sink, Options const* options = &Options::Defaults) {
dump(*slice, sink, options);
}
static std::string toString (Slice const& slice, Options const& options = Options::Defaults) {
StringSink sink;
static std::string toString (Slice const& slice, Options const* options = &Options::Defaults) {
std::string buffer;
StringSink sink(&buffer);
dump(slice, &sink, options);
return std::move(sink.buffer);
return std::move(buffer);
}
static std::string toString (Slice const* slice, Options const& options = Options::Defaults) {
static std::string toString (Slice const* slice, Options const* options = &Options::Defaults) {
return std::move(toString(*slice, options));
}
@ -127,7 +136,7 @@ namespace arangodb {
}
void handleUnsupportedType (Slice const*) {
if (options.unsupportedTypeBehavior == NullifyUnsupportedType) {
if (options->unsupportedTypeBehavior == NullifyUnsupportedType) {
_sink->append("null", 4);
return;
}

View File

@ -42,33 +42,56 @@ namespace arangodb {
ArrayIterator () = delete;
ArrayIterator (Slice const& slice)
: _slice(slice), _size(_slice.length()), _position(0) {
: _slice(slice), _size(_slice.length()), _position(0), _current(nullptr) {
if (slice.type() != ValueType::Array) {
throw Exception(Exception::InvalidValueType, "Expecting Array slice");
}
if (slice.head() == 0x13 && slice.length() > 0) {
_current = slice.at(0).start();
}
}
ArrayIterator (ArrayIterator const& other)
: _slice(other._slice), _size(other._size), _position(other._position) {
: _slice(other._slice), _size(other._size), _position(other._position), _current(other._current) {
}
ArrayIterator& operator= (ArrayIterator const& other) {
_slice = other._slice;
_size = other._size;
_position = other._position;
_current = other._current;
return *this;
}
Slice operator++ () {
return _slice.at(_position++);
// prefix ++
ArrayIterator& operator++ () {
++_position;
if (_position <= _size && _current != nullptr) {
_current += Slice(_current, _slice.options).byteSize();
}
else {
_current = nullptr;
}
return *this;
}
// postfix ++
ArrayIterator operator++ (int) {
ArrayIterator result(*this);
++(*this);
return result;
}
bool operator!= (ArrayIterator const& other) const {
return _position != other._position;
}
Slice operator* () const {
Slice operator* () const {
if (_current != nullptr) {
return Slice(_current, _slice.options);
}
return _slice.at(_position);
}
@ -100,19 +123,32 @@ namespace arangodb {
if (_position >= _size) {
throw Exception(Exception::IndexOutOfBounds);
}
return _slice.at(_position);
return operator*();
}
inline bool next () throw() {
++_position;
operator++();
return valid();
}
inline ValueLength index () const throw() {
return _position;
}
inline bool isFirst () const throw() {
return (_position == 0);
}
inline bool isLast () const throw() {
return (_position + 1 >= _size);
}
private:
Slice _slice;
ValueLength _size;
ValueLength _position;
uint8_t const* _current;
};
class ObjectIterator {
@ -130,28 +166,49 @@ namespace arangodb {
ObjectIterator () = delete;
ObjectIterator (Slice const& slice)
: _slice(slice), _size(_slice.length()), _position(0) {
: _slice(slice), _size(_slice.length()), _position(0), _current(nullptr) {
if (slice.type() != ValueType::Object) {
throw Exception(Exception::InvalidValueType, "Expecting Object slice");
}
if (slice.head() == 0x14 && slice.length() > 0) {
_current = slice.keyAt(0).start();
}
}
ObjectIterator (ObjectIterator const& other)
: _slice(other._slice), _size(other._size), _position(other._position) {
: _slice(other._slice), _size(other._size), _position(other._position), _current(other._current) {
}
ObjectIterator& operator= (ObjectIterator const& other) {
_slice = other._slice;
_size = other._size;
_position = other._position;
_current = other._current;
return *this;
}
ObjectPair operator++ () {
ObjectPair current(_slice.keyAt(_position), _slice.valueAt(_position));
// prefix ++
ObjectIterator& operator++ () {
++_position;
return current;
if (_position <= _size && _current != nullptr) {
// skip over key
_current += Slice(_current, _slice.options).byteSize();
// skip over value
_current += Slice(_current, _slice.options).byteSize();
}
else {
_current = nullptr;
}
return *this;
}
// postfix ++
ObjectIterator operator++ (int) {
ObjectIterator result(*this);
++(*this);
return result;
}
bool operator!= (ObjectIterator const& other) const {
@ -159,6 +216,10 @@ namespace arangodb {
}
ObjectPair operator* () const {
if (_current != nullptr) {
Slice key = Slice(_current, _slice.options);
return ObjectPair(key, Slice(_current + key.byteSize(), _slice.options));
}
return ObjectPair(_slice.keyAt(_position), _slice.valueAt(_position));
}
@ -190,6 +251,9 @@ namespace arangodb {
if (_position >= _size) {
throw Exception(Exception::IndexOutOfBounds);
}
if (_current != nullptr) {
return Slice(_current, _slice.options);
}
return _slice.keyAt(_position);
}
@ -197,19 +261,36 @@ namespace arangodb {
if (_position >= _size) {
throw Exception(Exception::IndexOutOfBounds);
}
if (_current != nullptr) {
Slice key = Slice(_current, _slice.options);
return Slice(_current + key.byteSize(), _slice.options);
}
return _slice.valueAt(_position);
}
inline bool next () throw() {
++_position;
operator++();
return valid();
}
inline ValueLength index () const throw() {
return _position;
}
inline bool isFirst () const throw() {
return (_position == 0);
}
inline bool isLast () const throw() {
return (_position + 1 >= _size);
}
private:
Slice _slice;
ValueLength _size;
ValueLength _position;
uint8_t const* _current;
};
} // namespace arangodb::velocypack

View File

@ -31,7 +31,7 @@
namespace arangodb {
namespace velocypack {
struct Sink;
class Dumper;
class Slice;
struct AttributeExcludeHandler {
@ -45,7 +45,7 @@ namespace arangodb {
virtual ~CustomTypeHandler () {
}
virtual void toJson (Slice const& value, Sink* sink, Slice const& base) = 0;
virtual void toJson (Slice const& value, Dumper* dumper, Slice const& base) = 0;
virtual ValueLength byteSize (Slice const& value) = 0;
};
@ -68,6 +68,12 @@ namespace arangodb {
// custom type handler used for processing custom types by Dumper and Slicer
CustomTypeHandler* customTypeHandler = nullptr;
// allow building Arrays without index table?
bool buildUnindexedArrays = false;
// allow building Objects without index table?
bool buildUnindexedObjects = false;
// pretty-print JSON output when dumping with Dumper
bool prettyPrint = false;

View File

@ -84,7 +84,7 @@ namespace arangodb {
bool isInteger;
};
Builder _b;
Builder _b;
uint8_t const* _start;
size_t _size;
size_t _pos;
@ -92,22 +92,28 @@ namespace arangodb {
public:
Options options;
Options const* options;
Parser (Parser const&) = delete;
Parser& operator= (Parser const&) = delete;
Parser () : _start(nullptr), _size(0), _pos(0), _nesting(0) {
Parser (Options const* options = &Options::Defaults)
: _start(nullptr), _size(0), _pos(0), _nesting(0), options(options) {
VELOCYPACK_ASSERT(options != nullptr);
if (options == nullptr) {
throw Exception(Exception::InternalError, "Options cannot be a nullptr");
}
}
static Builder fromJson (std::string const& json, Options const& options = Options::Defaults) {
static Builder fromJson (std::string const& json, Options const* options = &Options::Defaults) {
Parser parser;
parser.options = options;
parser.parse(json);
return parser.steal();
}
static Builder fromJson (uint8_t const* start, size_t size, Options const& options = Options::Defaults) {
static Builder fromJson (uint8_t const* start, size_t size, Options const* options = &Options::Defaults) {
Parser parser;
parser.options = options;
parser.parse(start, size);
@ -115,12 +121,12 @@ namespace arangodb {
}
ValueLength parse (std::string const& json, bool multi = false) {
_start = reinterpret_cast<uint8_t const*>(json.c_str());
_size = json.size();
_pos = 0;
_b.clear();
_b.options = options;
return parseInternal(multi);
return parse(reinterpret_cast<uint8_t const*>(json.c_str()), json.size(), multi);
}
ValueLength parse (char const* start, size_t size,
bool multi = false) {
return parse(reinterpret_cast<uint8_t const*>(start), size, multi);
}
ValueLength parse (uint8_t const* start, size_t size,
@ -133,20 +139,13 @@ namespace arangodb {
return parseInternal(multi);
}
ValueLength parse (char const* start, size_t size,
bool multi = false) {
_start = reinterpret_cast<uint8_t const*>(start);
_size = size;
_pos = 0;
_b.clear();
_b.options = options;
return parseInternal(multi);
}
// We probably want a parse from stream at some stage...
// Not with this high-performance two-pass approach. :-(
Builder&& steal () {
if (_b._buffer == nullptr) {
throw Exception(Exception::InternalError, "Buffer of Builder is already gone");
}
return std::move(_b);
}

View File

@ -28,6 +28,8 @@
#define VELOCYPACK_SINK_H 1
#include <string>
#include <fstream>
#include <sstream>
#include "velocypack/velocypack-common.h"
#include "velocypack/Buffer.h"
@ -51,71 +53,70 @@ namespace arangodb {
};
template<typename T>
struct ByteBufferSink final : public Sink {
ByteBufferSink ()
: buffer() {
}
ByteBufferSink (ValueLength size)
: buffer(size) {
}
void push_back (char c) override final {
buffer.push_back(c);
}
void append (std::string const& p) override final {
buffer.append(p.c_str(), p.size());
}
void append (char const* p) override final {
buffer.append(p, strlen(p));
}
void append (char const* p, ValueLength len) override final {
buffer.append(p, len);
}
void reserve (ValueLength len) override final {
buffer.reserve(len);
}
Buffer<T> buffer;
};
struct StringSink final : public Sink {
StringSink ()
: buffer() {
struct ByteBufferSinkImpl final : public Sink {
explicit ByteBufferSinkImpl (Buffer<T>* buffer)
: buffer(buffer) {
}
void push_back (char c) override final {
buffer.push_back(c);
buffer->push_back(c);
}
void append (std::string const& p) override final {
buffer.append(p);
buffer->append(p.c_str(), p.size());
}
void append (char const* p) override final {
buffer.append(p, strlen(p));
buffer->append(p, strlen(p));
}
void append (char const* p, ValueLength len) override final {
buffer.append(p, len);
buffer->append(p, len);
}
void reserve (ValueLength len) override final {
buffer.reserve(len);
buffer->reserve(len);
}
std::string buffer;
Buffer<T>* buffer;
};
typedef ByteBufferSink<char> CharBufferSink;
typedef ByteBufferSinkImpl<char> CharBufferSink;
template<typename T>
struct StreamSink final : public Sink {
StreamSink (T* stream)
struct StringSinkImpl final : public Sink {
explicit StringSinkImpl (T* buffer)
: buffer(buffer) {
}
void push_back (char c) override final {
buffer->push_back(c);
}
void append (std::string const& p) override final {
buffer->append(p);
}
void append (char const* p) override final {
buffer->append(p, strlen(p));
}
void append (char const* p, ValueLength len) override final {
buffer->append(p, len);
}
void reserve (ValueLength len) override final {
buffer->reserve(len);
}
T* buffer;
};
typedef StringSinkImpl<std::string> StringSink;
template<typename T>
struct StreamSinkImpl final : public Sink {
explicit StreamSinkImpl (T* stream)
: stream(stream) {
}
@ -140,6 +141,9 @@ namespace arangodb {
T* stream;
};
typedef StreamSinkImpl<std::ostringstream> StringStreamSink;
typedef StreamSinkImpl<std::ofstream> OutputFileStreamSink;
} // namespace arangodb::velocypack
} // namespace arangodb

View File

@ -58,28 +58,44 @@ namespace arangodb {
public:
CustomTypeHandler* customTypeHandler;
Options const* options;
// constructor for an empty Value of type None
Slice ()
: Slice("\x00") {
: Slice("\x00", &Options::Defaults) {
}
explicit Slice (uint8_t const* start, CustomTypeHandler* handler = nullptr)
: _start(start), customTypeHandler(handler) {
explicit Slice (uint8_t const* start, Options const* options = &Options::Defaults)
: _start(start), options(options) {
VELOCYPACK_ASSERT(options != nullptr);
}
explicit Slice (char const* start, CustomTypeHandler* handler = nullptr)
: _start(reinterpret_cast<uint8_t const*>(start)), customTypeHandler(handler) {
explicit Slice (char const* start, Options const* options = &Options::Defaults)
: _start(reinterpret_cast<uint8_t const*>(start)), options(options) {
VELOCYPACK_ASSERT(options != nullptr);
}
Slice (Slice const& other)
: _start(other._start), customTypeHandler(other.customTypeHandler) {
: _start(other._start), options(other.options) {
VELOCYPACK_ASSERT(options != nullptr);
}
Slice (Slice&& other)
: _start(other._start), options(other.options) {
VELOCYPACK_ASSERT(options != nullptr);
}
Slice& operator= (Slice const& other) {
_start = other._start;
customTypeHandler = other.customTypeHandler;
options = other.options;
VELOCYPACK_ASSERT(options != nullptr);
return *this;
}
Slice& operator= (Slice&& other) {
_start = other._start;
options = other.options;
VELOCYPACK_ASSERT(options != nullptr);
return *this;
}
@ -299,13 +315,19 @@ namespace arangodb {
return 0;
}
if (h == 0x13 || h == 0x14) {
// compact Array or Object
ValueLength end = readVariableValueLength<false>(_start + 1);
return readVariableValueLength<true>(_start + end - 1);
}
ValueLength const offsetSize = indexEntrySize(h);
ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize);
// find number of items
if (h <= 0x05) { // No offset table or length, need to compute:
ValueLength firstSubOffset = findDataOffset(h);
Slice first(_start + firstSubOffset, customTypeHandler);
Slice first(_start + firstSubOffset, options);
return (end - firstSubOffset) / first.byteSize();
}
else if (offsetSize < 8) {
@ -335,7 +357,7 @@ namespace arangodb {
Slice valueAt (ValueLength index) const {
Slice key = keyAt(index);
return Slice(key.start() + key.byteSize(), customTypeHandler);
return Slice(key.start() + key.byteSize(), options);
}
// look for the specified attribute path inside an Object
@ -347,7 +369,7 @@ namespace arangodb {
}
// use ourselves as the starting point
Slice last = Slice(start(), customTypeHandler);
Slice last = Slice(start(), options);
for (size_t i = 0; i < attributes.size(); ++i) {
// fetch subattribute
last = last.get(attributes[i]);
@ -363,72 +385,7 @@ namespace arangodb {
// look for the specified attribute inside an Object
// returns a Slice(ValueType::None) if not found
Slice get (std::string const& attribute) const {
if (! isType(ValueType::Object)) {
throw Exception(Exception::InvalidValueType, "Expecting Object");
}
auto const h = head();
if (h == 0xa) {
// special case, empty object
return Slice();
}
ValueLength const offsetSize = indexEntrySize(h);
ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize);
ValueLength dataOffset = 0;
// read number of items
ValueLength n;
if (h <= 0x05) { // No offset table or length, need to compute:
dataOffset = findDataOffset(h);
Slice first(_start + dataOffset, customTypeHandler);
n = (end - dataOffset) / first.byteSize();
}
else if (offsetSize < 8) {
n = readInteger<ValueLength>(_start + 1 + offsetSize, offsetSize);
}
else {
n = readInteger<ValueLength>(_start + end - offsetSize, offsetSize);
}
if (n == 1) {
// Just one attribute, there is no index table!
if (dataOffset == 0) {
dataOffset = findDataOffset(h);
}
Slice attrName = Slice(_start + dataOffset, customTypeHandler);
if (! attrName.isString()) {
return Slice();
}
ValueLength attrLength;
char const* k = attrName.getString(attrLength);
if (attrLength != static_cast<ValueLength>(attribute.size())) {
// key must have the exact same length as the attribute we search for
return Slice();
}
if (memcmp(k, attribute.c_str(), attribute.size()) != 0) {
return Slice();
}
return Slice(attrName.start() + attrName.byteSize(), customTypeHandler);
}
ValueLength const ieBase = end - n * offsetSize
- (offsetSize == 8 ? offsetSize : 0);
// only use binary search for attributes if we have at least this many entries
// otherwise we'll always use the linear search
static ValueLength const SortedSearchEntriesThreshold = 4;
if (isSorted() && n >= SortedSearchEntriesThreshold) {
// This means, we have to handle the special case n == 1 only
// in the linear search!
return searchObjectKeyBinary(attribute, ieBase, offsetSize, n);
}
return searchObjectKeyLinear(attribute, ieBase, offsetSize, n);
}
Slice get (std::string const& attribute) const;
Slice operator[] (std::string const& attribute) const {
return get(attribute);
@ -450,91 +407,13 @@ namespace arangodb {
}
// return the value for an Int object
int64_t getInt () const {
uint8_t const h = head();
if (h >= 0x20 && h <= 0x27) {
// Int T
uint64_t v = readInteger<uint64_t>(_start + 1, h - 0x1f);
if (h == 0x27) {
return ToInt64(v);
}
else {
int64_t vv = static_cast<int64_t>(v);
int64_t shift = 1LL << ((h - 0x1f) * 8 - 1);
return vv < shift ? vv : vv - (shift << 1);
}
}
if (h >= 0x28 && h <= 0x2f) {
// UInt
uint64_t v = getUInt();
if (v > static_cast<uint64_t>(INT64_MAX)) {
throw Exception(Exception::NumberOutOfRange);
}
return static_cast<int64_t>(v);
}
if (h >= 0x30 && h <= 0x3f) {
// SmallInt
return getSmallInt();
}
throw Exception(Exception::InvalidValueType, "Expecting type Int");
}
int64_t getInt () const;
// return the value for a UInt object
uint64_t getUInt () const {
uint8_t const h = head();
if (h >= 0x28 && h <= 0x2f) {
// UInt
return readInteger<uint64_t>(_start + 1, h - 0x27);
}
if (h >= 0x20 && h <= 0x27) {
// Int
int64_t v = getInt();
if (v < 0) {
throw Exception(Exception::NumberOutOfRange);
}
return static_cast<int64_t>(v);
}
if (h >= 0x30 && h <= 0x39) {
// Smallint >= 0
return static_cast<uint64_t>(h - 0x30);
}
if (h >= 0x3a && h <= 0x3f) {
// Smallint < 0
throw Exception(Exception::NumberOutOfRange);
}
throw Exception(Exception::InvalidValueType, "Expecting type UInt");
}
uint64_t getUInt () const;
// return the value for a SmallInt object
int64_t getSmallInt () const {
uint8_t const h = head();
if (h >= 0x30 && h <= 0x39) {
// Smallint >= 0
return static_cast<int64_t>(h - 0x30);
}
if (h >= 0x3a && h <= 0x3f) {
// Smallint < 0
return static_cast<int64_t>(h - 0x3a) - 6;
}
if ((h >= 0x20 && h <= 0x27) ||
(h >= 0x28 && h <= 0x2f)) {
// Int and UInt
// we'll leave it to the compiler to detect the two ranges above are adjacent
return getInt();
}
throw Exception(Exception::InvalidValueType, "Expecting type Smallint");
}
int64_t getSmallInt () const;
template<typename T>
T getNumericValue () const {
@ -596,7 +475,7 @@ namespace arangodb {
int64_t getUTCDate () const {
assertType(ValueType::UTCDate);
uint64_t v = readInteger<uint64_t>(_start + 1, sizeof(uint64_t));
return ToInt64(v);
return toInt64(v);
}
// return the value for a String object
@ -611,7 +490,7 @@ namespace arangodb {
if (h == 0xbf) {
// long UTF-8 String
length = readInteger<ValueLength>(_start + 1, 8);
CheckValueLength(length);
checkValueLength(length);
return reinterpret_cast<char const*>(_start + 1 + 8);
}
@ -629,7 +508,7 @@ namespace arangodb {
if (h == 0xbf) {
ValueLength length = readInteger<ValueLength>(_start + 1, 8);
CheckValueLength(length);
checkValueLength(length);
return std::string(reinterpret_cast<char const*>(_start + 1 + 8), length);
}
@ -643,7 +522,7 @@ namespace arangodb {
if (h >= 0xc0 && h <= 0xc7) {
length = readInteger<ValueLength>(_start + 1, h - 0xbf);
CheckValueLength(length);
checkValueLength(length);
return _start + 1 + h - 0xbf;
}
@ -658,7 +537,7 @@ namespace arangodb {
if (h >= 0xc0 && h <= 0xc7) {
std::vector<uint8_t> out;
ValueLength length = readInteger<ValueLength>(_start + 1, h - 0xbf);
CheckValueLength(length);
checkValueLength(length);
out.reserve(static_cast<size_t>(length));
out.insert(out.end(), _start + 1 + h - 0xbf, _start + 1 + h - 0xbf + length);
return out;
@ -687,10 +566,16 @@ namespace arangodb {
case ValueType::Object: {
auto const h = head();
if (h == 0x01 || h == 0x0a) {
// empty array or object
// empty Array or Object
return 1;
}
if (h == 0x13 || h == 0x14) {
// compact Array or Object
return readVariableValueLength<false>(_start + 1);
}
VELOCYPACK_ASSERT(h <= 0x12);
return readInteger<ValueLength>(_start + 1, WidthMap[h]);
}
@ -738,11 +623,11 @@ namespace arangodb {
}
case ValueType::Custom: {
if (customTypeHandler == nullptr) {
if (options->customTypeHandler == nullptr) {
throw Exception(Exception::NeedCustomTypeHandler);
}
return customTypeHandler->byteSize(*this);
return options->customTypeHandler->byteSize(*this);
}
}
@ -750,14 +635,21 @@ namespace arangodb {
return 0;
}
int compareString (std::string const& attribute) const;
bool isEqualString (std::string const& attribute) const;
std::string toJson () const;
std::string toString () const;
std::string hexType () const;
private:
ValueLength findDataOffset (uint8_t const head) const {
Slice getFromCompactObject (std::string const& attribute) const;
ValueLength findDataOffset (uint8_t head) const {
// Must be called for a nonempty array or object at start():
VELOCYPACK_ASSERT(head <= 0x12);
unsigned int fsm = FirstSubMap[head];
if (fsm <= 2 && _start[2] != 0) {
return 2;
@ -770,139 +662,40 @@ namespace arangodb {
}
return 9;
}
ValueLength valueOffset (ValueLength index) const {
if (type() != ValueType::Array && type() != ValueType::Object) {
throw Exception(Exception::InvalidValueType, "Expecting Array or Object");
}
return getNthOffset(index);
}
// get the offset for the nth member from an Array or Object type
ValueLength getNthOffset (ValueLength index) const;
// extract the nth member from an Array or Object type
Slice getNth (ValueLength index) const {
VELOCYPACK_ASSERT(type() == ValueType::Array || type() == ValueType::Object);
auto const h = head();
if (h == 0x01 || h == 0x0a) {
// special case. empty array or object
throw Exception(Exception::IndexOutOfBounds);
}
ValueLength const offsetSize = indexEntrySize(h);
ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize);
ValueLength dataOffset = findDataOffset(h);
// find the number of items
ValueLength n;
if (h <= 0x05) { // No offset table or length, need to compute:
Slice first(_start + dataOffset, customTypeHandler);
n = (end - dataOffset) / first.byteSize();
}
else if (offsetSize < 8) {
n = readInteger<ValueLength>(_start + 1 + offsetSize, offsetSize);
}
else {
n = readInteger<ValueLength>(_start + end - offsetSize, offsetSize);
}
if (index >= n) {
throw Exception(Exception::IndexOutOfBounds);
}
// empty array case was already covered
VELOCYPACK_ASSERT(n > 0);
if (h <= 0x05 || n == 1) {
// no index table, but all array items have the same length
// now fetch first item and determine its length
if (dataOffset == 0) {
dataOffset = findDataOffset(h);
}
Slice firstItem(_start + dataOffset, customTypeHandler);
return Slice(_start + dataOffset + index * firstItem.byteSize(), customTypeHandler);
}
ValueLength const ieBase = end - n * offsetSize + index * offsetSize
- (offsetSize == 8 ? 8 : 0);
return Slice(_start + readInteger<ValueLength>(_start + ieBase, offsetSize), customTypeHandler);
}
Slice getNth (ValueLength index) const;
// get the offset for the nth member from a compact Array or Object type
ValueLength getNthOffsetFromCompact (ValueLength index) const;
ValueLength indexEntrySize (uint8_t head) const {
VELOCYPACK_ASSERT(head <= 0x12);
return static_cast<ValueLength>(WidthMap[head]);
}
// perform a linear search for the specified attribute inside an Object
Slice searchObjectKeyLinear (std::string const& attribute,
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const {
for (ValueLength index = 0; index < n; ++index) {
ValueLength offset = ieBase + index * offsetSize;
Slice key(_start + readInteger<ValueLength>(_start + offset, offsetSize), customTypeHandler);
if (! key.isString()) {
// invalid object
return Slice();
}
ValueLength keyLength;
char const* k = key.getString(keyLength);
if (keyLength != static_cast<ValueLength>(attribute.size())) {
// key must have the exact same length as the attribute we search for
continue;
}
if (memcmp(k, attribute.c_str(), attribute.size()) != 0) {
continue;
}
// key is identical. now return value
return Slice(key.start() + key.byteSize(), customTypeHandler);
}
// nothing found
return Slice();
}
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const;
// perform a binary search for the specified attribute inside an Object
Slice searchObjectKeyBinary (std::string const& attribute,
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const {
VELOCYPACK_ASSERT(n > 0);
ValueLength const attributeLength = static_cast<ValueLength>(attribute.size());
ValueLength l = 0;
ValueLength r = n - 1;
while (true) {
// midpoint
ValueLength index = l + ((r - l) / 2);
ValueLength offset = ieBase + index * offsetSize;
Slice key(_start + readInteger<ValueLength>(_start + offset, offsetSize), customTypeHandler);
if (! key.isString()) {
// invalid object
return Slice();
}
ValueLength keyLength;
char const* k = key.getString(keyLength);
size_t const compareLength = static_cast<size_t>((std::min)(keyLength, attributeLength));
int res = memcmp(k, attribute.c_str(), compareLength);
if (res == 0 && keyLength == attributeLength) {
// key is identical. now return value
return Slice(key.start() + key.byteSize(), customTypeHandler);
}
if (res > 0 || (res == 0 && keyLength > attributeLength)) {
if (index == 0) {
return Slice();
}
r = index - 1;
}
else {
l = index + 1;
}
if (r < l) {
return Slice();
}
}
}
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const;
// assert that the slice is of a specific type
// can be used for debugging and removed in production
@ -947,8 +740,8 @@ namespace arangodb {
private:
static ValueType const TypeMap[256];
static unsigned int const WidthMap[256];
static unsigned int const FirstSubMap[256];
static unsigned int const WidthMap[32];
static unsigned int const FirstSubMap[32];
};
} // namespace arangodb::velocypack

View File

@ -67,48 +67,65 @@ namespace arangodb {
std::string const* s; // 5: std::string
char const* c; // 6: char const*
void const* e; // external
} _value;
}
_value;
bool _unindexed;
public:
Value () = delete;
explicit Value (ValueType t)
: _valueType(t), _cType(CType::None) {
// creates a Value with the specified type Array or Object
explicit Value (ValueType t, bool allowUnindexed = false)
: _valueType(t), _cType(CType::None), _unindexed(allowUnindexed) {
if (allowUnindexed &&
(_valueType != ValueType::Array && _valueType != ValueType::Object)) {
throw Exception(Exception::InvalidValueType, "Expecting compound type");
}
}
explicit Value (bool b, ValueType t = ValueType::Bool)
: _valueType(t), _cType(CType::Bool) {
: _valueType(t), _cType(CType::Bool), _unindexed(false) {
_value.b = b;
}
explicit Value (double d, ValueType t = ValueType::Double)
: _valueType(t), _cType(CType::Double) {
: _valueType(t), _cType(CType::Double), _unindexed(false) {
_value.d = d;
}
explicit Value (void const* e, ValueType t = ValueType::External)
: _valueType(t), _cType(CType::VoidPtr) {
: _valueType(t), _cType(CType::VoidPtr), _unindexed(false) {
_value.e = e;
}
explicit Value (char const* c, ValueType t = ValueType::String)
: _valueType(t), _cType(CType::CharPtr) {
: _valueType(t), _cType(CType::CharPtr), _unindexed(false) {
_value.c = c;
}
explicit Value (int32_t i, ValueType t = ValueType::Int)
: _valueType(t), _cType(CType::Int64) {
: _valueType(t), _cType(CType::Int64), _unindexed(false) {
_value.i = static_cast<int64_t>(i);
}
explicit Value (uint32_t u, ValueType t = ValueType::UInt)
: _valueType(t), _cType(CType::UInt64) {
: _valueType(t), _cType(CType::UInt64), _unindexed(false) {
_value.u = static_cast<uint64_t>(u);
}
explicit Value (int64_t i, ValueType t = ValueType::Int)
: _valueType(t), _cType(CType::Int64) {
: _valueType(t), _cType(CType::Int64), _unindexed(false) {
_value.i = i;
}
explicit Value (uint64_t u, ValueType t = ValueType::UInt)
: _valueType(t), _cType(CType::UInt64) {
: _valueType(t), _cType(CType::UInt64), _unindexed(false) {
_value.u = u;
}
#ifdef __APPLE__
// MacOS uses the following typedefs:
// - typedef unsigned int uint32_t;
@ -120,12 +137,13 @@ namespace arangodb {
// however, defining the method on Linux and with MSVC will lead
// to ambiguous overloads, so this is restricted to __APPLE__ only
explicit Value (unsigned long i, ValueType t = ValueType::Int)
: _valueType(t), _cType(CType::UInt64) {
: _valueType(t), _cType(CType::UInt64), _unindexed(false) {
_value.i = static_cast<uint64_t>(i);
}
#endif
#endif
explicit Value (std::string const& s, ValueType t = ValueType::String)
: _valueType(t), _cType(CType::String) {
: _valueType(t), _cType(CType::String), _unindexed(false) {
_value.s = &s;
}

View File

@ -43,6 +43,7 @@ using VPackParser = arangodb::velocypack::Parser;
using VPackSink = arangodb::velocypack::Sink;
using VPackSlice = arangodb::velocypack::Slice;
using VPackStringSink = arangodb::velocypack::StringSink;
using VPackStringStreamSink = arangodb::velocypack::StringStreamSink;
using VPackValue = arangodb::velocypack::Value;
using VPackValueLength = arangodb::velocypack::ValueLength;
using VPackValueType = arangodb::velocypack::ValueType;

View File

@ -75,17 +75,69 @@ namespace arangodb {
#ifndef VELOCYPACK_64BIT
// check if the length is beyond the size of a SIZE_MAX on this platform
static void CheckValueLength (ValueLength);
static void checkValueLength (ValueLength);
#else
static inline void CheckValueLength (ValueLength) {
static inline void checkValueLength (ValueLength) {
// do nothing on a 64 bit platform
}
#endif
// returns current value for UTCDate
int64_t CurrentUTCDateValue ();
// calculate the length of a variable length integer in unsigned LEB128 format
static inline ValueLength getVariableValueLength (ValueLength value) throw() {
ValueLength len = 1;
while (value >= 0x80) {
value >>= 7;
++len;
}
return len;
}
static inline uint64_t ToUInt64 (int64_t v) {
// read a variable length integer in unsigned LEB128 format
template<bool reverse>
static inline ValueLength readVariableValueLength (uint8_t const* source) {
ValueLength len = 0;
uint8_t v;
ValueLength p = 0;
do {
v = *source;
len += (v & 0x7f) << p;
p += 7;
if (reverse) {
--source;
}
else {
++source;
}
}
while (v & 0x80);
return len;
}
// store a variable length integer in unsigned LEB128 format
template<bool reverse>
static inline void storeVariableValueLength (uint8_t* dst, ValueLength value) {
VELOCYPACK_ASSERT(value > 0);
if (reverse) {
while (value >= 0x80) {
*dst-- = static_cast<uint8_t>(value | 0x80);
value >>= 7;
}
*dst-- = static_cast<uint8_t>(value & 0x7f);
}
else {
while (value >= 0x80) {
*dst++ = static_cast<uint8_t>(value | 0x80);
value >>= 7;
}
*dst++ = static_cast<uint8_t>(value & 0x7f);
}
}
// returns current value for UTCDate
int64_t currentUTCDateValue ();
static inline uint64_t toUInt64 (int64_t v) throw() {
// If v is negative, we need to add 2^63 to make it positive,
// before we can cast it to an uint64_t:
uint64_t shift2 = 1ULL << 63;
@ -97,7 +149,7 @@ namespace arangodb {
// uint64_t is not guaranteed to work for negative values!
}
static inline int64_t ToInt64 (uint64_t v) {
static inline int64_t toInt64 (uint64_t v) throw() {
uint64_t shift2 = 1ULL << 63;
int64_t shift = static_cast<int64_t>(shift2 - 1);
return v >= shift2 ? (static_cast<int64_t>(v - shift2) - shift) - 1

View File

@ -151,12 +151,17 @@ void Builder::close () {
throw Exception(Exception::BuilderNeedOpenCompound);
}
ValueLength& tos = _stack.back();
if (_start[tos] != 0x06 && _start[tos] != 0x0b) {
uint8_t const head = _start[tos];
if (head != 0x06 && head != 0x0b && head != 0x13 && head != 0x14) {
throw Exception(Exception::BuilderNeedOpenObject);
}
bool const isArray = (head == 0x06 || head == 0x13);
std::vector<ValueLength>& index = _index[_stack.size() - 1];
if (index.empty()) {
_start[tos] = (_start[tos] == 0x06) ? 0x01 : 0x0a;
// empty Array or Object
_start[tos] = (isArray ? 0x01 : 0x0a);
VELOCYPACK_ASSERT(_pos == tos + 9);
_pos -= 8; // no bytelength and number subvalues needed
_stack.pop_back();
@ -167,6 +172,54 @@ void Builder::close () {
// From now on index.size() > 0
VELOCYPACK_ASSERT(index.size() > 0);
// check if we can use the compact Array / Object format
if (index.size() > 1 &&
((head == 0x13 || head == 0x14) ||
(head == 0x06 && options->buildUnindexedArrays) ||
(head == 0x0b && options->buildUnindexedObjects))) {
// use compact notation
ValueLength nLen = getVariableValueLength(static_cast<ValueLength>(index.size()));
VELOCYPACK_ASSERT(nLen > 0);
ValueLength byteSize = _pos - (tos + 8) + nLen;
VELOCYPACK_ASSERT(byteSize > 0);
ValueLength bLen = getVariableValueLength(byteSize);
byteSize += bLen;
if (getVariableValueLength(byteSize) != bLen) {
byteSize += 1;
bLen += 1;
}
if (bLen < 9) {
// can only use compact notation if total byte length is at most 8 bytes long
_start[tos] = (isArray ? 0x13 : 0x14);
ValueLength targetPos = 1 + bLen;
if (_pos > (tos + 9)) {
memmove(_start + tos + targetPos, _start + tos + 9,
_pos - (tos + 9));
}
// store byte length
VELOCYPACK_ASSERT(byteSize > 0);
storeVariableValueLength<false>(_start + tos + 1, byteSize);
// need additional memory for storing the number of values
if (nLen > 8 - bLen) {
reserveSpace(nLen);
}
storeVariableValueLength<true>(_start + tos + byteSize - 1, static_cast<ValueLength>(index.size()));
_pos -= 8;
_pos += nLen + bLen;
_stack.pop_back();
return;
}
}
// fix head byte in case a compact Array / Object was originally requested
_start[tos] = (isArray ? 0x06 : 0x0b);
bool needIndexTable = true;
bool needNrSubs = true;
if (index.size() == 1) {
@ -176,7 +229,7 @@ void Builder::close () {
}
// For objects we leave needNrSubs at true here!
}
else if (_start[tos] == 0x06 && // an array
else if (_start[tos] == 0x06 && // an Array
(_pos - tos) - index[0] == index.size() * (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:
@ -246,12 +299,13 @@ void Builder::close () {
reserveSpace(offsetSize * index.size() + (offsetSize == 8 ? 8 : 0));
tableBase = _pos;
_pos += offsetSize * index.size();
if (_start[tos] == 0x0b) { // an object
if (! options.sortAttributeNames) {
if (_start[tos] == 0x0b) {
// Object
if (! options->sortAttributeNames) {
_start[tos] = 0x0f; // unsorted
}
else if (index.size() >= 2 &&
options.sortAttributeNames) {
options->sortAttributeNames) {
sortObjectIndex(_start + tos, index);
}
}
@ -300,10 +354,10 @@ void Builder::close () {
}
// And, if desired, check attribute uniqueness:
if (options.checkAttributeUniqueness && index.size() > 1 &&
if (options->checkAttributeUniqueness && index.size() > 1 &&
_start[tos] >= 0x0b) {
// check uniqueness of attribute names
checkAttributeUniqueness(Slice(_start + tos, options.customTypeHandler));
checkAttributeUniqueness(Slice(_start + tos, options));
}
// Now the array or object is complete, we pop a ValueLength
@ -318,7 +372,7 @@ bool Builder::hasKey (std::string const& key) const {
throw Exception(Exception::BuilderNeedOpenObject);
}
ValueLength const& tos = _stack.back();
if (_start[tos] != 0x06 && _start[tos] != 0x0b) {
if (_start[tos] != 0x0b && _start[tos] != 0x14) {
throw Exception(Exception::BuilderNeedOpenObject);
}
std::vector<ValueLength> const& index = _index[_stack.size() - 1];
@ -326,8 +380,8 @@ bool Builder::hasKey (std::string const& key) const {
return false;
}
for (size_t i = 0; i < index.size(); ++i) {
Slice s(_start + tos + index[i], options.customTypeHandler);
if (s.isString() && s.copyString() == key) {
Slice s(_start + tos + index[i], options);
if (s.isString() && s.isEqualString(key)) {
return true;
}
}
@ -436,7 +490,7 @@ uint8_t* Builder::set (Value const& item) {
v = item.getInt64();
break;
case Value::CType::UInt64:
v = ToInt64(item.getUInt64());
v = toInt64(item.getUInt64());
break;
default:
throw Exception(Exception::BuilderUnexpectedValue, "Must give number for ValueType::Int");
@ -478,7 +532,7 @@ uint8_t* Builder::set (Value const& item) {
v = item.getInt64();
break;
case Value::CType::UInt64:
v = ToInt64(item.getUInt64());
v = toInt64(item.getUInt64());
break;
default:
throw Exception(Exception::BuilderUnexpectedValue, "Must give number for ValueType::UTCDate");
@ -518,11 +572,11 @@ uint8_t* Builder::set (Value const& item) {
break;
}
case ValueType::Array: {
addArray();
addArray(item._unindexed);
break;
}
case ValueType::Object: {
addObject();
addObject(item._unindexed);
break;
}
case ValueType::Binary: {
@ -620,8 +674,8 @@ uint8_t* Builder::set (ValuePair const& pair) {
throw Exception(Exception::BuilderUnexpectedType, "Only ValueType::Binary, ValueType::String and ValueType::Custom are valid for ValuePair argument");
}
void Builder::checkAttributeUniqueness (Slice const obj) const {
VELOCYPACK_ASSERT(options.checkAttributeUniqueness == true);
void Builder::checkAttributeUniqueness (Slice const& obj) const {
VELOCYPACK_ASSERT(options->checkAttributeUniqueness == true);
ValueLength const n = obj.length();
if (obj.isSorted()) {

View File

@ -28,6 +28,7 @@
#include "velocypack/velocypack-common.h"
#include "velocypack/Dumper.h"
#include "velocypack/Iterator.h"
#include "velocypack/ValueType.h"
using namespace arangodb::velocypack;
@ -141,7 +142,7 @@ void Dumper::dumpString (char const* src, ValueLength len) {
char esc = EscapeTable[c];
if (esc) {
if (c != '/' || options.escapeForwardSlashes) {
if (c != '/' || options->escapeForwardSlashes) {
// escape forward slashes only when requested
_sink->push_back('\\');
}
@ -220,28 +221,30 @@ void Dumper::dumpValue (Slice const* slice, Slice const* base) {
}
case ValueType::Array: {
ValueLength const n = slice->length();
ArrayIterator it(*slice);
_sink->push_back('[');
if (options.prettyPrint) {
if (options->prettyPrint) {
_sink->push_back('\n');
++_indentation;
for (ValueLength i = 0; i < n; ++i) {
while (it.valid()) {
indent();
dumpValue(slice->at(i), base);
if (i + 1 != n) {
dumpValue(it.value(), base);
if (! it.isLast()) {
_sink->push_back(',');
}
_sink->push_back('\n');
it.next();
}
--_indentation;
indent();
}
else {
for (ValueLength i = 0; i < n; ++i) {
if (i > 0) {
while (it.valid()) {
if (! it.isFirst()) {
_sink->push_back(',');
}
dumpValue(slice->at(i), base);
dumpValue(it.value(), base);
it.next();
}
}
_sink->push_back(']');
@ -249,32 +252,34 @@ void Dumper::dumpValue (Slice const* slice, Slice const* base) {
}
case ValueType::Object: {
ValueLength const n = slice->length();
ObjectIterator it(*slice);
_sink->push_back('{');
if (options.prettyPrint) {
if (options->prettyPrint) {
_sink->push_back('\n');
++_indentation;
for (ValueLength i = 0; i < n; ++i) {
while (it.valid()) {
indent();
dumpValue(slice->keyAt(i), base);
dumpValue(it.key(), base);
_sink->append(" : ", 3);
dumpValue(slice->valueAt(i), base);
if (i + 1 != n) {
dumpValue(it.value(), base);
if (! it.isLast()) {
_sink->push_back(',');
}
_sink->push_back('\n');
it.next();
}
--_indentation;
indent();
}
else {
for (ValueLength i = 0; i < n; ++i) {
if (i > 0) {
while (it.valid()) {
if (! it.isFirst()) {
_sink->push_back(',');
}
dumpValue(slice->keyAt(i), base);
dumpValue(it.key(), base);
_sink->push_back(':');
dumpValue(slice->valueAt(i), base);
dumpValue(it.value(), base);
it.next();
}
}
_sink->push_back('}');
@ -300,7 +305,7 @@ void Dumper::dumpValue (Slice const* slice, Slice const* base) {
}
case ValueType::External: {
Slice const external(slice->getExternal(), slice->customTypeHandler);
Slice const external(slice->getExternal(), slice->options);
dumpValue(&external, base);
break;
}
@ -339,11 +344,11 @@ void Dumper::dumpValue (Slice const* slice, Slice const* base) {
}
case ValueType::Custom: {
if (options.customTypeHandler == nullptr) {
if (options->customTypeHandler == nullptr) {
handleUnsupportedType(slice);
}
else {
options.customTypeHandler->toJson(*slice, _sink, *base);
options->customTypeHandler->toJson(*slice, this, *base);
}
break;
}

View File

@ -132,7 +132,7 @@ void Parser::parseNumber () {
if (numberValue.intValue <= static_cast<uint64_t>(INT64_MAX)) {
_b.addInt(-static_cast<int64_t>(numberValue.intValue));
}
else if (numberValue.intValue == ToUInt64(INT64_MIN)) {
else if (numberValue.intValue == toUInt64(INT64_MIN)) {
_b.addInt(INT64_MIN);
}
else {
@ -221,7 +221,7 @@ void Parser::parseString () {
if (remainder >= 16) {
_b.reserveSpace(remainder);
size_t count;
if (options.validateUtf8Strings) {
if (options->validateUtf8Strings) {
count = JSONStringCopyCheckUtf8(_b._start + _b._pos, _start + _pos,
remainder);
}
@ -370,7 +370,7 @@ void Parser::parseString () {
_b._start[_b._pos++] = static_cast<uint8_t>(i);
}
else {
if (! options.validateUtf8Strings) {
if (! options->validateUtf8Strings) {
highSurrogate = 0;
_b.reserveSpace(1);
_b._start[_b._pos++] = static_cast<uint8_t>(i);
@ -460,7 +460,7 @@ void Parser::parseObject () {
if (i == '}') {
// empty object
consume(); // the closing ']'
if (_nesting != 0 || ! options.keepTopLevelOpen) {
if (_nesting != 0 || ! options->keepTopLevelOpen) {
// only close if we've not been asked to keep top level open
_b.close();
}
@ -479,13 +479,13 @@ void Parser::parseObject () {
_b.reportAdd(base);
bool excludeAttribute = false;
if (options.attributeExcludeHandler == nullptr) {
if (options->attributeExcludeHandler == nullptr) {
parseString();
}
else {
auto lastPos = _b._pos;
parseString();
if (options.attributeExcludeHandler->shouldExclude(Slice(_b._start + lastPos, options.customTypeHandler), _nesting)) {
if (options->attributeExcludeHandler->shouldExclude(Slice(_b._start + lastPos, options), _nesting)) {
excludeAttribute = true;
}
}
@ -506,7 +506,7 @@ void Parser::parseObject () {
if (i == '}') {
// end of object
++_pos; // the closing '}'
if (_nesting != 1 || ! options.keepTopLevelOpen) {
if (_nesting != 1 || ! options->keepTopLevelOpen) {
// only close if we've not been asked to keep top level open
_b.close();
}

View File

@ -39,8 +39,8 @@ VT const Slice::TypeMap[256] = {
/* 0x04 */ VT::Array, /* 0x05 */ VT::Array, /* 0x06 */ VT::Array, /* 0x07 */ VT::Array,
/* 0x08 */ VT::Array, /* 0x09 */ VT::Array, /* 0x0a */ VT::Object, /* 0x0b */ VT::Object,
/* 0x0c */ VT::Object, /* 0x0d */ VT::Object, /* 0x0e */ VT::Object, /* 0x0f */ VT::Object,
/* 0x10 */ VT::Object, /* 0x11 */ VT::Object, /* 0x12 */ VT::Object, /* 0x13 */ VT::None,
/* 0x14 */ VT::None, /* 0x15 */ VT::None, /* 0x16 */ VT::None, /* 0x17 */ VT::None,
/* 0x10 */ VT::Object, /* 0x11 */ VT::Object, /* 0x12 */ VT::Object, /* 0x13 */ VT::Array,
/* 0x14 */ VT::Object, /* 0x15 */ VT::None, /* 0x16 */ VT::None, /* 0x17 */ VT::None,
/* 0x18 */ VT::Null, /* 0x19 */ VT::Bool, /* 0x1a */ VT::Bool, /* 0x1b */ VT::Double,
/* 0x1c */ VT::UTCDate, /* 0x1d */ VT::External, /* 0x1e */ VT::MinKey, /* 0x1f */ VT::MaxKey,
/* 0x20 */ VT::Int, /* 0x21 */ VT::Int, /* 0x22 */ VT::Int, /* 0x23 */ VT::Int,
@ -101,7 +101,7 @@ VT const Slice::TypeMap[256] = {
/* 0xfc */ VT::Custom, /* 0xfd */ VT::Custom, /* 0xfe */ VT::Custom, /* 0xff */ VT::Custom
};
unsigned int const Slice::WidthMap[256] = {
unsigned int const Slice::WidthMap[32] = {
0, // 0x00, None
1, // 0x01, empty array
1, // 0x02, array without index table
@ -124,7 +124,7 @@ unsigned int const Slice::WidthMap[256] = {
0
};
unsigned int const Slice::FirstSubMap[256] = {
unsigned int const Slice::FirstSubMap[32] = {
0, // 0x00, None
1, // 0x01, empty array
2, // 0x02, array without index table
@ -148,27 +148,383 @@ unsigned int const Slice::FirstSubMap[256] = {
};
std::string Slice::toJson () const {
Options options;
options.customTypeHandler = customTypeHandler;
StringSink sink;
std::string buffer;
StringSink sink(&buffer);
Dumper dumper(&sink, options);
dumper.dump(this);
return std::move(sink.buffer);
return std::move(buffer);
}
std::string Slice::toString () const {
Options options;
options.prettyPrint = true;
// copy options and set prettyPrint in copy
Options prettyOptions = *options;
prettyOptions.prettyPrint = true;
StringSink sink;
Dumper::dump(this, &sink, options);
return std::move(sink.buffer);
std::string buffer;
StringSink sink(&buffer);
Dumper::dump(this, &sink, &prettyOptions);
return std::move(buffer);
}
std::string Slice::hexType () const {
return std::move(HexDump::toHex(head()));
}
// look for the specified attribute inside an Object
// returns a Slice(ValueType::None) if not found
Slice Slice::get (std::string const& attribute) const {
if (! isType(ValueType::Object)) {
throw Exception(Exception::InvalidValueType, "Expecting Object");
}
auto const h = head();
if (h == 0x0a) {
// special case, empty object
return Slice();
}
if (h == 0x14) {
// compact Object
return getFromCompactObject(attribute);
}
ValueLength const offsetSize = indexEntrySize(h);
ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize);
ValueLength dataOffset = 0;
// read number of items
ValueLength n;
if (h <= 0x05) { // No offset table or length, need to compute:
dataOffset = findDataOffset(h);
Slice first(_start + dataOffset, options);
n = (end - dataOffset) / first.byteSize();
}
else if (offsetSize < 8) {
n = readInteger<ValueLength>(_start + 1 + offsetSize, offsetSize);
}
else {
n = readInteger<ValueLength>(_start + end - offsetSize, offsetSize);
}
if (n == 1) {
// Just one attribute, there is no index table!
if (dataOffset == 0) {
dataOffset = findDataOffset(h);
}
Slice key = Slice(_start + dataOffset, options);
if (! key.isString()) {
return Slice();
}
if (! key.isEqualString(attribute)) {
return Slice();
}
return Slice(key.start() + key.byteSize(), options);
}
ValueLength const ieBase = end - n * offsetSize
- (offsetSize == 8 ? offsetSize : 0);
// only use binary search for attributes if we have at least this many entries
// otherwise we'll always use the linear search
static ValueLength const SortedSearchEntriesThreshold = 4;
if (isSorted() && n >= SortedSearchEntriesThreshold) {
// This means, we have to handle the special case n == 1 only
// in the linear search!
return searchObjectKeyBinary(attribute, ieBase, offsetSize, n);
}
return searchObjectKeyLinear(attribute, ieBase, offsetSize, n);
}
// return the value for an Int object
int64_t Slice::getInt () const {
uint8_t const h = head();
if (h >= 0x20 && h <= 0x27) {
// Int T
uint64_t v = readInteger<uint64_t>(_start + 1, h - 0x1f);
if (h == 0x27) {
return toInt64(v);
}
else {
int64_t vv = static_cast<int64_t>(v);
int64_t shift = 1LL << ((h - 0x1f) * 8 - 1);
return vv < shift ? vv : vv - (shift << 1);
}
}
if (h >= 0x28 && h <= 0x2f) {
// UInt
uint64_t v = getUInt();
if (v > static_cast<uint64_t>(INT64_MAX)) {
throw Exception(Exception::NumberOutOfRange);
}
return static_cast<int64_t>(v);
}
if (h >= 0x30 && h <= 0x3f) {
// SmallInt
return getSmallInt();
}
throw Exception(Exception::InvalidValueType, "Expecting type Int");
}
// return the value for a UInt object
uint64_t Slice::getUInt () const {
uint8_t const h = head();
if (h >= 0x28 && h <= 0x2f) {
// UInt
return readInteger<uint64_t>(_start + 1, h - 0x27);
}
if (h >= 0x20 && h <= 0x27) {
// Int
int64_t v = getInt();
if (v < 0) {
throw Exception(Exception::NumberOutOfRange);
}
return static_cast<int64_t>(v);
}
if (h >= 0x30 && h <= 0x39) {
// Smallint >= 0
return static_cast<uint64_t>(h - 0x30);
}
if (h >= 0x3a && h <= 0x3f) {
// Smallint < 0
throw Exception(Exception::NumberOutOfRange);
}
throw Exception(Exception::InvalidValueType, "Expecting type UInt");
}
// return the value for a SmallInt object
int64_t Slice::getSmallInt () const {
uint8_t const h = head();
if (h >= 0x30 && h <= 0x39) {
// Smallint >= 0
return static_cast<int64_t>(h - 0x30);
}
if (h >= 0x3a && h <= 0x3f) {
// Smallint < 0
return static_cast<int64_t>(h - 0x3a) - 6;
}
if ((h >= 0x20 && h <= 0x27) ||
(h >= 0x28 && h <= 0x2f)) {
// Int and UInt
// we'll leave it to the compiler to detect the two ranges above are adjacent
return getInt();
}
throw Exception(Exception::InvalidValueType, "Expecting type Smallint");
}
int Slice::compareString (std::string const& attribute) const {
ValueLength keyLength;
char const* k = getString(keyLength);
size_t const attributeLength = attribute.size();
size_t const compareLength = (std::min)(static_cast<size_t>(keyLength), attributeLength);
int res = memcmp(k, attribute.c_str(), compareLength);
if (res == 0) {
if (keyLength != attributeLength) {
return (keyLength > attributeLength) ? 1 : -1;
}
}
return res;
}
bool Slice::isEqualString (std::string const& attribute) const {
ValueLength keyLength;
char const* k = getString(keyLength);
if (static_cast<size_t>(keyLength) != attribute.size()) {
return false;
}
return (memcmp(k, attribute.c_str(), attribute.size()) == 0);
}
Slice Slice::getFromCompactObject (std::string const& attribute) const {
ValueLength n = length();
ValueLength current = 0;
while (current != n) {
Slice key = keyAt(current);
if (key.isString()) {
if (key.isEqualString(attribute)) {
return Slice(key.start() + key.byteSize(), options);
}
}
++current;
}
// not found
return Slice();
}
// get the offset for the nth member from an Array or Object type
ValueLength Slice::getNthOffset (ValueLength index) const {
VELOCYPACK_ASSERT(type() == ValueType::Array || type() == ValueType::Object);
auto const h = head();
if (h == 0x01 || h == 0x0a) {
// special case. empty array or object
throw Exception(Exception::IndexOutOfBounds);
}
if (h == 0x13 || h == 0x14) {
// compact Array or Object
return getNthOffsetFromCompact(index);
}
ValueLength const offsetSize = indexEntrySize(h);
ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize);
ValueLength dataOffset = findDataOffset(h);
// find the number of items
ValueLength n;
if (h <= 0x05) { // No offset table or length, need to compute:
Slice first(_start + dataOffset, options);
n = (end - dataOffset) / first.byteSize();
}
else if (offsetSize < 8) {
n = readInteger<ValueLength>(_start + 1 + offsetSize, offsetSize);
}
else {
n = readInteger<ValueLength>(_start + end - offsetSize, offsetSize);
}
if (index >= n) {
throw Exception(Exception::IndexOutOfBounds);
}
// empty array case was already covered
VELOCYPACK_ASSERT(n > 0);
if (h <= 0x05 || n == 1) {
// no index table, but all array items have the same length
// now fetch first item and determine its length
if (dataOffset == 0) {
dataOffset = findDataOffset(h);
}
Slice firstItem(_start + dataOffset, options);
return dataOffset + index * firstItem.byteSize();
}
ValueLength const ieBase = end - n * offsetSize + index * offsetSize
- (offsetSize == 8 ? 8 : 0);
return readInteger<ValueLength>(_start + ieBase, offsetSize);
}
// extract the nth member from an Array or Object type
Slice Slice::getNth (ValueLength index) const {
VELOCYPACK_ASSERT(type() == ValueType::Array || type() == ValueType::Object);
auto const h = head();
if (h == 0x01 || h == 0x0a) {
// special case. empty array or object
throw Exception(Exception::IndexOutOfBounds);
}
return Slice(_start + getNthOffset(index), options);
}
// get the offset for the nth member from a compact Array or Object type
ValueLength Slice::getNthOffsetFromCompact (ValueLength index) const {
ValueLength end = readVariableValueLength<false>(_start + 1);
ValueLength n = readVariableValueLength<true>(_start + end - 1);
if (index >= n) {
throw Exception(Exception::IndexOutOfBounds);
}
auto const h = head();
ValueLength offset = 1 + getVariableValueLength(end);
ValueLength current = 0;
while (current != index) {
uint8_t const* s = _start + offset;
Slice key = Slice(s, options);
offset += key.byteSize();
if (h == 0x14) {
Slice value = Slice(_start + offset, options);
offset += value.byteSize();
}
++current;
}
return offset;
}
// perform a linear search for the specified attribute inside an Object
Slice Slice::searchObjectKeyLinear (std::string const& attribute,
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const {
for (ValueLength index = 0; index < n; ++index) {
ValueLength offset = ieBase + index * offsetSize;
Slice key(_start + readInteger<ValueLength>(_start + offset, offsetSize), options);
if (! key.isString()) {
// invalid object
return Slice();
}
if (! key.isEqualString(attribute)) {
continue;
}
// key is identical. now return value
return Slice(key.start() + key.byteSize(), options);
}
// nothing found
return Slice();
}
// perform a binary search for the specified attribute inside an Object
Slice Slice::searchObjectKeyBinary (std::string const& attribute,
ValueLength ieBase,
ValueLength offsetSize,
ValueLength n) const {
VELOCYPACK_ASSERT(n > 0);
ValueLength l = 0;
ValueLength r = n - 1;
while (true) {
// midpoint
ValueLength index = l + ((r - l) / 2);
ValueLength offset = ieBase + index * offsetSize;
Slice key(_start + readInteger<ValueLength>(_start + offset, offsetSize), options);
if (! key.isString()) {
// invalid object
return Slice();
}
int res = key.compareString(attribute);
if (res == 0) {
// found
return Slice(key.start() + key.byteSize(), options);
}
if (res > 0) {
if (index == 0) {
return Slice();
}
r = index - 1;
}
else {
l = index + 1;
}
if (r < l) {
return Slice();
}
}
}
std::ostream& operator<< (std::ostream& stream, Slice const* slice) {
stream << "[Slice " << valueTypeName(slice->type()) << " (" << slice->hexType() << "), byteSize: " << slice->byteSize() << "]";

View File

@ -65,15 +65,16 @@ static bool HasSSE42 () {
static size_t JSONStringCopySSE42 (uint8_t* dst, uint8_t const* src, size_t limit) {
alignas(16) static char const ranges[17]
= "\x00\x1f\"\"\\\\\"\"\"\"\"\"\"\"\"\"";
= "\x20\x21\x23\x5b\x5d\xff ";
//= "\x01\x1f\"\"\\\\\"\"\"\"\"\"\"\"\"\"";
__m128i const r = _mm_load_si128(reinterpret_cast<__m128i const*>(ranges));
size_t count = 0;
int x = 0;
while (limit >= 16) {
__m128i const s = _mm_loadu_si128(reinterpret_cast<__m128i const*>(src));
x = _mm_cmpestri(r, 6, s, 16,
x = _mm_cmpistri(r, /* 6, */ s, /* 16, */
_SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
_SIDD_POSITIVE_POLARITY |
_SIDD_NEGATIVE_POLARITY |
_SIDD_LEAST_SIGNIFICANT);
if (x < 16) {
memcpy(dst, src, x);
@ -92,9 +93,9 @@ static size_t JSONStringCopySSE42 (uint8_t* dst, uint8_t const* src, size_t limi
return count;
}
__m128i const s = _mm_loadu_si128(reinterpret_cast<__m128i const*>(src));
x = _mm_cmpestri(r, 6, s, limit,
x = _mm_cmpistri(r, /* 6, */ s, /* limit, */
_SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
_SIDD_POSITIVE_POLARITY |
_SIDD_NEGATIVE_POLARITY |
_SIDD_LEAST_SIGNIFICANT);
if (x > static_cast<int>(limit)) {
x = static_cast<int>(limit);
@ -119,15 +120,17 @@ static size_t DoInitCopy (uint8_t* dst, uint8_t const* src, size_t limit) {
static size_t JSONStringCopyCheckUtf8SSE42 (uint8_t* dst,
uint8_t const* src,
size_t limit) {
alignas(16) static unsigned char const ranges[17] = "\x00\x1f\x80\xff\"\"\\\\\"\"\"\"\"\"\"\"";
alignas(16) static unsigned char const ranges[17]
= "\x20\x21\x23\x5b\x5d\x7f ";
//= "\x01\x1f\x80\xff\"\"\\\\\"\"\"\"\"\"\"\"";
__m128i const r = _mm_load_si128(reinterpret_cast<__m128i const*>(ranges));
size_t count = 0;
int x = 0;
while (limit >= 16) {
__m128i const s = _mm_loadu_si128(reinterpret_cast<__m128i const*>(src));
x = _mm_cmpestri(r, 8, s, 16,
x = _mm_cmpistri(r, /* 8, */ s, /* 16, */
_SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
_SIDD_POSITIVE_POLARITY |
_SIDD_NEGATIVE_POLARITY |
_SIDD_LEAST_SIGNIFICANT);
if (x < 16) {
memcpy(dst, src, x);
@ -146,9 +149,9 @@ static size_t JSONStringCopyCheckUtf8SSE42 (uint8_t* dst,
return count;
}
__m128i const s = _mm_loadu_si128(reinterpret_cast<__m128i const*>(src));
x = _mm_cmpestri(r, 8, s, limit,
x = _mm_cmpistri(r, /* 8, */ s, /* limit, */
_SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |
_SIDD_POSITIVE_POLARITY |
_SIDD_NEGATIVE_POLARITY |
_SIDD_LEAST_SIGNIFICANT);
if (x > static_cast<int>(limit)) {
x = static_cast<int>(limit);
@ -296,7 +299,7 @@ void TestStringCopyCorrectness (uint8_t* src, uint8_t* dst, size_t size) {
src[pos] = merk;
// Test a 0 character:
src[pos] = 0;
src[pos] = 1;
copied = JSONStringCopy(dst, src, size);
if (copied != pos || memcmp(dst, src, copied) != 0) {
std::cout << "Error: " << salign << " " << dalign << " "
@ -371,7 +374,7 @@ void TestStringCopyCorrectnessCheckUtf8 (uint8_t* src, uint8_t* dst,
src[pos] = merk;
// Test a 0 character:
src[pos] = 0;
src[pos] = 1;
copied = JSONStringCopyCheckUtf8(dst, src, size);
if (copied != pos || memcmp(dst, src, copied) != 0) {
std::cout << "Error: " << salign << " " << dalign << " "

View File

@ -33,14 +33,14 @@ using namespace arangodb::velocypack;
#ifndef VELOCYPACK_64BIT
// check if the length is beyond the size of a SIZE_MAX on this platform
void CheckValueLength (ValueLength length) {
void checkValueLength (ValueLength length) {
if (length > static_cast<ValueLength>(SIZE_MAX)) {
throw Exception(Exception::NumberOutOfRange);
}
}
#endif
int64_t arangodb::velocypack::CurrentUTCDateValue () {
int64_t arangodb::velocypack::currentUTCDateValue () {
return static_cast<int64_t>(std::chrono::system_clock::now().time_since_epoch().count() / std::chrono::milliseconds(1).count());
}

View File

@ -75,10 +75,7 @@ std::string VelocyPackHelper::checkAndGetStringValue (VPackSlice const& slice,
}
TRI_json_t* VelocyPackHelper::velocyPackToJson (VPackSlice const& slice) {
VPackStringSink sink;
VPackDumper dumper(&sink);
dumper.dump(slice);
return JsonHelper::fromString(sink.buffer);
return JsonHelper::fromString(slice.toJson());
}
// -----------------------------------------------------------------------------

View File

@ -519,14 +519,14 @@ void HttpRequest::setHeader (char const* key,
////////////////////////////////////////////////////////////////////////////////
VPackBuilder HttpRequest::toVelocyPack () {
VPackParser parser;
parser.options.checkAttributeUniqueness = true;
VPackOptions options;
options.checkAttributeUniqueness = true;
VPackParser parser(&options);
parser.parse(body());
return parser.steal();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief gets the request body as TRI_json_t*
////////////////////////////////////////////////////////////////////////////////