diff --git a/source/mijin/container/sorted_vector.hpp b/source/mijin/container/sorted_vector.hpp new file mode 100644 index 0000000..dc54905 --- /dev/null +++ b/source/mijin/container/sorted_vector.hpp @@ -0,0 +1,126 @@ + +#pragma once + +#if !defined(MIJIN_CONTAINER_SORTED_VECTOR_HPP_INCLUDED) +#define MIJIN_CONTAINER_SORTED_VECTOR_HPP_INCLUDED 1 + +#include +#include +#include +#include "../util/functional.hpp" + +namespace mijin +{ +template TProj = std::identity, + ordering> TOrder = std::less>> +class SortedVector +{ +public: + using key_t = projected_t; + using base_t = std::vector; + using iterator = base_t::const_iterator; + using const_iterator = iterator; + using reverse_iterator = base_t::const_reverse_iterator; + using const_reverse_iterator = reverse_iterator; +private: + std::vector values_; + [[no_unique_address]] TProj proj_; + [[no_unique_address]] TOrder order_; +public: + explicit SortedVector(TProj proj = {}, TOrder order = {}) noexcept + : proj_(std::move(proj)), order_(std::move(order)) {} + explicit SortedVector(TOrder order) noexcept + : order_(std::move(order)) {} + SortedVector(const SortedVector&) = default; + SortedVector(SortedVector&&) noexcept = default; + explicit SortedVector(std::vector values) : values_(std::move(values)) + { + processValues(); + } + SortedVector& operator=(const SortedVector&) = default; + SortedVector& operator=(SortedVector&&) noexcept = default; + + // --- iterators --- + + [[nodiscard]] + const_iterator begin() const noexcept { return values_.begin(); } + + [[nodiscard]] + const_iterator cbegin() const noexcept { return values_.cbegin(); } + + [[nodiscard]] + const_reverse_iterator rbegin() const noexcept { return values_.rbegin(); } + + [[nodiscard]] + const_reverse_iterator crbegin() const noexcept { return values_.crbegin(); } + + [[nodiscard]] + const_iterator end() const noexcept { return values_.end(); } + + [[nodiscard]] + const_iterator cend() const noexcept { return values_.cend(); } + + [[nodiscard]] + const_reverse_iterator rend() const noexcept { return values_.rend(); } + + [[nodiscard]] + const_reverse_iterator crend() const noexcept { return values_.crend(); } + + // --- reading --- + [[nodiscard]] + const T& at(std::size_t idx) const noexcept { return values_.at(idx); } + + [[nodiscard]] + const T& operator[](std::size_t idx) const noexcept { return at(idx); } + + [[nodiscard]] + std::size_t size() const noexcept { return values_.size(); } + + [[nodiscard]] + bool empty() const noexcept { return values_.empty(); } + + [[nodiscard]] + iterator find(key_t key) const noexcept + { + auto it = std::ranges::lower_bound(values_, key, order_, proj_); + if (it == values_.end() || std::invoke(proj_, *it) != key) { + return values_.end(); + } + return it; + } + + // --- writing --- + void clear() + { + values_.clear(); + } + + void emplace(std::vector values) + { + values_ = std::move(values); + processValues(); + } + + template + void appendRange(TRange&& range) + { + values_.append_range(std::forward(range)); + processValues(); + } + + [[nodiscard]] + std::vector&& release() && noexcept + { + return std::exchange(values_, {}); + } +private: + void processValues() + { + values_.shrink_to_fit(); + std::ranges::sort(values_, order_, proj_); + } +}; +} // namespace mijin + +#endif // MIJIN_CONTAINER_SORTED_VECTOR_HPP_INCLUDED