SweepStore (formerly BinaryTable)
A high-performance binary storage format for Dart applications with efficient memory management and random access capabilities.
Overview
BinaryTable is a key-value storage system designed for applications requiring fast, compact data persistence. It features a custom binary format with automatic memory management, making it ideal for game save files, caching systems, and other performance-critical data storage needs.
Features
Core Storage Types
- Integers: 32-bit signed integers
- Floats: 32-bit floating-point numbers
- Strings: Variable-length UTF-16 strings
- Uniform Arrays: Homogeneous arrays of integers or floats with O(1) random access
Memory Management
- Free List System: Automatic memory reclamation and defragmentation
- Block Merging: Contiguous free blocks are automatically merged
- File Truncation: Unused space at end of file is reclaimed
- Lift/Drop Mechanism: Prevents free list fragmentation during operations
Performance Features
- Random Access: Direct addressing for all data types
- Minimal Seeks: Optimized file layout reduces disk I/O
- Compact Format: No padding or alignment overhead
- Efficient Arrays: Uniform arrays use offset calculations for O(1) access
Usage
Basic Operations
// Initialize
File file = File('data.bin');
file.createSync();
BinaryTable table = BinaryTable(file.path);
table.initialise();
// Store values
table["player_level"] = 42;
table["player_name"] = "Hero";
table["score"] = 1337.5;
// Retrieve values
int level = table["player_level"];
String name = table["player_name"];
double score = table["score"];
// Delete entries
table.delete("old_data");
Array Operations
// Create arrays
table["inventory"] = [10, 20, 30, 40, 50];
table["coordinates"] = [1.5, 2.7, 3.9];
// Random access
table["inventory"][0] = 15;
double y = table["coordinates"][1];
// Dynamic growth
table["inventory"].add(60);
table["inventory"].addAll([70, 80, 90]);
// Pre-sized arrays (recommended for game data)
table["heightmap"] = List.filled(1024 * 1024, 0.0);
Memory Management
// Reclaim unused space
table.truncate();
// Manual cleanup (usually automatic)
table.delete("unused_key");
table.truncate();
Version History
Version 3.0 - Current
Added: Uniform Arrays
BT_UniformArrayclass for homogeneous array storage- O(1) random access for integer and float arrays
- Dynamic array growth with automatic reallocation
- Type safety enforcement for array elements
- Support for empty arrays with type inference on first use
Improvements:
- Enhanced memory management for variable-size data
- Optimized file format for array storage
- Better error handling for type mismatches
Version 2.0
Added: Persistent Free Lists
- Free list data persists across program restarts
- Automatic block merging for defragmentation
- File truncation to reclaim unused space
- "Read from end" encoding for free list metadata
Improvements:
- Eliminated memory leaks from previous version
- Reduced file size growth over time
- Better space utilization efficiency
Version 1.0
Core Features:
- Basic key-value storage (integers, floats, strings)
- Binary file format with type identification
- Address table for O(1) key lookups
- Volatile garbage collection (in-memory only)
Initial Implementation:
- RandomAccessFile-based storage
- Little-endian encoding
- Hash-based key addressing
File Format
The BinaryTable uses a custom binary format optimized for random access:
Header Structure
[Address Table Pointer: 8 bytes]
[Free List Entry Count: 4 bytes]
Data Layout
- Address Table: Hash-to-pointer mappings at variable location
- Free List: Memory management metadata at end of file
- Data Blocks: Variable-size type-prefixed data scattered throughout
Type Encoding
- Each value prefixed with type identifier byte
- Variable-length data includes size information
- Arrays store length followed by elements
Performance Characteristics
Time Complexity
- Key Lookup: O(1) average case
- Array Access: O(1) for uniform arrays
- Insertion: O(1) amortized
- Deletion: O(1) plus defragmentation cost
Space Efficiency
- Overhead: ~13 bytes per key-value pair minimum
- Arrays: 5 bytes + (5 bytes × length) for uniform arrays
- Fragmentation: Automatically managed and minimized
Use Cases
Game Development
- Save file persistence with fast loading
- Level data storage (heightmaps, object placement)
- Player progression and inventory systems
- Settings and configuration data
General Applications
- High-performance caching systems
- Embedded database applications
- Configuration file storage
- Any scenario requiring fast binary persistence
Limitations
- Hash Collisions: Uses Dart's built-in string hashCode (risk of collisions)
- File Corruption: No built-in checksums or validation
- Concurrent Access: Single-threaded access only
- Type System: Limited compared to JSON (no nested objects, mixed arrays)
SweepStore Roadmap
v1.0.0 (Current)
- ✅ Key-value storage with hash-based indexing
- ✅ Uniform arrays with random access
- ✅ Automatic memory management via free list
- ✅ Single file format (.sws)
v2.0.0 (Planned)
Core Changes
- Nested objects - Store objects within objects for hierarchical data
- Key enumeration - Query and list all keys without knowing them upfront
- Object-based architecture - Each object has its own address table and key list
File Format Changes
v1: Global address table → All keys at root level
v2: Root object → Each object manages its own keys
New API
// Nested objects
table["player"] = {};
table["player"]["name"] = "Alice";
table["player"]["inventory"] = {};
// Key discovery
List<String> keys = table.keys();
bool exists = table.containsKey("player");
// Object operations
BT_Object player = table["player"];
for (String key in player.keys()) {
print("$key: ${player[key]}");
}
Implementation Strategy
- Introduce
BT_Objecttype with embedded address table + key list BinaryTablebecomes wrapper around root object- Maintain global free list (not per-object)
- Address table entries remain absolute pointers
- Key list stored alongside each object's address table
Breaking Changes
- File format incompatible with v1.0.0
- API remains largely the same (only additions)
- Migration tool needed for v1 → v2 files
Future Considerations
- Hash collision resolution
- Checksums for data integrity
- Optimized allocation strategies
- Incremental array resizing
- Compression options
Releases
1
The first release
Latest
Languages
C++
68.9%
Dart
29.6%
CMake
1.5%