13#include <userver/compiler/impl/lifetime.hpp>
15USERVER_NAMESPACE_BEGIN
19namespace any_storage::impl {
21using Offset = std::size_t;
23template <
typename StorageTag>
24inline Offset data_offset{0};
26template <
typename StorageTag>
27inline std::size_t count{0};
29template <
typename StorageTag>
30Offset RegisterData(std::size_t size, std::size_t alignment)
noexcept {
31 data_offset<StorageTag> += (alignment - (data_offset<StorageTag> % alignment)) % alignment;
32 const Offset result = data_offset<StorageTag>;
33 data_offset<StorageTag> += size;
39void AssertStaticRegistrationAllowed();
42void Delete(std::byte* data)
noexcept {
43 std::destroy_at(
reinterpret_cast<T*>(data));
48template <
typename StorageTag>
51template <
typename StorageTag,
typename Data>
52class AnyStorageDataTag final {
54 AnyStorageDataTag()
noexcept
55 : number_(any_storage::impl::count<StorageTag>),
56 offset_(any_storage::impl::RegisterData<StorageTag>(
sizeof(Data),
alignof(Data))) {
57 static_assert(!std::is_reference_v<Data>);
58 static_assert(!std::is_const_v<Data>);
60 __STDCPP_DEFAULT_NEW_ALIGNMENT__ >=
alignof(Data),
61 "Overaligned data members are not supported by AnyStorage"
64 any_storage::impl::AssertStaticRegistrationAllowed();
68 const std::size_t number_;
69 const any_storage::impl::Offset offset_;
71 friend class AnyStorage<StorageTag>;
80template <
typename StorageTag>
81class AnyStorage final {
85 AnyStorage(AnyStorage&& other)
noexcept =
default;
86 AnyStorage& operator=(AnyStorage&& other)
noexcept;
90 template <
typename Data>
91 const Data&
Get(
const AnyStorageDataTag<StorageTag, Data>& tag)
const USERVER_IMPL_LIFETIME_BOUND;
95 template <
typename Data>
96 Data&
Get(
const AnyStorageDataTag<StorageTag, Data>& tag) USERVER_IMPL_LIFETIME_BOUND;
99 template <
typename Data>
100 Data&
Set(AnyStorageDataTag<StorageTag, Data> tag, Data data) USERVER_IMPL_LIFETIME_BOUND;
104 template <
typename Data,
typename... Args>
105 Data&
Emplace(
const AnyStorageDataTag<StorageTag, Data>& tag, Args&&... args) USERVER_IMPL_LIFETIME_BOUND;
109 template <
typename Data>
110 Data*
GetOptional(
const AnyStorageDataTag<StorageTag, Data>& tag)
noexcept USERVER_IMPL_LIFETIME_BOUND;
114 template <
typename Data>
115 const Data*
GetOptional(
const AnyStorageDataTag<StorageTag, Data>& tag)
const noexcept USERVER_IMPL_LIFETIME_BOUND;
118 template <
typename Data>
119 void Erase(
const AnyStorageDataTag<StorageTag, Data>& tag);
123 void (*deleter)(std::byte*)
noexcept;
127 AllocRecord* GetRecords()
noexcept;
129 static any_storage::impl::Offset CalcOffset()
noexcept;
131 void Destroy()
noexcept;
133 std::unique_ptr<std::byte[]> raw_data_;
136template <
typename StorageTag>
137any_storage::impl::Offset AnyStorage<StorageTag>::CalcOffset()
noexcept {
138 const auto offset = any_storage::impl::data_offset<StorageTag>;
139 return ((offset +
alignof(AllocRecord) - 1) /
alignof(AllocRecord)) *
alignof(AllocRecord);
142template <
typename StorageTag>
143AnyStorage<StorageTag>::AnyStorage()
144 : raw_data_(
new std::byte[CalcOffset() + (
sizeof(AllocRecord) * any_storage::impl::count<StorageTag>)])
146 static_assert(std::is_trivial_v<AllocRecord>);
149 auto records = GetRecords();
150 for (std::size_t i = 0; i < any_storage::impl::count<StorageTag>; i++) {
151 auto& record = records[i];
152 record.deleter =
nullptr;
156template <
typename StorageTag>
157AnyStorage<StorageTag>& AnyStorage<StorageTag>::operator=(AnyStorage&& other)
noexcept {
158 if (
this != &other) {
160 raw_data_ = std::move(other.raw_data_);
165template <
typename StorageTag>
166AnyStorage<StorageTag>::~AnyStorage() {
170template <
typename StorageTag>
171void AnyStorage<StorageTag>::Destroy()
noexcept {
177 auto records = GetRecords();
179 for (std::size_t i = 0; i < any_storage::impl::count<StorageTag>; i++) {
180 auto& record = records[i];
181 if (record.deleter) {
182 record.deleter(&raw_data_[record.offset]);
188template <
typename Data>
190 USERVER_IMPL_LIFETIME_BOUND {
191 auto number = tag.number_;
192 if (!GetRecords()[number].deleter) {
193 return Emplace(tag, std::move(data));
196 auto offset = tag.offset_;
197 return *
reinterpret_cast<Data*>(&raw_data_[offset]) = std::move(data);
201template <
typename Data,
typename... Args>
203 USERVER_IMPL_LIFETIME_BOUND {
204 auto number = tag.number_;
205 auto& record = GetRecords()[number];
206 if (record.deleter) {
207 record.deleter(&raw_data_[tag.offset_]);
210 auto offset = tag.offset_;
211 auto ptr =
new (&raw_data_[offset]) Data(std::forward<Args>(args)...);
212 record = {&any_storage::impl::Delete<Data>, offset};
217template <
typename Data>
219 auto ptr = GetOptional(tag);
223 throw std::runtime_error(
"No data");
227template <
typename Data>
229)
const USERVER_IMPL_LIFETIME_BOUND {
231 return const_cast<AnyStorage<StorageTag>*>(
this)->Get<Data>(tag);
235template <
typename Data>
237)
noexcept USERVER_IMPL_LIFETIME_BOUND {
238 auto number = tag.number_;
239 auto offset = tag.offset_;
240 if (!GetRecords()[number].deleter) {
243 return reinterpret_cast<Data*>(&raw_data_[offset]);
247template <
typename Data>
249)
const noexcept USERVER_IMPL_LIFETIME_BOUND {
251 return const_cast<AnyStorage*>(
this)->GetOptional<Data>(tag);
254template <
typename StorageTag>
255typename AnyStorage<StorageTag>::AllocRecord* AnyStorage<StorageTag>::GetRecords()
noexcept {
256 return reinterpret_cast<AllocRecord*>(&raw_data_[CalcOffset()]);