25 #include <type_traits>
38 constexpr std::size_t max(std::size_t a, std::size_t b) {
39 return (a > b) ? a : b;
43 static constexpr std::size_t value = 0;
46 template<
typename T,
typename... Ts>
struct MaxSize<T, Ts...> {
51 template <
typename Tp,
typename... List >
54 template <
typename Tp,
typename Head,
typename... Rest >
56 : std::conditional< std::is_same<Tp, Head>::value,
61 template <
typename Tp >
66 template<
typename Base,
typename... Classes>
72 static_assert(
sizeof(Base) <= entry_size,
"entry_size was invalid");
74 const std::size_t chunk_size;
79 template<
bool IsConst>
94 using iterator_category = std::forward_iterator_tag;
95 using value_type = Base*;
96 using difference_type = uint32_t;
97 using pointer = Base**;
98 using reference = Base*&;
101 return current_ == rhs.current_;
105 return current_ != rhs.current_;
109 auto& chunk = owner_->chunks_[current_->chunk];
111 if(current_ != chunk->used_list_tail_) {
113 current_ = current_->next;
114 }
else if(current_->chunk < owner_->chunks_.size() - 1) {
117 auto next_chunk_id = current_->chunk + 1;
118 auto next_chunk = owner_->chunks_[next_chunk_id];
119 while((!next_chunk->used_list_head_) && next_chunk_id < (
int) owner_->chunks_.size() - 1) {
120 next_chunk = owner_->chunks_[++next_chunk_id];
123 current_ = next_chunk->used_list_head_;
132 reference operator*()
const {
133 return (reference) current_->entry;
136 pointer operator->()
const {
141 return &((reference) current_->entry);
150 typedef std::size_t id;
153 chunk_size(chunk_size) {
155 assert(chunk_size > 0);
164 template<
typename T,
typename... Args>
165 std::pair<T*, id> create(Args&&... args) {
166 static_assert(std::is_base_of<Base, T>::value,
"Must be a subclass of Base");
167 static_assert(_polylist::contains<T, Classes...>::value,
"T not in Classes...");
168 static_assert(
sizeof(T) <= entry_size,
"sizeof(T) was greater than entry_size");
171 EntryWithMeta* ewm = find_free_entry(&new_id);
173 assert(!ewm->meta.reserved);
176 ewm->meta.reserved = 1;
180 ret =
new (ewm->data) T(std::forward<Args>(args)...);
182 ewm->meta.reserved = 0;
184 ewm->meta.reserved = 0;
188 alloc_entry(ewm, (T*) ewm->data);
191 assert((
void*) ret == (
void*) ewm->data);
192 assert((
void*) ret == (
void*) ewm);
193 assert((Base*) ret == ewm->meta.entry);
195 return std::make_pair(ret, new_id);
198 iterator find(
const id& i)
const {
199 auto ewm = object_from_id(i);
200 Base* obj = ewm->meta.entry;
203 EntryMeta* meta = &ewm->meta;
216 iterator erase(iterator it) {
217 auto meta = it.current_;
220 auto next = iterator(
this, meta);
226 auto chunk_idx = meta->chunk;
227 auto& chunk = chunks_[chunk_idx];
233 meta->prev->next = meta->next;
234 meta->next->prev = meta->prev;
236 if(chunk->used_list_head_ == meta && chunk->used_list_tail_ == meta) {
237 chunk->used_list_head_ = chunk->used_list_tail_ =
nullptr;
238 }
else if(chunk->used_list_head_ == meta) {
239 chunk->used_list_head_ = meta->next;
240 }
else if(chunk->used_list_tail_ == meta) {
241 chunk->used_list_tail_ = meta->prev;
245 meta->next = meta->prev = meta;
251 Base* obj = meta->entry;
255 auto ewm = get_ewm(meta);
256 memset(ewm->data, 0,
sizeof(ewm->data));
259 meta->entry =
nullptr;
262 if(!chunk->free_list_tail_) {
263 assert(!chunk->free_list_head_);
265 chunk->free_list_head_ = chunk->free_list_tail_ = meta;
268 meta->prev = chunk->free_list_tail_;
269 meta->next = chunk->free_list_head_;
271 chunk->free_list_tail_->next = meta;
272 chunk->free_list_head_->prev = meta;
274 chunk->free_list_tail_ = meta;
278 assert(chunk->free_list_head_);
279 assert(chunk->free_list_tail_);
282 assert(chunk->free_list_head_ != chunk->used_list_head_);
283 assert(chunk->free_list_head_ != chunk->used_list_tail_);
284 assert(chunk->free_list_tail_ != chunk->used_list_head_);
285 assert(chunk->free_list_tail_ != chunk->used_list_tail_);
288 (chunk->used_list_head_ && chunk->used_list_tail_) ||
289 (!chunk->used_list_head_ && !chunk->used_list_tail_)
298 void shrink_to_fit() {
300 while(chunks_.size() && !chunks_.back()->used_count) {
305 void reserve(std::size_t amount) {
306 while(capacity() < amount) {
311 iterator begin()
const {
312 for(
auto& chunk: chunks_) {
313 if(chunk->used_list_head_){
314 return iterator(
this, chunk->used_list_head_);
318 return iterator(
this,
nullptr);
321 iterator end()
const {
322 return iterator(
this,
nullptr);
327 for(
auto it = begin(); it != end();) {
331 while(!chunks_.empty()) {
336 Base* operator[](
id i) {
337 auto obj = object_from_id(i);
339 return obj->meta.entry;
345 const Base* operator[](
id i)
const {
346 return object_from_id(i)->meta.entry;
353 std::size_t size()
const {
357 std::size_t capacity()
const {
358 return chunk_size * chunks_.size();
362 typedef uint8_t byte;
363 std::size_t size_ = 0;
366 EntryMeta* prev =
nullptr;
367 EntryMeta* next =
nullptr;
371 uint16_t reserved = 0;
373 Base* entry =
nullptr;
374 uint32_t padding = 0;
377 std::size_t index = 0;
381 byte data[entry_size] __attribute__((aligned(8)));
383 } __attribute__((aligned(8))) EntryWithMeta;
386 uint32_t used_count = 0;
387 EntryWithMeta* data =
nullptr;
389 EntryMeta* free_list_head_ =
nullptr;
390 EntryMeta* free_list_tail_ =
nullptr;
392 EntryMeta* used_list_head_ =
nullptr;
393 EntryMeta* used_list_tail_ =
nullptr;
396 const static std::size_t slot_size = entry_size +
sizeof(EntryMeta);
400 std::vector<std::shared_ptr<Chunk>> chunks_;
404 auto new_chunk = std::make_shared<Chunk>();
405 new_chunk->data =
new EntryWithMeta[chunk_size];
410 EntryWithMeta* ewm = new_chunk->data;
413 new_chunk->free_list_head_ = &ewm->meta;
415 EntryMeta* prev =
nullptr;
417 for(std::size_t i = 0; i < chunk_size; ++i) {
418 EntryMeta* meta = &ewm->meta;
419 memset(ewm->data, 0,
sizeof(ewm->data));
422 meta->chunk = chunks_.size();
424 meta->entry =
nullptr;
426 if(i < chunk_size - 1) {
427 meta->next = &(ewm + 1)->meta;
434 new_chunk->free_list_tail_ = prev;
437 new_chunk->free_list_head_->prev = new_chunk->free_list_tail_;
438 new_chunk->free_list_tail_->next = new_chunk->free_list_head_;
439 assert(new_chunk->free_list_head_->prev);
440 assert(new_chunk->free_list_tail_->next);
442 assert(new_chunk->free_list_head_);
443 assert(new_chunk->free_list_tail_);
444 assert(!new_chunk->used_list_head_);
445 assert(!new_chunk->used_list_tail_);
449 chunks_.push_back(new_chunk);
453 if(chunks_.empty()) {
457 auto chunk = chunks_.back();
459 auto it = iterator(
this, chunk->used_list_head_);
464 auto end = iterator(
this,
nullptr);
470 delete [] chunk->data;
471 chunk->data =
nullptr;
478 void alloc_entry(EntryWithMeta* ewm, Base* obj) {
479 auto& chunk = chunks_[ewm->meta.chunk];
481 assert(ewm->meta.next);
482 assert(ewm->meta.prev);
484 assert(chunk->free_list_head_);
485 assert(chunk->free_list_tail_);
487 if(chunk->free_list_head_ == &ewm->meta && chunk->free_list_tail_ == &ewm->meta) {
489 chunk->free_list_head_ = chunk->free_list_tail_ =
nullptr;
490 }
else if(chunk->free_list_head_ == &ewm->meta) {
491 chunk->free_list_head_ = chunk->free_list_head_->next;
492 }
else if(chunk->free_list_tail_ == &ewm->meta) {
493 chunk->free_list_tail_ = chunk->free_list_tail_->prev;
496 if(!chunk->used_list_tail_) {
497 assert(!chunk->used_list_head_);
499 chunk->used_list_head_ = chunk->used_list_tail_ = &ewm->meta;
502 ewm->meta.prev = chunk->used_list_tail_;
503 ewm->meta.next = chunk->used_list_head_;
505 ewm->meta.prev->next = &ewm->meta;
506 ewm->meta.next->prev = &ewm->meta;
508 chunk->used_list_tail_ = &ewm->meta;
511 ewm->meta.entry = obj;
513 assert(ewm->meta.next);
514 assert(ewm->meta.prev);
517 assert(chunk->used_list_head_);
518 assert(chunk->used_list_tail_);
521 (!chunk->free_list_head_ && !chunk->free_list_tail_) ||
522 (chunk->free_list_head_ && chunk->free_list_tail_)
529 EntryWithMeta* find_free_entry(
id* new_id) {
531 for(
auto chunk: chunks_) {
532 if(chunk->free_list_head_ && !chunk->free_list_head_->reserved) {
533 EntryMeta* result = chunk->free_list_head_;
535 assert(result->chunk == i);
536 *new_id = ((i * chunk_size) + result->index) + 1;
538 return get_ewm(result);
547 return find_free_entry(new_id);
550 EntryWithMeta* object_from_id(
id i)
const {
555 std::size_t idx = i - 1;
556 std::size_t chunk_id = (idx / chunk_size);
557 std::size_t index = (idx % chunk_size);
559 if(chunk_id >= chunks_.size()) {
563 if(index >= chunk_size) {
567 EntryWithMeta* ewm = &chunks_[chunk_id]->data[index];
569 assert(chunk_id == ewm->meta.chunk);
570 assert(index == ewm->meta.index);
575 EntryWithMeta* get_ewm(EntryMeta* meta) {
576 return &chunks_[meta->chunk]->data[meta->index];