// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2023 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_ALLOCATOR_FIXED_H namespace ircd::allocator { template struct fixed; } /// The fixed allocator creates a block of data with a size known at compile- /// time. This structure itself is the state object for the actual allocator /// instance used in the container. Create an instance of this structure, /// perhaps on your stack. Then specify the ircd::allocator::fixed::allocator /// in the template for the container. Then pass a reference to the state /// object as an argument to the container when constructing. STL containers /// have an overloaded constructor for this when specializing the allocator /// template as we are here. /// template struct ircd::allocator::fixed :state { struct allocator; using value = std::aligned_storage; std::array avail {{0}}; std::array buf; public: bool in_range(const T *const &ptr) const { const auto base(reinterpret_cast(buf.data())); return ptr >= base && ptr < base + MAX; } allocator operator()(); operator allocator(); fixed() { static_cast(*this) = { MAX, avail.data() }; } }; /// The actual allocator template as used by the container. /// /// This has to be a very light, small and copyable object which cannot hold /// our actual memory or state (lest we just use dynamic allocation for that!) /// which means we have to pass this a reference to our ircd::allocator::fixed /// instance. We can do that through the container's custom-allocator overload /// at its construction. /// template struct ircd::allocator::fixed::allocator { using value_type = T; using pointer = T *; using const_pointer = const T *; using reference = T &; using const_reference = const T &; using size_type = std::size_t; using difference_type = std::ptrdiff_t; fixed *s; public: template struct rebind { using other = typename fixed::allocator; }; size_type max_size() const { return SIZE; } auto address(reference x) const { return &x; } auto address(const_reference x) const { return &x; } pointer __attribute__((malloc, warn_unused_result)) allocate(std::nothrow_t, const size_type &n, const const_pointer &hint = nullptr) { const auto base(reinterpret_cast(s->buf.data())); const uint hintpos(hint? uint(hint - base) : uint(-1)); const pointer ret(base + s->state::allocate(std::nothrow, n, hintpos)); return s->in_range(ret)? ret : nullptr; } pointer __attribute__((malloc, returns_nonnull, warn_unused_result)) allocate(const size_type &n, const const_pointer &hint = nullptr) { const auto base(reinterpret_cast(s->buf.data())); const uint hintpos(hint? uint(hint - base) : uint(-1)); return base + s->state::allocate(n, hintpos); } void deallocate(const pointer &p, const size_type &n) { const auto base(reinterpret_cast(s->buf.data())); s->state::deallocate(p - base, n); } template allocator(const typename fixed::allocator &s) noexcept :s{reinterpret_cast *>(s.s)} { static_assert(OTHER_SIZE == SIZE); } allocator(fixed &s) noexcept :s{&s} {} allocator(allocator &&) = default; allocator(const allocator &) = default; friend bool operator==(const allocator &a, const allocator &b) { return &a == &b; } friend bool operator!=(const allocator &a, const allocator &b) { return &a == &b; } }; template inline typename ircd::allocator::fixed::allocator ircd::allocator::fixed::operator()() { return ircd::allocator::fixed::allocator(*this); } template inline ircd::allocator::fixed::operator allocator() { return ircd::allocator::fixed::allocator(*this); }