|
6 | 6 | #define __NBL_CORE_FIXED_CAPACITY_DOUBLY_LINKED_LIST_H_INCLUDED__
|
7 | 7 |
|
8 | 8 |
|
9 |
| -#include "nbl/core/alloc/PoolAddressAllocator.h" |
10 |
| -#include "nbl/core/decl/Types.h" |
11 |
| - |
12 |
| -#include <functional> |
| 9 | +#include "nbl/core/containers/lists/DoublyLinkedListBase.h" |
13 | 10 |
|
14 | 11 | namespace nbl
|
15 | 12 | {
|
16 | 13 | namespace core
|
17 | 14 | {
|
18 | 15 |
|
19 |
| -namespace impl |
20 |
| -{ |
21 |
| - class FixedCapacityDoublyLinkedListBase |
22 |
| - { |
23 |
| - public: |
24 |
| - _NBL_STATIC_INLINE_CONSTEXPR uint32_t invalid_iterator = PoolAddressAllocator<uint32_t>::invalid_address; |
25 |
| - protected: |
26 |
| - |
27 |
| - FixedCapacityDoublyLinkedListBase() = default; |
28 |
| - |
29 |
| - template<typename T> |
30 |
| - FixedCapacityDoublyLinkedListBase(const uint32_t capacity, void*& _reservedSpace, T*& _array) |
31 |
| - { |
32 |
| - const auto firstPart = core::alignUp(PoolAddressAllocator<uint32_t>::reserved_size(1u,capacity,1u),alignof(T)); |
33 |
| - _reservedSpace = _NBL_ALIGNED_MALLOC(firstPart+capacity*sizeof(T),alignof(T)); |
34 |
| - _array = reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(_reservedSpace)+firstPart); |
35 |
| - } |
36 |
| - }; |
37 |
| -} |
38 |
| - |
39 |
| -//Struct for use in a doubly linked list. Stores data and pointers to next and previous elements the list, or invalid iterator if it is first/last |
40 | 16 | template<typename Value>
|
41 |
| -struct alignas(void*) SDoublyLinkedNode |
| 17 | +class FixedCapacityDoublyLinkedList : public DoublyLinkedListBase<Value> |
42 | 18 | {
|
43 |
| - _NBL_STATIC_INLINE_CONSTEXPR uint32_t invalid_iterator = impl::FixedCapacityDoublyLinkedListBase::invalid_iterator; |
| 19 | +public: |
| 20 | + using base_t = DoublyLinkedListBase<Value>; |
| 21 | + using disposal_func_t = typename base_t::disposal_func_t; |
44 | 22 |
|
45 |
| - Value data; |
46 |
| - uint32_t prev; |
47 |
| - uint32_t next; |
48 |
| - |
49 |
| - SDoublyLinkedNode() {} |
50 |
| - SDoublyLinkedNode(const Value& val) : data(val) |
51 |
| - { |
52 |
| - prev = invalid_iterator; |
53 |
| - next = invalid_iterator; |
54 |
| - } |
55 |
| - SDoublyLinkedNode(Value&& val) : data(std::move(val)) |
56 |
| - { |
57 |
| - prev = invalid_iterator; |
58 |
| - next = invalid_iterator; |
59 |
| - } |
60 |
| - SDoublyLinkedNode(SDoublyLinkedNode<Value>&& other) : data(std::move(other.data)), prev(std::move(other.prev)), next(std::move(other.next)) |
61 |
| - {} |
62 |
| - |
63 |
| - ~SDoublyLinkedNode() |
| 23 | + //Constructor, capacity determines the amount of allocated space |
| 24 | + FixedCapacityDoublyLinkedList(const uint32_t capacity, disposal_func_t&& dispose_f = disposal_func_t()) : base_t(capacity, std::move(dispose_f)) |
64 | 25 | {}
|
65 | 26 |
|
66 |
| - SDoublyLinkedNode<Value>& operator=(const SDoublyLinkedNode<Value>& other) |
67 |
| - { |
68 |
| - this->data = other.data; |
69 |
| - this->prev = other.prev; |
70 |
| - this->next = other.next; |
71 |
| - return *this; |
72 |
| - } |
73 |
| - |
74 |
| - SDoublyLinkedNode<Value>& operator=(SDoublyLinkedNode<Value>&& other) |
75 |
| - { |
76 |
| - this->data = std::move(other.data); |
77 |
| - this->prev = std::move(other.prev); |
78 |
| - this->next = std::move(other.next); |
79 |
| - return *this; |
80 |
| - } |
81 |
| -}; |
82 |
| - |
83 |
| -template<typename Value> |
84 |
| -class FixedCapacityDoublyLinkedList : private impl::FixedCapacityDoublyLinkedListBase |
85 |
| -{ |
86 |
| - public: |
87 |
| - using AddressAllocator = PoolAddressAllocator<uint32_t>; |
88 |
| - _NBL_STATIC_INLINE_CONSTEXPR uint32_t invalid_iterator = impl::FixedCapacityDoublyLinkedListBase::invalid_iterator; |
89 |
| - |
90 |
| - using disposal_func_t = std::function<void(Value&)>; |
91 |
| - |
92 |
| - using node_t = SDoublyLinkedNode<Value>; |
93 |
| - |
94 |
| - // get the fixed capacity |
95 |
| - inline uint32_t getCapacity() const { return cap; } |
| 27 | + FixedCapacityDoublyLinkedList() = default; |
96 | 28 |
|
97 |
| - //get node at iterator |
98 |
| - inline node_t* get(const uint32_t address) |
99 |
| - { |
100 |
| - return (m_array + address); |
101 |
| - } |
102 |
| - inline const node_t* get(const uint32_t address) const |
103 |
| - { |
104 |
| - return (m_array + address); |
105 |
| - } |
| 29 | + FixedCapacityDoublyLinkedList(const FixedCapacityDoublyLinkedList& other) = delete; |
106 | 30 |
|
107 |
| - //remove the last element in the list |
108 |
| - inline void popBack() |
109 |
| - { |
110 |
| - if (m_back == invalid_iterator) |
111 |
| - return; |
| 31 | + FixedCapacityDoublyLinkedList& operator=(const FixedCapacityDoublyLinkedList& other) = delete; |
112 | 32 |
|
113 |
| - auto backNode = getBack(); |
114 |
| - if (backNode->prev != invalid_iterator) |
115 |
| - get(backNode->prev)->next = invalid_iterator; |
116 |
| - uint32_t temp = m_back; |
117 |
| - m_back = backNode->prev; |
118 |
| - common_delete(temp); |
119 |
| - } |
120 |
| - |
121 |
| - //add new item to the list. This function does not make space to store the new node. in case the list is full, popBack() needs to be called beforehand |
122 |
| - inline void pushFront(Value&& val) |
123 |
| - { |
124 |
| - insertAt(reserveAddress(), std::move(val)); |
125 |
| - } |
126 |
| - |
127 |
| - template <typename... Args> |
128 |
| - inline void emplaceFront(Args&&... args) |
129 |
| - { |
130 |
| - insertAt(reserveAddress(), Value(std::forward<Args>(args)...)); |
131 |
| - } |
132 |
| - |
133 |
| - //get node ptr of the first item in the list |
134 |
| - inline node_t* getBegin() { return m_array + m_begin; } |
135 |
| - inline const node_t* getBegin() const { return m_array + m_begin; } |
136 |
| - |
137 |
| - //get node ptr of the last item in the list |
138 |
| - inline node_t* getBack() { return m_array + m_back; } |
139 |
| - inline const node_t* getBack() const { return m_array + m_back; } |
140 |
| - |
141 |
| - //get index/iterator of the first element |
142 |
| - inline uint32_t getFirstAddress() const { return m_begin; } |
143 |
| - |
144 |
| - //get index/iterator of the last element |
145 |
| - inline uint32_t getLastAddress() const { return m_back; } |
146 |
| - |
147 |
| - //remove a node at nodeAddr from the list |
148 |
| - inline void erase(const uint32_t nodeAddr) |
149 |
| - { |
150 |
| - assert(nodeAddr != invalid_iterator); |
151 |
| - assert(nodeAddr < cap); |
152 |
| - node_t* node = get(nodeAddr); |
153 |
| - |
154 |
| - if (m_back == nodeAddr) |
155 |
| - m_back = node->prev; |
156 |
| - if (m_begin == nodeAddr) |
157 |
| - m_begin = node->next; |
158 |
| - |
159 |
| - common_detach(node); |
160 |
| - common_delete(nodeAddr); |
161 |
| - } |
162 |
| - |
163 |
| - //move a node at nodeAddr to the front of the list |
164 |
| - inline void moveToFront(const uint32_t nodeAddr) |
165 |
| - { |
166 |
| - if (m_begin == nodeAddr || nodeAddr == invalid_iterator) |
167 |
| - return; |
168 |
| - |
169 |
| - getBegin()->prev = nodeAddr; |
170 |
| - |
171 |
| - auto node = get(nodeAddr); |
172 |
| - |
173 |
| - if (m_back == nodeAddr) |
174 |
| - m_back = node->prev; |
175 |
| - |
176 |
| - common_detach(node); |
177 |
| - node->next = m_begin; |
178 |
| - node->prev = invalid_iterator; |
179 |
| - m_begin = nodeAddr; |
180 |
| - } |
181 |
| - |
182 |
| - //Constructor, capacity determines the amount of allocated space |
183 |
| - FixedCapacityDoublyLinkedList(const uint32_t capacity, disposal_func_t&& dispose_f = disposal_func_t()) : |
184 |
| - FixedCapacityDoublyLinkedListBase(capacity,m_reservedSpace,m_array), |
185 |
| - m_dispose_f(std::move(dispose_f)) |
186 |
| - { |
187 |
| - addressAllocator = std::unique_ptr<AddressAllocator>(new AddressAllocator(m_reservedSpace, 0u, 0u, 1u, capacity, 1u)); |
188 |
| - cap = capacity; |
189 |
| - m_back = invalid_iterator; |
190 |
| - m_begin = invalid_iterator; |
191 |
| - } |
192 |
| - |
193 |
| - FixedCapacityDoublyLinkedList() = default; |
194 |
| - |
195 |
| - FixedCapacityDoublyLinkedList(const FixedCapacityDoublyLinkedList& other) = delete; |
196 |
| - |
197 |
| - FixedCapacityDoublyLinkedList& operator=(const FixedCapacityDoublyLinkedList& other) = delete; |
198 |
| - |
199 |
| - FixedCapacityDoublyLinkedList& operator=(FixedCapacityDoublyLinkedList&& other) |
200 |
| - { |
201 |
| - addressAllocator = std::move(other.addressAllocator); |
202 |
| - m_reservedSpace = std::move(other.m_reservedSpace); |
203 |
| - m_array = std::move(other.m_array); |
204 |
| - m_dispose_f = std::move(other.m_dispose_f); |
205 |
| - cap = other.cap; |
206 |
| - m_back = other.m_back; |
207 |
| - m_begin = other.m_begin; |
208 |
| - |
209 |
| - // Nullify other |
210 |
| - other.addressAllocator = nullptr; |
211 |
| - other.m_reservedSpace = nullptr; |
212 |
| - other.m_array = nullptr; |
213 |
| - other.cap = 0u; |
214 |
| - other.m_back = 0u; |
215 |
| - other.m_begin = 0u; |
216 |
| - return *this; |
217 |
| - } |
218 |
| - |
219 |
| - ~FixedCapacityDoublyLinkedList() |
220 |
| - { |
221 |
| - if (m_dispose_f && m_begin != invalid_iterator) |
222 |
| - { |
223 |
| - auto* begin = getBegin(); |
224 |
| - auto* back = getBack(); |
225 |
| - do |
226 |
| - { |
227 |
| - m_dispose_f(begin->data); |
228 |
| - begin = get(begin->next); |
229 |
| - } while (begin != back); |
230 |
| - } |
231 |
| - _NBL_ALIGNED_FREE(m_reservedSpace); |
232 |
| - } |
233 |
| - |
234 |
| - |
235 |
| - private: |
236 |
| - std::unique_ptr<AddressAllocator> addressAllocator; |
237 |
| - void* m_reservedSpace; |
238 |
| - node_t* m_array; |
239 |
| - |
240 |
| - uint32_t cap; |
241 |
| - uint32_t m_back; |
242 |
| - uint32_t m_begin; |
243 |
| - |
244 |
| - disposal_func_t m_dispose_f; |
245 |
| - |
246 |
| - //allocate and get the address of the next free node |
247 |
| - inline uint32_t reserveAddress() |
248 |
| - { |
249 |
| - uint32_t addr = addressAllocator->alloc_addr(1u, 1u); |
250 |
| - return addr; |
251 |
| - } |
252 |
| - |
253 |
| - //create a new node which stores data at already allocated address, |
254 |
| - inline void insertAt(uint32_t addr, Value&& val) |
255 |
| - { |
256 |
| - assert(addr < cap); |
257 |
| - assert(addr != invalid_iterator); |
258 |
| - SDoublyLinkedNode<Value>* n = new(m_array + addr) SDoublyLinkedNode<Value>(std::move(val)); |
259 |
| - n->prev = invalid_iterator; |
260 |
| - n->next = m_begin; |
261 |
| - |
262 |
| - if (m_begin != invalid_iterator) |
263 |
| - getBegin()->prev = addr; |
264 |
| - if (m_back == invalid_iterator) |
265 |
| - m_back = addr; |
266 |
| - m_begin = addr; |
267 |
| - } |
268 |
| - |
269 |
| - inline void common_delete(uint32_t address) |
270 |
| - { |
271 |
| - if (m_dispose_f) |
272 |
| - m_dispose_f(get(address)->data); |
273 |
| - get(address)->~node_t(); |
274 |
| - addressAllocator->free_addr(address, 1u); |
275 |
| - } |
| 33 | + FixedCapacityDoublyLinkedList& operator=(FixedCapacityDoublyLinkedList&& other) |
| 34 | + { |
| 35 | + base_t::operator=(other); |
| 36 | + } |
276 | 37 |
|
277 |
| - inline void common_detach(node_t* node) |
278 |
| - { |
279 |
| - if (node->next != invalid_iterator) |
280 |
| - get(node->next)->prev = node->prev; |
281 |
| - if (node->prev != invalid_iterator) |
282 |
| - get(node->prev)->next = node->next; |
283 |
| - } |
| 38 | + ~FixedCapacityDoublyLinkedList() = default; |
284 | 39 | };
|
285 | 40 |
|
286 | 41 |
|
|
0 commit comments