]> git.tdb.fi Git - libs/game.git/blob - source/game/pool.cpp
Implement a basic ECS
[libs/game.git] / source / game / pool.cpp
1 #include "pool.h"
2 #include <stdexcept>
3 #include <msp/io/print.h>
4
5 using namespace std;
6
7 namespace Msp::Game {
8
9 unsigned PoolPool::get_next_id()
10 {
11         static unsigned next_id = 0;
12         return next_id++;
13 }
14
15
16 PoolBase::PoolBase(uint32_t s, DeleteFunc d):
17         object_size(s),
18         deleter(d)
19 { }
20
21 PoolBase::PoolBase(PoolBase &&other):
22         object_size(other.object_size),
23         blocks(other.blocks),
24         free_list(other.free_list),
25         object_count(other.object_count),
26         capacity(other.capacity),
27         deleter(other.deleter)
28 {
29         other.blocks = nullptr;
30         other.free_list = nullptr;
31         other.object_count = 0;
32         other.capacity = 0;
33 }
34
35 PoolBase &PoolBase::operator=(PoolBase &&other)
36 {
37         destroy_all();
38
39         object_size = other.object_size;
40         blocks = other.blocks;
41         free_list = other.free_list;
42         object_count = other.object_count;
43         capacity = other.capacity;
44         deleter = other.deleter;
45
46         other.blocks = nullptr;
47         other.free_list = nullptr;
48         other.object_count = 0;
49         other.capacity = 0;
50
51         return *this;
52 }
53
54 PoolBase::~PoolBase()
55 {
56         destroy_all();
57 }
58
59 void PoolBase::destroy_all()
60 {
61         if(object_count>0)
62                 IO::print(IO::cerr, "Warning: pool is being destroyed but has %d live objects", object_count);
63
64         unsigned block_count = capacity/BLOCK_SIZE;
65         for(unsigned i=0; i<block_count; ++i)
66                 delete[] blocks[i];
67         delete[] reinterpret_cast<char *>(blocks);
68 }
69
70 void *PoolBase::prepare_allocate()
71 {
72         if(object_count>=capacity)
73                 add_block();
74
75         uint32_t full_index = free_list[object_count];
76         unsigned block_index = full_index/BLOCK_SIZE;
77         unsigned object_index = full_index%BLOCK_SIZE;
78         return blocks[block_index]+object_index*object_size;
79 }
80
81 void PoolBase::commit_allocate(void *ptr)
82 {
83         uint32_t full_index = free_list[object_count];
84         unsigned block_index = full_index/BLOCK_SIZE;
85         unsigned object_index = full_index%BLOCK_SIZE;
86         void *expected = blocks[block_index]+object_index*object_size;
87         if(ptr!=expected)
88                 throw logic_error("PoolBase::commit_allocate does not match prepare_allocate");
89
90         FlagType *flags = reinterpret_cast<FlagType *>(blocks[block_index]+BLOCK_SIZE*object_size);
91         FlagType bit = 1<<(object_index%FLAG_BITS);
92         if(flags[object_index/FLAG_BITS]&bit)
93                 throw logic_error("PoolBase::commit_allocate to a not-free index");
94
95         flags[object_index/FLAG_BITS] |= bit;
96         ++object_count;
97 }
98
99 void PoolBase::add_block()
100 {
101         unsigned block_count = capacity/BLOCK_SIZE;
102         char *new_mem = new alignas(char *) char[(block_count+1)*(sizeof(char *)+BLOCK_SIZE*sizeof(uint32_t))];
103         char **new_blocks = reinterpret_cast<char **>(new_mem);
104         uint32_t *new_free = reinterpret_cast<uint32_t *>(new_mem+(block_count+1)*sizeof(char *));
105
106         copy(blocks, blocks+block_count, new_blocks);
107         copy(free_list, free_list+capacity, new_free);
108
109         char *block = new alignas(BLOCK_ALIGNMENT) char[BLOCK_SIZE*object_size+BLOCK_SIZE/8];
110         new_blocks[block_count] = block;
111         FlagType *flags = reinterpret_cast<FlagType *>(block+BLOCK_SIZE*object_size);
112         for(unsigned i=0; i<BLOCK_SIZE; ++i)
113                 new_free[capacity+i] = capacity+i;
114         for(unsigned i=0; i<BLOCK_SIZE/FLAG_BITS; ++i)
115                 flags[i] = 0;
116
117         delete[] reinterpret_cast<char *>(blocks);
118         blocks = new_blocks;
119         free_list = new_free;
120         capacity += BLOCK_SIZE;
121 }
122
123 void PoolBase::destroy(void *obj)
124 {
125         unsigned block_index = 0;
126         unsigned object_index = BLOCK_SIZE;
127         intptr_t addr = reinterpret_cast<intptr_t>(obj);
128         unsigned block_count = capacity/BLOCK_SIZE;
129         while(block_index<block_count)
130         {
131                 intptr_t mem = reinterpret_cast<intptr_t>(blocks[block_index]);
132                 intptr_t mem_end = mem+BLOCK_SIZE*object_size;
133                 if(addr>=mem && addr<mem_end)
134                 {
135                         object_index = (addr-mem)/object_size;
136                         break;
137                 }
138                 ++block_index;
139         }
140
141         if(object_index>=BLOCK_SIZE)
142                 throw invalid_argument("PoolBase::destroy");
143
144         FlagType *flags = reinterpret_cast<FlagType *>(blocks[block_index]+BLOCK_SIZE*object_size);
145         FlagType bit = 1<<(object_index%FLAG_BITS);
146         if(!(flags[object_index/FLAG_BITS]&bit))
147                 throw invalid_argument("PoolBase::destroy");
148
149         deleter(obj);
150         flags[object_index/FLAG_BITS] &= ~bit;
151         --object_count;
152         free_list[object_count] = block_index*BLOCK_SIZE+object_index;
153 }
154
155 } // namespace Msp::Game