Files
UniversalContainer/include/universal_container/ring_block.hpp
T
Efim Beshmenev aea6e330e3 Realisation v1
2026-08-11 22:19:34 +03:00

185 lines
5.5 KiB
C++

#pragma once
#include <cassert>
#include <cstddef>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace uc::detail {
// A fixed-capacity circular leaf. Empty slots are represented explicitly so
// the block also works for non-default-constructible and non-trivial values.
// Insert/erase shifts the shorter side and therefore moves at most size()/2
// elements. offset_ is the physical slot containing logical element zero.
template <class T>
class RingBlock {
public:
explicit RingBlock(std::size_t capacity = 256)
: slots_(capacity) {
if (capacity < 2) {
throw std::invalid_argument("RingBlock capacity must be at least two");
}
}
RingBlock(const RingBlock&) = default;
RingBlock(RingBlock&&) noexcept = default;
RingBlock& operator=(const RingBlock&) = default;
RingBlock& operator=(RingBlock&&) noexcept = default;
~RingBlock() = default;
[[nodiscard]] std::size_t size() const noexcept { return size_; }
[[nodiscard]] std::size_t capacity() const noexcept { return slots_.size(); }
[[nodiscard]] bool empty() const noexcept { return size_ == 0; }
[[nodiscard]] bool full() const noexcept { return size_ == capacity(); }
[[nodiscard]] std::size_t offset() const noexcept { return offset_; }
T& operator[](std::size_t index) noexcept {
assert(index < size_);
return *slots_[physical(index)];
}
const T& operator[](std::size_t index) const noexcept {
assert(index < size_);
return *slots_[physical(index)];
}
T& at(std::size_t index) {
if (index >= size_) {
throw std::out_of_range("RingBlock index out of range");
}
return (*this)[index];
}
const T& at(std::size_t index) const {
if (index >= size_) {
throw std::out_of_range("RingBlock index out of range");
}
return (*this)[index];
}
template <class U>
void insert(std::size_t index, U&& value) {
if (index > size_) {
throw std::out_of_range("RingBlock insertion index out of range");
}
if (full()) {
throw std::length_error("RingBlock is full");
}
const auto old_offset = offset_;
if (index < size_ / 2) {
const auto new_offset = decrement(old_offset);
for (std::size_t i = 0; i < index; ++i) {
move_slot((new_offset + i) % capacity(), (old_offset + i) % capacity());
}
offset_ = new_offset;
} else {
for (std::size_t i = size_; i > index; --i) {
move_slot((old_offset + i) % capacity(),
(old_offset + i - 1) % capacity());
}
}
slots_[physical(index)].emplace(std::forward<U>(value));
++size_;
assert_invariant();
}
template <class... Args>
T& emplace(std::size_t index, Args&&... args) {
T value(std::forward<Args>(args)...);
insert(index, std::move(value));
return (*this)[index];
}
template <class U>
void push_back(U&& value) {
insert(size_, std::forward<U>(value));
}
T erase(std::size_t index) {
if (index >= size_) {
throw std::out_of_range("RingBlock erase index out of range");
}
T removed(std::move((*this)[index]));
const auto old_offset = offset_;
slots_[physical(index)].reset();
if (index < size_ / 2) {
for (std::size_t i = index; i > 0; --i) {
move_slot((old_offset + i) % capacity(),
(old_offset + i - 1) % capacity());
}
offset_ = increment(old_offset);
} else {
for (std::size_t i = index; i + 1 < size_; ++i) {
move_slot((old_offset + i) % capacity(),
(old_offset + i + 1) % capacity());
}
}
--size_;
if (size_ == 0) {
offset_ = 0;
}
assert_invariant();
return removed;
}
void clear() noexcept {
for (auto& slot : slots_) {
slot.reset();
}
size_ = 0;
offset_ = 0;
}
[[nodiscard]] std::size_t allocated_bytes() const noexcept {
return slots_.capacity() * sizeof(typename decltype(slots_)::value_type);
}
private:
[[nodiscard]] std::size_t physical(std::size_t logical) const noexcept {
return (offset_ + logical) % capacity();
}
[[nodiscard]] std::size_t increment(std::size_t slot) const noexcept {
return slot + 1 == capacity() ? 0 : slot + 1;
}
[[nodiscard]] std::size_t decrement(std::size_t slot) const noexcept {
return slot == 0 ? capacity() - 1 : slot - 1;
}
void move_slot(std::size_t destination, std::size_t source) {
assert(!slots_[destination].has_value());
assert(slots_[source].has_value());
slots_[destination].emplace(std::move(*slots_[source]));
slots_[source].reset();
}
void assert_invariant() const noexcept {
#ifndef NDEBUG
std::size_t occupied = 0;
for (const auto& slot : slots_) {
occupied += slot.has_value() ? 1U : 0U;
}
assert(occupied == size_);
for (std::size_t i = 0; i < size_; ++i) {
assert(slots_[physical(i)].has_value());
}
#endif
}
std::vector<std::optional<T>> slots_;
std::size_t offset_ = 0;
std::size_t size_ = 0;
};
} // namespace uc::detail