diff --git a/pp/bare_bones/iterator.h b/pp/bare_bones/iterator.h index 6b6c0cedf7..a26c30e514 100644 --- a/pp/bare_bones/iterator.h +++ b/pp/bare_bones/iterator.h @@ -9,6 +9,10 @@ namespace BareBones::iterator { +// std-compatible universal sentinel +using IteratorSentinelType = std::default_sentinel_t; +inline constexpr IteratorSentinelType kSentinel = {}; + template class OperationIterator { public: diff --git a/pp/bare_bones/snug_composite.h b/pp/bare_bones/snug_composite.h index e8526fe41b..9e80021d41 100644 --- a/pp/bare_bones/snug_composite.h +++ b/pp/bare_bones/snug_composite.h @@ -8,6 +8,8 @@ #include +#include + #include "bare_bones/allocator.h" #include "bare_bones/exception.h" #include "bare_bones/streams.h" @@ -18,15 +20,18 @@ namespace BareBones::SnugComposite { /** * Serialization mode used to annotate encoded data and how to apply it on container. - * We use Delta for save difference between two states of container. It doesn't matter - * how first state was made (from snapshot or other delta). Snapshot may be explain - * as delta from init (zero) state. + * We use Delta to save the difference between two states of container. It doesn't matter + * how the first state was made (from snapshot or other delta). Snapshot may be explained + * as a delta from the init (zero) state. */ enum class SerializationMode : char { SNAPSHOT = 1, DELTA = 2 }; -template -concept is_shrinkable = requires(FilamentDataType& data_type) { - { data_type.shrink_to(uint32_t()) }; +template +concept ls_id_range = std::ranges::range && std::same_as, uint32_t>; + +template +concept is_shrinkable = requires(FilamentStorageType& storage) { + { storage.shrink() }; }; template @@ -39,21 +44,20 @@ concept has_rollback = requires(Derived derived, const Checkpoint& checkpoint) { { derived.rollback_impl(checkpoint) }; }; -template -concept has_after_items_load = requires(Derived derived) { - { derived.after_items_load_impl(uint32_t()) }; -}; +template +concept has_after_items_load = ls_id_range && requires(Derived derived, R&& range) { derived.after_items_load_impl(std::forward(range)); }; template class> class Filament, template class Vector> class GenericDecodingTable { - static_assert(!std::is_integral_v::composite_type>, "Filament::composite_type can't be an integral type"); + static_assert(!std::is_integral_v::storage_type::composite_type>, "Filament::composite_type can't be an integral type"); template class> class AnyFilament, template class AnyVector> friend class GenericDecodingTable; public: - using value_type = typename Filament::composite_type; - using data_type = typename Filament::data_type; // FIXME make it private + using filament_type = Filament; + using storage_type = filament_type::storage_type; + using value_type = storage_type::composite_type; static constexpr bool kIsReadOnly = IsSharedSpan>::value; @@ -65,12 +69,12 @@ class GenericDecodingTable { public: // NOLINTNEXTLINE(google-explicit-constructor) - inline __attribute__((always_inline)) Proxy(uint32_t id) noexcept : id_(id) {} + PROMPP_ALWAYS_INLINE Proxy(uint32_t id) noexcept : id_(id) {} // NOLINTNEXTLINE(google-explicit-constructor) - inline __attribute__((always_inline)) operator uint32_t() const noexcept { return id_; } + PROMPP_ALWAYS_INLINE operator uint32_t() const noexcept { return id_; } - inline __attribute__((always_inline)) bool operator==(const Proxy& o) noexcept { return id_ == o.id_; } + PROMPP_ALWAYS_INLINE bool operator==(const Proxy& o) noexcept { return id_ == o.id_; } }; struct Hasher { @@ -87,23 +91,20 @@ class GenericDecodingTable { return phmap::Hash()(c); } - PROMPP_ALWAYS_INLINE size_t operator()(const Proxy& p) const noexcept { - return this->operator()(decoding_table->items_[p].composite(decoding_table->data_)); - } + PROMPP_ALWAYS_INLINE size_t operator()(const Proxy& p) const noexcept { return this->operator()(decoding_table->storage_.composite(p)); } }; struct EqualityComparator { using is_transparent = void; const GenericDecodingTable* decoding_table; - inline __attribute__((always_inline)) explicit EqualityComparator(const GenericDecodingTable* _decoding_table = nullptr) noexcept - : decoding_table(_decoding_table) {} + PROMPP_ALWAYS_INLINE explicit EqualityComparator(const GenericDecodingTable* _decoding_table = nullptr) noexcept : decoding_table(_decoding_table) {} - inline __attribute__((always_inline)) bool operator()(const Proxy& a, const Proxy& b) const noexcept { return a == b; } + PROMPP_ALWAYS_INLINE bool operator()(const Proxy& a, const Proxy& b) const noexcept { return a == b; } template - inline __attribute__((always_inline)) bool operator()(const Proxy& a, const Class& b) const noexcept { - return decoding_table->items_[a].composite(decoding_table->data_) == b; + PROMPP_ALWAYS_INLINE bool operator()(const Proxy& a, const Class& b) const noexcept { + return decoding_table->storage_.composite(a) == b; } }; @@ -113,42 +114,38 @@ class GenericDecodingTable { PROMPP_ALWAYS_INLINE explicit LessComparator(const GenericDecodingTable* decoding_table) noexcept : decoding_table_(decoding_table) {} PROMPP_ALWAYS_INLINE bool operator()(const Proxy& a, const Proxy& b) const noexcept { - return decoding_table_->items_[a].composite(decoding_table_->data_) < decoding_table_->items_[b].composite(decoding_table_->data_); + return decoding_table_->storage_.composite(a) < decoding_table_->storage_.composite(b); } template PROMPP_ALWAYS_INLINE bool operator()(const Proxy& a, const Class& b) const noexcept { - return decoding_table_->items_[a].composite(decoding_table_->data_) < b; + return decoding_table_->storage_.composite(a) < b; } template requires(!std::is_same_v) PROMPP_ALWAYS_INLINE bool operator()(const Class& a, const Proxy& b) const noexcept { - return a < decoding_table_->items_[b].composite(decoding_table_->data_); + return a < decoding_table_->storage_.composite(b); } private: const GenericDecodingTable* decoding_table_; }; - template class Checkpoint { - const DecodingTable* decoding_table_; + const storage_type* storage_ptr_; uint32_t next_item_index_; uint32_t size_; - typename data_type::checkpoint_type data_checkpoint_; + typename storage_type::checkpoint_type storage_checkpoint_; public: - explicit inline __attribute__((always_inline)) Checkpoint(const DecodingTable& decoding_table, uint32_t next_item_index) noexcept - : decoding_table_(&decoding_table), - next_item_index_(next_item_index), - size_(decoding_table.size()), - data_checkpoint_(decoding_table.data().checkpoint()) {} + explicit PROMPP_ALWAYS_INLINE Checkpoint(const storage_type& storage, uint32_t next_item_index, uint32_t size) noexcept + : storage_ptr_(&storage), next_item_index_(next_item_index), size_(size), storage_checkpoint_(storage.checkpoint()) {} [[nodiscard]] PROMPP_ALWAYS_INLINE size_t size() const noexcept { return size_; } [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t next_item_index() const noexcept { return next_item_index_; } - const typename data_type::checkpoint_type& data_checkpoint() const noexcept { return data_checkpoint_; } + const typename storage_type::checkpoint_type& storage_checkpoint() const noexcept { return storage_checkpoint_; } template void save(S& out, const Checkpoint* from = nullptr) const { @@ -156,20 +153,21 @@ class GenericDecodingTable { auto sg1 = std::experimental::scope_exit([&]() { out.exceptions(original_exceptions); }); out.exceptions(std::ifstream::failbit | std::ifstream::badbit); - // write version + // write a version out.put(1); // write mode SerializationMode mode = (from != nullptr) ? SerializationMode::DELTA : SerializationMode::SNAPSHOT; out.put(static_cast(mode)); - // write index of first item in the portion + // write index of the first item in the portion uint32_t first_to_save_i = 0; if (from != nullptr) { first_to_save_i = from->next_item_index_; out.write(reinterpret_cast(&from->next_item_index_), sizeof(from->next_item_index_)); } - const uint32_t first_item_index_in_decoding_table = decoding_table_->next_item_index() - decoding_table_->items().size(); + const uint32_t first_item_index_in_decoding_table = next_item_index_ - size_; + const uint32_t id_offset = first_to_save_i - first_item_index_in_decoding_table; assert(first_to_save_i >= first_item_index_in_decoding_table); // write size @@ -180,15 +178,11 @@ class GenericDecodingTable { return; } - // write items - out.write(reinterpret_cast(&decoding_table_->items()[first_to_save_i - first_item_index_in_decoding_table]), - sizeof(Filament) * size_to_save); - // write data if (from != nullptr) { - data_checkpoint_.save(out, decoding_table_->data(), &from->data_checkpoint_); + storage_checkpoint_.save(out, *storage_ptr_, id_offset, size_to_save, &from->storage_checkpoint_); } else { - data_checkpoint_.save(out, decoding_table_->data()); + storage_checkpoint_.save(out, *storage_ptr_, id_offset, size_to_save); } } @@ -202,7 +196,7 @@ class GenericDecodingTable { // version is written and read by methods put() and get() and they write and read 1 byte size_t res = 1 + sizeof(SerializationMode); - // index of first item in the portion + // index of the first item in the portion uint32_t first_to_save_i = 0; if (from != nullptr) { first_to_save_i = from->next_item_index_; @@ -217,29 +211,26 @@ class GenericDecodingTable { if (!size_to_save) return res; - // items - res += sizeof(Filament) * size_to_save; - // data if (from != nullptr) { - res += data_checkpoint_.save_size(decoding_table_->data(), &from->data_checkpoint_); + res += storage_checkpoint_.save_size(*storage_ptr_, size_to_save, &from->storage_checkpoint_); } else { - res += data_checkpoint_.save_size(decoding_table_->data()); + res += storage_checkpoint_.save_size(*storage_ptr_, size_to_save); } return res; } /** - * ATTENTION! This class persists only pointers to checkpoint. It's a user resposability - * to prevent using delta out of checkpoints scope! + * ATTENTION! This class persists only pointers to checkpoint. It's a user responsibility + * to prevent using delta out of checkpoint scope! */ class Delta { Checkpoint const* from_; Checkpoint const* to_; public: - inline __attribute__((always_inline)) Delta(Checkpoint const& from, Checkpoint const& to) noexcept : from_(&from), to_(&to) {} + PROMPP_ALWAYS_INLINE Delta(Checkpoint const& from, Checkpoint const& to) noexcept : from_(&from), to_(&to) {} [[nodiscard]] bool empty() const noexcept { return from_->size() >= to_->size(); } @@ -259,11 +250,10 @@ class GenericDecodingTable { [[nodiscard]] PROMPP_ALWAYS_INLINE EqualityComparator equality_comparator() const noexcept { return EqualityComparator(this); } [[nodiscard]] PROMPP_ALWAYS_INLINE LessComparator less_comparator() const noexcept { return LessComparator(this); } - data_type data_; - Vector> items_; + storage_type storage_; public: - using checkpoint_type = Checkpoint; + using checkpoint_type = Checkpoint; using delta_type = typename checkpoint_type::Delta; GenericDecodingTable() noexcept = default; @@ -271,49 +261,45 @@ class GenericDecodingTable { template class> class AnotherFilament, template class AnotherVector> requires kIsReadOnly - explicit GenericDecodingTable(const GenericDecodingTable& other) : data_(other.data_), items_(other.items_) {} + explicit GenericDecodingTable(const GenericDecodingTable& other) : storage_(other.storage_) {} GenericDecodingTable(GenericDecodingTable&& other) noexcept = delete; GenericDecodingTable& operator=(const GenericDecodingTable& other) = delete; GenericDecodingTable& operator=(GenericDecodingTable&& other) noexcept = delete; - inline __attribute__((always_inline)) value_type operator[](uint32_t id) const noexcept { return items_[id].composite(data_); } + PROMPP_ALWAYS_INLINE value_type operator[](uint32_t id) const noexcept { return storage_.composite(id); } - [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t size() const noexcept { return items_.size(); } + [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t size() const noexcept { return storage_.count(); } - [[nodiscard]] PROMPP_ALWAYS_INLINE size_t allocated_memory() const noexcept { return mem::allocated_memory(data_) + mem::allocated_memory(items_); } + [[nodiscard]] PROMPP_ALWAYS_INLINE size_t allocated_memory() const noexcept { return mem::allocated_memory(storage_); } - inline __attribute__((always_inline)) const data_type& data() const noexcept { return data_; } - - inline __attribute__((always_inline)) const auto& items() const noexcept { return items_; } + PROMPP_ALWAYS_INLINE storage_type::view_type data_view() const noexcept { return storage_.view(); } template class> class FilamentOther, template class VectorOther> PROMPP_ALWAYS_INLINE void reserve(const GenericDecodingTable& other) { - items_.reserve(other.items_.size()); - data_.reserve(other.data_); + storage_.reserve(other.storage_); } [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t next_item_index() const noexcept { if constexpr (has_next_item_index) { return static_cast(this)->next_item_index_impl(); } else { - return items_.size(); + return storage_.next_item_index(); } } - inline __attribute__((always_inline)) auto checkpoint() const noexcept { return checkpoint_type(*this, items_.size()); } + PROMPP_ALWAYS_INLINE auto checkpoint() const noexcept { return checkpoint_type(storage_, next_item_index(), size()); } - PROMPP_ALWAYS_INLINE void rollback(const checkpoint_type& s) noexcept + PROMPP_ALWAYS_INLINE void rollback(const checkpoint_type& checkpoint) noexcept requires(!kIsReadOnly) { if constexpr (has_rollback) { - static_cast(this)->rollback_impl(s); + static_cast(this)->rollback_impl(checkpoint); } if constexpr (!kIsReadOnly) { - assert(s.size() <= items_.size()); - items_.resize(s.size()); - data_.rollback(s.data_checkpoint()); + assert(checkpoint.size() <= size()); + storage_.rollback(checkpoint.storage_checkpoint()); } } @@ -322,9 +308,10 @@ class GenericDecodingTable { // read version const uint8_t version = in.get(); - // return successfully, if stream is empty - if (in.eof()) + // return successfully if the stream is empty + if (in.eof()) { return; + } // check version if (version != 1) { @@ -338,7 +325,7 @@ class GenericDecodingTable { // read mode const auto mode = static_cast(in.get()); - // read index of first item in the portion, if we are reading wal + // read index of the first item in the portion if we are reading wal uint32_t first_to_load_i = 0; if (mode == SerializationMode::DELTA) { in.read(reinterpret_cast(&first_to_load_i), sizeof(first_to_load_i)); @@ -346,12 +333,12 @@ class GenericDecodingTable { if (first_to_load_i != next_item_index()) { if (mode == SerializationMode::SNAPSHOT) { throw BareBones::Exception(0x7bcd6011e39bbabc, "Attempt to load snapshot into non-empty DecodingTable"); - } else if (first_to_load_i < items_.size()) { + } else if (first_to_load_i < size()) { throw BareBones::Exception(0x3387739a7b4f574a, "Attempt to load segment over existing DecodingTable data"); } else { throw BareBones::Exception(0x4ece66e098927bc6, - "Attempt to load incomplete data from segment, DecodingTable data vector length (%u) is less than segment size(%d)", - items_.size(), first_to_load_i); + "Attempt to load incomplete data from segment, DecodingTable data vector length (%u) is less than segment size(%d)", size(), + first_to_load_i); } } @@ -359,31 +346,19 @@ class GenericDecodingTable { uint32_t size_to_load; in.read(reinterpret_cast(&size_to_load), sizeof(size_to_load)); - // read is completed, if there are no items + // read is completed if there are no items if (!size_to_load) { return; } - // read items - const auto original_size = items_.size(); - auto sg2 = std::experimental::scope_fail([&]() { items_.resize(original_size); }); - items_.resize(items_.size() + size_to_load); - in.read(reinterpret_cast(&items_[original_size]), sizeof(Filament) * size_to_load); - - // read data - auto data_checkpoint = data_.checkpoint(); - auto sg3 = std::experimental::scope_fail([&]() { data_.rollback(data_checkpoint); }); - data_.load(in); - - // validate each item (check ranges, etc) - for (auto i = original_size; i != items_.size(); ++i) { - items_[i].validate(data_); - } + auto storage_checkpoint = storage_.checkpoint(); + auto sg2 = std::experimental::scope_fail([&]() { storage_.rollback(storage_checkpoint); }); + const auto loaded_range = storage_.load(in, size_to_load); - // post processing - if constexpr (has_after_items_load) { - static_assert(noexcept(static_cast(this)->after_items_load_impl(original_size))); - static_cast(this)->after_items_load_impl(original_size); + // post-processing + if constexpr (has_after_items_load) { + static_assert(noexcept(static_cast(this)->after_items_load_impl(loaded_range))); + static_cast(this)->after_items_load_impl(loaded_range); } } @@ -399,123 +374,10 @@ class GenericDecodingTable { return in; } - template - class IteratorSentinel { - InnerIteratorType i_; - - public: - inline __attribute__((always_inline)) explicit IteratorSentinel(InnerIteratorType i = InnerIteratorType()) noexcept : i_(i) {} - - inline __attribute__((always_inline)) const InnerIteratorType& inner_iterator() const noexcept { return i_; } - }; - - template - class ItemIterator { - const GenericDecodingTable* decoding_table_; - InnerIteratorType i_; - - public: - using iterator_category = std::input_iterator_tag; - using value_type = typename Filament::composite_type; - using difference_type = std::ptrdiff_t; - - inline __attribute__((always_inline)) explicit ItemIterator(const GenericDecodingTable* decoding_table = nullptr, - InnerIteratorType i = InnerIteratorType()) noexcept - : decoding_table_(decoding_table), i_(i) {} - - inline __attribute__((always_inline)) ItemIterator& operator++() noexcept { - ++i_; - return *this; - } + PROMPP_ALWAYS_INLINE auto begin() const noexcept { return storage_.view().begin(); } + PROMPP_ALWAYS_INLINE auto end() const noexcept { return storage_.view().end(); } - inline __attribute__((always_inline)) ItemIterator operator++(int) noexcept { - ItemIterator retval = *this; - ++(*this); - return retval; - } - - inline __attribute__((always_inline)) bool operator==(const ItemIterator& other) const noexcept { return i_ == other.i_; } - - template - inline __attribute__((always_inline)) bool operator==(const IteratorSentinel& other) const noexcept { - return i_ == other.inner_iterator(); - } - - inline __attribute__((always_inline)) value_type operator*() const noexcept { return i_->composite(decoding_table_->data()); } - }; - - template - class ItemIDIterator { - const GenericDecodingTable* decoding_table_; - InnerIteratorType i_; - - public: - using iterator_category = std::input_iterator_tag; - using value_type = typename Filament::composite_type; - using difference_type = std::ptrdiff_t; - - inline __attribute__((always_inline)) explicit ItemIDIterator(const GenericDecodingTable* decoding_table = nullptr, - InnerIteratorType i = InnerIteratorType()) noexcept - : decoding_table_(decoding_table), i_(i) {} - inline __attribute__((always_inline)) ItemIDIterator& operator++() { - ++i_; - return *this; - } - - inline __attribute__((always_inline)) ItemIDIterator operator++(int) noexcept { - ItemIDIterator retval = *this; - ++(*this); - return retval; - } - - inline __attribute__((always_inline)) bool operator==(const ItemIDIterator& other) const noexcept { return i_ == other.i_; } - - template - inline __attribute__((always_inline)) bool operator==(const IteratorSentinel& other) const noexcept { - return i_ == other.inner_iterator(); - } - - inline __attribute__((always_inline)) value_type operator*() const noexcept { return (*decoding_table_)[*i_]; } - - [[nodiscard]] PROMPP_ALWAYS_INLINE uint32_t id() const noexcept { return *i_; } - }; - - template - class Resolver { - using inner_iterator_type = InnerIteratorType; - using inner_iterator_sentinel_type = InnerIteratorSentinelType; - - const GenericDecodingTable* decoding_table_; - inner_iterator_type begin_; - inner_iterator_sentinel_type end_; - - public: - using value_type = typename Filament::composite_type; - - inline __attribute__((always_inline)) explicit Resolver(const GenericDecodingTable* decoding_table = nullptr, - inner_iterator_type begin = inner_iterator_type(), - inner_iterator_sentinel_type end = inner_iterator_sentinel_type()) noexcept - : decoding_table_(decoding_table), begin_(begin), end_(end) {} - - inline __attribute__((always_inline)) auto begin() const noexcept { return ItemIDIterator(decoding_table_, begin_); } - inline __attribute__((always_inline)) auto end() const noexcept { return IteratorSentinel(end_); } - [[nodiscard]] PROMPP_ALWAYS_INLINE size_t size() const noexcept { return end_ - begin_; } - }; - - template - inline __attribute__((always_inline)) Resolver resolve(IteratorType begin, IteratorSentinelType end) const noexcept { - return Resolver(this, begin, end); - } - - template - inline __attribute__((always_inline)) auto resolve(const R& range) const noexcept { - return resolve(range.begin(), range.end()); - } - - inline __attribute__((always_inline)) auto begin() const noexcept { return ItemIterator(this, items_.begin()); } - inline __attribute__((always_inline)) auto end() const noexcept { return IteratorSentinel(items_.end()); } - - inline __attribute__((always_inline)) auto remainder_size() const noexcept { return this->data_.remainder_size(); } + PROMPP_ALWAYS_INLINE auto remainder_size() const noexcept { return storage_.remainder_size(); } }; template