| 1 | #ifndef SEAD_OFFSET_LIST_H_ |
| 2 | #define SEAD_OFFSET_LIST_H_ |
| 3 | |
| 4 | #include <basis/seadRawPrint.h> |
| 5 | #include <basis/seadTypes.h> |
| 6 | #include <container/seadListImpl.h> |
| 7 | #include <prim/seadPtrUtil.h> |
| 8 | |
| 9 | namespace sead |
| 10 | { |
| 11 | template <typename T> |
| 12 | class OffsetList : public ListImpl |
| 13 | { |
| 14 | public: |
| 15 | OffsetList() = default; |
| 16 | |
| 17 | void initOffset(s32 offset) { mOffset = offset; } |
| 18 | |
| 19 | void clear() { ListImpl::clear(); } |
| 20 | |
| 21 | void pushBack(T* item) |
| 22 | { |
| 23 | SEAD_ASSERT(mOffset >= 0); |
| 24 | ListImpl::pushBack(item: objToListNode(item)); |
| 25 | } |
| 26 | |
| 27 | void pushFront(T* item) |
| 28 | { |
| 29 | SEAD_ASSERT(mOffset >= 0); |
| 30 | ListImpl::pushFront(item: objToListNode(item)); |
| 31 | } |
| 32 | |
| 33 | T* popBack() { return listNodeToObjWithNullCheck(ListImpl::popBack()); } |
| 34 | |
| 35 | T* popFront() { return listNodeToObjWithNullCheck(ListImpl::popFront()); } |
| 36 | |
| 37 | void insertBefore(const T* obj, T* obj_to_insert) |
| 38 | { |
| 39 | ListImpl::insertBefore(node: const_cast<ListNode*>(objToListNode(obj)), |
| 40 | node_to_insert: objToListNode(obj_to_insert)); |
| 41 | } |
| 42 | void insertAfter(const T* obj, T* obj_to_insert) |
| 43 | { |
| 44 | ListImpl::insertAfter(node: const_cast<ListNode*>(objToListNode(obj)), |
| 45 | node_to_insert: objToListNode(obj_to_insert)); |
| 46 | } |
| 47 | |
| 48 | void erase(T* item) { ListImpl::erase(item: objToListNode(item)); } |
| 49 | |
| 50 | T* front() const { return listNodeToObjWithNullCheck(ListImpl::front()); } |
| 51 | |
| 52 | T* back() const { return listNodeToObjWithNullCheck(ListImpl::back()); } |
| 53 | |
| 54 | T* prev(const T* obj) const |
| 55 | { |
| 56 | ListNode* prev_node = objToListNode(obj)->prev(); |
| 57 | if (prev_node == &mStartEnd) |
| 58 | return nullptr; |
| 59 | return listNodeToObj(prev_node); |
| 60 | } |
| 61 | |
| 62 | T* next(const T* obj) const |
| 63 | { |
| 64 | ListNode* next_node = objToListNode(obj)->next(); |
| 65 | if (next_node == &mStartEnd) |
| 66 | return nullptr; |
| 67 | return listNodeToObj(next_node); |
| 68 | } |
| 69 | |
| 70 | T* nth(s32 n) const { return listNodeToObjWithNullCheck(ListImpl::nth(n)); } |
| 71 | |
| 72 | s32 indexOf(const T* obj) const { return ListImpl::indexOf(objToListNode(obj)); } |
| 73 | |
| 74 | bool isNodeLinked(const T* obj) const { return objToListNode(obj)->isLinked(); } |
| 75 | |
| 76 | void swap(T* obj1, T* obj2) { ListImpl::swap(n1: objToListNode(obj1), n2: objToListNode(obj2)); } |
| 77 | void moveAfter(T* basis, T* obj) |
| 78 | { |
| 79 | ListImpl::moveAfter(basis: objToListNode(basis), n: objToListNode(obj)); |
| 80 | } |
| 81 | void moveBefore(T* basis, T* obj) |
| 82 | { |
| 83 | ListImpl::moveBefore(basis: objToListNode(basis), n: objToListNode(obj)); |
| 84 | } |
| 85 | |
| 86 | using CompareCallback = int (*)(const T*, const T*); |
| 87 | |
| 88 | void sort() { sort(compareT); } |
| 89 | void sort(CompareCallback cmp) { ListImpl::sort<T>(mOffset, cmp); } |
| 90 | void mergeSort() { mergeSort(compareT); } |
| 91 | void mergeSort(CompareCallback cmp) { ListImpl::mergeSort<T>(mOffset, cmp); } |
| 92 | |
| 93 | T* find(const T* obj) const { return find(obj, compareT); } |
| 94 | T* find(const T* obj, CompareCallback cmp) const |
| 95 | { |
| 96 | return listNodeToObj(ListImpl::find(ptr: obj, offset: mOffset, cmp)); |
| 97 | } |
| 98 | |
| 99 | void uniq() { uniq(compareT); } |
| 100 | void uniq(CompareCallback cmp) { ListImpl::uniq(offset: mOffset, cmp); } |
| 101 | |
| 102 | class iterator |
| 103 | { |
| 104 | public: |
| 105 | iterator(T* ptr, s32 offset) : mPtr{ptr}, mOffset{offset} {} |
| 106 | bool operator==(const iterator& other) const { return mPtr == other.mPtr; } |
| 107 | bool operator!=(const iterator& other) const { return !(*this == other); } |
| 108 | iterator& operator++() |
| 109 | { |
| 110 | ListNode* node = static_cast<ListNode*>(PtrUtil::addOffset(ptr: mPtr, offset: mOffset))->next(); |
| 111 | mPtr = static_cast<T*>(PtrUtil::addOffset(ptr: node, offset: -mOffset)); |
| 112 | return *this; |
| 113 | } |
| 114 | T& operator*() const { return *mPtr; } |
| 115 | T* operator->() const { return mPtr; } |
| 116 | |
| 117 | private: |
| 118 | T* mPtr; |
| 119 | s32 mOffset; |
| 120 | }; |
| 121 | |
| 122 | iterator begin() const { return iterator(listNodeToObj(mStartEnd.next()), mOffset); } |
| 123 | iterator end() const |
| 124 | { |
| 125 | return iterator(listNodeToObj(const_cast<ListNode*>(&mStartEnd)), mOffset); |
| 126 | } |
| 127 | iterator begin(T* ptr) const { return iterator(ptr, mOffset); } |
| 128 | |
| 129 | class robustIterator |
| 130 | { |
| 131 | public: |
| 132 | robustIterator(T* ptr, s32 offset) |
| 133 | : mPtr{ptr}, mNextNode(static_cast<ListNode*>(PtrUtil::addOffset(ptr, offset))->next()), |
| 134 | mOffset{offset} |
| 135 | { |
| 136 | } |
| 137 | bool operator==(const robustIterator& other) const { return mPtr == other.mPtr; } |
| 138 | bool operator!=(const robustIterator& other) const { return !operator==(other); } |
| 139 | robustIterator& operator++() |
| 140 | { |
| 141 | mPtr = static_cast<T*>(PtrUtil::addOffset(ptr: mNextNode, offset: -mOffset)); |
| 142 | mNextNode = mNextNode->next(); |
| 143 | return *this; |
| 144 | } |
| 145 | T& operator*() const { return *mPtr; } |
| 146 | T* operator->() const { return mPtr; } |
| 147 | |
| 148 | private: |
| 149 | T* mPtr; |
| 150 | ListNode* mNextNode; |
| 151 | s32 mOffset; |
| 152 | }; |
| 153 | |
| 154 | robustIterator robustBegin() const |
| 155 | { |
| 156 | return robustIterator(listNodeToObj(mStartEnd.next()), mOffset); |
| 157 | } |
| 158 | |
| 159 | robustIterator robustEnd() const |
| 160 | { |
| 161 | return robustIterator(listNodeToObj(const_cast<ListNode*>(&mStartEnd)), mOffset); |
| 162 | } |
| 163 | |
| 164 | robustIterator robustBegin(T* ptr) const { return robustIterator(ptr, mOffset); } |
| 165 | |
| 166 | struct RobustRange |
| 167 | { |
| 168 | auto begin() const { return mList.robustBegin(); } |
| 169 | auto end() const { return mList.robustEnd(); } |
| 170 | const OffsetList& mList; |
| 171 | }; |
| 172 | RobustRange robustRange() const { return {*this}; } |
| 173 | |
| 174 | protected: |
| 175 | static int compareT(const T* lhs, const T* rhs) |
| 176 | { |
| 177 | if (lhs < rhs) |
| 178 | return -1; |
| 179 | if (lhs > rhs) |
| 180 | return 1; |
| 181 | return 0; |
| 182 | } |
| 183 | |
| 184 | ListNode* objToListNode(T* obj) const |
| 185 | { |
| 186 | return static_cast<ListNode*>(PtrUtil::addOffset(ptr: obj, offset: mOffset)); |
| 187 | } |
| 188 | |
| 189 | const ListNode* objToListNode(const T* obj) const |
| 190 | { |
| 191 | return static_cast<const ListNode*>(PtrUtil::addOffset(ptr: obj, offset: mOffset)); |
| 192 | } |
| 193 | |
| 194 | T* listNodeToObj(ListNode* node) const |
| 195 | { |
| 196 | return static_cast<T*>(PtrUtil::addOffset(ptr: node, offset: -mOffset)); |
| 197 | } |
| 198 | |
| 199 | const T* listNodeToObj(const ListNode* node) const |
| 200 | { |
| 201 | return static_cast<const T*>(PtrUtil::addOffset(ptr: node, offset: -mOffset)); |
| 202 | } |
| 203 | |
| 204 | T* listNodeToObjWithNullCheck(ListNode* node) const |
| 205 | { |
| 206 | return node ? listNodeToObj(node) : nullptr; |
| 207 | } |
| 208 | |
| 209 | const T* listNodeToObjWithNullCheck(const ListNode* node) const |
| 210 | { |
| 211 | return node ? listNodeToObj(node) : nullptr; |
| 212 | } |
| 213 | |
| 214 | s32 mOffset = -1; |
| 215 | }; |
| 216 | |
| 217 | } // namespace sead |
| 218 | |
| 219 | #endif // SEAD_OFFSET_LIST_H_ |
| 220 | |