- Notifications
You must be signed in to change notification settings - Fork15
A slot map is a high-performance associative container with persistent unique 32/64 bit keys to access stored values.
License
SergeyMakeev/SlotMap
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
A Slot Map is a high-performance associative container with persistent unique keys to access stored values. It's designed for performance-critical applications where stable references, O(1) operations, and memory efficiency are essential.
- What is a Slot Map?
- Key Features
- Quick Start
- Building
- API Reference
- Key Types
- Advanced Features
- Performance
- Implementation Details
- Thread Safety
- Examples
- References
A Slot Map solves the problem of storing collections of objects that need stable, safe references but have no clear ownership hierarchy. Unlikestd::unordered_map where you provide keys, a slot mapgenerates unique keys when you insert values.
Key differences fromstd::unordered_map:
- Slot map generates keys for you (no key collisions)
- Guaranteed O(1) insertion, removal, and access
- Memory-stable pointers (values never move in memory)
- Automatic key invalidation when items are removed
- Efficient memory layout with page-based allocation
Perfect for:
- Game entities and component systems
- Resource management (textures, sounds, etc.)
- Object pools and handle-based systems
- Any scenario requiring safe, stable references
- O(1) Performance: Insertion, removal, and access are all O(1) in best, worst, and average case
- Memory Stability: Pointers to values remain valid until explicitly removed
- Safe References: Keys automatically become invalid when items are removed
- Memory Efficient: Page-based allocation prevents memory fragmentation
- Type Safety: Keys are typed to prevent mixing between different slot maps
- Iteration Support: Both value-only and key-value iteration
- Custom Allocators: Support for custom memory allocators
- Header Only: Single header file, easy to integrate
#include"slot_map.h"// Create a slot map for stringsdod::slot_map<std::string> strings;// Insert values and get unique keysauto red_key = strings.emplace("Red");auto green_key = strings.emplace("Green");auto blue_key = strings.emplace("Blue");// Access values using keysconst std::string* red_value = strings.get(red_key);if (red_value) {printf("Color: %s\n", red_value->c_str());// Output: Color: Red}// Remove a valuestrings.erase(green_key);// Check if keys are still validprintf("Green exists: %d\n", strings.has_key(green_key));// Output: 0printf("Blue exists: %d\n", strings.has_key(blue_key));// Output: 1// Iterate over all valuesfor (constauto& color : strings) {printf("Color: %s\n", color.c_str());}// Iterate over key-value pairsfor (constauto& [key, color] : strings.items()) {printf("Key: %" PRIslotkey", Color: %s\n",uint64_t(key), color.get().c_str());}
- C++17 or later
- CMake 3.10 or later (for building tests)
Simply include the header file in your project:
#include"slot_map/slot_map.h"
git clone https://github.com/SergeyMakeev/SlotMap.gitcd SlotMapmkdir build&&cd buildcmake ..cmake --build.# Run tests./SlotMapTest01./SlotMapTest02./SlotMapTest03./SlotMapTest04
Define custom allocator macros before including the header:
#defineSLOT_MAP_ALLOC(sizeInBytes, alignment) your_aligned_alloc(alignment, sizeInBytes)#defineSLOT_MAP_FREE(ptr) your_free(ptr)#include"slot_map.h"
Constructs element in-place and returns a unique key.
auto key = slot_map.emplace("Hello","World");// Construct string from args
Returns pointer to value ornullptr if key is invalid.
const std::string* value = slot_map.get(key);Returnstrue if the key exists and is valid.
if (slot_map.has_key(key)) {/* key is valid*/ }
Removes element if key exists. Key becomes invalid.
slot_map.erase(key);
Removes and returns the value if key exists.
auto value = slot_map.pop(key);// Returns optional<T>
Returns number of elements.
Returnstrue if container is empty.
Removes all elements but keeps allocated memory. Invalidates all keys.
Removes all elements and releases memory.Warning: Only call when no keys are in use.
Exchanges contents with another slot map.
for (constauto& value : slot_map) {// Process value}
for (constauto& [key, value] : slot_map.items()) {// Process key and value}
Returns internal statistics (O(n) complexity).
auto stats = slot_map.debug_stats();printf("Active items: %u\n", stats.numAliveItems);
dod::slot_map<T>// Uses 64-bit keysdod::slot_map64<T>// Explicit 64-bit keys
| Component | Bits | Range |
|---|---|---|
| Tag | 12 | 0..4,095 |
| Version | 20 | 0..1,048,575 |
| Index | 32 | 0..4,294,967,295 |
dod::slot_map32<T>// Uses 32-bit keys| Component | Bits | Range |
|---|---|---|
| Tag | 2 | 0..3 |
| Version | 10 | 0..1,023 |
| Index | 20 | 0..1,048,575 |
auto key = slot_map.emplace(value);// Type-safe: this won't compile// slot_map<int>::key int_key = int_map.emplace(42);// string_map.get(int_key); // Compiler error!// Convert to/from raw numeric typeuint64_t raw_key = key;// Implicit conversionslot_map<T>::key restored_key{raw_key};// Explicit construction
Keys can store small amounts of user data:
auto key = slot_map.emplace("Value");key.set_tag(42);// Store application dataauto tag = key.get_tag();// Retrieve: tag == 42// Tag limits:// 64-bit keys: 0..4,095 (12 bits)// 32-bit keys: 0..3 (2 bits)
Adjust memory allocation granularity:
// Default page size is 4096 elementsdod::slot_map<T, dod::slot_map_key64<T>,8192> large_pages;dod::slot_map<T, dod::slot_map_key64<T>,1024> small_pages;
Control when slot indices are reused:
// Default threshold is 64dod::slot_map<T, dod::slot_map_key64<T>,4096,128> conservative_reuse;
- Insertion: O(1) amortized
- Removal: O(1)
- Access: O(1)
- Iteration: O(n) where n is number of alive elements
- Page-based allocation: 4096 elements per page by default
- Pointer stability: Values never move once allocated
- Memory efficiency: Pages are released when all slots become inactive
- Cache-friendly: Sequential iteration over alive elements only
When a slot's version counter reaches maximum value:
- The slot is marked as inactive
- No new keys will be generated for this slot
- Guarantees no key collisions even with version wrap-around
- Recently freed slots aren't immediately reused
- Minimum threshold (default 64) prevents rapid version increments
- Balances memory usage with collision avoidance
- Elements are allocated in pages (default 4096 elements)
- Pages are released when all slots in a page become inactive
- Provides memory stability and prevents fragmentation
Slot maps are NOT thread-safe. External synchronization is required for:
- Concurrent access from multiple threads
- Reader-writer scenarios
For thread-safe usage:
std::shared_mutex mutex;dod::slot_map<T> slot_map;// Reader{ std::shared_locklock(mutex);auto value = slot_map.get(key);}// Writer{ std::unique_locklock(mutex);auto key = slot_map.emplace(value);}
structTransform {float x, y, z; };structHealth {int hp, max_hp; };dod::slot_map<Transform> transforms;dod::slot_map<Health> healths;// Create entityauto entity_id = generate_entity_id();auto transform_key = transforms.emplace(Transform{0,0,0});auto health_key = healths.emplace(Health{100,100});// Store keys in entityregister_component(entity_id, transform_key);register_component(entity_id, health_key);
dod::slot_map<Texture> textures;dod::slot_map<Sound> sounds;classResourceManager {using TextureHandle = dod::slot_map<Texture>::key;using SoundHandle = dod::slot_map<Sound>::key; TextureHandleload_texture(const std::string& path) {return textures.emplace(load_texture_from_file(path)); }const Texture*get_texture(TextureHandle handle) {return textures.get(handle); }};
classBulletPool {structBullet {float x, y, dx, dy;bool active; }; dod::slot_map<Bullet> bullets;public:using BulletHandle = dod::slot_map<Bullet>::key; BulletHandlespawn(float x,float y,float dx,float dy) {return bullets.emplace(Bullet{x, y, dx, dy,true}); }voidupdate() {for (auto& bullet : bullets) {if (bullet.active) { bullet.x += bullet.dx; bullet.y += bullet.dy; } } }voiddestroy(BulletHandle handle) { bullets.erase(handle);// Handle becomes invalid automatically }};
- Sean Middleditch:Data Structures for Game Developers: The Slot Map, 2013
- Niklas Gray:Building a Data-Oriented Entity System, 2014
- Noel Llopis:Managing Data Relationships, 2010
- Stefan Reinalter:Adventures in Data-Oriented Design - External References, 2013
- Niklas Gray:Managing Decoupling Part 4 - The ID Lookup Table, 2011
- Sander Mertens:Making the Most of ECS Identifiers, 2020
- Michele Caini:ECS back and forth. Part 9 - Sparse Sets and EnTT, 2020
- Andre Weissflog:Handles are the Better Pointers, 2018
- Allan Deutsch:C++Now 2017: "The Slot Map Data Structure", 2017
- Jeff Gates:Init, Update, Draw - Data Arrays, 2012
- Niklas Gray:Data Structures Part 1: Bulk Data, 2019
About
A slot map is a high-performance associative container with persistent unique 32/64 bit keys to access stored values.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.