Skip to content

Commit 3813a79

Browse files
authored
Update README.md
1 parent 1ef0e20 commit 3813a79

File tree

1 file changed

+85
-89
lines changed

1 file changed

+85
-89
lines changed

README.md

Lines changed: 85 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
11
# Rea
22
Rea is a lightweight library of data structures implemented in C++11, designed for constant time insertion, erasure, lookup, and fastest possible iteration. Great for using in games or any other software which needs to manage thousands upon thousands of objects.
33

4-
There are 6 data structures included in this library : `slot_map`, `controlled_slot_map`, `versioned_slot_map`, `regulated_slot_map`, `dense_map`
5-
and `versioned_dense_map`. The two key data structures are `slot_map` and `dense_map`: the other four are only simple variations of those 2.
4+
There are 6 data structures included in this library : `slot_list`, `controlled_slot_list`, `versioned_slot_list`, `regulated_slot_list`, `slot_map` and `versioned_slot_map`. The two key data structures are `slot_list` and `list_map`: the other four are only simple variations of those 2.
65

7-
# SlotMap
8-
Use SlotMap when you have to insert, erase, or look up data in constant time, without the need for constantly repeated iteration. If you require all of those things plus fast iteration, use DenseMap.
6+
# SlotList
7+
Use SlotList when you have to insert, erase, or look up data in constant time, without the need for constantly repeated iteration. If you require all of those things plus fast iteration, use SlotMap.
98

109
## Implementation
11-
SlotMap internally stores its objects in some RandomAccessContainer (by default `std::deque`). Once you erase an object from SlotMap, the slot where that object used to reside becomes available for reuse. The next object you insert will be put in the last emptied slot.
10+
SlotList internally stores its objects in some RandomAccessContainer (by default `std::deque`). Once you erase an object from SlotList, the slot where that object used to reside becomes available for reuse. The next object you insert will be put in the last emptied slot.
1211
The internal container will never grow unless all slots are filled. The "Discussion" section shows how to change the
1312
internal container from `std::deque` to some other container.
1413

15-
Whenever you insert a value into the SlotMap you get its id, which you can later use to access that object. "id" is an index with or without a version count in case of the version variatons of SlotMaps and DenseMaps. More on that later on. That index is what allows us to access the objects in constant time.
14+
Whenever you insert a value into the SlotList you get its id, which you can later use to access that object. "id" is an index with or without a version count in case of the version variatons of SlotLists and DenseMaps. More on that later on. That index is what allows us to access the objects in constant time.
1615

17-
While you can use the ids to iterate over all stored objects, it's not advisable to do it repeatedly: you might be jumping all over memory and hence destroying cache locality. As stated above, for iteration use DenseMap.
16+
While you can use the ids to iterate over all stored objects, it's not advisable to do it repeatedly: you might be jumping all over memory and hence destroying cache locality. As stated above, for iteration use SlotMap.
1817

1918
## Usage
20-
All SlotMaps have the same first, and the last 2 template arguments.
19+
All SlotLists have the same first, and the last 2 template arguments.
2120
```cpp
22-
some_slot_map<T, // value_type
23-
.
24-
.
25-
.
26-
S = std::size_t, // size_type
27-
A = std::allocator<T>>; // allocator_type
21+
some_slot_list<T, // value_type
22+
.
23+
.
24+
.
25+
S = std::size_t, // size_type
26+
A = std::allocator<T>>; // allocator_type
2827
```
29-
Considering that each slot will store two objects of "size_type" type as well as the single object of "value_type", if you know in advance the maximum size of the container, you might want to restrict how much space slots take up by choosing a smaller "size_type" that can still store that maximum container size.
28+
Considering that each slot will store two objects of "size_type" type as well as the single object of "value_type", if you know in advance the maximum size of the container, you might want to restrict how much space slots take up by choosing a smaller "size_type" which can still be used to reference all objects in the container.
3029

