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
9namespace sead
10{
11template <typename T>
12class OffsetList : public ListImpl
13{
14public:
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
174protected:
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