]> git.tdb.fi Git - libs/game.git/blob - source/game/pool.h
Implement a basic ECS
[libs/game.git] / source / game / pool.h
1 #ifndef MSP_GAME_STORAGE_H_
2 #define MSP_GAME_STORAGE_H_
3
4 #include <cstdint>
5 #include <memory>
6 #include <vector>
7 #include <msp/core/noncopyable.h>
8
9 namespace Msp::Game {
10
11 class PoolBase;
12
13 template<typename T>
14 class Pool;
15
16 class PoolPool: public NonCopyable
17 {
18 private:
19         std::vector<std::unique_ptr<PoolBase>> pools;
20
21 public:
22         PoolPool() = default;
23         PoolPool(PoolPool &&) = default;
24         PoolPool &operator=(PoolPool &&) = default;
25
26 private:
27         static unsigned get_next_id();
28
29 public:
30         template<typename T>
31         static unsigned get_type_id() { static unsigned id = get_next_id(); return id; }
32
33         template<typename T>
34         Pool<T> &get_pool();
35 };
36
37
38 class PoolBase: public NonCopyable
39 {
40 private:
41         using DeleteFunc = void(void *);
42         using FlagType = uint32_t;
43
44         static constexpr std::size_t BLOCK_SIZE = 512;
45         static constexpr std::size_t BLOCK_ALIGNMENT = 64;
46         static constexpr std::size_t FLAG_BITS = sizeof(FlagType)*8;
47
48         std::size_t object_size = 0;
49         char **blocks = nullptr;
50         std::uint32_t *free_list = nullptr;
51         std::uint32_t object_count = 0;
52         std::uint32_t capacity = 0;
53         DeleteFunc *deleter = nullptr;
54
55 protected:
56         PoolBase(std::uint32_t, DeleteFunc);
57 public:
58         PoolBase(PoolBase &&);
59         PoolBase &operator=(PoolBase &&);
60         ~PoolBase();
61
62 protected:
63         void destroy_all();
64
65         template<typename F>
66         void iterate_objects(const F &);
67
68         void *prepare_allocate();
69         void commit_allocate(void *);
70
71 private:
72         void add_block();
73
74 public:
75         void destroy(void *);
76 };
77
78
79 template<typename T>
80 class Pool: public PoolBase
81 {
82 public:
83         Pool(): PoolBase(sizeof(T), delete_object) { }
84
85         template<typename... Args>
86         T *create(Args &&...);
87
88         template<typename F>
89         void iterate_objects(const F &func)
90         { PoolBase::iterate_objects([&func](void *ptr){ func(*static_cast<T *>(ptr)); }); }
91
92 private:
93         static void delete_object(void *ptr) { std::destroy_at(static_cast<T *>(ptr)); }
94 };
95
96
97 template<typename T>
98 inline Pool<T> &PoolPool::get_pool()
99 {
100         unsigned id = get_type_id<T>();
101         if(pools.size()<=id)
102                 pools.resize(id+1);
103
104         std::unique_ptr<PoolBase> &ptr = pools[id];
105         if(!ptr)
106                 ptr = std::make_unique<Pool<T>>();
107
108         return *static_cast<Pool<T> *>(ptr.get());
109 }
110
111
112 template<typename F>
113 inline void PoolBase::iterate_objects(const F &func)
114 {
115         unsigned block_count = capacity/BLOCK_SIZE;
116         for(unsigned i=0; i<block_count; ++i)
117         {
118                 char *ptr = blocks[i];
119                 const FlagType *flags = reinterpret_cast<FlagType *>(ptr+BLOCK_SIZE*object_size);
120                 for(unsigned j=0; j<BLOCK_SIZE; j+=FLAG_BITS)
121                 {
122                         char *end = ptr+FLAG_BITS*object_size;
123                         if(FlagType f = *flags)
124                         {
125                                 if(!static_cast<FlagType>(~f))
126                                 {
127                                         for(; ptr!=end; ptr+=object_size)
128                                                 func(static_cast<void *>(ptr));
129                                 }
130                                 else
131                                 {
132                                         for(; ptr!=end; ptr+=object_size, f>>=1)
133                                                 if(f&1)
134                                                         func(static_cast<void *>(ptr));
135                                 }
136                         }
137                         else
138                                 ptr = end;
139                         ++flags;
140                 }
141         }
142 }
143
144
145 template<typename T>
146 template<typename... Args>
147 inline T *Pool<T>::create(Args &&... args)
148 {
149         void *ptr = prepare_allocate();
150         T *obj = std::construct_at(static_cast<T *>(ptr), std::forward<Args>(args)...);
151         commit_allocate(ptr);
152         return obj;
153 }
154
155 } // namespace Msp::Game
156
157 #endif