16#include <unordered_map>
49 static std::uint64_t
sum(std::uint64_t a, std::uint64_t b)
noexcept {
50 const auto max = std::numeric_limits<std::uint64_t>::max();
51 return b > max - a ? max : a + b;
62 : std::numeric_limits<std::uint64_t>::max();
72 std::vector<Identity> identities_;
73 void add(std::uint64_t& target, std::uint64_t bytes)
noexcept {
74 if (bytes > std::numeric_limits<std::uint64_t>::max() - target)
75 usage_.overflow =
true;
78 bool claim_identity(
const void* address,
const void* kind)
noexcept {
79 if (!address)
return false;
80 for (
const auto& identity : identities_)
81 if (identity.address == address && identity.kind == kind)
return false;
82 try { identities_.push_back({address, kind}); }
88 usage_.unsupported |=
static_cast<std::uint64_t
>(reason);
90 std::uint64_t
product(std::uint64_t count, std::uint64_t width)
noexcept {
91 if (width && count > std::numeric_limits<std::uint64_t>::max() / width) {
92 usage_.overflow =
true;
93 return std::numeric_limits<std::uint64_t>::max();
97 bool claim(
const void* identity)
noexcept {
98 return claim_identity(identity,
nullptr);
100 template<
class T>
bool visit(
const T&
object)
noexcept {
104 static const char kind = 0;
105 return claim_identity(&
object, &kind);
107 void metadata(std::uint64_t bytes)
noexcept { add(usage_.resident_metadata_bytes, bytes); }
108 void payload(std::uint64_t bytes)
noexcept { add(usage_.resident_payload_bytes, bytes); }
109 void backing(std::uint64_t bytes)
noexcept { add(usage_.backing_bytes, bytes); }
110 void logical(std::uint64_t bytes)
noexcept { add(usage_.logical_payload_bytes, bytes); }
112 usage_.workspace_bytes = std::max(usage_.workspace_bytes, bytes);
115 auto result = usage_;
118 const auto max = std::numeric_limits<std::uint64_t>::max();
119 result.overflow |= result.resident_payload_bytes > max - result.resident_metadata_bytes;
120 result.overflow |= result.backing_bytes > max - result.resident_bytes();
145template<
class T,
class Element = T>
149 void retain() noexcept { ledger_->
references.fetch_add(1, std::memory_order_relaxed); }
150 void release() noexcept {
151 if (ledger_->
references.fetch_sub(1, std::memory_order_acq_rel) == 1)
delete ledger_;
167 if (ledger_ != other.ledger_) {
168 other.ledger_->references.fetch_add(1, std::memory_order_relaxed);
170 ledger_ = other.ledger_;
177 if (count > std::numeric_limits<std::size_t>::max() /
sizeof(T))
178 throw std::bad_array_new_length();
179 T* data = std::allocator<T>{}.allocate(count);
181 data, count *
sizeof(T), std::is_same_v<T, Element>,
true};
183 auto record = std::find_if(
184 ledger_->allocations.begin(), ledger_->allocations.end(),
186 return !candidate.active;
188 if (record != ledger_->allocations.end()) {
191 ledger_->allocations.push_back(entry);
193 }
catch (...) { std::allocator<T>{}.deallocate(data, count);
throw; }
197 for (
auto& record : ledger_->allocations) {
198 if (!record.active || record.address != data)
continue;
200 std::allocator<T>{}.deallocate(data, count);
206 if (!out.claim(ledger_))
return;
207 out.metadata(
sizeof(*ledger_));
209 out.metadata(out.product(ledger_->allocations.capacity(),
211 for (
const auto& record : ledger_->allocations) {
212 if (!record.active)
continue;
215 if (record.payload) out.payload(record.bytes);
216 else out.metadata(record.bytes);
222 bool contains(
const void*
object, std::size_t bytes)
const noexcept {
223 const auto address =
reinterpret_cast<std::uintptr_t
>(object);
224 for (
const auto& record : ledger_->allocations) {
225 if (!record.active)
continue;
226 const auto base =
reinterpret_cast<std::uintptr_t
>(record.address);
227 if (address >= base && address - base <= record.bytes
228 && bytes <= record.bytes - (address - base))
return true;
234 return ledger_ == other.ledger_;
238 return !(*
this == other);
242template<
class T,
class =
void>
245 if constexpr (!std::is_arithmetic_v<T> && !std::is_enum_v<T>
246 && !std::is_same_v<T, std::nullptr_t>)
252 std::declval<const T&>().observe_owned_storage(std::declval<OwnedStorage&>()))>> {
254 try { value.observe_owned_storage(out); }
269 const auto object =
reinterpret_cast<std::uintptr_t
>(&value);
270 const auto data =
reinterpret_cast<std::uintptr_t
>(value.data());
273 if (data >=
object && data -
object <
sizeof(value))
return;
274 if (out.claim(value.data())) {
275 out.payload(out.product(value.capacity(),
sizeof(
char)));
282template<
class T,
class A>
285 if (!out.visit(value))
return;
287 value.get_allocator().observe_owned_storage(out);
288 }
else if constexpr (std::is_same_v<A, std::allocator<T>>) {
289 if constexpr (std::is_same_v<T, bool>) {
290 constexpr auto bits = std::numeric_limits<unsigned char>::digits;
291 out.payload(value.capacity() / bits + (value.capacity() % bits != 0));
292 }
else if (out.claim(value.data())) {
293 out.payload(out.product(value.capacity(),
sizeof(T)));
296 if constexpr (!std::is_arithmetic_v<T> && !std::is_enum_v<T>)
300template<
class T,
class A>
303 if (!out.visit(value))
return;
305 value.get_allocator().observe_owned_storage(out);
307 if constexpr (!std::is_arithmetic_v<T> && !std::is_enum_v<T>)
311template<
class K,
class V,
class H,
class E,
class A>
314 if (!out.visit(value))
return;
316 value.get_allocator().observe_owned_storage(out);
327template<
class A,
class B>
333template<
class T, std::
size_t N>
353 using Base = std::deque<T, OwnedAllocator<T>>;
357 OwnedDeque(std::size_t count,
const T& value) : Base(count, value) {}
361 Base::operator=(other);
365 if (
this != &other) Base::operator=(std::move(other));
372template<
class K,
class V>
374 OwnedAllocator<std::pair<const K, V>>> {
376 using Base = std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, Allocator>;
382 Base::operator=(other);
386 if (
this != &other) Base::operator=(std::move(other));
OwnedAllocator(const OwnedAllocator &other) noexcept
std::false_type is_always_equal
std::true_type propagate_on_container_swap
void observe_owned_storage(OwnedStorage &out) const noexcept
OwnedAllocator select_on_container_copy_construction() const
bool operator!=(const OwnedAllocator< U, Element > &other) const noexcept
bool contains(const void *object, std::size_t bytes) const noexcept
void deallocate(T *data, std::size_t count) noexcept
T * allocate(std::size_t count)
std::false_type propagate_on_container_move_assignment
bool operator==(const OwnedAllocator< U, Element > &other) const noexcept
OwnedAllocator & operator=(const OwnedAllocator &other) noexcept
friend class OwnedAllocator
OwnedAllocator(const OwnedAllocator< U, Element > &other) noexcept
std::false_type propagate_on_container_copy_assignment
OwnedDeque(OwnedDeque &&other)
OwnedDeque & operator=(const OwnedDeque &other)
OwnedDeque(std::size_t count)
OwnedDeque(std::size_t count, const T &value)
void observe_owned_storage(OwnedStorage &out) const noexcept
OwnedDeque & operator=(OwnedDeque &&other)
OwnedDeque(const OwnedDeque &other)
void logical(std::uint64_t bytes) noexcept
bool visit(const T &object) noexcept
void backing(std::uint64_t bytes) noexcept
void incomplete(OwnedStorageUnsupported reason) noexcept
OwnedStorageUsage usage() const noexcept
void metadata(std::uint64_t bytes) noexcept
void workspace(std::uint64_t bytes) noexcept
std::uint64_t product(std::uint64_t count, std::uint64_t width) noexcept
void payload(std::uint64_t bytes) noexcept
bool claim(const void *identity) noexcept
OwnedUnorderedMap & operator=(OwnedUnorderedMap &&other)
OwnedUnorderedMap & operator=(const OwnedUnorderedMap &other)
OwnedUnorderedMap()=default
void observe_owned_storage(OwnedStorage &out) const noexcept
OwnedUnorderedMap(OwnedUnorderedMap &&other)
OwnedUnorderedMap(const OwnedUnorderedMap &other)
void observe_owned_storage(OwnedStorage &out, const T &value) noexcept
void observe_owned_members(OwnedStorage &out, const T &... values) noexcept
OwnedAllocator< U, Element > other
static void observe(OwnedStorage &out, const T &value) noexcept
static void observe(OwnedStorage &out, const std::array< T, N > &value) noexcept
static void observe(OwnedStorage &out, const std::deque< T, A > &value) noexcept
static void observe(OwnedStorage &out, const std::optional< T > &value) noexcept
static void observe(OwnedStorage &out, const std::pair< A, B > &value) noexcept
static void observe(OwnedStorage &out, const std::string &value) noexcept
static void observe(OwnedStorage &out, const std::tuple< T... > &value) noexcept
static void observe(OwnedStorage &out, const std::unordered_map< K, V, H, E, A > &value) noexcept
static void observe(OwnedStorage &out, const std::vector< T, A > &value) noexcept
static void observe(OwnedStorage &out, const T &) noexcept
std::uint64_t resident_payload_bytes
std::uint64_t observer_workspace_bytes
std::uint64_t logical_payload_bytes
std::uint64_t unsupported
std::uint64_t resident_metadata_bytes
std::uint64_t storage_bytes() const noexcept
std::uint64_t backing_bytes
static std::uint64_t sum(std::uint64_t a, std::uint64_t b) noexcept
std::uint64_t resident_bytes() const noexcept
std::uint64_t workspace_bytes
bool complete() const noexcept
std::vector< OwnedAllocation > allocations
std::atomic< std::size_t > references