|
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