A high-performance, configurable B+ tree implementation for persistent key-value storage with pluggable storage backends. This library provides a clean, type-safe interface for building persistent data structures with customizable storage backends.
- π§ Pluggable Storage: Generic
BlockStoragetrait enables different storage backends - π Unified Node Structure: Single
UniversalNode<K, V>for both internal and leaf nodes - ποΈ Configurable Block Size: Adapts to different storage characteristics
- π High Performance: Optimized algorithms with efficient serialization
- π‘ Type Safe: Full Rust type safety with serde integration for any serializable types
- πΎ Persistent: Atomic writes with crash recovery and metadata management
- π§ͺ Well Tested: Comprehensive test suite with integration tests
- π Range Queries: Efficient range scan capabilities
- ποΈ Full Operations: Complete CRUD operations with proper B+ tree balancing
- π’ Entry Tracking: O(1) access to total entry count
- π Iterators: Sequential and range iterators with error handling
- β Validation: Built-in tree integrity and invariant checking
src/
βββ lib.rs # Main library interface and exports
βββ bptree/ # B+ tree implementation
β βββ mod.rs # Module exports
β βββ node.rs # Unified node structure (internal/leaf)
β βββ tree.rs # Core B+ tree operations and persistence
β βββ iterator.rs # Iterator implementation
β βββ error.rs # B+ tree specific error types
βββ storage/ # Storage abstraction layer
β βββ mod.rs # Module exports
β βββ storage_trait.rs # BlockStorage trait interface
β βββ file.rs # Default file-based storage implementation
βββ ser/ # Serialization utilities
β βββ mod.rs # Block-aware serialization with padding
βββ tests/ # Integration tests
use bptree::{BPTree, FileBlockStorage};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create storage with 4KB blocks
let storage = FileBlockStorage::new("./bptree_data", 4096)?;
// Create B+ tree
let mut bptree: BPTree<String, String, _> = BPTree::new(storage)?;
// Insert some data
bptree.insert("hello".to_string(), "world".to_string())?;
bptree.insert("rust".to_string(), "awesome".to_string())?;
// Check size
println!("Entries: {}", bptree.len()); // 2
// Retrieve data
if let Some(value) = bptree.get(&"hello".to_string())? {
println!("hello: {}", value);
}
// Range iterator
for result in bptree.iter_range("h".to_string().."z".to_string())? {
let (key, value) = result?;
println!("{}: {}", key, value);
}
// Validate integrity
bptree.validate()?;
Ok(())
}use bptree::{BPTree, BlockStorage, BlockId};
use std::io::Result;
struct CustomStorage {
// Custom implementation details...
}
impl BlockStorage for CustomStorage {
type Error = std::io::Error;
// implement functions here
}
// B+ tree with custom storage
let mut tree: BPTree<String, i32, CustomStorage> = BPTree::new(custom_storage)?;use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
struct UserKey {
user_id: u64,
timestamp: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
struct UserValue {
name: String,
email: String,
}
let mut tree: BPTree<UserKey, UserValue, FileBlockStorage> = BPTree::new(storage)?;
// Insert complex data
let key = UserKey { user_id: 123, timestamp: 1640995200 };
let value = UserValue {
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
};
tree.insert(key, value)?;// Single insert
bptree.insert("key1".to_string(), "value1".to_string())?;
// Batch inserts (loop through data)
for (k, v) in data {
bptree.insert(k, v)?;
}// Exact match
if let Some(value) = bptree.get(&"key1".to_string())? {
println!("Found: {}", value);
}
// Range query
for (key, value) in bptree.range("a".to_string().."m".to_string())? {
println!("{}: {}", key, value);
}// Remove single key, returns old value
if let Some(old_val) = bptree.delete(&"key1".to_string())? {
println!("Removed: {}", old_val);
}- Insert: O(log n) operations with automatic rebalancing
- Search: O(log n) worst-case, O(1) for direct key lookup
- Delete: O(log n) with proper tree rebalancing
- Space Efficiency: ~90% block utilization with intelligent padding
- Serialization Overhead: ~5-10% per node with block-aware encoding
- Rust 1.70+ with edition 2024
- Standard library (no
no_stdsupport currently)
Add to your Cargo.toml:
[dependencies]
bptree = { path = "/path/to/bptree" }The library provides comprehensive error types:
use bptree::BPTreeError;
match bptree.insert(key, value) {
Ok(_) => println!("Insert successful"),
Err(BPTreeError::Storage(e)) => eprintln!("Storage error: {}", e),
Err(BPTreeError::NodeCorrupted) => eprintln!("Tree corruption detected"),
Err(BPTreeError::ValidationFailed(msg)) => eprintln!("Validation failed: {}", msg),
_ => eprintln!("Other B+ tree error"),
}- Structure: Flat directory with
block_0,block_1, etc. - Atomic Writes: Temporary files + rename for crash safety
- Free Space Management: Bitmap tracking of deallocated blocks
- Metadata: Special block (ID 0) for tree metadata
- Block IDs: Sequential integers starting from 1 (0 reserved for metadata)
- Allocation: Automatic block allocation with free space tracking
- Deallocation: Proper cleanup with bitmap management
- Padding: Data padded to exact block boundaries for efficiency
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
This implementation provides a production-ready foundation for persistent B+ tree storage with excellent extensibility through its trait-based storage design and unified node structure.