From d57328e0f2e948e5dfd707467e3942ecf2690179 Mon Sep 17 00:00:00 2001 From: Julia CP Date: Wed, 13 Aug 2025 11:31:41 -0300 Subject: [PATCH 1/7] Introduce supervised buffer template, must fix Builder.cpp to accomodate it --- include/velocypack/Buffer.h | 4 +- include/velocypack/Builder.h | 29 ++++---- src/Builder.cpp | 128 ++++++++++++++++++++--------------- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/include/velocypack/Buffer.h b/include/velocypack/Buffer.h index 71d7b9f3..aacc2702 100644 --- a/include/velocypack/Buffer.h +++ b/include/velocypack/Buffer.h @@ -194,7 +194,7 @@ class Buffer { // Steal external memory; only allowed when the buffer is not local, // i.e. !usesLocalMemory() - T* steal() noexcept { + virtual T* steal() noexcept { VELOCYPACK_ASSERT(!usesLocalMemory()); auto buffer = _buffer; @@ -284,7 +284,7 @@ class Buffer { inline void poison(T*, ValueLength) noexcept {} #endif - void grow(ValueLength len) { + virtual void grow(ValueLength len) { VELOCYPACK_ASSERT(_size + len >= sizeof(_local)); // need reallocation diff --git a/include/velocypack/Builder.h b/include/velocypack/Builder.h index 9e10e81c..02d17bf7 100644 --- a/include/velocypack/Builder.h +++ b/include/velocypack/Builder.h @@ -50,6 +50,7 @@ namespace arangodb::velocypack { class ArrayIterator; class ObjectIterator; +template> class Builder { friend class Parser; // The parser needs access to internals. @@ -74,12 +75,12 @@ class Builder { // buffer. Whenever the stack is empty, one can use the start, // size and slice methods to get out the ready built VPack // object(s). + public: + using buffer_type = BufferType; private: - // Here we collect the result - std::shared_ptr> _buffer; - // used for quicker access than shared_ptr - Buffer* _bufferPtr; + std::shared_ptr _buffer; + BufferType* _bufferPtr; // Always points to the start of _buffer uint8_t* _start; // the append position @@ -114,19 +115,19 @@ class Builder { explicit Builder(Options const* options); // create an empty Builder, using an existing buffer and default Options - explicit Builder(std::shared_ptr> buffer); + explicit Builder(std::shared_ptr buffer); // create an empty Builder, using an existing buffer and Options - explicit Builder(std::shared_ptr> buffer, + explicit Builder(std::shared_ptr buffer, Options const* options); // create a Builder that uses an existing Buffer and default Options. // the Builder will not claim ownership for its Buffer - explicit Builder(Buffer& buffer) noexcept; + explicit Builder(BufferType& buffer) noexcept; // create a Builder that uses an existing Buffer. the Builder will not // claim ownership for this Buffer - explicit Builder(Buffer& buffer, Options const* options); + explicit Builder(BufferType& buffer, Options const* options); // populate a Builder from a Slice explicit Builder(Slice slice, Options const* options = &Options::Defaults); @@ -141,9 +142,9 @@ class Builder { // get a reference to the Builder's Buffer object // note: this object may be a nullptr if the buffer was already stolen // from the Builder, or if the Builder has no ownership for the Buffer - std::shared_ptr> const& buffer() const { return _buffer; } + std::shared_ptr const& buffer() const { return _buffer; } - Buffer& bufferRef() const { + BufferType& bufferRef() const { if (_bufferPtr == nullptr) { throw Exception(Exception::InternalError, "Builder has no Buffer"); } @@ -153,9 +154,9 @@ class Builder { // steal the Builder's Buffer object. afterwards the Builder // is unusable - note: this may return a nullptr if the Builder does not // own the Buffer! - std::shared_ptr> steal() { + std::shared_ptr steal() { // After a steal the Builder is broken! - std::shared_ptr> res(std::move(_buffer)); + std::shared_ptr res(std::move(_buffer)); _bufferPtr = nullptr; _start = nullptr; clear(); @@ -613,7 +614,7 @@ class Builder { close(); return *this; } - + void resetTo(std::size_t value) { _pos = value; VELOCYPACK_ASSERT(_bufferPtr != nullptr); @@ -1145,7 +1146,7 @@ struct ArrayBuilder final : public BuilderContainer, } // namespace arangodb::velocypack -using VPackBuilder = arangodb::velocypack::Builder; +using VPackBuilder = arangodb::velocypack::Builder>; using VPackBuilderNonDeleter = arangodb::velocypack::BuilderNonDeleter; using VPackBuilderContainer = arangodb::velocypack::BuilderContainer; using VPackObjectBuilder = arangodb::velocypack::ObjectBuilder; diff --git a/src/Builder.cpp b/src/Builder.cpp index a75ae5e5..1a2422c2 100644 --- a/src/Builder.cpp +++ b/src/Builder.cpp @@ -33,20 +33,20 @@ #include "velocypack/Iterator.h" #include "velocypack/Sink.h" -using namespace arangodb::velocypack; + namespace { // checks whether a memmove operation is allowed to get rid of the padding -bool isAllowedToMemmove(Options const* options, uint8_t const* start, - std::vector::iterator indexStart, - std::vector::iterator indexEnd, - ValueLength offsetSize) { +bool isAllowedToMemmove(arangodb::velocypack::Options const* options, uint8_t const* start, + std::vector::iterator indexStart, + std::vector::iterator indexEnd, + arangodb::velocypack::ValueLength offsetSize) { VELOCYPACK_ASSERT(offsetSize == 1 || offsetSize == 2); - if (options->paddingBehavior == Options::PaddingBehavior::NoPadding || + if (options->paddingBehavior == arangodb::velocypack::Options::PaddingBehavior::NoPadding || (offsetSize == 1 && - options->paddingBehavior == Options::PaddingBehavior::Flexible)) { + options->paddingBehavior == arangodb::velocypack::Options::PaddingBehavior::Flexible)) { std::size_t const distance = std::distance(indexStart, indexEnd); std::size_t const n = (std::min)(std::size_t(8 - 2 * offsetSize), distance); for (std::size_t i = 0; i < n; i++) { @@ -60,7 +60,7 @@ bool isAllowedToMemmove(Options const* options, uint8_t const* start, return false; } -uint8_t determineArrayType(bool needIndexTable, ValueLength offsetSize) { +uint8_t determineArrayType(bool needIndexTable, arangodb::velocypack::ValueLength offsetSize) { uint8_t type; // Now build the table: if (needIndexTable) { @@ -79,7 +79,7 @@ uint8_t determineArrayType(bool needIndexTable, ValueLength offsetSize) { return type; } -constexpr ValueLength linearAttributeUniquenessCutoff = 4; +constexpr arangodb::velocypack::ValueLength linearAttributeUniquenessCutoff = 4; // struct used when sorting index tables for objects: struct SortEntry { @@ -129,16 +129,16 @@ uint8_t const* findAttrName(uint8_t const* base, uint64_t& len) { return findAttrName(arangodb::velocypack::Slice(base).makeKey().start(), len); } -bool checkAttributeUniquenessUnsortedBrute(ObjectIterator& it) { +bool checkAttributeUniquenessUnsortedBrute(arangodb::velocypack::ObjectIterator& it) { std::array keys; do { // key(true) guarantees a String as returned type std::string_view key = it.key(true).stringView(); - ValueLength index = it.index(); + arangodb::velocypack::ValueLength index = it.index(); // compare with all other already looked-at keys - for (ValueLength i = 0; i < index; ++i) { + for (arangodb::velocypack::ValueLength i = 0; i < index; ++i) { if (VELOCYPACK_UNLIKELY(keys[i] == key)) { return false; } @@ -151,7 +151,7 @@ bool checkAttributeUniquenessUnsortedBrute(ObjectIterator& it) { return true; } -bool checkAttributeUniquenessUnsortedSet(ObjectIterator& it) { +bool checkAttributeUniquenessUnsortedSet(arangodb::velocypack::ObjectIterator& it) { #ifndef VELOCYPACK_NO_THREADLOCALS std::unique_ptr>& tmp = ::duplicateKeys; @@ -165,7 +165,7 @@ bool checkAttributeUniquenessUnsortedSet(ObjectIterator& it) { #endif do { - Slice key = it.key(true); + arangodb::velocypack::Slice key = it.key(true); // key(true) guarantees a String as returned type VELOCYPACK_ASSERT(key.isString()); if (VELOCYPACK_UNLIKELY(!tmp->emplace(key.stringView()).second)) { @@ -181,8 +181,9 @@ bool checkAttributeUniquenessUnsortedSet(ObjectIterator& it) { } // namespace // create an empty Builder, using default Options -Builder::Builder() - : _buffer(std::make_shared>()), +template +arangodb::velocypack::Builder::Builder() + : _buffer(std::make_shared()), _bufferPtr(_buffer.get()), _start(_bufferPtr->data()), _pos(0), @@ -195,7 +196,8 @@ Builder::Builder() } // create an empty Builder, using Options -Builder::Builder(Options const* opts) : Builder() { +template +arangodb::velocypack::Builder::Builder(arangodb::velocypack::Options const* opts) : Builder() { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); } @@ -203,7 +205,8 @@ Builder::Builder(Options const* opts) : Builder() { } // create an empty Builder, using an existing buffer and default Options -Builder::Builder(std::shared_ptr> buffer) +template +arangodb::velocypack::Builder::Builder(std::shared_ptr buffer) : _buffer(std::move(buffer)), _bufferPtr(_buffer.get()), _start(nullptr), @@ -223,7 +226,9 @@ Builder::Builder(std::shared_ptr> buffer) } // create an empty Builder, using an existing buffer -Builder::Builder(std::shared_ptr> buffer, Options const* opts) +template +arangodb::velocypack::Builder::Builder(std::shared_ptr buffer, + Options const* opts) : Builder(std::move(buffer)) { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); @@ -233,7 +238,8 @@ Builder::Builder(std::shared_ptr> buffer, Options const* opts) // create a Builder that uses an existing Buffer and options. // the Builder will not claim ownership for its Buffer -Builder::Builder(Buffer& buffer) noexcept +template +arangodb::velocypack::Builder::Builder(BufferType& buffer) noexcept : _bufferPtr(&buffer), _start(_bufferPtr->data()), _pos(buffer.size()), @@ -247,7 +253,8 @@ Builder::Builder(Buffer& buffer) noexcept // create a Builder that uses an existing Buffer. the Builder will not // claim ownership for its Buffer -Builder::Builder(Buffer& buffer, Options const* opts) +template +arangodb::velocypack::Builder::Builder(BufferType& buffer, Options const* opts) : Builder(buffer) { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); @@ -256,11 +263,13 @@ Builder::Builder(Buffer& buffer, Options const* opts) } // populate a Builder from a Slice -Builder::Builder(Slice slice, Options const* options) : Builder(options) { +arangodb::velocypack::Builder::Builder(Slice slice, Options const* options) + : Builder(options) { add(slice); } -Builder::Builder(Builder const& that) +template +arangodb::velocypack::Builder::Builder(Builder const& that) : _bufferPtr(nullptr), _start(nullptr), _pos(that._pos), @@ -276,7 +285,7 @@ Builder::Builder(Builder const& that) if (that._buffer == nullptr) { _bufferPtr = that._bufferPtr; } else { - _buffer = std::make_shared>(*that._buffer); + _buffer = std::make_shared(*that._buffer); _bufferPtr = _buffer.get(); } @@ -288,13 +297,14 @@ Builder::Builder(Builder const& that) _stack.reserve(arenaSize / sizeof(decltype(_stack)::value_type)); } -Builder& Builder::operator=(Builder const& that) { +template +arangodb::velocypack::Builder& arangodb::velocypack::Builder::operator=(Builder const& that) { if (this != &that) { if (that._buffer == nullptr) { _buffer.reset(); _bufferPtr = that._bufferPtr; } else { - _buffer = std::make_shared>(*that._buffer); + _buffer = std::make_shared(*that._buffer); _bufferPtr = _buffer.get(); } if (_bufferPtr == nullptr) { @@ -312,7 +322,8 @@ Builder& Builder::operator=(Builder const& that) { return *this; } -Builder::Builder(Builder&& that) noexcept +template +arangodb::velocypack::Builder::Builder(Builder&& that) noexcept : _buffer(std::move(that._buffer)), _bufferPtr(nullptr), _start(nullptr), @@ -339,7 +350,8 @@ Builder::Builder(Builder&& that) noexcept that.clear(); } -Builder& Builder::operator=(Builder&& that) noexcept { +template +arangodb::velocypack::Builder& Builder::operator=(arangodb::velocypack::Builder&& that) noexcept { if (this != &that) { _buffer = std::move(that._buffer); if (_buffer != nullptr) { @@ -366,7 +378,7 @@ Builder& Builder::operator=(Builder&& that) noexcept { return *this; } -std::string Builder::toString() const { +std::string Builder::toString() const { Options opts; opts.prettyPrint = true; @@ -376,14 +388,14 @@ std::string Builder::toString() const { return buffer; } -std::string Builder::toJson() const { +std::string Builder::toJson() const { std::string buffer; StringSink sink(&buffer); Dumper::dump(slice(), &sink); return buffer; } -void Builder::sortObjectIndexShort( +void Builder::sortObjectIndexShort( uint8_t* objBase, std::vector::iterator indexStart, std::vector::iterator indexEnd) const { std::sort(indexStart, indexEnd, @@ -407,7 +419,7 @@ void Builder::sortObjectIndexShort( }); } -void Builder::sortObjectIndexLong( +void Builder::sortObjectIndexLong( uint8_t* objBase, std::vector::iterator indexStart, std::vector::iterator indexEnd) const { #ifndef VELOCYPACK_NO_THREADLOCALS @@ -456,7 +468,8 @@ void Builder::sortObjectIndexLong( } } -Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, bool isArray) { +Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, + bool isArray) { // empty Array or Object _start[pos] = (isArray ? 0x01 : 0x0a); VELOCYPACK_ASSERT(_pos == pos + 9); @@ -465,7 +478,7 @@ Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, bool isArray) { return *this; } -bool Builder::closeCompactArrayOrObject( +bool Builder::closeCompactArrayOrObject( ValueLength pos, bool isArray, std::vector::iterator indexStart, std::vector::iterator indexEnd) { @@ -523,9 +536,9 @@ bool Builder::closeCompactArrayOrObject( return false; } -Builder& Builder::closeArray(ValueLength pos, - std::vector::iterator indexStart, - std::vector::iterator indexEnd) { +Builder& Builder::closeArray( + ValueLength pos, std::vector::iterator indexStart, + std::vector::iterator indexEnd) { std::size_t const n = std::distance(indexStart, indexEnd); VELOCYPACK_ASSERT(n > 0); @@ -672,7 +685,7 @@ Builder& Builder::closeArray(ValueLength pos, return *this; } -Builder& Builder::close() { +Builder& Builder::close() { if (VELOCYPACK_UNLIKELY(isClosed())) { throw Exception(Exception::BuilderNeedOpenCompound); } @@ -854,12 +867,12 @@ Builder& Builder::close() { } // checks whether an Object value has a specific key attribute -bool Builder::hasKey(std::string_view key) const { +bool Builder::hasKey(std::string_view key) const { return !getKey(key).isNone(); } // return the value for a specific key of an Object value -Slice Builder::getKey(std::string_view key) const { +Slice Builder::getKey(std::string_view key) const { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenObject); } @@ -882,7 +895,7 @@ Slice Builder::getKey(std::string_view key) const { return Slice(); } -void Builder::appendTag(uint64_t tag) { +void Builder::appendTag(uint64_t tag) { if (options->disallowTags) { // Tagged values explicitly disallowed throw Exception(Exception::BuilderTagsDisallowed); @@ -898,7 +911,7 @@ void Builder::appendTag(uint64_t tag) { } } -uint8_t* Builder::set(Value const& item) { +uint8_t* Builder::set(Value const& item) { auto const oldPos = _pos; auto ctype = item.cType(); @@ -1165,7 +1178,7 @@ uint8_t* Builder::set(Value const& item) { return _start + oldPos; } -uint8_t* Builder::set(Slice const& item) { +uint8_t* Builder::set(Slice const& item) { checkKeyHasValidType(item); if (VELOCYPACK_UNLIKELY(options->disallowCustom && item.isCustom())) { @@ -1183,7 +1196,7 @@ uint8_t* Builder::set(Slice const& item) { return _start + _pos - l; } -uint8_t* Builder::set(ValuePair const& pair) { +uint8_t* Builder::set(ValuePair const& pair) { // This method builds a single further VPack item at the current // append position. This is the case for ValueType::String, // ValueType::Binary, or ValueType::Custom, which can be built @@ -1242,7 +1255,7 @@ uint8_t* Builder::set(ValuePair const& pair) { "ValueType::Custom are valid for ValuePair argument"); } -uint8_t* Builder::set(IStringFromParts const& parts) { +uint8_t* Builder::set(IStringFromParts const& parts) { // This method builds a single VPack String item composed of the 2 parts. auto const oldPos = _pos; @@ -1269,13 +1282,13 @@ uint8_t* Builder::set(IStringFromParts const& parts) { return _start + oldPos; } -void Builder::cleanupAdd() noexcept { +void Builder::cleanupAdd() noexcept { VELOCYPACK_ASSERT(!_stack.empty()); VELOCYPACK_ASSERT(!_indexes.empty()); _indexes.pop_back(); } -void Builder::reportAdd() { +void Builder::reportAdd() { VELOCYPACK_ASSERT(!_stack.empty()); if (_indexes.capacity() == 0) { // make an initial reservation for several items at @@ -1286,14 +1299,14 @@ void Builder::reportAdd() { _indexes.push_back(_pos - _stack.back().startPos); } -void Builder::closeLevel() noexcept { +void Builder::closeLevel() noexcept { VELOCYPACK_ASSERT(!_stack.empty()); ValueLength const indexStartPos = _stack.back().indexStartPos; _stack.pop_back(); _indexes.erase(_indexes.begin() + indexStartPos, _indexes.end()); } -bool Builder::checkAttributeUniqueness(Slice obj) const { +bool Builder::checkAttributeUniqueness(Slice obj) const { VELOCYPACK_ASSERT(options->checkAttributeUniqueness == true); VELOCYPACK_ASSERT(obj.isObject()); VELOCYPACK_ASSERT(obj.length() >= 2); @@ -1306,7 +1319,7 @@ bool Builder::checkAttributeUniqueness(Slice obj) const { return checkAttributeUniquenessUnsorted(obj); } -bool Builder::checkAttributeUniquenessSorted(Slice obj) const { +bool Builder::checkAttributeUniquenessSorted(Slice obj) const { ObjectIterator it(obj, false); // fetch initial key @@ -1338,7 +1351,7 @@ bool Builder::checkAttributeUniquenessSorted(Slice obj) const { return true; } -bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { +bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { // cutoff value for linear attribute uniqueness scan // unsorted objects with this amount of attributes (or less) will // be validated using a non-allocating scan over the attributes @@ -1355,7 +1368,7 @@ bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { // Add all subkeys and subvalues into an object from an ObjectIterator // and leaves open the object intentionally -uint8_t* Builder::add(ObjectIterator&& sub) { +uint8_t* Builder::add(ObjectIterator&& sub) { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenObject); } @@ -1378,7 +1391,7 @@ uint8_t* Builder::add(ObjectIterator&& sub) { // Add all subkeys and subvalues into an object from an ArrayIterator // and leaves open the array intentionally -uint8_t* Builder::add(ArrayIterator&& sub) { +uint8_t* Builder::add(ArrayIterator&& sub) { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenArray); } @@ -1394,16 +1407,21 @@ uint8_t* Builder::add(ArrayIterator&& sub) { return _start + oldPos; } -ValueLength Builder::effectivePaddingForOneByteMembers() const noexcept { +ValueLength Builder::effectivePaddingForOneByteMembers() + const noexcept { // 8 bytes - object length (1 byte) - number of items (1 byte) = 6 bytes return (options->paddingBehavior == Options::PaddingBehavior::UsePadding ? 6 : 0); } -ValueLength Builder::effectivePaddingForTwoByteMembers() const noexcept { +ValueLength Builder::effectivePaddingForTwoByteMembers() + const noexcept { // 8 bytes - object length (2 bytes) - number of items (2 bytes) = 4 bytes return (options->paddingBehavior == Options::PaddingBehavior::UsePadding ? 4 : 0); } static_assert(sizeof(double) == 8, "double is not 8 bytes"); +// remember instantiate template + +template class arangodb::velocypack::Builder>; From 87909e19bb4155fd67270df8951288db55c13bfc Mon Sep 17 00:00:00 2001 From: Julia CP Date: Wed, 13 Aug 2025 11:48:55 -0300 Subject: [PATCH 2/7] Intermediate commit of the supervised buffer, must fix template errors still --- include/velocypack/Builder.h | 2 +- src/Builder.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/velocypack/Builder.h b/include/velocypack/Builder.h index 02d17bf7..e2450220 100644 --- a/include/velocypack/Builder.h +++ b/include/velocypack/Builder.h @@ -1146,7 +1146,7 @@ struct ArrayBuilder final : public BuilderContainer, } // namespace arangodb::velocypack -using VPackBuilder = arangodb::velocypack::Builder>; +using VPackBuilder = arangodb::velocypack::Builder>; using VPackBuilderNonDeleter = arangodb::velocypack::BuilderNonDeleter; using VPackBuilderContainer = arangodb::velocypack::BuilderContainer; using VPackObjectBuilder = arangodb::velocypack::ObjectBuilder; diff --git a/src/Builder.cpp b/src/Builder.cpp index 1a2422c2..bfb9873f 100644 --- a/src/Builder.cpp +++ b/src/Builder.cpp @@ -298,7 +298,8 @@ arangodb::velocypack::Builder::Builder(Builder const& that) } template -arangodb::velocypack::Builder& arangodb::velocypack::Builder::operator=(Builder const& that) { +arangodb::velocypack::Builder& +arangodb::velocypack::Builder::operator=(arangodb::velocypack::Builder const& that) { if (this != &that) { if (that._buffer == nullptr) { _buffer.reset(); From e953ea5d2472eaf29b764a5a6d08f12c27313103 Mon Sep 17 00:00:00 2001 From: Julia CP Date: Wed, 13 Aug 2025 12:09:45 -0300 Subject: [PATCH 3/7] Make destructor virtual --- include/velocypack/Buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/velocypack/Buffer.h b/include/velocypack/Buffer.h index aacc2702..b1f12080 100644 --- a/include/velocypack/Buffer.h +++ b/include/velocypack/Buffer.h @@ -132,7 +132,7 @@ class Buffer { return *this; } - ~Buffer() { + virtual ~Buffer() { if (_buffer != _local) { velocypack_free(_buffer); } From 852e6eebb2886e0ad31475f9426c8b2f97cff31e Mon Sep 17 00:00:00 2001 From: Julia CP Date: Thu, 14 Aug 2025 13:49:34 -0300 Subject: [PATCH 4/7] Restore files to their original state from main branch so just Buffer is changed --- include/velocypack/Builder.h | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/include/velocypack/Builder.h b/include/velocypack/Builder.h index e2450220..9e10e81c 100644 --- a/include/velocypack/Builder.h +++ b/include/velocypack/Builder.h @@ -50,7 +50,6 @@ namespace arangodb::velocypack { class ArrayIterator; class ObjectIterator; -template> class Builder { friend class Parser; // The parser needs access to internals. @@ -75,12 +74,12 @@ class Builder { // buffer. Whenever the stack is empty, one can use the start, // size and slice methods to get out the ready built VPack // object(s). - public: - using buffer_type = BufferType; private: - std::shared_ptr _buffer; - BufferType* _bufferPtr; + // Here we collect the result + std::shared_ptr> _buffer; + // used for quicker access than shared_ptr + Buffer* _bufferPtr; // Always points to the start of _buffer uint8_t* _start; // the append position @@ -115,19 +114,19 @@ class Builder { explicit Builder(Options const* options); // create an empty Builder, using an existing buffer and default Options - explicit Builder(std::shared_ptr buffer); + explicit Builder(std::shared_ptr> buffer); // create an empty Builder, using an existing buffer and Options - explicit Builder(std::shared_ptr buffer, + explicit Builder(std::shared_ptr> buffer, Options const* options); // create a Builder that uses an existing Buffer and default Options. // the Builder will not claim ownership for its Buffer - explicit Builder(BufferType& buffer) noexcept; + explicit Builder(Buffer& buffer) noexcept; // create a Builder that uses an existing Buffer. the Builder will not // claim ownership for this Buffer - explicit Builder(BufferType& buffer, Options const* options); + explicit Builder(Buffer& buffer, Options const* options); // populate a Builder from a Slice explicit Builder(Slice slice, Options const* options = &Options::Defaults); @@ -142,9 +141,9 @@ class Builder { // get a reference to the Builder's Buffer object // note: this object may be a nullptr if the buffer was already stolen // from the Builder, or if the Builder has no ownership for the Buffer - std::shared_ptr const& buffer() const { return _buffer; } + std::shared_ptr> const& buffer() const { return _buffer; } - BufferType& bufferRef() const { + Buffer& bufferRef() const { if (_bufferPtr == nullptr) { throw Exception(Exception::InternalError, "Builder has no Buffer"); } @@ -154,9 +153,9 @@ class Builder { // steal the Builder's Buffer object. afterwards the Builder // is unusable - note: this may return a nullptr if the Builder does not // own the Buffer! - std::shared_ptr steal() { + std::shared_ptr> steal() { // After a steal the Builder is broken! - std::shared_ptr res(std::move(_buffer)); + std::shared_ptr> res(std::move(_buffer)); _bufferPtr = nullptr; _start = nullptr; clear(); @@ -614,7 +613,7 @@ class Builder { close(); return *this; } - + void resetTo(std::size_t value) { _pos = value; VELOCYPACK_ASSERT(_bufferPtr != nullptr); @@ -1146,7 +1145,7 @@ struct ArrayBuilder final : public BuilderContainer, } // namespace arangodb::velocypack -using VPackBuilder = arangodb::velocypack::Builder>; +using VPackBuilder = arangodb::velocypack::Builder; using VPackBuilderNonDeleter = arangodb::velocypack::BuilderNonDeleter; using VPackBuilderContainer = arangodb::velocypack::BuilderContainer; using VPackObjectBuilder = arangodb::velocypack::ObjectBuilder; From 7467ac98b530f05a25a6a6eb91e7ca41ed89080d Mon Sep 17 00:00:00 2001 From: Julia CP Date: Fri, 15 Aug 2025 10:55:28 -0300 Subject: [PATCH 5/7] Restored Builder to its original state --- src/Builder.cpp | 129 +++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 74 deletions(-) diff --git a/src/Builder.cpp b/src/Builder.cpp index bfb9873f..a75ae5e5 100644 --- a/src/Builder.cpp +++ b/src/Builder.cpp @@ -33,20 +33,20 @@ #include "velocypack/Iterator.h" #include "velocypack/Sink.h" - +using namespace arangodb::velocypack; namespace { // checks whether a memmove operation is allowed to get rid of the padding -bool isAllowedToMemmove(arangodb::velocypack::Options const* options, uint8_t const* start, - std::vector::iterator indexStart, - std::vector::iterator indexEnd, - arangodb::velocypack::ValueLength offsetSize) { +bool isAllowedToMemmove(Options const* options, uint8_t const* start, + std::vector::iterator indexStart, + std::vector::iterator indexEnd, + ValueLength offsetSize) { VELOCYPACK_ASSERT(offsetSize == 1 || offsetSize == 2); - if (options->paddingBehavior == arangodb::velocypack::Options::PaddingBehavior::NoPadding || + if (options->paddingBehavior == Options::PaddingBehavior::NoPadding || (offsetSize == 1 && - options->paddingBehavior == arangodb::velocypack::Options::PaddingBehavior::Flexible)) { + options->paddingBehavior == Options::PaddingBehavior::Flexible)) { std::size_t const distance = std::distance(indexStart, indexEnd); std::size_t const n = (std::min)(std::size_t(8 - 2 * offsetSize), distance); for (std::size_t i = 0; i < n; i++) { @@ -60,7 +60,7 @@ bool isAllowedToMemmove(arangodb::velocypack::Options const* options, uint8_t co return false; } -uint8_t determineArrayType(bool needIndexTable, arangodb::velocypack::ValueLength offsetSize) { +uint8_t determineArrayType(bool needIndexTable, ValueLength offsetSize) { uint8_t type; // Now build the table: if (needIndexTable) { @@ -79,7 +79,7 @@ uint8_t determineArrayType(bool needIndexTable, arangodb::velocypack::ValueLengt return type; } -constexpr arangodb::velocypack::ValueLength linearAttributeUniquenessCutoff = 4; +constexpr ValueLength linearAttributeUniquenessCutoff = 4; // struct used when sorting index tables for objects: struct SortEntry { @@ -129,16 +129,16 @@ uint8_t const* findAttrName(uint8_t const* base, uint64_t& len) { return findAttrName(arangodb::velocypack::Slice(base).makeKey().start(), len); } -bool checkAttributeUniquenessUnsortedBrute(arangodb::velocypack::ObjectIterator& it) { +bool checkAttributeUniquenessUnsortedBrute(ObjectIterator& it) { std::array keys; do { // key(true) guarantees a String as returned type std::string_view key = it.key(true).stringView(); - arangodb::velocypack::ValueLength index = it.index(); + ValueLength index = it.index(); // compare with all other already looked-at keys - for (arangodb::velocypack::ValueLength i = 0; i < index; ++i) { + for (ValueLength i = 0; i < index; ++i) { if (VELOCYPACK_UNLIKELY(keys[i] == key)) { return false; } @@ -151,7 +151,7 @@ bool checkAttributeUniquenessUnsortedBrute(arangodb::velocypack::ObjectIterator& return true; } -bool checkAttributeUniquenessUnsortedSet(arangodb::velocypack::ObjectIterator& it) { +bool checkAttributeUniquenessUnsortedSet(ObjectIterator& it) { #ifndef VELOCYPACK_NO_THREADLOCALS std::unique_ptr>& tmp = ::duplicateKeys; @@ -165,7 +165,7 @@ bool checkAttributeUniquenessUnsortedSet(arangodb::velocypack::ObjectIterator& i #endif do { - arangodb::velocypack::Slice key = it.key(true); + Slice key = it.key(true); // key(true) guarantees a String as returned type VELOCYPACK_ASSERT(key.isString()); if (VELOCYPACK_UNLIKELY(!tmp->emplace(key.stringView()).second)) { @@ -181,9 +181,8 @@ bool checkAttributeUniquenessUnsortedSet(arangodb::velocypack::ObjectIterator& i } // namespace // create an empty Builder, using default Options -template -arangodb::velocypack::Builder::Builder() - : _buffer(std::make_shared()), +Builder::Builder() + : _buffer(std::make_shared>()), _bufferPtr(_buffer.get()), _start(_bufferPtr->data()), _pos(0), @@ -196,8 +195,7 @@ arangodb::velocypack::Builder::Builder() } // create an empty Builder, using Options -template -arangodb::velocypack::Builder::Builder(arangodb::velocypack::Options const* opts) : Builder() { +Builder::Builder(Options const* opts) : Builder() { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); } @@ -205,8 +203,7 @@ arangodb::velocypack::Builder::Builder(arangodb::velocypack::Options } // create an empty Builder, using an existing buffer and default Options -template -arangodb::velocypack::Builder::Builder(std::shared_ptr buffer) +Builder::Builder(std::shared_ptr> buffer) : _buffer(std::move(buffer)), _bufferPtr(_buffer.get()), _start(nullptr), @@ -226,9 +223,7 @@ arangodb::velocypack::Builder::Builder(std::shared_ptr b } // create an empty Builder, using an existing buffer -template -arangodb::velocypack::Builder::Builder(std::shared_ptr buffer, - Options const* opts) +Builder::Builder(std::shared_ptr> buffer, Options const* opts) : Builder(std::move(buffer)) { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); @@ -238,8 +233,7 @@ arangodb::velocypack::Builder::Builder(std::shared_ptr b // create a Builder that uses an existing Buffer and options. // the Builder will not claim ownership for its Buffer -template -arangodb::velocypack::Builder::Builder(BufferType& buffer) noexcept +Builder::Builder(Buffer& buffer) noexcept : _bufferPtr(&buffer), _start(_bufferPtr->data()), _pos(buffer.size()), @@ -253,8 +247,7 @@ arangodb::velocypack::Builder::Builder(BufferType& buffer) noexcept // create a Builder that uses an existing Buffer. the Builder will not // claim ownership for its Buffer -template -arangodb::velocypack::Builder::Builder(BufferType& buffer, Options const* opts) +Builder::Builder(Buffer& buffer, Options const* opts) : Builder(buffer) { if (VELOCYPACK_UNLIKELY(opts == nullptr)) { throw Exception(Exception::InternalError, "Options cannot be a nullptr"); @@ -263,13 +256,11 @@ arangodb::velocypack::Builder::Builder(BufferType& buffer, Options c } // populate a Builder from a Slice -arangodb::velocypack::Builder::Builder(Slice slice, Options const* options) - : Builder(options) { +Builder::Builder(Slice slice, Options const* options) : Builder(options) { add(slice); } -template -arangodb::velocypack::Builder::Builder(Builder const& that) +Builder::Builder(Builder const& that) : _bufferPtr(nullptr), _start(nullptr), _pos(that._pos), @@ -285,7 +276,7 @@ arangodb::velocypack::Builder::Builder(Builder const& that) if (that._buffer == nullptr) { _bufferPtr = that._bufferPtr; } else { - _buffer = std::make_shared(*that._buffer); + _buffer = std::make_shared>(*that._buffer); _bufferPtr = _buffer.get(); } @@ -297,15 +288,13 @@ arangodb::velocypack::Builder::Builder(Builder const& that) _stack.reserve(arenaSize / sizeof(decltype(_stack)::value_type)); } -template -arangodb::velocypack::Builder& -arangodb::velocypack::Builder::operator=(arangodb::velocypack::Builder const& that) { +Builder& Builder::operator=(Builder const& that) { if (this != &that) { if (that._buffer == nullptr) { _buffer.reset(); _bufferPtr = that._bufferPtr; } else { - _buffer = std::make_shared(*that._buffer); + _buffer = std::make_shared>(*that._buffer); _bufferPtr = _buffer.get(); } if (_bufferPtr == nullptr) { @@ -323,8 +312,7 @@ arangodb::velocypack::Builder::operator=(arangodb::velocypack::Build return *this; } -template -arangodb::velocypack::Builder::Builder(Builder&& that) noexcept +Builder::Builder(Builder&& that) noexcept : _buffer(std::move(that._buffer)), _bufferPtr(nullptr), _start(nullptr), @@ -351,8 +339,7 @@ arangodb::velocypack::Builder::Builder(Builder&& that) noexcept that.clear(); } -template -arangodb::velocypack::Builder& Builder::operator=(arangodb::velocypack::Builder&& that) noexcept { +Builder& Builder::operator=(Builder&& that) noexcept { if (this != &that) { _buffer = std::move(that._buffer); if (_buffer != nullptr) { @@ -379,7 +366,7 @@ arangodb::velocypack::Builder& Builder::operator=(arangodb::velocypa return *this; } -std::string Builder::toString() const { +std::string Builder::toString() const { Options opts; opts.prettyPrint = true; @@ -389,14 +376,14 @@ std::string Builder::toString() const { return buffer; } -std::string Builder::toJson() const { +std::string Builder::toJson() const { std::string buffer; StringSink sink(&buffer); Dumper::dump(slice(), &sink); return buffer; } -void Builder::sortObjectIndexShort( +void Builder::sortObjectIndexShort( uint8_t* objBase, std::vector::iterator indexStart, std::vector::iterator indexEnd) const { std::sort(indexStart, indexEnd, @@ -420,7 +407,7 @@ void Builder::sortObjectIndexShort( }); } -void Builder::sortObjectIndexLong( +void Builder::sortObjectIndexLong( uint8_t* objBase, std::vector::iterator indexStart, std::vector::iterator indexEnd) const { #ifndef VELOCYPACK_NO_THREADLOCALS @@ -469,8 +456,7 @@ void Builder::sortObjectIndexLong( } } -Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, - bool isArray) { +Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, bool isArray) { // empty Array or Object _start[pos] = (isArray ? 0x01 : 0x0a); VELOCYPACK_ASSERT(_pos == pos + 9); @@ -479,7 +465,7 @@ Builder& Builder::closeEmptyArrayOrObject(ValueLength pos, return *this; } -bool Builder::closeCompactArrayOrObject( +bool Builder::closeCompactArrayOrObject( ValueLength pos, bool isArray, std::vector::iterator indexStart, std::vector::iterator indexEnd) { @@ -537,9 +523,9 @@ bool Builder::closeCompactArrayOrObject( return false; } -Builder& Builder::closeArray( - ValueLength pos, std::vector::iterator indexStart, - std::vector::iterator indexEnd) { +Builder& Builder::closeArray(ValueLength pos, + std::vector::iterator indexStart, + std::vector::iterator indexEnd) { std::size_t const n = std::distance(indexStart, indexEnd); VELOCYPACK_ASSERT(n > 0); @@ -686,7 +672,7 @@ Builder& Builder::closeArray( return *this; } -Builder& Builder::close() { +Builder& Builder::close() { if (VELOCYPACK_UNLIKELY(isClosed())) { throw Exception(Exception::BuilderNeedOpenCompound); } @@ -868,12 +854,12 @@ Builder& Builder::close() { } // checks whether an Object value has a specific key attribute -bool Builder::hasKey(std::string_view key) const { +bool Builder::hasKey(std::string_view key) const { return !getKey(key).isNone(); } // return the value for a specific key of an Object value -Slice Builder::getKey(std::string_view key) const { +Slice Builder::getKey(std::string_view key) const { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenObject); } @@ -896,7 +882,7 @@ Slice Builder::getKey(std::string_view key) const { return Slice(); } -void Builder::appendTag(uint64_t tag) { +void Builder::appendTag(uint64_t tag) { if (options->disallowTags) { // Tagged values explicitly disallowed throw Exception(Exception::BuilderTagsDisallowed); @@ -912,7 +898,7 @@ void Builder::appendTag(uint64_t tag) { } } -uint8_t* Builder::set(Value const& item) { +uint8_t* Builder::set(Value const& item) { auto const oldPos = _pos; auto ctype = item.cType(); @@ -1179,7 +1165,7 @@ uint8_t* Builder::set(Value const& item) { return _start + oldPos; } -uint8_t* Builder::set(Slice const& item) { +uint8_t* Builder::set(Slice const& item) { checkKeyHasValidType(item); if (VELOCYPACK_UNLIKELY(options->disallowCustom && item.isCustom())) { @@ -1197,7 +1183,7 @@ uint8_t* Builder::set(Slice const& item) { return _start + _pos - l; } -uint8_t* Builder::set(ValuePair const& pair) { +uint8_t* Builder::set(ValuePair const& pair) { // This method builds a single further VPack item at the current // append position. This is the case for ValueType::String, // ValueType::Binary, or ValueType::Custom, which can be built @@ -1256,7 +1242,7 @@ uint8_t* Builder::set(ValuePair const& pair) { "ValueType::Custom are valid for ValuePair argument"); } -uint8_t* Builder::set(IStringFromParts const& parts) { +uint8_t* Builder::set(IStringFromParts const& parts) { // This method builds a single VPack String item composed of the 2 parts. auto const oldPos = _pos; @@ -1283,13 +1269,13 @@ uint8_t* Builder::set(IStringFromParts const& parts) { return _start + oldPos; } -void Builder::cleanupAdd() noexcept { +void Builder::cleanupAdd() noexcept { VELOCYPACK_ASSERT(!_stack.empty()); VELOCYPACK_ASSERT(!_indexes.empty()); _indexes.pop_back(); } -void Builder::reportAdd() { +void Builder::reportAdd() { VELOCYPACK_ASSERT(!_stack.empty()); if (_indexes.capacity() == 0) { // make an initial reservation for several items at @@ -1300,14 +1286,14 @@ void Builder::reportAdd() { _indexes.push_back(_pos - _stack.back().startPos); } -void Builder::closeLevel() noexcept { +void Builder::closeLevel() noexcept { VELOCYPACK_ASSERT(!_stack.empty()); ValueLength const indexStartPos = _stack.back().indexStartPos; _stack.pop_back(); _indexes.erase(_indexes.begin() + indexStartPos, _indexes.end()); } -bool Builder::checkAttributeUniqueness(Slice obj) const { +bool Builder::checkAttributeUniqueness(Slice obj) const { VELOCYPACK_ASSERT(options->checkAttributeUniqueness == true); VELOCYPACK_ASSERT(obj.isObject()); VELOCYPACK_ASSERT(obj.length() >= 2); @@ -1320,7 +1306,7 @@ bool Builder::checkAttributeUniqueness(Slice obj) const { return checkAttributeUniquenessUnsorted(obj); } -bool Builder::checkAttributeUniquenessSorted(Slice obj) const { +bool Builder::checkAttributeUniquenessSorted(Slice obj) const { ObjectIterator it(obj, false); // fetch initial key @@ -1352,7 +1338,7 @@ bool Builder::checkAttributeUniquenessSorted(Slice obj) const { return true; } -bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { +bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { // cutoff value for linear attribute uniqueness scan // unsorted objects with this amount of attributes (or less) will // be validated using a non-allocating scan over the attributes @@ -1369,7 +1355,7 @@ bool Builder::checkAttributeUniquenessUnsorted(Slice obj) const { // Add all subkeys and subvalues into an object from an ObjectIterator // and leaves open the object intentionally -uint8_t* Builder::add(ObjectIterator&& sub) { +uint8_t* Builder::add(ObjectIterator&& sub) { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenObject); } @@ -1392,7 +1378,7 @@ uint8_t* Builder::add(ObjectIterator&& sub) { // Add all subkeys and subvalues into an object from an ArrayIterator // and leaves open the array intentionally -uint8_t* Builder::add(ArrayIterator&& sub) { +uint8_t* Builder::add(ArrayIterator&& sub) { if (VELOCYPACK_UNLIKELY(_stack.empty())) { throw Exception(Exception::BuilderNeedOpenArray); } @@ -1408,21 +1394,16 @@ uint8_t* Builder::add(ArrayIterator&& sub) { return _start + oldPos; } -ValueLength Builder::effectivePaddingForOneByteMembers() - const noexcept { +ValueLength Builder::effectivePaddingForOneByteMembers() const noexcept { // 8 bytes - object length (1 byte) - number of items (1 byte) = 6 bytes return (options->paddingBehavior == Options::PaddingBehavior::UsePadding ? 6 : 0); } -ValueLength Builder::effectivePaddingForTwoByteMembers() - const noexcept { +ValueLength Builder::effectivePaddingForTwoByteMembers() const noexcept { // 8 bytes - object length (2 bytes) - number of items (2 bytes) = 4 bytes return (options->paddingBehavior == Options::PaddingBehavior::UsePadding ? 4 : 0); } static_assert(sizeof(double) == 8, "double is not 8 bytes"); -// remember instantiate template - -template class arangodb::velocypack::Builder>; From 08a17c1a7f16ac92024490c77bce0d0dc1e757e2 Mon Sep 17 00:00:00 2001 From: Julia CP Date: Mon, 18 Aug 2025 10:29:32 -0300 Subject: [PATCH 6/7] Make clear method virtual --- include/velocypack/Buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/velocypack/Buffer.h b/include/velocypack/Buffer.h index b1f12080..3ba17e0e 100644 --- a/include/velocypack/Buffer.h +++ b/include/velocypack/Buffer.h @@ -181,7 +181,7 @@ class Buffer { _size -= value; } - void clear() noexcept { + virtual void clear() noexcept { _size = 0; if (_buffer != _local) { velocypack_free(_buffer); From ed08005cf5a0f6c95c7257b3ecec9d1f80a93447 Mon Sep 17 00:00:00 2001 From: Julia CP Date: Thu, 21 Aug 2025 17:23:30 -0300 Subject: [PATCH 7/7] Make grow method protected --- include/velocypack/Buffer.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/velocypack/Buffer.h b/include/velocypack/Buffer.h index 3ba17e0e..ab309062 100644 --- a/include/velocypack/Buffer.h +++ b/include/velocypack/Buffer.h @@ -284,6 +284,8 @@ class Buffer { inline void poison(T*, ValueLength) noexcept {} #endif + protected: + virtual void grow(ValueLength len) { VELOCYPACK_ASSERT(_size + len >= sizeof(_local)); @@ -319,6 +321,8 @@ class Buffer { VELOCYPACK_ASSERT(_size <= _capacity); } + private: + T* _buffer; ValueLength _capacity; ValueLength _size;