Stand/Stand/AtomicVector.hpp
2024-10-16 11:20:42 +08:00

259 lines
4.4 KiB
C++

#pragma once
#include <memory>
#include <vector>
namespace Stand
{
template <typename T>
class AtomicVector
{
private:
struct Data
{
T* arr;
const size_t size;
const unsigned mutation;
explicit Data(size_t size, unsigned mutation)
: arr((T*)malloc(sizeof(T) * size)), size(size), mutation(mutation)
{
}
~Data() noexcept
{
for (size_t i = 0; i != size; ++i)
{
std::destroy_at(&arr[i]);
}
free(arr);
}
};
std::atomic<unsigned> mutation;
std::shared_ptr<Data> data;
public:
struct Iterator
{
std::shared_ptr<Data> data{};
size_t i = 0;
[[nodiscard]] bool isEndIterator() const noexcept
{
return !data;
}
[[nodiscard]] bool hasReachedEnd() const noexcept
{
return i == data->size;
}
[[nodiscard]] bool operator ==(const Iterator& b) const noexcept
{
if (b.isEndIterator())
{
return hasReachedEnd();
}
if (isEndIterator())
{
return b.hasReachedEnd();
}
return data == b.data
&& i == b.i
;
}
[[nodiscard]] bool operator !=(const Iterator& b) const noexcept
{
return !operator==(b);
}
[[nodiscard]] T& operator*() const noexcept
{
return data->arr[i];
}
[[nodiscard]] T* operator->() const noexcept
{
return &data->arr[i];
}
[[nodiscard]] T* get() const noexcept
{
return &data->arr[i];
}
Iterator& operator++() noexcept
{
++i;
return *this;
}
void operator +=(size_t off) noexcept
{
i += off;
}
[[nodiscard]] Iterator operator+(size_t off) const noexcept
{
Iterator it(*this);
it += off;
return it;
}
[[nodiscard]] size_t operator-(const Iterator& b) const noexcept
{
if (isEndIterator())
{
return b.data->size;
}
return i - b.i;
}
};
AtomicVector(std::initializer_list<T>&& il)
{
operator=(std::move(il));
}
void operator =(std::initializer_list<T>&& il)
{
if (il.size() == 0)
{
clear();
}
else
{
data = std::make_shared<Data>(il.size(), ++mutation);
size_t i = 0;
for (auto&& elm : il)
{
std::construct_at<T>(&data->arr[i++], std::move(elm));
}
}
}
[[nodiscard]] Iterator begin() const noexcept
{
return Iterator{ data };
}
[[nodiscard]] Iterator cbegin() const noexcept
{
return Iterator{ data };
}
[[nodiscard]] Iterator end() const noexcept
{
return Iterator{};
}
[[nodiscard]] Iterator cend() const noexcept
{
return Iterator{};
}
[[nodiscard]] bool empty() const noexcept
{
return !data;
}
[[nodiscard]] size_t size() const noexcept
{
if (auto d = data; d)
{
return d->size;
}
return 0;
}
void clear() noexcept
{
++mutation;
data.reset();
}
T& emplace_back(T&& elm)
{
std::shared_ptr<Data> d;
size_t size;
unsigned mutation;
std::shared_ptr<Data> r;
T* emplaced;
do
{
d = data;
if (d)
{
size = d->size;
mutation = d->mutation;
}
else
{
size = 0;
mutation = this->mutation.load();
}
r = std::make_shared<Data>(size + 1, mutation + 1);
for (size_t i = 0; i != size; ++i)
{
std::construct_at(&r->arr[i], d->arr[i]);
}
emplaced = std::construct_at(&r->arr[size], std::move(elm));
} while (!this->mutation.compare_exchange_strong(mutation, r->mutation));
this->data = std::move(r);
return *emplaced;
}
Iterator erase(const Iterator& it)
{
erase(it.get());
return begin() + it.i;
}
void erase(const T* target)
{
std::shared_ptr<Data> d;
std::shared_ptr<Data> r;
unsigned mutation;
do
{
d = data;
if (!d)
{
// No data, so no items, so nothing to erase
return;
}
if (d->size == 1)
{
if (&d->arr[0] == target)
{
clear();
}
return;
}
bool had_target = false;
r = std::make_shared<Data>(d->size - 1, d->mutation + 1);
size_t ri = 0;
for (size_t di = 0; di != d->size; ++di)
{
if (&d->arr[di] == target)
{
had_target = true;
}
else
{
std::construct_at(&r->arr[ri++], d->arr[di]);
}
}
if (!had_target)
{
// Data didn't have the erase target, so we can go home early
return;
}
} while (mutation = d->mutation, !this->mutation.compare_exchange_strong(mutation, r->mutation));
this->data = std::move(r);
}
};
}