31-
Template arguments for all SlotMap variations are explained below.
30+
Template arguments for all SlotList variations are explained below.
3231

33-
### variation 1 : slot_map
34-
rea::slot_map acts as a basic SlotMap. It has no additional template arguments, only the ones described above.
32+
### variation 1 : slot_list
33+
`rea::slot_list` acts as a basic SlotList. It has no additional template arguments, only the ones described above.
3534
```cpp
36-
rea::slot_map<T, // value_type
37-
S = std::size_t, // size_type
38-
A = std::allocator<T>> // allocator_type
35+
rea::slot_list<T, // value_type
36+
S = std::size_t, // size_type
37+
A = std::allocator<T>> // allocator_type
3938
```
4039

4140
```cpp
@@ -63,45 +62,45 @@ T get_and_remove_rand_value(std::vector<T> &values) {
6362
}
6463

6564
int main() {
66-
using sm_type = rea::slot_map<std::pair<int, int>>;
67-
sm_type sm;
65+
using sl_type = rea::slot_list<std::pair<int, int>>;
66+
sl_type sl;
6867
std::vector<sm_type::id_type> ids;
6968
ids.reserve(1000);
70-
sm.reserve(1000);
69+
sl.reserve(1000);
7170

7271
// insertion
7372
for(size_t i = 0; i < 1000; ++i) {
74-
ids.push_back(sm.insert({i, i*2}));
73+
ids.push_back(sl.insert({i, i*2}));
7574
}
7675

7776
// erasure
7877
for(size_t i = 0; i < 100; ++i) {
7978
auto id = get_and_remove_rand_value(ids);
80-
sm.erase(id);
79+
sl.erase(id);
8180
}
8281

8382
// lookup
8483
for(auto id : ids)
85-
std::cout << sm.id_value(id).first << std::endl;
84+
std::cout << sl.id_value(id).first << std::endl;
8685

8786

8887
// iteration
89-
for(auto id = sm.id_begin(); !sm.id_is_end(id); id = sm.id_next(id))
90-
std::cout << sm.id_value(id).first << std::endl;
88+
for(auto id = sl.id_begin(); !sl.id_is_end(id); id = sl.id_next(id))
89+
std::cout << sl.id_value(id).first << std::endl;
9190
}
9291
```
9392
9493
95-
### variation 2 : controlled_slot_map
94+
### variation 2 : controlled_slot_list
9695
Objects which you erase are not destroyed, only "marked" as empty, so they can be reassigned to in the future. If you are storing objects which themselves are holding some resources (e.g. a pointer to some allocated memory) this could be problematic, beacuse you won't be able to release that memory until the entire SlotMap is destroyed or another value is assigned to that object.
9796
98-
To solve that issue, controlled_slot_map is introduced. Its second template argument is a functor, which returns a value to be assigned to all empty slots (it's defaulted to a `rea::get_empty` functor which returns a default constructed object).
97+
To solve that issue, `rea::controlled_slot_list` is introduced. Its second template argument is a functor, which returns a value to be assigned to all empty slots (it's defaulted to a `rea::get_empty` functor which returns a default constructed object).
9998
10099
```cpp
101-
rea::controlled_slot_map<T, // value_type
102-
E = rea::get_empty<T>, // get_empty_type
103-
S = std::size_t, // size_type
104-
A = std::allocator<T>> // allocator_type
100+
rea::controlled_slot_list<T, // value_type
101+
E = rea::get_empty<T>, // get_empty_type
102+
S = std::size_t, // size_type
103+
A = std::allocator<T>> // allocator_type
105104
```
106105

107106
```cpp
@@ -113,21 +112,21 @@ struct set_empty_double() {
113112
double value;
114113
}
115114

116-
rea::controlled_slot_map<double, set_empty_double, std::unit16_t> c_sm(set_empty_double{3.14159});
115+
rea::controlled_slot_list<double, set_empty_double, std::unit16_t> c_sm(set_empty_double{3.14159});
117116
```
118117
119118
120119
121-
### variation 3 : versioned_slot_map
120+
### variation 3 : versioned_slot_list
122121
If, for instance, you are erasing objects inside the SlotMap from 2 different parts of your program, ids might no longer
123122
point to correct objects, but to either erased or objects filled with different values than what the id originally pointed to.
124123
125-
To solve that issue, `versioned_slot_map` is introduced. It takes as a second template argument an IntegralType, which will represent the current version of the slot (default is `std::size_t`). Each time an object is erased, the slot which contains that object increases its version count by 1.
124+
To solve that issue, `rea::versioned_slot_list` is introduced. It takes as a second template argument an IntegralType, which will represent the current version of the slot (default is `std::size_t`). Each time an object is erased, the slot which contains that object increases its version count by 1.
126125
```cpp
127-
rea::versioned_slot_map<T, // value_type
128-
V = std::size_t, // version_type
129-
S = std::size_t, // size_type
130-
A = std::allocator<T>> // allocator_type
126+
rea::versioned_slot_list<T, // value_type
127+
V = std::size_t, // version_type
128+
S = std::size_t, // size_type
129+
A = std::allocator<T>> // allocator_type
131130
```
132131
Here slots also store a "version_type". Choosing different "version_type" may effect size of the slot, and hence the amount of memory needed.
133132

@@ -136,34 +135,33 @@ Here slots also store a "version_type". Choosing different "version_type" may ef
136135
#include <vector>
137136

138137
int main() {
139-
using sm_type = rea::versioned_slot_map<int>;
140-
sm_type sm;
141-
std::vector<sm_type::id_type> ids;
138+
using sl_type = rea::versioned_slot_list<int>;
139+
sl_type sl;
140+
std::vector<sl_type::id_type> ids;
142141
ids.reserve(1000);
143-
sm.reserve(1000);
142+
sl.reserve(1000);
144143

145144
for(int i = 0; i < 1000; ++i) {
146-
ids.push_back(sm.insert(i));
145+
ids.push_back(sl.insert(i));
147146
}
148147

149148
auto some_random_valid_id = ids[36];
150149

151-
bool is_valid = sm.id_is_valid(some_random_valid_id);
150+
bool is_valid = sl.id_is_valid(some_random_valid_id);
152151
// is_valid == true;
153-
sm.erase(some_random_valid_id);
154-
bool is_valid_now = sm.id_is_valid(some_random_valid_id);
152+
sl.erase(some_random_valid_id);
153+
bool is_valid_now = sl.id_is_valid(some_random_valid_id);
155154
// is_valid_now == false;
156155

157156
}
158157
```
159-
slot_map and controlled_slot_map also have id_is_valid(id_type id) method defined.
158+
`rea::slot_list` and `rea::controlled_slot_list` also have `id_is_valid(id_type id)` method defined.
160159
It's a constexpr function which always returns true.
161160

162161

163162

164-
### variation 4 : regulated_slot_map
165-
If you need both the ability to set an empty value and to keep a version count, use regulated_slot_map. Its second template argument
166-
is a functor which returns an empty value, and its third is an IntegralType to be used for versioning. They are defaulted same as controlled_slot_map and versiond_slot_map.
163+
### variation 4 : regulated_slot_list
164+
If you need both the ability to set an empty value and to keep a version count, use `rea::regulated_slot_list`. Its second template argument is functor which returns an empty value, and its third is an IntegralType to be used for versioning. They are defaulted same as `rea::controlled_slot_list` and `rea::versiond_slot_list`.
167165

168166
```cpp
169167
rea::versioned_slot_map<T, // value_type
@@ -172,7 +170,7 @@ rea::versioned_slot_map<T, // value_type
172170
S = std::size_t, // size_type
173171
A = std::allocator<T>> // allocator_type
174172
```
175-
Here, slots store same things as in the version_slot_map.
173+
Here, slots store same things as in the `rea::version_slot_list`.
176174

177175
```cpp
178176
struct get_empty_string {
@@ -181,48 +179,48 @@ struct get_empty_string {
181179
}
182180
}
183181

184-
rea::regulated_slot_map<std::string, get_empty_string> sm_strings;
182+
rea::regulated_slot_list<std::string, get_empty_string> sm_strings;
185183
```
186184
187185
188-
# DenseMap
189-
If like in the SlotMap you need constant time insertion, removal, and lookup, as well as cache friendly iteration through a contiguous array, use DenseMap.
186+
# SlotMap
187+
If like in the SlotList you need constant time insertion, removal, and lookup, as well as cache friendly iteration through a contiguous array, use SlotMap.
190188
191-
Implementation details are given below, although there is a video which explains exactly what this data structure is. In [the video](https://www.youtube.com/watch?v=SHaAR7XPtNU) this data structure is called SlotMap not DenseMap. If you've seen it, "Implementation" section won't give you any more details and could be skipped.
189+
Implementation details are given below, although there is a [video](https://www.youtube.com/watch?v=SHaAR7XPtNU) which explains exactly what this data structure is. If you've seen it, "Implementation" section won't give you any more details and could be skipped.
192190
193191
## Implementation
194-
DenseMap is internally implemented as 2 std::vectors and a slot_map like data structure.
192+
SlotMap is internally implemented as 2 std::vectors and a slot_list like data structure.
195193
- vector 1 = *ValueContainer*;
196194
- vector 2 = *IDPosContainer*;
197-
- slot_map = *IDSlotContainer*;
195+
- slot_list = *IDSlotContainer*;
198196
199-
*ValueContainer* stores objects of type "value_type" of the DenseMap, and just like any other vector they are stored contiguously. The *IDSlotContainer* stores indices which point to an object inside the *ValueContainer*. Once an object is erased, last object inside the *ValueContainer* is moved into its place, hence all objects remain densely packed at the cost of not preserving order. Slot of the *IDSlotContainer* which points to the erased object becomes available for reuse.
197+
*ValueContainer* stores objects of type "value_type" of the SlotMap, and just like any other vector they are stored contiguously. The *IDSlotContainer* stores indices which point to an object inside the *ValueContainer*. Once an object is erased, last object inside the *ValueContainer* is moved into its place, hence all objects remain densely packed at the cost of not preserving order. Slot of the *IDSlotContainer* which points to the erased object becomes available for reuse.
200198
201199
Now we have a problem though. The slot which pointed to the last object inside *ValueContainer* now points to past the end object. In order to find that slot and update it to point to a new location, we introduce the *IDPosContainer*.
202200
203201
*IDPosContainer* stores indices of *IDSlotContainer* slots, which correspond to objects stored *ValueContainer*. E.g., third object of *IDPosContainer* is an index of an *IDSlotContainer* slot, and that slot points to the third object of *ValueContainer*. Once past the end object is moved to the erased location, its index inside *IDSlotContainer* is also moved to the corresponding location of *IDSlotContainer*. In that way all lookup operations are done in constant time.
204202
205203
## Usage
206-
As stated earlier the main difference between the SlotMap and the DenseMap is in iteration. It's not possible to iterate through the objects stored in DenseMap using their ids. IDs can only be used for lookup. For iteration regular RandomAccess iterators are used (by default std::vector::iterator, as with SlotMap you can change the internal containers, "Discussion" section shows how to do that).
204+
As stated earlier the main difference between the SlotList and the SlotMap is in iteration. It's not possible to iterate through the objects stored in SlotMap using their ids. IDs can only be used for lookup. For iteration regular RandomAccess iterators are used (by default `std::vector::iterator`, as with SlotList you can change the internal containers, "Discussion" section shows how to do that).
207205
208-
Considering all of the users objects are kept in a contiguous array, and all erased objects are destructed, there is no need for controlled or regulated version of DenseMap.
206+
Considering all of the users objects are kept in a contiguous array, and all erased objects are destructed, there is no need for controlled or regulated version of SlotMap.
209207
210-
All DenseMaps, just like all SlotMaps, have the same first, and the last 2 template arguments.
208+
All SlotMaps, just like all SlotLists, have the same first, and the last 2 template arguments.
211209
```cpp
212-
some_dense_map<T, // value_type
213-
.
214-
.
215-
.
216-
S = std::size_t, // size_type
217-
A = std::allocator<T>>; // allocator_type
210+
some_slot_map<T, // value_type
211+
.
212+
.
213+
.
214+
S = std::size_t, // size_type
215+
A = std::allocator<T>>; // allocator_type
218216
```
219217

220-
### variation 1 : dense_map
221-
`rea::dense_map` acts as a basic DenseMap. It has no additional template arguments, only the ones described above.
218+
### variation 1 : slot_map
219+
`rea::slot_map` acts as a basic SlotMap. It has no additional template arguments, only the ones described above.
222220
```cpp
223-
rea::dense_map<T, // value_type
224-
S = std::size_t, // size_type
225-
A = std::allocator<T>> // allocator_type
221+
rea::slot_map<T, // value_type
222+
S = std::size_t, // size_type
223+
A = std::allocator<T>> // allocator_type
226224
```
227225

228226
```cpp
@@ -231,21 +229,21 @@ rea::dense_map<T, // value_type
231229
#include <iostream>
232230

233231
int main() {
234-
using dm_type = rea::dense_map<int>;
235-
using dm;
236-
std::vector<dm_type::id_type> ids;
232+
using sm_type = rea::slot_map<int>;
233+
sm_type sm;
234+
std::vector<sm_type::id_type> ids;
237235
ids.reserve(1000);
238236
sm.reserve(1000);
239237

240238
// insertion
241239
for(size_t i = 0; i < 1000; ++i) {
242-
dm.push_back(sm.insert({i, i*2}));
240+
ids.push_back(sm.insert({i, i*2}));
243241
}
244242

245243
// erasure
246244
for(size_t i = 0; i < 100; ++i) {
247245
auto id = get_and_remove_rand_value(ids); //this function is defined in slot map code above
248-
dm.erase(id);
246+
sm.erase(id);
249247
}
250248

251249
// lookup
@@ -264,19 +262,17 @@ int main() {
264262
// "ids" are know in value iteration order
265263
}
266264
```
267-
### variation 2 : versioned_dense_map
268-
`rea::versioned_dense_map` is to `rea::dense_map`, what `rea::versioned_slot_map` is to `rea::slot_map`. It keeps a version count for you.
269-
And just like for `rea::versioned_slot_map`, `rea::versioned_dense_map::id_is_valid(id_type)` method may return false if the version counts dont match.
265+
### variation 2 : versioned_slot_map
266+
`rea::versioned_slot_map` is to `rea::slot_map`, what `rea::versioned_slot_list` is to `rea::slot_list`. It keeps a version count for you. And just like for `rea::versioned_slot_list`, `rea::versioned_slot_map::id_is_valid(id_type)` method may return false if the version counts dont match.
270267
```cpp
271-
rea::versioned_dense_map<T, // value_type
272-
V = std::size_t, // version_type
273-
S = std::size_t, // size_type
274-
A = std::allocator<T>> // allocator_type
268+
rea::versioned_slot_map<T, // value_type
269+
V = std::size_t, // version_type
270+
S = std::size_t, // size_type
271+
A = std::allocator<T>> // allocator_type
275272
```
276273
# Installation
277274
Include "rea.h" header file in your project, and you're ready to go. It should work with any C++11 compliant compiler.
278275

279276
# Discussion
280277
Discussion section will be added shortly in the future.
281278

282-

0 commit comments

Comments
 (0)