#pragma once #include "defines.h" #include "ptrwrap.h" #include #include namespace snmalloc { template typename Ptr = Pointer> class CDLLNodeBase { /** * to_next is used to handle a zero initialised data structure. * This means that `is_empty` works even when the constructor hasn't * been run. */ ptrdiff_t to_next = 0; protected: void set_next(Ptr c) { to_next = pointer_diff_signed(Ptr>(this), c); } public: SNMALLOC_FAST_PATH bool is_empty() { return to_next == 0; } SNMALLOC_FAST_PATH Ptr get_next() { return static_cast>(pointer_offset_signed(this, to_next)); } }; template typename Ptr = Pointer> class CDLLNodeBaseNext { /** * Like to_next in the pointer-less case, this version still works with * zero-initialized data structure. To make `is_empty` work in this case, * next is set to `nullptr` rather than `this` when the list is empty. * */ Ptr next = nullptr; protected: void set_next(Ptr c) { next = (c == static_cast>(this)) ? nullptr : c; } public: SNMALLOC_FAST_PATH bool is_empty() { return next == nullptr; } SNMALLOC_FAST_PATH Ptr get_next() { return next == nullptr ? static_cast>(this) : next; } }; template typename Ptr = Pointer> using CDLLNodeParent = std::conditional_t< aal_supports, CDLLNodeBaseNext, CDLLNodeBase>; /** * Special class for cyclic doubly linked non-empty linked list * * This code assumes there is always one element in the list. The client * must ensure there is a sentinal element. */ template typename Ptr = Pointer> class CDLLNode : public CDLLNodeParent, Ptr> { Ptr prev = nullptr; public: /** * Single element cyclic list. This is the empty case. */ CDLLNode() { this->set_next(Ptr(this)); prev = Ptr(this); } /** * Removes this element from the cyclic list is it part of. */ SNMALLOC_FAST_PATH void remove() { SNMALLOC_ASSERT(!this->is_empty()); debug_check(); this->get_next()->prev = prev; prev->set_next(this->get_next()); // As this is no longer in the list, check invariant for // neighbouring element. this->get_next()->debug_check(); #ifndef NDEBUG this->set_next(nullptr); prev = nullptr; #endif } /** * Nulls the previous pointer * * The Meta-slab uses nullptr in prev to mean that it is not part of a * size class list. **/ void null_prev() { prev = nullptr; } SNMALLOC_FAST_PATH Ptr get_prev() { return prev; } SNMALLOC_FAST_PATH void insert_next(Ptr item) { debug_check(); item->set_next(this->get_next()); this->get_next()->prev = item; item->prev = this; set_next(item); debug_check(); } SNMALLOC_FAST_PATH void insert_prev(Ptr item) { debug_check(); item->prev = prev; prev->set_next(item); item->set_next(Ptr(this)); prev = item; debug_check(); } /** * Checks the lists invariants * x->next->prev = x * for all x in the list. */ void debug_check() { #ifndef NDEBUG Ptr item = this->get_next(); auto p = Ptr(this); do { SNMALLOC_ASSERT(item->prev == p); p = item; item = item->get_next(); } while (item != Ptr(this)); #endif } }; } // namespace snmalloc