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