├── core ├── rustfmt.toml ├── benches │ ├── units │ │ ├── mod.rs │ │ ├── basic_mapx_raw.rs │ │ └── batch_write.rs │ └── basic.rs ├── src │ ├── basic │ │ ├── mod.rs │ │ └── mapx_raw │ │ │ └── test.rs │ ├── lib.rs │ └── common │ │ └── mod.rs ├── Cargo.toml ├── docs │ └── api.md ├── README.md └── tests │ └── basic_mapx_raw_test.rs ├── wrappers ├── rustfmt.toml ├── src │ ├── common │ │ ├── mod.rs │ │ └── macros.rs │ ├── basic_multi_key │ │ ├── mod.rs │ │ ├── mapx_double_key │ │ │ ├── test.rs │ │ │ └── mod.rs │ │ ├── mapx_triple_key │ │ │ ├── test.rs │ │ │ └── mod.rs │ │ ├── mapx_raw │ │ │ └── test.rs │ │ └── mapx_rawkey │ │ │ ├── test.rs │ │ │ └── mod.rs │ ├── basic │ │ ├── mod.rs │ │ ├── orphan │ │ │ └── test.rs │ │ ├── mapx_ord_rawvalue │ │ │ └── test.rs │ │ ├── mapx │ │ │ └── test.rs │ │ ├── mapx_ord │ │ │ └── test.rs │ │ └── mapx_ord_rawkey │ │ │ └── test.rs │ ├── lib.rs │ └── dagmap │ │ ├── mod.rs │ │ ├── rawkey │ │ ├── test.rs │ │ └── mod.rs │ │ └── raw │ │ └── test.rs ├── benches │ ├── units │ │ ├── mod.rs │ │ ├── batch_vs_normal.rs │ │ ├── basic_multi_key_mapx_double_key.rs │ │ ├── basic_multi_key_mapx_triple_key.rs │ │ ├── basic_mapx_ord.rs │ │ ├── basic_mapx_ord_rawvalue.rs │ │ ├── basic_mapx_ord_rawkey.rs │ │ ├── basic_multi_key_mapx_raw.rs │ │ ├── basic_mapx.rs │ │ └── basic_multi_key_mapx_rawkey.rs │ └── basic.rs ├── tests │ ├── basic_multi_key_mapx_double_key_test.rs │ ├── basic_multi_key_mapx_triple_key_test.rs │ ├── batch_api_test.rs │ ├── basic_mapx_test.rs │ ├── basic_mapx_ord_rawkey_test.rs │ ├── basic_mapx_ord_rawvalue_test.rs │ ├── basic_orphan_test.rs │ ├── basic_mapx_ord_test.rs │ ├── basic_multi_key_mapx_rawkey_test.rs │ └── basic_multi_key_mapx_raw_test.rs ├── docs │ └── api.md ├── Cargo.toml └── README.md ├── utils ├── slot_db │ ├── rustfmt.toml │ ├── Cargo.toml │ ├── docs │ │ └── api.md │ ├── README.md │ ├── benches │ │ ├── 2_slot_db_reverse.rs │ │ ├── 0_slot_db_reverse_swap_order.rs │ │ └── 1_slot_db_positive.rs │ └── src │ │ └── test.rs └── trie_db │ ├── src │ ├── config.rs │ ├── trie │ │ ├── mod.rs │ │ └── query.rs │ ├── storage │ │ ├── mod.rs │ │ └── vsdb_impl.rs │ ├── error.rs │ ├── node │ │ ├── mod.rs │ │ └── codec.rs │ ├── test.rs │ ├── lib.rs │ └── nibbles.rs │ ├── Makefile │ ├── Cargo.toml │ ├── LICENSE │ ├── docs │ └── api.md │ └── README.md ├── .cargo └── config.toml ├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── tools └── fmt.sh ├── Makefile └── README.md /core/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 89 2 | -------------------------------------------------------------------------------- /wrappers/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 89 2 | -------------------------------------------------------------------------------- /utils/slot_db/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 79 2 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [net] 2 | git-fetch-with-cli = true 3 | -------------------------------------------------------------------------------- /core/benches/units/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_mapx_raw; 2 | pub mod batch_write; 3 | -------------------------------------------------------------------------------- /utils/trie_db/src/config.rs: -------------------------------------------------------------------------------- 1 | pub const HASH_LEN: usize = 32; 2 | // pub const NIBBLE_LEN: usize = 16; 3 | -------------------------------------------------------------------------------- /utils/trie_db/src/trie/mod.rs: -------------------------------------------------------------------------------- 1 | mod mutation; 2 | mod query; 3 | 4 | pub use mutation::TrieMut; 5 | pub use query::TrieRo; 6 | -------------------------------------------------------------------------------- /core/benches/basic.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | mod units; 4 | 5 | criterion::criterion_main! { 6 | units::basic_mapx_raw::benches, 7 | units::batch_write::benches, 8 | } 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | open-pull-requests-limit: 10 6 | schedule: 7 | interval: "daily" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug/ 2 | release/ 3 | 4 | target 5 | 6 | **/*.rs.bk 7 | .DS_Store 8 | **/*~ 9 | **/.vscode 10 | .*.sw* 11 | .stack-work 12 | stack.yaml.lock 13 | **/history.txt 14 | **/__pycache__ 15 | .idea 16 | 17 | node_modules 18 | package-lock.json 19 | 20 | Cargo.lock 21 | *.tar.gz 22 | nohup.out 23 | 24 | log_tester.tar.gz* 25 | -------------------------------------------------------------------------------- /utils/trie_db/src/storage/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | 3 | pub trait TrieBackend: Send + Sync { 4 | fn get(&self, key: &[u8]) -> Result>>; 5 | fn insert_batch(&mut self, batch: Vec<(Vec, Vec)>) -> Result<()>; 6 | fn remove_batch(&mut self, keys: &[Vec]) -> Result<()>; 7 | } 8 | 9 | pub mod vsdb_impl; 10 | -------------------------------------------------------------------------------- /wrappers/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Common Components 3 | //! 4 | //! This module provides common components and utilities used throughout the `vsdb` wrappers. 5 | //! It re-exports items from `vsdb_core::common` and includes the `ende` module for 6 | //! encoding and decoding traits. 7 | //! 8 | 9 | /// A module for encoding and decoding traits. 10 | pub mod ende; 11 | pub mod macros; 12 | 13 | pub use vsdb_core::common::*; 14 | -------------------------------------------------------------------------------- /core/src/basic/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Basic Unversioned Data Structures 3 | //! 4 | //! This module provides fundamental, unversioned data structures that serve as 5 | //! the building blocks for more complex, versioned structures in the VSDB framework. 6 | //! These structures are designed for direct, high-performance access to the 7 | //! underlying key-value store without versioning overhead. 8 | //! 9 | 10 | /// A module for the raw, unversioned map implementation. 11 | pub mod mapx_raw; 12 | -------------------------------------------------------------------------------- /wrappers/benches/units/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_multi_key_mapx_double_key; 2 | pub mod basic_multi_key_mapx_raw; 3 | pub mod basic_multi_key_mapx_rawkey; 4 | pub mod basic_multi_key_mapx_triple_key; 5 | 6 | pub mod basic_mapx; 7 | pub mod basic_mapx_ord; 8 | pub mod basic_mapx_ord_rawkey; 9 | pub mod basic_mapx_ord_rawvalue; 10 | pub mod batch_vs_normal; 11 | // Vecx and VecxRaw have been removed - they relied on unreliable len() tracking 12 | // pub mod basic_vecx; 13 | // pub mod basic_vecx_raw; 14 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Basic Data Structures with Multiple Keys 3 | //! 4 | //! This module provides fundamental data structures that support multiple keys, 5 | //! allowing for more complex data relationships and indexing strategies. 6 | //! 7 | 8 | /// A map with two keys. 9 | pub mod mapx_double_key; 10 | /// A map with raw keys and values. 11 | pub mod mapx_raw; 12 | /// A map with raw keys. 13 | pub mod mapx_rawkey; 14 | /// A map with three keys. 15 | pub mod mapx_triple_key; 16 | -------------------------------------------------------------------------------- /wrappers/benches/basic.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | mod units; 4 | 5 | criterion::criterion_main! { 6 | units::basic_multi_key_mapx_double_key::benches, 7 | units::basic_multi_key_mapx_raw::benches, 8 | units::basic_multi_key_mapx_rawkey::benches, 9 | units::basic_multi_key_mapx_triple_key::benches, 10 | 11 | units::basic_mapx::benches, 12 | units::basic_mapx_ord::benches, 13 | units::basic_mapx_ord_rawkey::benches, 14 | units::basic_mapx_ord_rawvalue::benches, 15 | units::batch_vs_normal::benches, 16 | } 17 | -------------------------------------------------------------------------------- /utils/trie_db/Makefile: -------------------------------------------------------------------------------- 1 | all: fmt lint test 2 | 3 | fmt: 4 | cargo fmt 5 | 6 | lint: 7 | cargo clippy --workspace 8 | cargo clippy --workspace --tests 9 | @ # cargo clippy --workspace --examples 10 | @ # cargo clippy --workspace --features="benchmark" 11 | 12 | musl_lint: 13 | if [ `uname -s` = "Linux" ]; then \ 14 | cargo clippy --workspace --target=x86_64-unknown-linux-musl; \ 15 | fi 16 | 17 | test: 18 | rm -rf ~/.vsdb 19 | cargo test --workspace -- --nocapture 20 | 21 | update: 22 | rustup update stable 23 | cargo update 24 | 25 | clean: 26 | cargo clean 27 | -------------------------------------------------------------------------------- /utils/trie_db/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum TrieError { 5 | #[error("Database error: {0}")] 6 | DatabaseError(String), 7 | 8 | #[error("Decoding error: {0}")] 9 | DecodeError(String), 10 | 11 | #[error("Invalid state: {0}")] 12 | InvalidState(String), 13 | 14 | #[error("Node not found: {0}")] 15 | NodeNotFound(String), 16 | } 17 | 18 | pub type Result = std::result::Result; 19 | 20 | impl From> for TrieError { 21 | fn from(e: Box) -> Self { 22 | TrieError::DatabaseError(e.to_string()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /wrappers/src/basic/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Basic Data Structures 3 | //! 4 | //! This module provides a collection of fundamental, unversioned data structures 5 | //! that wrap the raw storage implementations from `vsdb_core`. These wrappers 6 | //! offer typed APIs for keys and values, leveraging the encoding and decoding 7 | //! capabilities defined in the `common::ende` module. 8 | //! 9 | //! The structures available include various map and vector implementations, 10 | //! each tailored for specific use cases. 11 | //! 12 | 13 | //! 14 | //! Contains basic data structures with typed keys and values. 15 | //! 16 | 17 | pub mod mapx; 18 | pub mod mapx_ord; 19 | pub mod mapx_ord_rawkey; 20 | pub mod mapx_ord_rawvalue; 21 | pub mod orphan; 22 | // vecx and vecx_raw have been removed - they relied on unreliable len() tracking 23 | // pub mod vecx; 24 | // pub mod vecx_raw; 25 | -------------------------------------------------------------------------------- /utils/trie_db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vsdb_trie_db" 3 | version = "7.1.0" 4 | edition = "2024" 5 | keywords = ["mpt", "trie", "database", "blockchain"] 6 | license = "MIT" 7 | authors = ["hui.fan@mail.ru"] 8 | description = "A lightweight, production-grade Merkle Patricia Trie (MPT) implementation for vsdb" 9 | homepage = "https://github.com/rust-util-collections/vsdb" 10 | repository = "https://github.com/rust-util-collections/vsdb" 11 | 12 | [dependencies] 13 | serde = { workspace = true } 14 | ruc = { workspace = true } 15 | vsdb = { workspace = true } 16 | 17 | sha3 = { workspace = true } 18 | thiserror = { workspace = true } 19 | 20 | [dev-dependencies] 21 | rand = { workspace = true } 22 | 23 | [features] 24 | default = ["parity_backend", "msgpack_codec"] 25 | parity_backend = ["vsdb/parity_backend"] 26 | rocks_backend = ["vsdb/rocks_backend"] 27 | msgpack_codec = ["vsdb/msgpack_codec"] 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "core", 4 | "wrappers", 5 | "utils/trie_db", 6 | "utils/slot_db", 7 | ] 8 | resolver = "3" 9 | 10 | [workspace.dependencies] 11 | ruc = "9.0.0" 12 | thiserror = "2.0" 13 | 14 | rand = "0.9.2" 15 | parking_lot = "0.12.1" 16 | 17 | serde = { version = "1.0.136", features = ["derive"] } 18 | serde_json = { version = "1.0.87" } 19 | msgpack = { package = "rmp-serde", version = "1.1.1" } 20 | serde_cbor_2 = "0.13.0" 21 | sha3 = "0.10" 22 | 23 | threadpool = "1.8.1" # used in a background cleaner 24 | 25 | parity-db = { version = "0.5.4" } 26 | rocksdb = { version = "0.24.0", default-features = false, features = ["bindgen-runtime"] } 27 | 28 | vsdb = { path = "wrappers", version = "7.1.0", default-features = false } 29 | vsdb_core = { path = "core", version = "7.1.0", default-features = false } 30 | 31 | vsdb_trie_db = { path = "utils/trie_db", version = "7.1.0", default-features = false } 32 | -------------------------------------------------------------------------------- /wrappers/tests/basic_multi_key_mapx_double_key_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use vsdb::{basic_multi_key::mapx_double_key::MapxDk, vsdb_set_base_dir}; 3 | 4 | #[test] 5 | fn basic_cases() { 6 | info_omit!(vsdb_set_base_dir(&format!( 7 | "/tmp/vsdb_testing/{}", 8 | rand::random::() 9 | ))); 10 | 11 | let mut map = MapxDk::new(); 12 | 13 | map.insert(&(&1u8, &1u8), &9u8); 14 | map.insert(&(&1, &2), &8); 15 | map.insert(&(&1, &3), &7); 16 | 17 | assert_eq!(map.get(&(&1, &1)).unwrap(), 9); 18 | assert_eq!(map.get(&(&1, &2)).unwrap(), 8); 19 | assert_eq!(map.get(&(&1, &3)).unwrap(), 7); 20 | 21 | // does not exist 22 | map.remove(&(&1, Some(&4))); 23 | 24 | map.remove(&(&1, Some(&1))); 25 | assert!(map.get(&(&1, &1)).is_none()); 26 | 27 | // partial-path remove 28 | map.remove(&(&1, None)); 29 | assert!(map.get(&(&1, &2)).is_none()); 30 | assert!(map.get(&(&1, &3)).is_none()); 31 | 32 | map.entry(&(&1, &99)).or_insert(100); 33 | assert_eq!(map.get(&(&1, &99)).unwrap(), 100); 34 | } 35 | -------------------------------------------------------------------------------- /utils/slot_db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vsdb_slot_db" 3 | version = "7.1.0" 4 | edition = "2024" 5 | keywords = ["index", "cache", "timestamp", "slot", "database"] 6 | license = "MIT" 7 | authors = ["hui.fan@mail.ru"] 8 | description = "A skip-list like index cache" 9 | homepage = "https://github.com/rust-util-collections/vsdb" 10 | repository = "https://github.com/rust-util-collections/vsdb" 11 | 12 | [dependencies] 13 | ruc = { workspace = true } 14 | vsdb = { workspace = true } 15 | serde = { workspace = true } 16 | 17 | [dev-dependencies] 18 | rand = { workspace = true } 19 | criterion = "0.8" 20 | 21 | [features] 22 | default = ["parity_backend", "compress", "msgpack_codec"] 23 | 24 | parity_backend = ["vsdb/parity_backend"] 25 | rocks_backend = ["vsdb/rocks_backend"] 26 | 27 | compress = ["vsdb/compress"] 28 | 29 | json_codec = ["vsdb/json_codec"] 30 | msgpack_codec = ["vsdb/msgpack_codec"] 31 | 32 | [[bench]] 33 | name = "0_slot_db_reverse_swap_order" 34 | harness = false 35 | 36 | [[bench]] 37 | name = "1_slot_db_positive" 38 | harness = false 39 | 40 | [[bench]] 41 | name = "2_slot_db_reverse" 42 | harness = false 43 | -------------------------------------------------------------------------------- /wrappers/tests/basic_multi_key_mapx_triple_key_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use vsdb::{basic_multi_key::mapx_triple_key::MapxTk, vsdb_set_base_dir}; 3 | 4 | #[test] 5 | fn basic_cases() { 6 | info_omit!(vsdb_set_base_dir(&format!( 7 | "/tmp/vsdb_testing/{}", 8 | rand::random::() 9 | ))); 10 | 11 | let mut map = MapxTk::new(); 12 | 13 | map.insert(&(&1u8, &1u8, &1u8), &9u8); 14 | map.insert(&(&1, &1, &2), &8); 15 | map.insert(&(&1, &1, &3), &7); 16 | 17 | assert_eq!(map.get(&(&1, &1, &1)).unwrap(), 9); 18 | assert_eq!(map.get(&(&1, &1, &2)).unwrap(), 8); 19 | assert_eq!(map.get(&(&1, &1, &3)).unwrap(), 7); 20 | 21 | // does not exist 22 | map.remove(&(&1, Some((&1, Some(&4))))); 23 | 24 | map.remove(&(&1, Some((&1, Some(&1))))); 25 | assert!(map.get(&(&1, &1, &1)).is_none()); 26 | 27 | // partial-path remove 28 | map.remove(&(&1, Some((&1, None)))); 29 | assert!(map.get(&(&1, &1, &2)).is_none()); 30 | assert!(map.get(&(&1, &1, &3)).is_none()); 31 | 32 | map.entry(&(&1, &1, &99)).or_insert(100); 33 | assert_eq!(map.get(&(&1, &1, &99)).unwrap(), 100); 34 | } 35 | -------------------------------------------------------------------------------- /utils/trie_db/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Findora Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | # coverage: 14 | # runs-on: ubuntu-latest 15 | # steps: 16 | # - uses: actions/checkout@v2 17 | # - uses: actions-rs/toolchain@v1 18 | # with: 19 | # toolchain: stable 20 | # components: llvm-tools-preview 21 | # - name: Install cargo-llvm-cov 22 | # uses: taiki-e/install-action@cargo-llvm-cov 23 | # - name: Generate code coverage 24 | # run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info 25 | # - name: Upload coverage to Codecov 26 | # uses: codecov/codecov-action@v1 27 | # with: 28 | # files: lcov.info 29 | # fail_ci_if_error: true 30 | build: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v2 34 | - name: Build 35 | run: rustup update stable; rustup component add clippy; make lintall 36 | - name: Run tests 37 | run: rustup update stable; make testall 38 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vsdb_core" 3 | version = "7.1.0" 4 | authors = ["hui.fan@mail.ru"] 5 | edition = "2024" 6 | description = "A std-collection-like database" 7 | homepage = "https://github.com/rust-util-collections/vsdb" 8 | repository = "https://github.com/rust-util-collections/vsdb/tree/master/core" 9 | keywords = ["kv", "leveldb", "rocksdb", "std"] 10 | categories = ["database-implementations", "data-structures", "algorithms", "caching"] 11 | license = "GPL-3.0" 12 | 13 | [dependencies] 14 | ruc = { workspace = true } 15 | serde = { workspace = true } 16 | rand = { workspace = true } 17 | parking_lot = { workspace = true } 18 | 19 | threadpool = { workspace = true } # used in a background cleaner 20 | 21 | parity-db = { workspace = true, optional = true } 22 | rocksdb = { workspace = true, optional = true } 23 | 24 | [dev-dependencies] 25 | msgpack = { workspace = true } 26 | hex = "0.4" 27 | criterion = "0.8" 28 | 29 | [features] 30 | default = ["compress", "parity_backend"] 31 | 32 | parity_backend = ["parity-db"] 33 | rocks_backend = ["rocksdb"] 34 | 35 | compress = ["rocksdb?/zstd"] 36 | 37 | # [[bench]] 38 | # name = "basic" 39 | # harness = false 40 | -------------------------------------------------------------------------------- /utils/trie_db/src/storage/vsdb_impl.rs: -------------------------------------------------------------------------------- 1 | use super::TrieBackend; 2 | use crate::error::Result; 3 | use serde::{Deserialize, Serialize}; 4 | use vsdb::MapxOrdRawKey; 5 | 6 | #[derive(Clone, Serialize, Deserialize, Debug)] 7 | pub struct VsdbTrieBackend { 8 | inner: MapxOrdRawKey>, 9 | } 10 | 11 | impl VsdbTrieBackend { 12 | pub fn new() -> Self { 13 | Self { 14 | inner: MapxOrdRawKey::new(), 15 | } 16 | } 17 | 18 | pub fn clear(&mut self) { 19 | self.inner.clear(); 20 | } 21 | } 22 | 23 | impl Default for VsdbTrieBackend { 24 | fn default() -> Self { 25 | Self::new() 26 | } 27 | } 28 | 29 | impl TrieBackend for VsdbTrieBackend { 30 | fn get(&self, key: &[u8]) -> Result>> { 31 | Ok(self.inner.get(key)) 32 | } 33 | 34 | fn insert_batch(&mut self, batch: Vec<(Vec, Vec)>) -> Result<()> { 35 | for (k, v) in batch { 36 | self.inner.insert(&k, &v); 37 | } 38 | Ok(()) 39 | } 40 | 41 | fn remove_batch(&mut self, keys: &[Vec]) -> Result<()> { 42 | for k in keys { 43 | self.inner.remove(k); 44 | } 45 | Ok(()) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/docs/api.md: -------------------------------------------------------------------------------- 1 | # vsdb_core API Examples 2 | 3 | This document provides examples for the public APIs in the `vsdb_core` crate. 4 | 5 | ## MapxRaw 6 | 7 | `MapxRaw` is a raw key-value store. 8 | 9 | ```rust 10 | use vsdb_core::MapxRaw; 11 | use vsdb_core::common::{vsdb_set_base_dir, vsdb_get_base_dir}; 12 | 13 | // It's recommended to set a base directory for the database. 14 | // vsdb_set_base_dir("/tmp/vsdb_core_test").unwrap(); 15 | 16 | let mut map = MapxRaw::new(); 17 | 18 | // Insert raw bytes 19 | map.insert(b"key1", b"value1"); 20 | map.insert(b"key2", b"value2"); 21 | 22 | // Get raw bytes 23 | assert_eq!(map.get(b"key1").as_deref(), Some(&b"value1"[..])); 24 | 25 | // Check for existence 26 | assert!(map.contains_key(b"key2")); 27 | 28 | // Remove a key 29 | map.remove(b"key1"); 30 | assert!(!map.contains_key(b"key1")); 31 | ``` 32 | 33 | ## Utility Functions 34 | 35 | Example for getting and setting the base directory. 36 | 37 | ```rust 38 | use vsdb_core::{vsdb_set_base_dir, vsdb_get_base_dir}; 39 | 40 | // Set a custom base directory 41 | vsdb_set_base_dir("/tmp/my_vsdb_data").unwrap(); 42 | 43 | // Get the current base directory 44 | let dir = vsdb_get_base_dir(); 45 | assert_eq!(dir.to_str().unwrap(), "/tmp/my_vsdb_data"); 46 | ``` 47 | -------------------------------------------------------------------------------- /utils/trie_db/src/node/mod.rs: -------------------------------------------------------------------------------- 1 | mod codec; 2 | pub use codec::NodeCodec; 3 | 4 | use crate::nibbles::Nibbles; 5 | 6 | #[derive(Clone, Debug, PartialEq, Eq)] 7 | pub enum NodeHandle { 8 | InMemory(Box), 9 | Hash(Vec), 10 | Cached(Vec, Box), // Hash, Node 11 | } 12 | 13 | impl NodeHandle { 14 | // pub fn as_node(&self) -> Option<&Node> { 15 | // match self { 16 | // NodeHandle::InMemory(n) => Some(n), 17 | // NodeHandle::Cached(_, n) => Some(n), 18 | // NodeHandle::Hash(_) => None, 19 | // } 20 | // } 21 | 22 | pub fn hash(&self) -> Option<&[u8]> { 23 | match self { 24 | NodeHandle::Hash(h) => Some(h), 25 | NodeHandle::Cached(h, _) => Some(h), 26 | NodeHandle::InMemory(_) => None, 27 | } 28 | } 29 | } 30 | 31 | #[derive(Clone, Debug, PartialEq, Eq, Default)] 32 | pub enum Node { 33 | #[default] 34 | Null, 35 | Leaf { 36 | path: Nibbles, 37 | value: Vec, 38 | }, 39 | Extension { 40 | path: Nibbles, 41 | child: NodeHandle, 42 | }, 43 | Branch { 44 | children: Box<[Option; 16]>, 45 | value: Option>, 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /wrappers/benches/units/batch_vs_normal.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use std::{ 3 | sync::atomic::{AtomicUsize, Ordering}, 4 | time::Duration, 5 | }; 6 | use vsdb::basic::mapx::Mapx; 7 | 8 | fn batch_vs_normal_write(c: &mut Criterion) { 9 | let mut group = c.benchmark_group("** Batch vs Normal Write (100 items) **"); 10 | group 11 | .measurement_time(Duration::from_secs(10)) 12 | .sample_size(50); 13 | 14 | let i = AtomicUsize::new(0); 15 | let mut db = Mapx::new(); 16 | 17 | // Case 1: Normal write 100 items sequentially 18 | group.bench_function(" normal write 100 ", |b| { 19 | b.iter(|| { 20 | for _ in 0..100 { 21 | let n = i.fetch_add(1, Ordering::Relaxed); 22 | db.insert(&n, &n); 23 | } 24 | }) 25 | }); 26 | 27 | // Case 2: Batch write 100 items (entry struct) 28 | group.bench_function(" batch entry 100 ", |b| { 29 | b.iter(|| { 30 | let mut batch = db.batch_entry(); 31 | for _ in 0..100 { 32 | let n = i.fetch_add(1, Ordering::Relaxed); 33 | batch.insert(&n, &n); 34 | } 35 | batch.commit().unwrap(); 36 | }) 37 | }); 38 | 39 | group.finish(); 40 | } 41 | 42 | criterion_group!(benches, batch_vs_normal_write); 43 | -------------------------------------------------------------------------------- /wrappers/tests/batch_api_test.rs: -------------------------------------------------------------------------------- 1 | use vsdb::Mapx; 2 | use vsdb::vsdb_set_base_dir; 3 | 4 | #[test] 5 | fn test_batch_entry_basic() { 6 | let dir = format!( 7 | "/tmp/vsdb_testing/batch_api_test_{}", 8 | rand::random::() 9 | ); 10 | vsdb_set_base_dir(&dir).unwrap(); 11 | 12 | let mut map = Mapx::new(); 13 | 14 | // 1. Basic insert and commit 15 | let mut batch = map.batch_entry(); 16 | batch.insert(&1, &"one".to_string()); 17 | batch.insert(&2, &"two".to_string()); 18 | batch.commit().unwrap(); 19 | 20 | assert_eq!(map.get(&1), Some("one".to_string())); 21 | assert_eq!(map.get(&2), Some("two".to_string())); 22 | 23 | // 2. Remove in batch 24 | let mut batch = map.batch_entry(); 25 | batch.remove(&1); 26 | batch.insert(&3, &"three".to_string()); 27 | batch.commit().unwrap(); 28 | 29 | assert_eq!(map.get(&1), None); 30 | assert_eq!(map.get(&2), Some("two".to_string())); 31 | assert_eq!(map.get(&3), Some("three".to_string())); 32 | 33 | // 3. Drop without commit (should discard changes? No, batch is only a buffer) 34 | // Actually, RocksDB WriteBatch is just a list of operations. 35 | // If we don't commit, nothing happens to DB. 36 | { 37 | let mut batch = map.batch_entry(); 38 | batch.insert(&4, &"four".to_string()); 39 | // dropped here 40 | } 41 | assert_eq!(map.get(&4), None); 42 | } 43 | -------------------------------------------------------------------------------- /utils/slot_db/docs/api.md: -------------------------------------------------------------------------------- 1 | # vsdb_slot_db API Examples 2 | 3 | This document provides examples for the public APIs in the `vsdb_slot_db` crate. 4 | 5 | ## SlotDB 6 | 7 | `SlotDB` is a skip-list-like data structure designed for fast paged queries. 8 | 9 | ```rust 10 | use vsdb_slot_db::SlotDB; 11 | 12 | // Create a new SlotDB with a tier capacity of 10 and normal order. 13 | let mut db = SlotDB::::new(10, false); 14 | 15 | // Insert some keys into different slots. 16 | db.insert(100, "entry_a".to_string()).unwrap(); 17 | db.insert(100, "entry_b".to_string()).unwrap(); 18 | db.insert(200, "entry_c".to_string()).unwrap(); 19 | db.insert(300, "entry_d".to_string()).unwrap(); 20 | 21 | // Check the total number of entries. 22 | assert_eq!(db.total(), 4); 23 | 24 | // Get entries by page. 25 | // Get the first page with a size of 2, in reverse order. 26 | let entries = db.get_entries_by_page(2, 0, true); 27 | assert_eq!(entries, vec!["entry_d".to_string(), "entry_c".to_string()]); 28 | 29 | // Get entries within a slot range. 30 | let entries_in_slot = db.get_entries_by_page_slot(Some(100), Some(100), 10, 0, false); 31 | assert_eq!(entries_in_slot.len(), 2); 32 | assert!(entries_in_slot.contains(&"entry_a".to_string())); 33 | assert!(entries_in_slot.contains(&"entry_b".to_string())); 34 | 35 | // Remove an entry. 36 | db.remove(100, &"entry_a".to_string()); 37 | assert_eq!(db.total(), 3); 38 | 39 | // Clear the database. 40 | db.clear(); 41 | assert_eq!(db.total(), 0); 42 | ``` 43 | -------------------------------------------------------------------------------- /utils/trie_db/docs/api.md: -------------------------------------------------------------------------------- 1 | # vsdb_trie_db API Examples 2 | 3 | This document provides examples for the public APIs in the `vsdb_trie_db` crate. 4 | 5 | ## MptStore 6 | 7 | `MptStore` provides a high-level wrapper for managing Merkle Patricia Tries with persistent storage. 8 | 9 | ```rust 10 | use vsdb_trie_db::MptStore; 11 | 12 | // Create a new MptStore 13 | let store = MptStore::new(); 14 | 15 | // Initialize a new trie (starts with an empty root) 16 | let mut trie = store.trie_init(); 17 | 18 | // Insert key-value pairs (automatically commits) 19 | trie.insert(b"key1", b"value1").unwrap(); 20 | trie.insert(b"key2", b"value2").unwrap(); 21 | 22 | // Get the current root hash 23 | let root = trie.root(); 24 | 25 | // Retrieve values 26 | let value = trie.get(b"key1").unwrap().unwrap(); 27 | assert_eq!(value, b"value1"); 28 | 29 | // Load an existing trie from a root hash 30 | let loaded_trie = store.trie_load(&root); 31 | let value = loaded_trie.get(b"key1").unwrap().unwrap(); 32 | assert_eq!(value, b"value1"); 33 | 34 | // Remove a key 35 | trie.remove(b"key2").unwrap(); 36 | assert!(trie.get(b"key2").unwrap().is_none()); 37 | 38 | // Batch update operations 39 | let ops = vec![ 40 | (b"key3".as_ref(), Some(b"value3".as_ref())), // Insert 41 | (b"key1".as_ref(), None), // Remove 42 | ]; 43 | trie.batch_update(&ops).unwrap(); 44 | 45 | // Verify changes 46 | assert!(trie.get(b"key1").unwrap().is_none()); 47 | assert_eq!(trie.get(b"key3").unwrap().unwrap(), b"value3"); 48 | ``` 49 | -------------------------------------------------------------------------------- /wrappers/docs/api.md: -------------------------------------------------------------------------------- 1 | # vsdb API Examples 2 | 3 | This document provides examples for the public APIs in the `vsdb` crate. 4 | 5 | ## Mapx 6 | 7 | `Mapx` is a hash map-like data structure that stores key-value pairs. 8 | 9 | ```rust 10 | use vsdb::Mapx; 11 | 12 | let mut map = Mapx::new(); 13 | 14 | // Insert some key-value pairs 15 | map.insert(&"key1", &"value1"); 16 | map.insert(&"key2", &"value2"); 17 | 18 | // Get a value 19 | assert_eq!(map.get(&"key1"), Some("value1".to_string())); 20 | 21 | // Check if a key exists 22 | assert!(map.contains_key(&"key2")); 23 | 24 | // Iterate over the key-value pairs 25 | for (key, value) in map.iter() { 26 | println!("{}: {}", key, value); 27 | } 28 | 29 | // Remove a key-value pair 30 | map.remove(&"key1"); 31 | ``` 32 | 33 | ### MapxOrd 34 | 35 | `MapxOrd` is a B-tree map-like data structure that stores key-value pairs in a sorted order. 36 | 37 | ```rust 38 | use vsdb::MapxOrd; 39 | 40 | let mut map = MapxOrd::new(); 41 | 42 | // Insert some key-value pairs 43 | map.insert(&3, &"three"); 44 | map.insert(&1, &"one"); 45 | map.insert(&2, &"two"); 46 | 47 | // Get a value 48 | assert_eq!(map.get(&1), Some("one".to_string())); 49 | 50 | // Iterate over the key-value pairs in sorted order 51 | for (key, value) in map.iter() { 52 | println!("{}: {}", key, value); 53 | } 54 | 55 | // Get the first and last key-value pairs 56 | assert_eq!(map.first(), Some((1, "one".to_string()))); 57 | assert_eq!(map.last(), Some((3, "three".to_string()))); 58 | ``` 59 | -------------------------------------------------------------------------------- /wrappers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vsdb" 3 | version = "7.1.0" 4 | authors = ["hui.fan@mail.ru"] 5 | edition = "2024" 6 | description = "A std-collection-like database" 7 | homepage = "https://github.com/rust-util-collections/vsdb" 8 | repository = "https://github.com/rust-util-collections/vsdb/tree/master/wrappers" 9 | keywords = ["kv", "leveldb", "rocksdb", "std"] 10 | categories = ["database-implementations", "data-structures", "algorithms", "caching"] 11 | license = "GPL-3.0" 12 | 13 | [package.metadata.docs.rs] 14 | # features = [] 15 | 16 | [dependencies] 17 | serde = { workspace = true } 18 | parking_lot = { workspace = true } 19 | 20 | msgpack = { workspace = true, optional = true } 21 | serde_json = { workspace = true, optional = true } 22 | serde_cbor_2 = { workspace = true, optional = true } 23 | 24 | ruc = { workspace = true } 25 | vsdb_core = { workspace = true } 26 | 27 | [dev-dependencies] 28 | rand = { workspace = true } 29 | hex = "0.4" 30 | criterion = "0.8" 31 | 32 | [features] 33 | default = ["parity_backend", "compress", "msgpack_codec"] 34 | 35 | parity_backend = ["vsdb_core/parity_backend"] 36 | rocks_backend = ["vsdb_core/rocks_backend"] 37 | 38 | compress = ["vsdb_core/compress"] 39 | 40 | serde_ende = [] 41 | msgpack_codec = ["serde_ende", "msgpack"] 42 | json_codec = ["serde_ende", "serde_json"] 43 | cbor_codec = ["serde_ende", "serde_cbor_2"] 44 | 45 | # [[bench]] 46 | # name = "basic" 47 | # harness = false 48 | 49 | # [[bench]] 50 | # name = "versioned" 51 | # harness = false 52 | -------------------------------------------------------------------------------- /wrappers/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # vsdb 2 | //! 3 | //! `vsdb` is a high-performance, embedded database designed to feel like using 4 | //! Rust's standard collections. It provides a suite of familiar data structures 5 | //! like `Vecx` (a `Vec`-like vector) and `Mapx` (a `HashMap`-like map), all 6 | //! backed by a persistent, on-disk key-value store. 7 | //! 8 | //! This crate is the primary entry point for most users. 9 | 10 | #![deny(warnings)] 11 | #![cfg_attr(test, allow(warnings))] 12 | #![recursion_limit = "512"] 13 | 14 | #[macro_use] 15 | pub mod common; 16 | 17 | /// User-facing, typed data structures (e.g., `Mapx`, `Vecx`). 18 | pub mod basic; 19 | /// Data structures that use multiple keys for indexing. 20 | pub mod basic_multi_key; 21 | /// Data structures for representing directed acyclic graphs (DAGs). 22 | pub mod dagmap; 23 | 24 | // --- Re-exports --- 25 | 26 | // Basic data structures 27 | pub use basic::{ 28 | mapx::Mapx, 29 | mapx_ord::MapxOrd, 30 | mapx_ord_rawkey::MapxOrdRawKey, 31 | mapx_ord_rawvalue::MapxOrdRawValue, 32 | orphan::Orphan, 33 | // vecx::Vecx, vecx_raw::VecxRaw, // Removed - relied on unreliable len() tracking 34 | }; 35 | 36 | // Common traits and types 37 | pub use common::{ 38 | NULL, 39 | ende::{KeyDe, KeyEn, KeyEnDe, KeyEnDeOrdered, ValueDe, ValueEn, ValueEnDe}, 40 | }; 41 | 42 | // DAG-related structures 43 | pub use dagmap::{DagMapId, raw::DagMapRaw, rawkey::DagMapRawKey}; 44 | 45 | // Re-export all of vsdb_core for convenience 46 | pub use vsdb_core::{self, *}; 47 | -------------------------------------------------------------------------------- /core/README.md: -------------------------------------------------------------------------------- 1 | # vsdb_core 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/vsdb_core.svg)](https://crates.io/crates/vsdb_core) 4 | [![Docs.rs](https://docs.rs/vsdb_core/badge.svg)](https://docs.rs/vsdb_core) 5 | [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg)](../../LICENSE) 6 | [![Rust](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml) 7 | 8 | > `vsdb_core` provides the low-level building blocks for `vsdb`. 9 | 10 | This crate contains the foundational components of `vsdb`, including: 11 | - **Storage Abstractions**: An `Engine` trait that abstracts over key-value storage backends. 12 | - **Raw Data Structures**: Untyped, high-performance data structures like `MapxRaw` that operate on raw bytes. 13 | - **Utilities**: Shared functions for environment management, such as setting the database directory. 14 | 15 | Most users should use the `vsdb` crate instead, which provides high-level, typed APIs. 16 | 17 | ## Installation 18 | 19 | Add this to your `Cargo.toml`: 20 | 21 | ```toml 22 | [dependencies] 23 | vsdb_core = "7.0.0" 24 | ``` 25 | 26 | ## Features 27 | 28 | For detailed API examples, see [API Examples](docs/api.md). 29 | 30 | - `fjall_backend`: **(Default)** Use `fjall` as the backend database. Pure Rust implementation. 31 | - `rocks_backend`: Use `rocksdb` as the backend database. C++ implementation. 32 | - `compress`: Enable data compression in the backend database. 33 | 34 | 35 | ## License 36 | 37 | This project is licensed under the **GPL-3.0** license. 38 | -------------------------------------------------------------------------------- /wrappers/src/dagmap/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Directed Acyclic Graph (DAG) Map 3 | //! 4 | //! This module provides data structures for representing and managing directed 5 | //! acyclic graphs (DAGs) on disk. 6 | //! 7 | 8 | /// A module for raw DAG maps. 9 | pub mod raw; 10 | /// A module for DAG maps with raw keys. 11 | pub mod rawkey; 12 | 13 | /// A type alias for a DAG map ID. 14 | pub type DagMapId = [u8]; 15 | 16 | /// Generates a new, unique ID for a DAG map. 17 | /// 18 | /// This function maintains a persistent counter to ensure that each generated 19 | /// ID is unique. 20 | pub fn gen_dag_map_id_num() -> u128 { 21 | use crate::{Orphan, ValueEnDe}; 22 | use parking_lot::Mutex; 23 | use ruc::*; 24 | use std::{fs, io::ErrorKind, sync::LazyLock}; 25 | 26 | static ID_NUM: LazyLock>> = LazyLock::new(|| { 27 | let mut meta_path = vsdb_core::vsdb_get_custom_dir().to_owned(); 28 | meta_path.push("id_num"); 29 | 30 | match fs::read(&meta_path) { 31 | Ok(m) => Mutex::new(ValueEnDe::decode(&m).unwrap()), 32 | Err(e) => match e.kind() { 33 | ErrorKind::NotFound => { 34 | let i = Orphan::new(0); 35 | fs::write(&meta_path, i.encode()).unwrap(); 36 | Mutex::new(i) 37 | } 38 | _ => { 39 | pnk!(Err(eg!("Error!"))) 40 | } 41 | }, 42 | } 43 | }); 44 | 45 | let mut hdr = ID_NUM.lock(); 46 | let mut hdr = hdr.get_mut(); 47 | *hdr += 1; 48 | *hdr 49 | } 50 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # vsdb_core 2 | //! 3 | //! `vsdb_core` provides the low-level building blocks for `vsdb`, including storage 4 | //! engine abstractions, raw data structures, and common utilities. It is not 5 | //! typically used directly by end-users, but forms the foundation of the `vsdb` 6 | //! ecosystem. 7 | 8 | #![deny(warnings)] 9 | #![cfg_attr(test, allow(warnings))] 10 | #![recursion_limit = "512"] 11 | 12 | /// Manages storage backends, raw data types, and shared utilities. 13 | /// 14 | /// This module provides the `Engine` trait for abstracting over different 15 | /// key-value stores (like RocksDB and Fjall), along with fundamental 16 | /// types such as `RawKey`, `RawValue`, and environment management functions. 17 | pub mod common; 18 | 19 | /// Contains raw, untyped data structures. 20 | /// 21 | /// This module provides `MapxRaw`, a basic, high-performance key-value map that 22 | /// operates on raw bytes. It serves as the foundation for the typed, user-facing 23 | /// collections in the `vsdb` crate. 24 | pub mod basic; 25 | 26 | /// A raw, high-performance, disk-backed key-value map. 27 | pub use basic::mapx_raw::MapxRaw; 28 | 29 | /// Commonly used items, re-exported for convenience. 30 | /// 31 | /// This includes data size constants (KB, MB, GB), a null terminator constant (`NULL`), 32 | /// raw data types (`RawBytes`, `RawKey`, `RawValue`), and functions for managing 33 | /// the database environment (e.g., `vsdb_flush`, `vsdb_set_base_dir`). 34 | pub use common::{ 35 | BatchTrait, GB, KB, MB, NULL, RawBytes, RawKey, RawValue, vsdb_flush, 36 | vsdb_get_base_dir, vsdb_get_custom_dir, vsdb_set_base_dir, 37 | }; 38 | -------------------------------------------------------------------------------- /utils/trie_db/src/test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use crate::MptStore; 4 | 5 | #[test] 6 | fn test_simple_insert_get() { 7 | let store = MptStore::new(); 8 | let mut trie = store.trie_init(); 9 | 10 | trie.insert(b"hello", b"world").unwrap(); 11 | assert_eq!(trie.get(b"hello").unwrap(), Some(b"world".to_vec())); 12 | assert_eq!(trie.get(b"hell").unwrap(), None); 13 | 14 | trie.insert(b"foo", b"bar").unwrap(); 15 | assert_eq!(trie.get(b"foo").unwrap(), Some(b"bar".to_vec())); 16 | 17 | trie.remove(b"hello").unwrap(); 18 | assert_eq!(trie.get(b"hello").unwrap(), None); 19 | assert_eq!(trie.get(b"foo").unwrap(), Some(b"bar".to_vec())); 20 | } 21 | 22 | #[test] 23 | fn test_branch_split() { 24 | let store = MptStore::new(); 25 | let mut trie = store.trie_init(); 26 | 27 | // "do", "dog" -> Split leaf into Branch 28 | trie.insert(b"dog", b"puppy").unwrap(); 29 | trie.insert(b"do", b"verb").unwrap(); 30 | 31 | assert_eq!(trie.get(b"dog").unwrap(), Some(b"puppy".to_vec())); 32 | assert_eq!(trie.get(b"do").unwrap(), Some(b"verb".to_vec())); 33 | } 34 | 35 | #[test] 36 | fn test_extension_split() { 37 | let store = MptStore::new(); 38 | let mut trie = store.trie_init(); 39 | 40 | // "abc", "abd" -> Extension "ab", Branch at 'c'/'d' 41 | trie.insert(b"abc", b"1").unwrap(); 42 | trie.insert(b"abd", b"2").unwrap(); 43 | 44 | assert_eq!(trie.get(b"abc").unwrap(), Some(b"1".to_vec())); 45 | assert_eq!(trie.get(b"abd").unwrap(), Some(b"2".to_vec())); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mapx_double_key/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ValueEnDe; 2 | 3 | use super::*; 4 | use ruc::*; 5 | 6 | #[test] 7 | fn test_insert() { 8 | let mut hdr: MapxDk = MapxDk::new(); 9 | assert_eq!(2, hdr.key_size()); 10 | let max = 100; 11 | (0..max) 12 | .map(|i: usize| (i, max + i)) 13 | .for_each(|(i, value)| { 14 | let key = &(&i, &i); 15 | assert!(hdr.get(key).is_none()); 16 | hdr.entry(key).or_insert(value); 17 | hdr.insert(key, &value); 18 | assert!(hdr.contains_key(&key)); 19 | assert_eq!(pnk!(hdr.get(&key)), value); 20 | hdr.remove(&(&i, Some(&i))); 21 | assert!(hdr.get(&key).is_none()); 22 | hdr.insert(&key, &value); 23 | }); 24 | hdr.clear(); 25 | (0..max).map(|i: usize| i).for_each(|i| { 26 | let key = &(&i, &i); 27 | assert!(hdr.get(key).is_none()); 28 | }); 29 | } 30 | 31 | #[test] 32 | fn test_valueende() { 33 | let cnt = 100; 34 | let dehdr = { 35 | let mut hdr: MapxDk = MapxDk::new(); 36 | let max = 100; 37 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 38 | let key = &(&key, &key); 39 | hdr.insert(key, &value); 40 | }); 41 | as ValueEnDe>::encode(&hdr) 42 | }; 43 | let mut reloaded: MapxDk = 44 | pnk!( as ValueEnDe>::decode(&dehdr)); 45 | 46 | (0..cnt).map(|i: usize| i).for_each(|i| { 47 | let key = &(&i, &i); 48 | assert_eq!(i, pnk!(reloaded.get(key))); 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /wrappers/tests/basic_mapx_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use serde::{Deserialize, Serialize}; 3 | use vsdb::{Mapx, ValueEnDe, vsdb_set_base_dir}; 4 | 5 | #[derive(Serialize, Deserialize, Default, Debug, Eq, PartialEq, Clone)] 6 | struct SampleBlock { 7 | idx: usize, 8 | data: Vec, 9 | } 10 | 11 | fn gen_sample(idx: usize) -> SampleBlock { 12 | SampleBlock { 13 | idx, 14 | data: vec![idx], 15 | } 16 | } 17 | 18 | #[test] 19 | fn basic_cases() { 20 | let cnt = 200; 21 | info_omit!(vsdb_set_base_dir(&format!( 22 | "/tmp/vsdb_testing/{}", 23 | rand::random::() 24 | ))); 25 | 26 | let hdr = { 27 | let mut hdr_i = Mapx::new(); 28 | 29 | (0..cnt).for_each(|i| { 30 | assert!(hdr_i.get(&i).is_none()); 31 | }); 32 | 33 | (0..cnt).map(|i| (i, gen_sample(i))).for_each(|(i, b)| { 34 | hdr_i.entry(&i).or_insert(b.clone()); 35 | assert_eq!(pnk!(hdr_i.get(&i)).idx, i); 36 | hdr_i.remove(&i); 37 | assert!(hdr_i.get(&i).is_none()); 38 | hdr_i.insert(&i, &b); 39 | hdr_i.insert(&i, &b); 40 | }); 41 | 42 | as ValueEnDe>::encode(&hdr_i) 43 | }; 44 | 45 | let mut reloaded = pnk!( as ValueEnDe>::decode(&hdr)); 46 | 47 | (0..cnt).for_each(|i| { 48 | assert_eq!(i, reloaded.get(&i).unwrap().idx); 49 | }); 50 | 51 | (1..cnt).for_each(|i| { 52 | pnk!(reloaded.get_mut(&i)).idx = 1 + i; 53 | assert_eq!(pnk!(reloaded.get(&i)).idx, 1 + i); 54 | assert!(reloaded.contains_key(&i)); 55 | reloaded.remove(&i); 56 | assert!(!reloaded.contains_key(&i)); 57 | }); 58 | 59 | reloaded.clear(); 60 | } 61 | -------------------------------------------------------------------------------- /tools/fmt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ################################################# 4 | #### Ensure we are in the right path. ########### 5 | ################################################# 6 | if [[ 0 -eq $(echo $0 | grep -c '^/') ]]; then 7 | # relative path 8 | EXEC_PATH=$(dirname "`pwd`/$0") 9 | else 10 | # absolute path 11 | EXEC_PATH=$(dirname "$0") 12 | fi 13 | 14 | EXEC_PATH=$(echo ${EXEC_PATH} | sed 's@/\./@/@g' | sed 's@/\.*$@@') 15 | cd $EXEC_PATH || exit 1 16 | ################################################# 17 | 18 | export LC_ALL=en_US.UTF-8 # perl 19 | export LANGUAGE=en_US.UTF-8 # perl 20 | 21 | for file in $(find .. -path "../target" -a -prune \ 22 | -a -type f \ 23 | -o -name "*.c" \ 24 | -o -name "*.h" \ 25 | -o -name "*.sh" \ 26 | -o -name "*.toml" \ 27 | -o -name "*.json" \ 28 | -o -name "*.md" \ 29 | -o -name "rc.local" \ 30 | | grep -v "$(basename $0)" \ 31 | | grep -v '\.git' \ 32 | | grep -v 'submodules' \ 33 | | grep -v 'target' \ 34 | | grep -v '\.tmp' \ 35 | | grep -v '/debug/' \ 36 | | grep -v '/release/' \ 37 | | grep -iv 'Makefile' \ 38 | | grep -iv 'LICENSE' \ 39 | | grep -v 'tendermint'); do 40 | 41 | perl -pi -e 's/ / /g' $file 42 | perl -pi -e 's/!/!/g' $file 43 | perl -pi -e 's/(/(/g' $file 44 | perl -pi -e 's/)/)/g' $file 45 | 46 | perl -pi -e 's/:/: /g' $file 47 | perl -pi -e 's/, */, /g' $file 48 | perl -pi -e 's/。 */. /g' $file 49 | perl -pi -e 's/、 +/、/g' $file 50 | 51 | perl -pi -e 's/, +(\S)/, $1/g' $file 52 | perl -pi -e 's/\. +(\S)/. $1/g' $file 53 | 54 | perl -pi -e 's/\t/ /g' $file 55 | echo $file | grep -c '\.md$'>/dev/null || perl -pi -e 's/ +$//g' $file 56 | done 57 | 58 | cd .. && cargo fmt 59 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mapx_triple_key/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ValueEnDe; 2 | 3 | use super::*; 4 | use ruc::*; 5 | 6 | #[test] 7 | fn test_insert() { 8 | let mut hdr: MapxTk = MapxTk::new(); 9 | assert_eq!(3, hdr.key_size()); 10 | let max = 100; 11 | (0..max) 12 | .map(|i: usize| (i, max + i)) 13 | .for_each(|(i, value)| { 14 | let key = &(&i, &i, &i); 15 | assert!(hdr.get(key).is_none()); 16 | hdr.entry(key).or_insert(value); 17 | hdr.insert(key, &value); 18 | assert!(hdr.contains_key(&key)); 19 | assert_eq!(pnk!(hdr.get(&key)), value); 20 | hdr.remove(&(&i, Some((&i, Some(&i))))); 21 | assert!(hdr.get(&key).is_none()); 22 | hdr.insert(key, &value); 23 | }); 24 | hdr.clear(); 25 | (0..max).map(|i: usize| i).for_each(|i| { 26 | let key = &(&i, &i, &i); 27 | assert!(hdr.get(key).is_none()); 28 | }); 29 | } 30 | 31 | #[test] 32 | fn test_valueende() { 33 | let cnt = 100; 34 | let dehdr = { 35 | let mut hdr: MapxTk = MapxTk::new(); 36 | let max = 100; 37 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 38 | let key = &(&key, &key, &key); 39 | hdr.insert(key, &value); 40 | }); 41 | as ValueEnDe>::encode(&hdr) 42 | }; 43 | let mut reloaded: MapxTk = 44 | pnk!( as ValueEnDe>::decode( 45 | &dehdr 46 | )); 47 | 48 | (0..cnt).map(|i: usize| i).for_each(|i| { 49 | let key = &(&i, &i, &i); 50 | assert_eq!(i, pnk!(reloaded.get(key))); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /core/benches/units/basic_mapx_raw.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb_core::MapxRaw; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = c.benchmark_group("** vsdb::basic::mapx_raw::MapxRaw **"); 11 | group 12 | .measurement_time(Duration::from_secs(9)) 13 | .sample_size(100); 14 | 15 | let i = AtomicUsize::new(0); 16 | let mut db = MapxRaw::new(); 17 | group.bench_function(" write ", |b| { 18 | b.iter(|| { 19 | let n = i.fetch_add(1, Ordering::SeqCst); 20 | let val = n.to_be_bytes(); 21 | db.insert(&val, &val); 22 | }) 23 | }); 24 | 25 | group.bench_function(" read ", |b| { 26 | b.iter(|| { 27 | let n = i.fetch_sub(1, Ordering::SeqCst); 28 | db.get(&n.to_be_bytes()); 29 | }) 30 | }); 31 | group.finish(); 32 | } 33 | 34 | fn random_read_write(c: &mut Criterion) { 35 | let mut group = c.benchmark_group("** vsdb::basic::mapx_raw::MapxRaw **"); 36 | group 37 | .measurement_time(Duration::from_secs(9)) 38 | .sample_size(100); 39 | 40 | let mut rng = rand::thread_rng(); 41 | let mut db = MapxRaw::new(); 42 | let mut keys = vec![]; 43 | group.bench_function(" random write ", |b| { 44 | b.iter(|| { 45 | let n = rng.random::() as usize; 46 | let key = n.to_be_bytes(); 47 | db.insert(&key, &key); 48 | keys.push(key); 49 | }) 50 | }); 51 | 52 | group.bench_function(" random read ", |b| { 53 | b.iter(|| { 54 | let index: usize = rng.random_range(0..keys.len()); 55 | keys.get(index).map(|key| db.get(key)); 56 | }) 57 | }); 58 | group.finish(); 59 | } 60 | 61 | criterion_group!(benches, read_write, random_read_write); 62 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_multi_key_mapx_double_key.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::basic_multi_key::mapx_double_key::MapxDk; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic_multi_key::mapx_double_key::MapxDk **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxDk::new(); 18 | group.bench_function(" write ", |b| { 19 | b.iter(|| { 20 | let n = i.fetch_add(1, Ordering::SeqCst); 21 | db.insert(&(&n, &n), &n); 22 | }) 23 | }); 24 | 25 | group.bench_function(" read ", |b| { 26 | b.iter(|| { 27 | let n = i.fetch_sub(1, Ordering::SeqCst); 28 | db.get(&(&n, &n)); 29 | }) 30 | }); 31 | group.finish(); 32 | } 33 | 34 | fn random_read_write(c: &mut Criterion) { 35 | let mut group = 36 | c.benchmark_group("** vsdb::basic_multi_key::mapx_double_key::MapxDk **"); 37 | group 38 | .measurement_time(Duration::from_secs(9)) 39 | .sample_size(100); 40 | 41 | let mut rng = rand::thread_rng(); 42 | let mut db = MapxDk::new(); 43 | let mut keys = vec![]; 44 | group.bench_function(" random write ", |b| { 45 | b.iter(|| { 46 | let n = rng.random::() as usize; 47 | db.insert(&(&n, &n), &n); 48 | keys.push(n); 49 | }) 50 | }); 51 | 52 | group.bench_function(" random read ", |b| { 53 | b.iter(|| { 54 | let index: usize = rng.random_range(0..keys.len()); 55 | keys.get(index).map(|key| db.get(&(&key, &key))); 56 | }) 57 | }); 58 | group.finish(); 59 | } 60 | 61 | criterion_group!(benches, read_write, random_read_write); 62 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_multi_key_mapx_triple_key.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::basic_multi_key::mapx_triple_key::MapxTk; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic_multi_key::mapx_triple_key::MapxTk **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxTk::new(); 18 | group.bench_function(" write ", |b| { 19 | b.iter(|| { 20 | let n = i.fetch_add(1, Ordering::SeqCst); 21 | db.insert(&(&n, &n, &n), &n); 22 | }) 23 | }); 24 | 25 | group.bench_function(" read ", |b| { 26 | b.iter(|| { 27 | let n = i.fetch_sub(1, Ordering::SeqCst); 28 | db.get(&(&n, &n, &n)); 29 | }) 30 | }); 31 | group.finish(); 32 | } 33 | 34 | fn random_read_write(c: &mut Criterion) { 35 | let mut group = 36 | c.benchmark_group("** vsdb::basic_multi_key::mapx_triple_key::MapxTk **"); 37 | group 38 | .measurement_time(Duration::from_secs(9)) 39 | .sample_size(100); 40 | 41 | let mut rng = rand::thread_rng(); 42 | let mut db = MapxTk::new(); 43 | let mut keys = vec![]; 44 | group.bench_function(" random write ", |b| { 45 | b.iter(|| { 46 | let n = rng.random::() as usize; 47 | db.insert(&(&n, &n, &n), &n); 48 | keys.push(n); 49 | }) 50 | }); 51 | 52 | group.bench_function(" random read ", |b| { 53 | b.iter(|| { 54 | let index: usize = rng.random_range(0..keys.len()); 55 | keys.get(index).map(|key| db.get(&(&key, &key, &key))); 56 | }) 57 | }); 58 | group.finish(); 59 | } 60 | 61 | criterion_group!(benches, read_write, random_read_write); 62 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_mapx_ord.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::{ValueEnDe, basic::mapx_ord::MapxOrd}; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = c.benchmark_group("** vsdb::basic::mapx_ord::MapxOrd **"); 11 | group 12 | .measurement_time(Duration::from_secs(9)) 13 | .sample_size(100); 14 | 15 | let i = AtomicUsize::new(0); 16 | let mut db = MapxOrd::new(); 17 | 18 | group.bench_function(" write ", |b| { 19 | b.iter(|| { 20 | let n = i.fetch_add(1, Ordering::SeqCst); 21 | let key = ::encode(&n); 22 | db.set_value(&key, &n); 23 | }) 24 | }); 25 | 26 | group.bench_function(" read ", |b| { 27 | b.iter(|| { 28 | let n = i.fetch_sub(1, Ordering::SeqCst); 29 | let key = ::encode(&n); 30 | db.get(&key); 31 | }) 32 | }); 33 | group.finish(); 34 | } 35 | 36 | fn random_read_write(c: &mut Criterion) { 37 | let mut group = c.benchmark_group("** vsdb::basic::mapx_ord::MapxOrd **"); 38 | group 39 | .measurement_time(Duration::from_secs(9)) 40 | .sample_size(100); 41 | 42 | let mut rng = rand::thread_rng(); 43 | let mut db = MapxOrd::new(); 44 | let mut keys = vec![]; 45 | group.bench_function(" random write ", |b| { 46 | b.iter(|| { 47 | let n = rng.random::() as usize; 48 | let key = ::encode(&n); 49 | db.set_value(&key, &n); 50 | keys.push(key); 51 | }) 52 | }); 53 | 54 | group.bench_function(" random read ", |b| { 55 | b.iter(|| { 56 | let index: usize = rng.random_range(0..keys.len()); 57 | keys.get(index).map(|key| db.get(key)); 58 | }) 59 | }); 60 | 61 | group.finish(); 62 | } 63 | 64 | criterion_group!(benches, read_write, random_read_write); 65 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_mapx_ord_rawvalue.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::{ValueEnDe, basic::mapx_ord_rawvalue::MapxOrdRawValue}; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic::mapx_ord_rawvalue::MapxOrdRawValue **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxOrdRawValue::new(); 18 | 19 | group.bench_function(" write ", |b| { 20 | b.iter(|| { 21 | let n = i.fetch_add(1, Ordering::SeqCst); 22 | let val = ::encode(&n); 23 | db.set_value(&n, &val); 24 | }) 25 | }); 26 | 27 | group.bench_function(" read ", |b| { 28 | b.iter(|| { 29 | let n = i.fetch_sub(1, Ordering::SeqCst); 30 | db.get(&n); 31 | }) 32 | }); 33 | group.finish(); 34 | } 35 | 36 | fn random_read_write(c: &mut Criterion) { 37 | let mut group = 38 | c.benchmark_group("** vsdb::basic::mapx_ord_rawvalue::MapxOrdRawValue **"); 39 | group 40 | .measurement_time(Duration::from_secs(9)) 41 | .sample_size(100); 42 | 43 | let mut rng = rand::thread_rng(); 44 | let mut db = MapxOrdRawValue::new(); 45 | let mut keys = vec![]; 46 | group.bench_function(" random write ", |b| { 47 | b.iter(|| { 48 | let n = rng.random::() as usize; 49 | let val = ::encode(&n); 50 | db.set_value(&n, &val); 51 | keys.push(n); 52 | }) 53 | }); 54 | 55 | group.bench_function(" random read ", |b| { 56 | b.iter(|| { 57 | let index: usize = rng.random_range(0..keys.len()); 58 | keys.get(index).map(|key| db.get(key)); 59 | }) 60 | }); 61 | 62 | group.finish(); 63 | } 64 | criterion_group!(benches, read_write, random_read_write); 65 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_mapx_ord_rawkey.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::{ValueEnDe, basic::mapx_ord_rawkey::MapxOrdRawKey}; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic::mapx_ord_rawkey::MapxOrdRawKey **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxOrdRawKey::new(); 18 | 19 | group.bench_function(" write ", |b| { 20 | b.iter(|| { 21 | let n = i.fetch_add(1, Ordering::SeqCst); 22 | let key = ::encode(&n); 23 | db.set_value(&key, &n); 24 | }) 25 | }); 26 | 27 | group.bench_function(" read ", |b| { 28 | b.iter(|| { 29 | let n = i.fetch_sub(1, Ordering::SeqCst); 30 | let key = ::encode(&n); 31 | db.get(&key); 32 | }) 33 | }); 34 | group.finish(); 35 | } 36 | 37 | fn random_read_write(c: &mut Criterion) { 38 | let mut group = 39 | c.benchmark_group("** vsdb::basic::mapx_ord_rawkey::MapxOrdRawKey **"); 40 | group 41 | .measurement_time(Duration::from_secs(9)) 42 | .sample_size(100); 43 | 44 | let mut rng = rand::thread_rng(); 45 | let mut db = MapxOrdRawKey::new(); 46 | let mut keys = vec![]; 47 | group.bench_function(" random write ", |b| { 48 | b.iter(|| { 49 | let n = rng.random::() as usize; 50 | let key = ::encode(&n); 51 | db.set_value(&key, &n); 52 | keys.push(key); 53 | }) 54 | }); 55 | 56 | group.bench_function(" random read ", |b| { 57 | b.iter(|| { 58 | let index: usize = rng.random_range(0..keys.len()); 59 | keys.get(index).map(|key| db.get(key)); 60 | }) 61 | }); 62 | 63 | group.finish(); 64 | } 65 | 66 | criterion_group!(benches, read_write, random_read_write); 67 | -------------------------------------------------------------------------------- /wrappers/tests/basic_mapx_ord_rawkey_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use serde::{Deserialize, Serialize}; 3 | use vsdb::{ValueEnDe, basic::mapx_ord_rawkey::MapxOrdRawKey, vsdb_set_base_dir}; 4 | 5 | #[derive(Serialize, Deserialize, Default, Debug, Eq, PartialEq, Clone)] 6 | struct SampleBlock { 7 | data: Vec, 8 | } 9 | 10 | fn gen_sample(bytes: &[u8]) -> SampleBlock { 11 | SampleBlock { 12 | data: bytes.to_vec(), 13 | } 14 | } 15 | 16 | #[test] 17 | fn basic_cases() { 18 | let cnt = 200; 19 | info_omit!(vsdb_set_base_dir(&format!( 20 | "/tmp/vsdb_testing/{}", 21 | rand::random::() 22 | ))); 23 | 24 | let hdr = { 25 | let mut hdr_i = MapxOrdRawKey::new(); 26 | 27 | (0usize..cnt).map(|i| i.to_be_bytes()).for_each(|i| { 28 | assert!(hdr_i.get(&i).is_none()); 29 | }); 30 | 31 | (0usize..cnt) 32 | .map(|i| i.to_be_bytes()) 33 | .map(|i| (i, gen_sample(&i))) 34 | .for_each(|(i, b)| { 35 | hdr_i.entry(&i[..]).or_insert(b.clone()); 36 | assert_eq!(&hdr_i.get(&i).unwrap().data, &i); 37 | hdr_i.remove(&i); 38 | assert!(hdr_i.get(&i).is_none()); 39 | hdr_i.insert(&i, &b); 40 | hdr_i.insert(&i, &b); 41 | }); 42 | 43 | as ValueEnDe>::encode(&hdr_i) 44 | }; 45 | 46 | let mut reloaded = pnk!( as ValueEnDe>::decode(&hdr)); 47 | 48 | (0usize..cnt).map(|i| i.to_be_bytes()).for_each(|i| { 49 | assert_eq!(&i[..], &reloaded.get(&i).unwrap().data); 50 | }); 51 | 52 | (1usize..cnt).for_each(|i| { 53 | (*reloaded.get_mut(&i.to_be_bytes()).unwrap()).data = 54 | (1 + i).to_be_bytes().to_vec(); 55 | assert_eq!( 56 | &reloaded.get(&i.to_be_bytes()).unwrap().data, 57 | &(1 + i).to_be_bytes()[..] 58 | ); 59 | assert!(reloaded.contains_key(&i.to_be_bytes())); 60 | reloaded.remove(&i.to_be_bytes()); 61 | assert!(!reloaded.contains_key(&i.to_be_bytes())); 62 | }); 63 | 64 | reloaded.clear(); 65 | } 66 | -------------------------------------------------------------------------------- /utils/trie_db/README.md: -------------------------------------------------------------------------------- 1 | # vsdb_trie_db 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/vsdb_trie_db.svg)](https://crates.io/crates/vsdb_trie_db) 4 | [![Docs.rs](https://docs.rs/vsdb_trie_db/badge.svg)](https://docs.rs/vsdb_trie_db) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) 6 | [![Rust](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml) 7 | 8 | > An out-of-the-box wrapper for `trie-db` with persistent storage. 9 | 10 | This crate provides `MptStore`, a high-level wrapper for [`trie-db`](https://crates.io/crates/trie-db) that uses `vsdb` for its underlying storage. It simplifies the management of multiple Merkle Patricia Tries (MPTs), handling state, commits, and backend storage automatically. 11 | 12 | ## Installation 13 | 14 | Add this to your `Cargo.toml`: 15 | 16 | ```toml 17 | [dependencies] 18 | vsdb_trie_db = "6.0.1" 19 | ``` 20 | 21 | ## Usage 22 | 23 | For more detailed API examples, see [API Examples](docs/api.md). 24 | 25 | `vsdb_trie_db` provides an easy-to-use interface for creating and managing persistent MPTs. 26 | 27 | ```rust 28 | use vsdb_trie_db::MptStore; 29 | 30 | // Create a new MptStore 31 | let store = MptStore::new(); 32 | 33 | // Initialize a new trie (starts with an empty root) 34 | let mut trie = store.trie_init(); 35 | 36 | // Insert key-value pairs (automatically commits) 37 | trie.insert(b"key1", b"value1").unwrap(); 38 | trie.insert(b"key2", b"value2").unwrap(); 39 | 40 | // Get the current root hash 41 | let root = trie.root(); 42 | 43 | // Retrieve values 44 | let value = trie.get(b"key1").unwrap().unwrap(); 45 | assert_eq!(value, b"value1"); 46 | 47 | // Load an existing trie from a root hash 48 | let loaded_trie = store.trie_load(&root); 49 | let value = loaded_trie.get(b"key1").unwrap().unwrap(); 50 | assert_eq!(value, b"value1"); 51 | 52 | // Batch update operations 53 | let ops = vec![ 54 | (b"key3".as_ref(), Some(b"value3".as_ref())), 55 | (b"key1".as_ref(), None), // Remove key1 56 | ]; 57 | trie.batch_update(&ops).unwrap(); 58 | ``` 59 | 60 | ## License 61 | 62 | This project is licensed under the **MIT** license. 63 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_multi_key_mapx_raw.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::basic_multi_key::mapx_raw::MapxRawMk; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic_multi_key::mapx_raw::MapxRawMk **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxRawMk::new(2); 18 | group.bench_function(" write ", |b| { 19 | b.iter(|| { 20 | let n = i.fetch_add(1, Ordering::SeqCst); 21 | let n = n.to_be_bytes(); 22 | let key: &[&[u8]] = &[&n, &n]; 23 | db.insert(key, &n).unwrap(); 24 | }) 25 | }); 26 | 27 | group.bench_function(" read ", |b| { 28 | b.iter(|| { 29 | let n = i.fetch_sub(1, Ordering::SeqCst); 30 | let n = n.to_be_bytes(); 31 | let key: &[&[u8]] = &[&n, &n]; 32 | db.get(key); 33 | }) 34 | }); 35 | group.finish(); 36 | } 37 | 38 | fn random_read_write(c: &mut Criterion) { 39 | let mut group = 40 | c.benchmark_group("** vsdb::basic_multi_key::mapx_raw::MapxRawMk **"); 41 | group 42 | .measurement_time(Duration::from_secs(9)) 43 | .sample_size(100); 44 | 45 | let mut rng = rand::thread_rng(); 46 | let mut db = MapxRawMk::new(2); 47 | let mut keys = vec![]; 48 | group.bench_function(" random write ", |b| { 49 | b.iter(|| { 50 | let n = rng.random::() as usize; 51 | let n = n.to_be_bytes(); 52 | let key: &[&[u8]] = &[&n, &n]; 53 | db.insert(key, &n).unwrap(); 54 | keys.push((n, n)); 55 | }) 56 | }); 57 | 58 | group.bench_function(" random read ", |b| { 59 | b.iter(|| { 60 | let index: usize = rng.random_range(0..keys.len()); 61 | keys.get(index).map(|key| db.get(&[&key.0, &key.1])); 62 | }) 63 | }); 64 | group.finish(); 65 | } 66 | 67 | criterion_group!(benches, read_write, random_read_write); 68 | -------------------------------------------------------------------------------- /wrappers/src/basic/orphan/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn test_compare() { 5 | assert_eq!(Orphan::new(0), 0); 6 | assert!(Orphan::new(111) > 0); 7 | assert!(Orphan::new(111) >= 0); 8 | assert!(Orphan::new(0) < 111); 9 | assert!(Orphan::new(0) <= 111); 10 | } 11 | 12 | #[test] 13 | fn test_calc() { 14 | assert_eq!(Orphan::new(111) + 111, 222); 15 | assert_eq!(Orphan::new(111) - 111, 0); 16 | assert_eq!(Orphan::new(111) * 111, 111 * 111); 17 | assert_eq!(Orphan::new(111) / 2, 55); 18 | assert_eq!(Orphan::new(111) % 2, 1); 19 | 20 | assert_eq!(-Orphan::new(111), -111); 21 | assert_eq!(!Orphan::new(111), !111); 22 | 23 | assert_eq!(Orphan::new(111) >> 2, 111 >> 2); 24 | assert_eq!(Orphan::new(111) << 2, 111 << 2); 25 | 26 | assert_eq!(Orphan::new(111) | 2, 111 | 2); 27 | assert_eq!(Orphan::new(111) & 2, 111 & 2); 28 | assert_eq!(Orphan::new(111) ^ 2, 111 ^ 2); 29 | } 30 | #[test] 31 | fn test_mut() { 32 | let mut v = Orphan::new(1); 33 | v += 1; 34 | assert_eq!(v, 2); 35 | v *= 100; 36 | assert_eq!(v, 200); 37 | v -= 1; 38 | assert_eq!(v, 199); 39 | v /= 10; 40 | assert_eq!(v, 19); 41 | v %= 10; 42 | assert_eq!(v, 9); 43 | 44 | *v.get_mut() = -v.get_value(); 45 | assert_eq!(v, -9); 46 | } 47 | 48 | #[test] 49 | fn custom_types() { 50 | #[derive( 51 | Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, 52 | )] 53 | struct Foo { 54 | a: i32, 55 | b: String, 56 | c: bool, 57 | } 58 | 59 | assert_eq!(Orphan::new(Foo::default()), Foo::default()); 60 | assert_eq!(Orphan::new(Foo::default()), Orphan::new(Foo::default())); 61 | 62 | assert!( 63 | Orphan::new(Foo::default()) 64 | < Foo { 65 | a: 1, 66 | b: "".to_string(), 67 | c: true 68 | } 69 | ); 70 | assert!( 71 | Orphan::new(Foo::default()) 72 | <= Foo { 73 | a: 1, 74 | b: "".to_string(), 75 | c: true 76 | } 77 | ); 78 | 79 | assert!(Orphan::new(Foo::default()) >= Foo::default()); 80 | assert!(Orphan::new(Foo::default()) >= Orphan::new(Foo::default())); 81 | } 82 | -------------------------------------------------------------------------------- /utils/slot_db/README.md: -------------------------------------------------------------------------------- 1 | # vsdb_slot_db 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/vsdb_slot_db.svg)](https://crates.io/crates/vsdb_slot_db) 4 | [![Docs.rs](https://docs.rs/vsdb_slot_db/badge.svg)](https://docs.rs/vsdb_slot_db) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE) 6 | [![Rust](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml) 7 | 8 | > A skip-list-like index for efficient, timestamp-based paged queries. 9 | 10 | This crate provides `SlotDB`, a data structure that uses a tiered, skip-list-like index to enable fast pagination and range queries over large, ordered datasets. It is ideal for applications where data is associated with a "slot" (such as a timestamp or block number) and needs to be queried in pages. 11 | 12 | ## Installation 13 | 14 | Add this to your `Cargo.toml`: 15 | 16 | ```toml 17 | [dependencies] 18 | vsdb_slot_db = "7.0.0" 19 | ``` 20 | 21 | ## Usage 22 | 23 | `SlotDB` provides an efficient way to query large, ordered datasets in pages. 24 | 25 | ```rust 26 | use vsdb_slot_db::SlotDB; 27 | 28 | // Create a new SlotDB with a tier capacity of 10 and normal order. 29 | let mut db = SlotDB::::new(10, false); 30 | 31 | // Insert some keys into different slots. 32 | db.insert(100, "entry_a".to_string()).unwrap(); 33 | db.insert(100, "entry_b".to_string()).unwrap(); 34 | db.insert(200, "entry_c".to_string()).unwrap(); 35 | db.insert(300, "entry_d".to_string()).unwrap(); 36 | 37 | // Check the total number of entries. 38 | assert_eq!(db.total(), 4); 39 | 40 | // Get entries by page. 41 | // Get the first page with a size of 2, in reverse order. 42 | let entries = db.get_entries_by_page(2, 0, true); 43 | assert_eq!(entries, vec!["entry_d".to_string(), "entry_c".to_string()]); 44 | 45 | // Get entries within a slot range. 46 | let entries_in_slot = db.get_entries_by_page_slot(Some(100), Some(100), 10, 0, false); 47 | assert_eq!(entries_in_slot.len(), 2); 48 | assert!(entries_in_slot.contains(&"entry_a".to_string())); 49 | assert!(entries_in_slot.contains(&"entry_b".to_string())); 50 | ``` 51 | 52 | ## License 53 | 54 | For API examples, see [API Examples](docs/api.md). 55 | 56 | This project is licensed under the **MIT** license. 57 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_mapx.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::{ValueEnDe, basic::mapx::Mapx}; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = c.benchmark_group("** vsdb::basic::mapx::Mapx **"); 11 | group 12 | .measurement_time(Duration::from_secs(9)) 13 | .sample_size(100); 14 | 15 | let i = AtomicUsize::new(0); 16 | let mut db = Mapx::new(); 17 | 18 | group.bench_function(" write ", |b| { 19 | b.iter(|| { 20 | let n = i.fetch_add(1, Ordering::SeqCst); 21 | db.set_value(&[n; 2], &vec![n; 128]); 22 | }) 23 | }); 24 | 25 | group.bench_function(" read ", |b| { 26 | b.iter(|| { 27 | let n = i.fetch_sub(1, Ordering::SeqCst); 28 | db.get(&[n; 2]); 29 | }) 30 | }); 31 | 32 | group.bench_function(" batch write (100 items) ", |b| { 33 | b.iter(|| { 34 | let mut batch = db.batch_entry(); 35 | for _ in 0..100 { 36 | let n = i.fetch_add(1, Ordering::SeqCst); 37 | batch.insert(&[n; 2], &vec![n; 128]); 38 | } 39 | batch.commit().unwrap(); 40 | }) 41 | }); 42 | 43 | group.finish(); 44 | } 45 | 46 | fn random_read_write(c: &mut Criterion) { 47 | let mut group = c.benchmark_group("** vsdb::basic::mapx::Mapx **"); 48 | group 49 | .measurement_time(Duration::from_secs(9)) 50 | .sample_size(100); 51 | 52 | let mut rng = rand::thread_rng(); 53 | let mut db = Mapx::new(); 54 | let mut keys = vec![]; 55 | group.bench_function(" random write ", |b| { 56 | b.iter(|| { 57 | let n = rng.random::() as usize; 58 | let key = [n; 2]; 59 | db.set_value(&key, &vec![n; 128]); 60 | keys.push(key); 61 | }) 62 | }); 63 | 64 | group.bench_function(" random read ", |b| { 65 | b.iter(|| { 66 | let index: usize = rng.random_range(0..keys.len()); 67 | keys.get(index).map(|key| db.get(key)); 68 | }) 69 | }); 70 | group.finish(); 71 | } 72 | 73 | criterion_group!(benches, read_write, random_read_write); 74 | -------------------------------------------------------------------------------- /wrappers/benches/units/basic_multi_key_mapx_rawkey.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group}; 2 | use rand::Rng; 3 | use std::{ 4 | sync::atomic::{AtomicUsize, Ordering}, 5 | time::Duration, 6 | }; 7 | use vsdb::basic_multi_key::mapx_rawkey::MapxRawKeyMk; 8 | 9 | fn read_write(c: &mut Criterion) { 10 | let mut group = 11 | c.benchmark_group("** vsdb::basic_multi_key::mapx_rawkey::MapxRawKeyMk **"); 12 | group 13 | .measurement_time(Duration::from_secs(9)) 14 | .sample_size(100); 15 | 16 | let i = AtomicUsize::new(0); 17 | let mut db = MapxRawKeyMk::new(2); 18 | 19 | group.bench_function(" write ", |b| { 20 | b.iter(|| { 21 | let n = i.fetch_add(1, Ordering::SeqCst); 22 | let key: &[&[u8]] = &[&n.to_be_bytes(), &n.to_be_bytes()]; 23 | db.insert(key, &n).unwrap(); 24 | }) 25 | }); 26 | 27 | group.bench_function(" read ", |b| { 28 | b.iter(|| { 29 | let n = i.fetch_sub(1, Ordering::SeqCst); 30 | let key: &[&[u8]] = &[&n.to_be_bytes(), &n.to_be_bytes()]; 31 | db.get(key); 32 | }) 33 | }); 34 | group.finish(); 35 | } 36 | 37 | fn random_read_write(c: &mut Criterion) { 38 | let mut group = 39 | c.benchmark_group("** vsdb::basic_multi_key::mapx_rawkey::MapxRawKeyMk **"); 40 | group 41 | .measurement_time(Duration::from_secs(9)) 42 | .sample_size(100); 43 | 44 | let mut rng = rand::thread_rng(); 45 | let mut db = MapxRawKeyMk::new(2); 46 | let mut keys = vec![]; 47 | group.bench_function(" random write ", |b| { 48 | b.iter(|| { 49 | let n = rng.random::() as usize; 50 | keys.push(n); 51 | let n = n.to_be_bytes(); 52 | let key: &[&[u8]] = &[&n, &n]; 53 | db.insert(key, &n).unwrap(); 54 | }) 55 | }); 56 | 57 | group.bench_function(" random read ", |b| { 58 | b.iter(|| { 59 | let index: usize = rng.random_range(0..keys.len()); 60 | keys.get(index).map(|i| { 61 | let n = i.to_be_bytes(); 62 | let key: &[&[u8]] = &[&n, &n]; 63 | db.get(key); 64 | }); 65 | }) 66 | }); 67 | 68 | group.finish(); 69 | } 70 | 71 | criterion_group!(benches, read_write, random_read_write); 72 | -------------------------------------------------------------------------------- /wrappers/src/basic/mapx_ord_rawvalue/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ValueEnDe; 2 | 3 | use super::*; 4 | use ruc::*; 5 | 6 | #[test] 7 | fn test_insert() { 8 | let mut hdr: MapxOrdRawValue = MapxOrdRawValue::new(); 9 | let max = 100; 10 | (0..max) 11 | .map(|i: usize| (i, (max + i).to_be_bytes())) 12 | .for_each(|(key, value)| { 13 | assert!(hdr.get(&key).is_none()); 14 | hdr.entry(key).or_insert(&value); 15 | hdr.set_value(&key, &value[..]); 16 | hdr.insert(&key, &value[..]); 17 | assert!(hdr.contains_key(&key)); 18 | 19 | assert_eq!(*pnk!(hdr.get(&key)), value); 20 | hdr.remove(&key); 21 | 22 | assert!(hdr.get(&key).is_none()); 23 | hdr.insert(&key, &value[..]); 24 | }); 25 | hdr.clear(); 26 | (0..max).map(|i: usize| i).for_each(|key| { 27 | assert!(hdr.get(&key).is_none()); 28 | }); 29 | } 30 | 31 | #[test] 32 | fn test_valueende() { 33 | let cnt = 100; 34 | let dehdr = { 35 | let mut hdr: MapxOrdRawValue = MapxOrdRawValue::new(); 36 | (0..cnt) 37 | .map(|i: usize| (i, ::encode(&i))) 38 | .for_each(|(key, value)| { 39 | hdr.insert(&key, &value); 40 | }); 41 | as ValueEnDe>::encode(&hdr) 42 | }; 43 | let mut reloaded = pnk!( as ValueEnDe>::decode(&dehdr)); 44 | (0..cnt).map(|i: usize| i).for_each(|i| { 45 | let val = pnk!(::decode(&pnk!(reloaded.get(&i)))); 46 | assert_eq!(i, val); 47 | }); 48 | } 49 | 50 | #[test] 51 | fn test_iter() { 52 | let mut hdr: MapxOrdRawValue = MapxOrdRawValue::new(); 53 | let max = 100; 54 | (0..max) 55 | .map(|i: usize| (i, i.to_be_bytes())) 56 | .for_each(|(key, value)| { 57 | hdr.insert(&key, &value[..]); 58 | }); 59 | for (key, _) in hdr.iter().collect::>().into_iter() { 60 | hdr.unset_value(&key); 61 | } 62 | } 63 | 64 | #[test] 65 | fn test_first_last() { 66 | let mut hdr: MapxOrdRawValue = MapxOrdRawValue::new(); 67 | let max = 100; 68 | (0..max) 69 | .map(|i: usize| (i, ::encode(&i))) 70 | .for_each(|(key, value)| { 71 | hdr.insert(&key, &value); 72 | }); 73 | 74 | let (_, value) = pnk!(hdr.first()); 75 | let val = pnk!(::decode(&value)); 76 | assert_eq!(0, val); 77 | 78 | let (_, value) = pnk!(hdr.last()); 79 | let val = pnk!(::decode(&value)); 80 | assert_eq!(max - 1, val); 81 | } 82 | -------------------------------------------------------------------------------- /utils/trie_db/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # vsdb_trie_db 2 | //! 3 | //! A lightweight, production-grade Merkle Patricia Trie (MPT) implementation. 4 | //! 5 | //! This crate provides a storage-agnostic MPT implementation, with a default 6 | //! backend using `vsdb`. 7 | 8 | mod config; 9 | mod error; 10 | mod nibbles; 11 | mod node; 12 | mod storage; 13 | mod trie; 14 | 15 | #[cfg(test)] 16 | mod test; 17 | 18 | pub use error::{Result, TrieError}; 19 | pub use storage::TrieBackend; 20 | pub use storage::vsdb_impl::VsdbTrieBackend; 21 | use trie::{TrieMut, TrieRo}; 22 | 23 | /// A handle to the Trie storage. 24 | #[derive(Clone, Default)] 25 | pub struct MptStore { 26 | backend: VsdbTrieBackend, 27 | } 28 | 29 | impl MptStore { 30 | pub fn new() -> Self { 31 | Self { 32 | backend: VsdbTrieBackend::new(), 33 | } 34 | } 35 | 36 | /// Initialize a new Trie with an empty root. 37 | pub fn trie_init(&self) -> MptOnce { 38 | MptOnce::new(self.backend.clone(), vec![0u8; 32]) 39 | } 40 | 41 | /// Load an existing Trie from a root hash. 42 | pub fn trie_load(&self, root: &[u8]) -> MptOnce { 43 | MptOnce::new(self.backend.clone(), root.to_vec()) 44 | } 45 | } 46 | 47 | /// An owned MPT instance that can be mutated. 48 | pub struct MptOnce { 49 | backend: VsdbTrieBackend, 50 | root: Vec, 51 | } 52 | 53 | impl MptOnce { 54 | pub fn new(backend: VsdbTrieBackend, root: Vec) -> Self { 55 | Self { backend, root } 56 | } 57 | 58 | pub fn get(&self, key: &[u8]) -> Result>> { 59 | let trie = TrieRo::new(self.root.clone(), &self.backend); 60 | trie.get(key) 61 | } 62 | 63 | pub fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<()> { 64 | let mut trie = TrieMut::new(&self.root, &mut self.backend); 65 | trie.insert(key, value)?; 66 | self.root = trie.commit()?; 67 | Ok(()) 68 | } 69 | 70 | pub fn remove(&mut self, key: &[u8]) -> Result<()> { 71 | let mut trie = TrieMut::new(&self.root, &mut self.backend); 72 | trie.remove(key)?; 73 | self.root = trie.commit()?; 74 | Ok(()) 75 | } 76 | 77 | pub fn root(&self) -> Vec { 78 | self.root.clone() 79 | } 80 | 81 | pub fn batch_update(&mut self, ops: &[(&[u8], Option<&[u8]>)]) -> Result<()> { 82 | let mut trie = TrieMut::new(&self.root, &mut self.backend); 83 | for (key, val) in ops { 84 | if let Some(v) = val { 85 | trie.insert(key, v)?; 86 | } else { 87 | trie.remove(key)?; 88 | } 89 | } 90 | self.root = trie.commit()?; 91 | Ok(()) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: fmt lintall testall 2 | 3 | export CARGO_NET_GIT_FETCH_WITH_CLI = true 4 | 5 | lint: 6 | cargo clippy --workspace 7 | cargo check --workspace --tests 8 | cargo check --workspace --benches 9 | 10 | lintall: lint 11 | cargo clippy --workspace --no-default-features --features "rocks_backend,compress,cbor_codec" 12 | cargo check --workspace --tests --no-default-features --features "rocks_backend,json_codec" 13 | 14 | lintmusl: 15 | cargo clippy --workspace --target x86_64-unknown-linux-musl \ 16 | --no-default-features \ 17 | --features "parity_backend,cbor_codec" 18 | 19 | test: 20 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 21 | cargo test --workspace --release --tests -- --test-threads=1 22 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 23 | cargo test --workspace --tests -- --test-threads=1 24 | 25 | testall: test 26 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 27 | cargo test --workspace --tests \ 28 | --no-default-features \ 29 | --features "rocks_backend,cbor_codec" \ 30 | -- --test-threads=1 #--nocapture 31 | 32 | testmusl: 33 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 34 | cargo test --workspace --target x86_64-unknown-linux-musl --release --tests \ 35 | --no-default-features \ 36 | --features "parity_backend,cbor_codec" \ 37 | -- --test-threads=1 #--nocapture 38 | 39 | bench: 40 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 41 | cargo bench --workspace 42 | du -sh ~/.vsdb 43 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 44 | cargo bench --workspace --features "compress" 45 | du -sh ~/.vsdb 46 | 47 | bench_rocksdb: 48 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 49 | cargo bench --workspace --no-default-features --features "rocks_backend,cbor_codec" 50 | du -sh ~/.vsdb 51 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 52 | cargo bench --workspace --no-default-features --features "rocks_backend,compress,cbor_codec" 53 | du -sh ~/.vsdb 54 | 55 | benchmusl: 56 | - rm -rf ~/.vsdb /tmp/.vsdb /tmp/vsdb_testing $(VSDB_BASE_DIR) 57 | cargo bench --workspace --target x86_64-unknown-linux-musl \ 58 | --no-default-features --features "parity_backend,cbor_codec" 59 | du -sh ~/.vsdb 60 | 61 | fmt: 62 | cargo fmt 63 | 64 | fmtall: 65 | bash tools/fmt.sh 66 | 67 | update: 68 | cargo update --verbose 69 | 70 | clean: 71 | cargo clean 72 | 73 | clean_all: clean 74 | git stash 75 | git clean -fdx 76 | 77 | doc: 78 | cargo doc --open 79 | 80 | publish: 81 | cargo publish -p vsdb_core 82 | cargo publish -p vsdb 83 | 84 | publish_all: publish 85 | cargo publish -p vsdb_slot_db 86 | cargo publish -p vsdb_trie_db 87 | -------------------------------------------------------------------------------- /utils/trie_db/src/trie/query.rs: -------------------------------------------------------------------------------- 1 | // use crate::config::HASH_LEN; 2 | use crate::error::{Result, TrieError}; 3 | use crate::nibbles::Nibbles; 4 | use crate::node::{Node, NodeCodec, NodeHandle}; 5 | use crate::storage::TrieBackend; 6 | 7 | pub struct TrieRo<'a, B: TrieBackend + ?Sized> { 8 | root: Vec, 9 | backend: &'a B, 10 | } 11 | 12 | impl<'a, B: TrieBackend + ?Sized> TrieRo<'a, B> { 13 | pub fn new(root: Vec, backend: &'a B) -> Self { 14 | Self { root, backend } 15 | } 16 | 17 | pub fn get(&self, key: &[u8]) -> Result>> { 18 | if self.root.iter().all(|&b| b == 0) { 19 | return Ok(None); 20 | } 21 | 22 | let path = Nibbles::from_raw(key, false); 23 | let root_node = self.load_node(&self.root)?; 24 | self.step(&root_node, path) 25 | } 26 | 27 | fn step(&self, node: &Node, path: Nibbles) -> Result>> { 28 | match node { 29 | Node::Null => Ok(None), 30 | Node::Leaf { 31 | path: leaf_path, 32 | value, 33 | } => { 34 | if *leaf_path == path { 35 | Ok(Some(value.clone())) 36 | } else { 37 | Ok(None) 38 | } 39 | } 40 | Node::Extension { 41 | path: ext_path, 42 | child, 43 | } => { 44 | if path.starts_with(ext_path) { 45 | let (_, remaining) = path.split_at(ext_path.len()); 46 | let child_node = self.resolve(child)?; 47 | self.step(&child_node, remaining) 48 | } else { 49 | Ok(None) 50 | } 51 | } 52 | Node::Branch { children, value } => { 53 | if path.is_empty() { 54 | return Ok(value.clone()); 55 | } 56 | let idx = path.at(0) as usize; 57 | if let Some(child_handle) = &children[idx] { 58 | let child_node = self.resolve(child_handle)?; 59 | let (_, remaining) = path.split_at(1); 60 | self.step(&child_node, remaining) 61 | } else { 62 | Ok(None) 63 | } 64 | } 65 | } 66 | } 67 | 68 | fn resolve(&self, handle: &NodeHandle) -> Result { 69 | match handle { 70 | NodeHandle::InMemory(n) => Ok(*n.clone()), 71 | NodeHandle::Cached(_, n) => Ok(*n.clone()), 72 | NodeHandle::Hash(h) => self.load_node(h), 73 | } 74 | } 75 | 76 | fn load_node(&self, hash: &[u8]) -> Result { 77 | match self.backend.get(hash)? { 78 | Some(data) => NodeCodec::decode(&data), 79 | None => Err(TrieError::NodeNotFound(format!("Hash: {:?}", hash))), 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub top language](https://img.shields.io/github/languages/top/rust-util-collections/vsdb) 2 | [![Rust](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml) 3 | [![Minimum rustc version](https://img.shields.io/badge/rustc-1.85+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements) 4 | 5 | # vsdb 6 | 7 | `vsdb` is a high-performance, embedded database designed to feel like using Rust's standard collections (`Vec`, `HashMap`, etc.). It provides persistent, disk-backed data structures with a familiar, in-memory feel. 8 | 9 | This repository is a simplified version of the original [**vsdb**](https://crates.io/crates/vsdb/0.70.0), retaining the most practical and stable features while focusing on performance and ease of use. 10 | 11 | For a detailed guide and API examples, see the [**vsdb crate documentation**](wrappers/README.md). 12 | 13 | ### Crate Ecosystem 14 | 15 | The `vsdb` project is a workspace containing several related crates: 16 | 17 | | Crate | Version | Documentation | Path | Description | 18 | | :--- | :--- | :--- | :--- | :--- | 19 | | [**vsdb**](wrappers) | [![Crates.io](https://img.shields.io/crates/v/vsdb.svg)](https://crates.io/crates/vsdb) | [![Docs.rs](https://docs.rs/vsdb/badge.svg)](https://docs.rs/vsdb) | `wrappers` | High-level, typed data structures (e.g., `Mapx`, `MapxOrd`). This is the primary crate for most users. | 20 | | [**vsdb_core**](core) | [![Crates.io](https://img.shields.io/crates/v/vsdb_core.svg)](https://crates.io/crates/vsdb_core) | [![Docs.rs](https://docs.rs/vsdb_core/badge.svg)](https://docs.rs/vsdb_core) | `core` | Low-level implementations, including storage backends and raw data structures. | 21 | | [**vsdb_slot_db**](utils/slot_db) | [![Crates.io](https://img.shields.io/crates/v/vsdb_slot_db.svg)](https://crates.io/crates/vsdb_slot_db) | [![Docs.rs](https://docs.rs/vsdb_slot_db/badge.svg)](https://docs.rs/vsdb_slot_db) | `utils/slot_db` | A skip-list-like, timestamp-based index for efficient paged queries. | 22 | | [**vsdb_trie_db**](utils/trie_db) | [![Crates.io](https://img.shields.io/crates/v/vsdb_trie_db.svg)](https://crates.io/crates/vsdb_trie_db) | [![Docs.rs](https://docs.rs/vsdb_trie_db/badge.svg)](https://docs.rs/vsdb_trie_db) | `utils/trie_db` | An out-of-the-box Merkle Patricia Trie (MPT) implementation. | 23 | 24 | ### Important Changes 25 | 26 | - **Performance-focused API**: The `insert()` and `remove()` methods no longer return the old value, eliminating expensive read-before-write operations and significantly improving write performance. 27 | - **Simplified API**: The unreliable `len()` and `is_empty()` methods have been removed from map structures. If you need to track collection size, maintain a separate counter in your application. 28 | - **Removed Types**: `Vecx` and `VecxRaw` have been removed as they heavily depended on the unreliable `len()` tracking. 29 | 30 | -------------------------------------------------------------------------------- /core/tests/basic_mapx_raw_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use std::borrow::Cow; 3 | use vsdb_core::{MapxRaw, vsdb_set_base_dir}; 4 | 5 | #[test] 6 | fn basic_cases() { 7 | let cnt = 200; 8 | info_omit!(vsdb_set_base_dir(&format!( 9 | "/tmp/vsdb_testing/{}", 10 | rand::random::() 11 | ))); 12 | 13 | let hdr = { 14 | let mut hdr_i = MapxRaw::new(); 15 | 16 | (0..cnt).for_each(|i: usize| { 17 | assert!(hdr_i.get(&i.to_be_bytes()).is_none()); 18 | }); 19 | 20 | (0..cnt) 21 | .map(|i: usize| (i.to_be_bytes(), i.to_be_bytes())) 22 | .for_each(|(i, b)| { 23 | hdr_i.entry(&i).or_insert(&b); 24 | assert_eq!(&hdr_i.get(&i).unwrap()[..], &i[..]); 25 | 26 | // Remove check 27 | assert!(hdr_i.contains_key(&i)); 28 | hdr_i.remove(&i); 29 | assert!(hdr_i.get(&i).is_none()); 30 | 31 | // Insert checks 32 | hdr_i.insert(&i, &b); 33 | assert!(hdr_i.contains_key(&i)); 34 | 35 | // Overwrite check 36 | hdr_i.insert(&i, &b); 37 | assert!(hdr_i.contains_key(&i)); 38 | }); 39 | 40 | pnk!(msgpack::to_vec(&hdr_i)) 41 | }; 42 | 43 | let mut reloaded = pnk!(msgpack::from_slice::(&hdr)); 44 | 45 | (0..cnt).map(|i: usize| i.to_be_bytes()).for_each(|i| { 46 | assert_eq!(&i[..], &reloaded.get(&i).unwrap()[..]); 47 | }); 48 | 49 | (1..cnt).map(|i: usize| i.to_be_bytes()).for_each(|i| { 50 | *reloaded.get_mut(&i).unwrap() = i.to_vec(); 51 | assert_eq!(&reloaded.get(&i).unwrap()[..], &i[..]); 52 | assert!(reloaded.contains_key(&i)); 53 | reloaded.remove(&i); 54 | assert!(!reloaded.contains_key(&i)); 55 | }); 56 | 57 | reloaded.clear(); 58 | 59 | reloaded.insert(&[1], &[1]); 60 | reloaded.insert(&[4], &[4]); 61 | reloaded.insert(&[6], &[6]); 62 | reloaded.insert(&[80], &[80]); 63 | 64 | assert!( 65 | reloaded 66 | .range(Cow::Borrowed(&[][..])..Cow::Borrowed(&[1][..])) 67 | .next() 68 | .is_none() 69 | ); 70 | assert_eq!( 71 | vec![4], 72 | reloaded 73 | .range(Cow::Borrowed(&[2][..])..Cow::Borrowed(&[10][..])) 74 | .next() 75 | .unwrap() 76 | .1 77 | ); 78 | assert_eq!( 79 | vec![6], 80 | reloaded 81 | .range(Cow::Borrowed(&[2][..])..Cow::Borrowed(&[10][..])) 82 | .rev() 83 | .next() 84 | .unwrap() 85 | .1 86 | ); 87 | 88 | assert_eq!(vec![80], reloaded.get_ge(&[79]).unwrap().1); 89 | assert_eq!(vec![80], reloaded.get_ge(&[80]).unwrap().1); 90 | assert_eq!(vec![80], reloaded.get_le(&[80]).unwrap().1); 91 | assert_eq!(vec![80], reloaded.get_le(&[100]).unwrap().1); 92 | } 93 | -------------------------------------------------------------------------------- /wrappers/src/basic/mapx/test.rs: -------------------------------------------------------------------------------- 1 | use super::{Mapx, ValueEnDe}; 2 | use ruc::*; 3 | 4 | #[test] 5 | fn test_insert() { 6 | let mut hdr: Mapx = Mapx::new(); 7 | let max = 100; 8 | (0..max) 9 | .map(|i: usize| (i, (max + i))) 10 | .for_each(|(key, value)| { 11 | assert!(hdr.get(&key).is_none()); 12 | hdr.set_value(&key, &value); 13 | hdr.insert(&key, &value); 14 | assert!(hdr.contains_key(&key)); 15 | assert_eq!(pnk!(hdr.get(&key)), value); 16 | hdr.remove(&key); 17 | assert!(hdr.get(&key).is_none()); 18 | }); 19 | hdr.clear(); 20 | (0..max).map(|i: usize| i).for_each(|key| { 21 | assert!(hdr.get(&key).is_none()); 22 | }); 23 | } 24 | 25 | #[test] 26 | fn xx_test_valueende() { 27 | let cnt = 100; 28 | let dehdr = { 29 | let mut hdr: Mapx = Mapx::new(); 30 | (0..cnt).map(|i: usize| (i, i)).for_each(|(key, value)| { 31 | hdr.insert(&key, &value); 32 | }); 33 | hdr.encode() 34 | }; 35 | let mut reloaded = pnk!( as ValueEnDe>::decode(&dehdr)); 36 | (0..cnt).map(|i: usize| i).for_each(|i| { 37 | assert_eq!(i, reloaded.get(&i).unwrap()); 38 | }); 39 | } 40 | 41 | #[test] 42 | fn test_iter() { 43 | let mut hdr: Mapx = Mapx::new(); 44 | let max = 100; 45 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 46 | hdr.insert(&key, &value); 47 | }); 48 | for (key, value) in hdr.iter().collect::>().into_iter() { 49 | assert_eq!(key, value); 50 | hdr.remove(&key); 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_first_last() { 56 | let mut hdr: Mapx = Mapx::new(); 57 | let max = 100; 58 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 59 | hdr.insert(&key, &value); 60 | }); 61 | let (key, value) = pnk!(hdr.iter().next()); 62 | assert_eq!(key, value); 63 | // order is not guaranteed in Mapx 64 | // assert_eq!(0, key); 65 | 66 | let (key, value) = pnk!(hdr.iter().next_back()); 67 | assert_eq!(key, value); 68 | // assert_eq!(max - 1, key); 69 | } 70 | 71 | #[test] 72 | fn test_values() { 73 | let mut hdr: Mapx = Mapx::new(); 74 | let max = 100usize; 75 | (0..max).map(|i| (i, i)).for_each(|(key, value)| { 76 | hdr.insert(&key, &value); 77 | }); 78 | for (k, v) in hdr.iter() { 79 | assert_eq!(k, v); 80 | } 81 | } 82 | 83 | #[test] 84 | fn test_values_first_last() { 85 | let mut hdr: Mapx = Mapx::new(); 86 | let max = 100; 87 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 88 | hdr.insert(&key, &value); 89 | }); 90 | let value = pnk!(hdr.values().next()); 91 | // assert_eq!(0, value); 92 | 93 | let value = pnk!(hdr.values().next_back()); 94 | // assert_eq!(max - 1, value); 95 | } 96 | -------------------------------------------------------------------------------- /wrappers/src/basic/mapx_ord/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use ruc::*; 3 | 4 | #[test] 5 | fn test_insert() { 6 | let mut hdr: MapxOrd = MapxOrd::new(); 7 | let max = 100; 8 | (0..max) 9 | .map(|i: usize| (i, (max + i))) 10 | .for_each(|(key, value)| { 11 | assert!(hdr.get(&key).is_none()); 12 | hdr.set_value(&key, &value); 13 | hdr.insert(&key, &value); 14 | assert!(hdr.contains_key(&key)); 15 | assert_eq!(pnk!(hdr.get(&key)), value); 16 | hdr.remove(&key); 17 | assert!(hdr.get(&key).is_none()); 18 | }); 19 | hdr.clear(); 20 | (0..max).map(|i: usize| i).for_each(|key| { 21 | assert!(hdr.get(&key).is_none()); 22 | }); 23 | } 24 | 25 | #[test] 26 | fn test_valueende() { 27 | let cnt = 100; 28 | let dehdr = { 29 | let mut hdr: MapxOrd = MapxOrd::new(); 30 | (0..cnt).map(|i: usize| (i, i)).for_each(|(key, value)| { 31 | hdr.insert(&key, &value); 32 | }); 33 | as ValueEnDe>::encode(&hdr) 34 | }; 35 | let mut reloaded = pnk!( as ValueEnDe>::decode(&dehdr)); 36 | (0..cnt).map(|i: usize| i).for_each(|i| { 37 | assert_eq!(i, reloaded.get(&i).unwrap()); 38 | }); 39 | } 40 | 41 | #[test] 42 | fn test_iter() { 43 | let mut hdr: MapxOrd = MapxOrd::new(); 44 | let max = 100; 45 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 46 | hdr.insert(&key, &value); 47 | }); 48 | for (key, value) in hdr.iter().collect::>().into_iter() { 49 | assert_eq!(key, value); 50 | hdr.unset_value(&key); 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_first_last() { 56 | let mut hdr: MapxOrd = MapxOrd::new(); 57 | let max = 100; 58 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 59 | hdr.insert(&key, &value); 60 | }); 61 | let (key, value) = pnk!(hdr.first()); 62 | assert_eq!(key, value); 63 | assert_eq!(0, key); 64 | 65 | let (key, value) = pnk!(hdr.last()); 66 | assert_eq!(key, value); 67 | assert_eq!(max - 1, key); 68 | } 69 | 70 | #[test] 71 | fn test_values() { 72 | let mut hdr: MapxOrd = MapxOrd::new(); 73 | let max = 100; 74 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 75 | hdr.insert(&key, &value); 76 | }); 77 | let mut i = 0; 78 | for it in hdr.values() { 79 | assert_eq!(i, it); 80 | i = i + 1; 81 | } 82 | } 83 | 84 | #[test] 85 | fn test_values_first_last() { 86 | let mut hdr: MapxOrd = MapxOrd::new(); 87 | let max = 100; 88 | (0..max).map(|i: usize| (i, i)).for_each(|(key, value)| { 89 | hdr.insert(&key, &value); 90 | }); 91 | let value = pnk!(hdr.values().next()); 92 | assert_eq!(0, value); 93 | 94 | let value = pnk!(hdr.values().next_back()); 95 | assert_eq!(max - 1, value); 96 | } 97 | -------------------------------------------------------------------------------- /utils/trie_db/src/nibbles.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Clone, PartialEq, Eq, Hash, Default)] 4 | pub struct Nibbles { 5 | // Stored as one nibble per byte (0x00..0x0F) for easier manipulation 6 | data: Vec, 7 | } 8 | 9 | impl Nibbles { 10 | // pub fn new() -> Self { 11 | // Self { data: Vec::new() } 12 | // } 13 | 14 | pub fn from_raw(key: &[u8], is_leaf: bool) -> Self { 15 | let mut data = Vec::with_capacity(key.len() * 2); 16 | for &b in key { 17 | data.push(b >> 4); 18 | data.push(b & 0x0F); 19 | } 20 | if is_leaf { 21 | // In some MPT implementations, there's a terminator. 22 | // But usually we handle leaf vs extension by node type. 23 | // We'll keep it simple: raw nibbles. 24 | } 25 | Self { data } 26 | } 27 | 28 | pub fn from_nibbles_unsafe(nibbles: Vec) -> Self { 29 | Self { data: nibbles } 30 | } 31 | 32 | pub fn len(&self) -> usize { 33 | self.data.len() 34 | } 35 | 36 | pub fn is_empty(&self) -> bool { 37 | self.data.is_empty() 38 | } 39 | 40 | pub fn at(&self, index: usize) -> u8 { 41 | self.data[index] 42 | } 43 | 44 | // pub fn slice(&self, start: usize, end: usize) -> Self { 45 | // Self { 46 | // data: self.data[start..end].to_vec(), 47 | // } 48 | // } 49 | 50 | pub fn common_prefix(&self, other: &Nibbles) -> usize { 51 | let len = std::cmp::min(self.len(), other.len()); 52 | let mut i = 0; 53 | while i < len && self.data[i] == other.data[i] { 54 | i += 1; 55 | } 56 | i 57 | } 58 | 59 | pub fn starts_with(&self, other: &Nibbles) -> bool { 60 | if self.len() < other.len() { 61 | return false; 62 | } 63 | &self.data[..other.len()] == other.data.as_slice() 64 | } 65 | 66 | pub fn split_at(&self, idx: usize) -> (Self, Self) { 67 | let (a, b) = self.data.split_at(idx); 68 | (Self { data: a.to_vec() }, Self { data: b.to_vec() }) 69 | } 70 | 71 | // pub fn join(&self, other: &Nibbles) -> Self { 72 | // let mut data = self.data.clone(); 73 | // data.extend_from_slice(&other.data); 74 | // Self { data } 75 | // } 76 | 77 | // pub fn push(&mut self, nibble: u8) { 78 | // debug_assert!(nibble < 16); 79 | // self.data.push(nibble); 80 | // } 81 | 82 | // pub fn pop(&mut self) -> Option { 83 | // self.data.pop() 84 | // } 85 | 86 | pub fn as_slice(&self) -> &[u8] { 87 | &self.data 88 | } 89 | } 90 | 91 | impl fmt::Debug for Nibbles { 92 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 93 | write!(f, "Nibbles(")?; 94 | for n in &self.data { 95 | write!(f, "{:x}", n)?; 96 | } 97 | write!(f, ")") 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /wrappers/tests/basic_mapx_ord_rawvalue_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use std::ops::Bound; 3 | use vsdb::{ValueEnDe, basic::mapx_ord_rawvalue::MapxOrdRawValue, vsdb_set_base_dir}; 4 | 5 | #[test] 6 | fn basic_cases() { 7 | let cnt = 200; 8 | info_omit!(vsdb_set_base_dir(&format!( 9 | "/tmp/vsdb_testing/{}", 10 | rand::random::() 11 | ))); 12 | 13 | let hdr = { 14 | let mut hdr_i = MapxOrdRawValue::new(); 15 | 16 | (0usize..cnt).for_each(|i| { 17 | assert!(hdr_i.get(&i).is_none()); 18 | }); 19 | 20 | (0usize..cnt) 21 | .map(|i| (i, i.to_be_bytes())) 22 | .for_each(|(i, b)| { 23 | hdr_i.entry(i).or_insert(b.clone()); 24 | assert_eq!(&hdr_i.get(&i).unwrap()[..], &b[..]); 25 | hdr_i.remove(&i); 26 | assert!(hdr_i.get(&i).is_none()); 27 | hdr_i.insert(&i, &b); 28 | hdr_i.insert(&i, &b); 29 | }); 30 | 31 | as ValueEnDe>::encode(&hdr_i) 32 | }; 33 | 34 | let mut reloaded = pnk!( as ValueEnDe>::decode(&hdr)); 35 | 36 | (0usize..cnt).for_each(|i| { 37 | assert_eq!(&i.to_be_bytes(), &reloaded.get(&i).unwrap()[..]); 38 | }); 39 | 40 | (1usize..cnt).for_each(|i| { 41 | *reloaded.get_mut(&i).unwrap() = (1 + i).to_be_bytes().to_vec(); 42 | assert_eq!(&reloaded.get(&i).unwrap()[..], &(1 + i).to_be_bytes()); 43 | assert!(reloaded.contains_key(&i)); 44 | reloaded.remove(&i); 45 | assert!(!reloaded.contains_key(&i)); 46 | }); 47 | 48 | reloaded.clear(); 49 | 50 | reloaded.insert(&1, &1usize.to_be_bytes()); 51 | reloaded.insert(&10, &10usize.to_be_bytes()); 52 | reloaded.insert(&100, &100usize.to_be_bytes()); 53 | reloaded.insert(&1000, &1000usize.to_be_bytes()); 54 | 55 | assert!(reloaded.range(&0..&1).next().is_none()); 56 | 57 | assert_eq!( 58 | &100usize.to_be_bytes()[..], 59 | &reloaded.range(&12..&999).next().unwrap().1[..] 60 | ); 61 | assert_eq!( 62 | &100usize.to_be_bytes()[..], 63 | &reloaded.range(&12..=&999).next().unwrap().1[..] 64 | ); 65 | 66 | assert_eq!( 67 | &100usize.to_be_bytes()[..], 68 | &reloaded.range(&100..=&999).next().unwrap().1[..] 69 | ); 70 | assert!( 71 | reloaded 72 | .range((Bound::Excluded(&100), Bound::Included(&999))) 73 | .next() 74 | .is_none() 75 | ); 76 | 77 | assert_eq!( 78 | &100usize.to_be_bytes()[..], 79 | &reloaded.get_ge(&99).unwrap().1[..] 80 | ); 81 | assert_eq!( 82 | &100usize.to_be_bytes()[..], 83 | &reloaded.get_ge(&100).unwrap().1[..] 84 | ); 85 | assert_eq!( 86 | &100usize.to_be_bytes()[..], 87 | &reloaded.get_le(&100).unwrap().1[..] 88 | ); 89 | assert_eq!( 90 | &100usize.to_be_bytes()[..], 91 | &reloaded.get_le(&101).unwrap().1[..] 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /core/benches/units/batch_write.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, black_box, criterion_group}; 2 | use std::time::Duration; 3 | use vsdb_core::MapxRaw; 4 | 5 | // Benchmark single insert operations 6 | fn single_inserts(c: &mut Criterion) { 7 | let mut group = c.benchmark_group("** RocksDB Write Performance **"); 8 | group 9 | .measurement_time(Duration::from_secs(10)) 10 | .sample_size(50); 11 | 12 | group.bench_function(" single inserts (1000 ops) ", |b| { 13 | b.iter(|| { 14 | let mut db = MapxRaw::new(); 15 | for i in 0usize..1000 { 16 | let key = i.to_be_bytes(); 17 | let value = (i * 2).to_be_bytes(); 18 | db.insert(&key, &value); 19 | } 20 | black_box(db); 21 | }) 22 | }); 23 | 24 | group.finish(); 25 | } 26 | 27 | // Benchmark mixed read/write workload 28 | fn mixed_workload(c: &mut Criterion) { 29 | let mut group = c.benchmark_group("** RocksDB Mixed Workload **"); 30 | group 31 | .measurement_time(Duration::from_secs(10)) 32 | .sample_size(50); 33 | 34 | group.bench_function(" 80% read / 20% write (1000 ops) ", |b| { 35 | b.iter(|| { 36 | let mut db = MapxRaw::new(); 37 | // Pre-populate 38 | for i in 0usize..800 { 39 | let key = i.to_be_bytes(); 40 | let value = i.to_be_bytes(); 41 | db.insert(&key, &value); 42 | } 43 | 44 | // Mixed workload 45 | for i in 0usize..1000 { 46 | if i % 5 == 0 { 47 | // 20% writes 48 | let key = (800 + i / 5).to_be_bytes(); 49 | let value = i.to_be_bytes(); 50 | db.insert(&key, &value); 51 | } else { 52 | // 80% reads 53 | let key = (i % 800).to_be_bytes(); 54 | black_box(db.get(&key)); 55 | } 56 | } 57 | black_box(db); 58 | }) 59 | }); 60 | 61 | group.finish(); 62 | } 63 | 64 | // Benchmark range scans 65 | fn range_scans(c: &mut Criterion) { 66 | let mut group = c.benchmark_group("** RocksDB Range Scans **"); 67 | group 68 | .measurement_time(Duration::from_secs(10)) 69 | .sample_size(50); 70 | 71 | let mut db = MapxRaw::new(); 72 | // Pre-populate with 10000 entries 73 | for i in 0u64..10000u64 { 74 | let key = i.to_be_bytes(); 75 | let value = i.to_be_bytes(); 76 | db.insert(&key, &value); 77 | } 78 | 79 | group.bench_function(" scan 100 entries ", |b| { 80 | b.iter(|| { 81 | let count = db.iter().take(100).count(); 82 | black_box(count); 83 | }) 84 | }); 85 | 86 | group.bench_function(" scan 1000 entries ", |b| { 87 | b.iter(|| { 88 | let count = db.iter().take(1000).count(); 89 | black_box(count); 90 | }) 91 | }); 92 | 93 | group.finish(); 94 | } 95 | 96 | criterion_group!(benches, single_inserts, mixed_workload, range_scans); 97 | -------------------------------------------------------------------------------- /core/src/basic/mapx_raw/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use ruc::*; 3 | use std::mem::size_of; 4 | 5 | #[test] 6 | fn test_insert() { 7 | let mut hdr = MapxRaw::new(); 8 | let max = 100; 9 | (0..max) 10 | .map(|i: u64| (to_bytes(i), to_bytes(max + i))) 11 | .for_each(|(key, value)| { 12 | assert!(hdr.get(&key).is_none()); 13 | hdr.entry(&key).or_insert(&value); 14 | 15 | // After inserting, should exist 16 | assert!(hdr.contains_key(&key)); 17 | assert_eq!(&pnk!(hdr.get(&key))[..], &value[..]); 18 | 19 | // Remove it 20 | hdr.remove(&key); 21 | assert!(hdr.get(&key).is_none()); 22 | 23 | // Insert again 24 | hdr.insert(&key, &value); 25 | assert!(hdr.contains_key(&key)); 26 | }); 27 | 28 | hdr.clear(); 29 | (0..max).map(|i: u64| to_bytes(i)).for_each(|key| { 30 | assert!(hdr.get(&key).is_none()); 31 | }); 32 | } 33 | 34 | #[test] 35 | fn test_iter() { 36 | let mut hdr = MapxRaw::new(); 37 | let max = 100; 38 | (0..max) 39 | .map(|i: u64| (to_bytes(i), to_bytes(i))) 40 | .for_each(|(key, value)| { 41 | hdr.insert(&key, &value); 42 | }); 43 | 44 | hdr.iter_mut().for_each(|(k, mut v)| { 45 | *v = to_bytes(to_u64(&v) + 1).to_vec().into(); 46 | }); 47 | 48 | for (idx, (key, value)) in hdr.iter().enumerate() { 49 | assert_eq!(idx as u64 + 1, to_u64(&value)); 50 | } 51 | } 52 | 53 | #[test] 54 | fn test_first_last() { 55 | let mut hdr = MapxRaw::new(); 56 | let max = 100; 57 | (0..max) 58 | .map(|i: u64| (to_bytes(i), to_bytes(i))) 59 | .for_each(|(key, value)| { 60 | hdr.insert(&key, &value); 61 | }); 62 | 63 | let (_, value) = pnk!(hdr.iter().next()); 64 | let val = to_u64(&value); 65 | assert_eq!(0, val); 66 | 67 | let (_, value) = pnk!(hdr.iter().next_back()); 68 | let val = to_u64(&value); 69 | assert_eq!(max - 1, val); 70 | } 71 | 72 | #[test] 73 | fn test_batch() { 74 | let mut hdr = MapxRaw::new(); 75 | let max = 100u64; 76 | 77 | { 78 | let mut batch = hdr.batch_entry(); 79 | for i in 0..max { 80 | let key = to_bytes(i); 81 | let value = to_bytes(max + i); 82 | batch.insert(&key, &value); 83 | } 84 | batch.commit().unwrap(); 85 | } 86 | 87 | for i in 0..max { 88 | let key = to_bytes(i); 89 | let value = to_bytes(max + i); 90 | assert_eq!(&pnk!(hdr.get(&key))[..], &value[..]); 91 | } 92 | 93 | { 94 | let mut batch = hdr.batch_entry(); 95 | for i in 0..max { 96 | let key = to_bytes(i); 97 | batch.remove(&key); 98 | } 99 | batch.commit().unwrap(); 100 | } 101 | 102 | for i in 0..max { 103 | let key = to_bytes(i); 104 | assert!(hdr.get(&key).is_none()); 105 | } 106 | } 107 | 108 | fn to_u64(bytes: &[u8]) -> u64 { 109 | u64::from_be_bytes(<[u8; size_of::()]>::try_from(bytes).unwrap()) 110 | } 111 | 112 | fn to_bytes(i: u64) -> [u8; size_of::()] { 113 | i.to_be_bytes() 114 | } 115 | -------------------------------------------------------------------------------- /wrappers/tests/basic_orphan_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use serde::{Deserialize, Serialize}; 3 | use vsdb::{basic::orphan::Orphan, vsdb_set_base_dir}; 4 | 5 | #[test] 6 | fn basic_cases() { 7 | info_omit!(vsdb_set_base_dir(&format!( 8 | "/tmp/vsdb_testing/{}", 9 | rand::random::() 10 | ))); 11 | 12 | assert_eq!(Orphan::new(0), 0); 13 | assert!(Orphan::new(111) > 0); 14 | assert!(Orphan::new(111) >= 0); 15 | assert!(Orphan::new(0) < 111); 16 | assert!(Orphan::new(0) <= 111); 17 | 18 | assert_eq!(Orphan::new(0), Orphan::new(0)); 19 | assert!(Orphan::new(111) > Orphan::new(0)); 20 | assert!(Orphan::new(111) >= Orphan::new(111)); 21 | assert!(Orphan::new(0) < Orphan::new(111)); 22 | assert!(Orphan::new(111) <= Orphan::new(111)); 23 | 24 | assert_eq!(Orphan::new(111) + 111, 222); 25 | assert_eq!(Orphan::new(111) - 111, 0); 26 | assert_eq!(Orphan::new(111) * 111, 111 * 111); 27 | assert_eq!(Orphan::new(111) / 2, 55); 28 | assert_eq!(Orphan::new(111) % 2, 1); 29 | 30 | assert_eq!(-Orphan::new(111), -111); 31 | assert_eq!(!Orphan::new(111), !111); 32 | 33 | assert_eq!(Orphan::new(111) >> 2, 111 >> 2); 34 | assert_eq!(Orphan::new(111) << 2, 111 << 2); 35 | 36 | assert_eq!(Orphan::new(111) | 2, 111 | 2); 37 | assert_eq!(Orphan::new(111) & 2, 111 & 2); 38 | assert_eq!(Orphan::new(111) ^ 2, 111 ^ 2); 39 | 40 | let mut v = Orphan::new(1); 41 | v += 1; 42 | assert_eq!(v, 2); 43 | v *= 100; 44 | assert_eq!(v, 200); 45 | v -= 1; 46 | assert_eq!(v, 199); 47 | v /= 10; 48 | assert_eq!(v, 19); 49 | v %= 10; 50 | assert_eq!(v, 9); 51 | 52 | *v.get_mut() = -v.get_value(); 53 | assert_eq!(v, -9); 54 | 55 | *v.get_mut() = !v.get_value(); 56 | assert_eq!(v, !-9); 57 | 58 | *v.get_mut() = 732; 59 | v >>= 2; 60 | assert_eq!(v, 732 >> 2); 61 | 62 | *v.get_mut() = 732; 63 | v <<= 2; 64 | assert_eq!(v, 732 << 2); 65 | 66 | *v.get_mut() = 732; 67 | v |= 2; 68 | assert_eq!(v, 732 | 2); 69 | 70 | *v.get_mut() = 732; 71 | v &= 2; 72 | assert_eq!(v, 732 & 2); 73 | 74 | *v.get_mut() = 732; 75 | v ^= 2; 76 | assert_eq!(v, 732 ^ 2); 77 | } 78 | 79 | #[test] 80 | fn custom_types() { 81 | #[derive( 82 | Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, 83 | )] 84 | struct Foo { 85 | a: i32, 86 | b: String, 87 | c: bool, 88 | } 89 | assert_eq!(Orphan::new(Foo::default()), Foo::default()); 90 | assert_eq!(Orphan::new(Foo::default()), Orphan::new(Foo::default())); 91 | 92 | assert!( 93 | Orphan::new(Foo::default()) 94 | < Foo { 95 | a: 1, 96 | b: "".to_string(), 97 | c: true 98 | } 99 | ); 100 | assert!( 101 | Orphan::new(Foo::default()) 102 | <= Foo { 103 | a: 1, 104 | b: "".to_string(), 105 | c: true 106 | } 107 | ); 108 | 109 | assert!(Orphan::new(Foo::default()) >= Foo::default()); 110 | assert!(Orphan::new(Foo::default()) >= Orphan::new(Foo::default())); 111 | } 112 | -------------------------------------------------------------------------------- /wrappers/src/basic/mapx_ord_rawkey/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use ruc::*; 3 | 4 | #[test] 5 | fn test_insert() { 6 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 7 | let max = 100; 8 | (0..max) 9 | .map(|i: usize| (i.to_be_bytes(), (max + i))) 10 | .for_each(|(key, value)| { 11 | let key = key.to_vec(); 12 | assert!(hdr.get(&key).is_none()); 13 | hdr.entry(&key[..]).or_insert(value); 14 | hdr.set_value(&key[..], &value); 15 | hdr.insert(&key[..], &value); 16 | assert!(hdr.contains_key(&key[..])); 17 | assert_eq!(pnk!(hdr.get(&key[..])), value); 18 | hdr.remove(&key[..]); 19 | assert!(hdr.get(&key[..]).is_none()); 20 | hdr.insert(&key[..], &value); 21 | }); 22 | hdr.clear(); 23 | (0..max).map(|i: usize| i.to_be_bytes()).for_each(|key| { 24 | assert!(hdr.get(&key).is_none()); 25 | }); 26 | } 27 | 28 | #[test] 29 | fn test_valueende() { 30 | let cnt = 100; 31 | let dehdr = { 32 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 33 | (0..cnt) 34 | .map(|i: usize| (i.to_be_bytes(), i)) 35 | .for_each(|(key, value)| { 36 | hdr.insert(&key[..], &value); 37 | }); 38 | as ValueEnDe>::encode(&hdr) 39 | }; 40 | let mut reloaded = pnk!( as ValueEnDe>::decode(&dehdr)); 41 | (0..cnt).map(|i: usize| i).for_each(|i| { 42 | assert_eq!(i, reloaded.get(&i.to_be_bytes()).unwrap()); 43 | }); 44 | } 45 | 46 | #[test] 47 | fn test_iter() { 48 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 49 | let max = 100; 50 | (0..max) 51 | .map(|i: usize| (i.to_be_bytes(), i)) 52 | .for_each(|(key, value)| { 53 | hdr.insert(&key[..], &value); 54 | }); 55 | for (key, _) in hdr.iter().collect::>().into_iter() { 56 | hdr.remove(&key); 57 | } 58 | } 59 | 60 | #[test] 61 | fn test_first_last() { 62 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 63 | let max = 100; 64 | (0..max) 65 | .map(|i: usize| (i.to_be_bytes(), i)) 66 | .for_each(|(key, value)| { 67 | hdr.insert(&key[..], &value); 68 | }); 69 | let (_, value) = pnk!(hdr.first()); 70 | assert_eq!(0, value); 71 | 72 | let (_, value) = pnk!(hdr.last()); 73 | assert_eq!(max - 1, value); 74 | } 75 | 76 | #[test] 77 | fn test_values() { 78 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 79 | let max = 100; 80 | (0..max) 81 | .map(|i: usize| (i.to_be_bytes(), i)) 82 | .for_each(|(key, value)| { 83 | hdr.insert(&key[..], &value); 84 | }); 85 | let mut i = 0; 86 | for (_, it) in hdr.iter() { 87 | assert_eq!(i, it); 88 | i = i + 1; 89 | } 90 | } 91 | 92 | #[test] 93 | fn test_values_first_last() { 94 | let mut hdr: MapxOrdRawKey = MapxOrdRawKey::new(); 95 | let max = 100; 96 | (0..max) 97 | .map(|i: usize| (i.to_be_bytes(), i)) 98 | .for_each(|(key, value)| { 99 | hdr.insert(&key[..], &value); 100 | }); 101 | let (_, value) = pnk!(hdr.iter().next()); 102 | assert_eq!(0, value); 103 | 104 | let (_, value) = pnk!(hdr.iter().next_back()); 105 | assert_eq!(max - 1, value); 106 | } 107 | -------------------------------------------------------------------------------- /wrappers/tests/basic_mapx_ord_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use serde::{Deserialize, Serialize}; 3 | use std::ops::Bound; 4 | use vsdb::{MapxOrd, ValueEnDe, vsdb_set_base_dir}; 5 | 6 | #[derive(Serialize, Deserialize, Default, Debug, Eq, PartialEq, Clone)] 7 | struct SampleBlock { 8 | idx: usize, 9 | data: Vec, 10 | } 11 | 12 | fn gen_sample(idx: usize) -> SampleBlock { 13 | SampleBlock { 14 | idx, 15 | data: vec![idx], 16 | } 17 | } 18 | 19 | #[test] 20 | fn basic_cases() { 21 | let cnt = 200; 22 | info_omit!(vsdb_set_base_dir(&format!( 23 | "/tmp/vsdb_testing/{}", 24 | rand::random::() 25 | ))); 26 | 27 | let hdr = { 28 | let mut hdr_i = MapxOrd::new(); 29 | 30 | (0..cnt).for_each(|i| { 31 | assert!(hdr_i.get(&i).is_none()); 32 | }); 33 | 34 | (0..cnt).map(|i| (i, gen_sample(i))).for_each(|(i, b)| { 35 | hdr_i.entry(&i).or_insert(b.clone()); 36 | assert_eq!(pnk!(hdr_i.get(&i)).idx, i); 37 | hdr_i.remove(&i); 38 | assert!(hdr_i.get(&i).is_none()); 39 | hdr_i.insert(&i, &b); 40 | hdr_i.insert(&i, &b); 41 | }); 42 | 43 | as ValueEnDe>::encode(&hdr_i) 44 | }; 45 | 46 | let mut reloaded = pnk!( as ValueEnDe>::decode(&hdr)); 47 | 48 | (0..cnt).for_each(|i| { 49 | assert_eq!(i, reloaded.get(&i).unwrap().idx); 50 | }); 51 | 52 | (1..cnt).for_each(|i| { 53 | pnk!(reloaded.get_mut(&i)).idx = 1 + i; 54 | assert_eq!(pnk!(reloaded.get(&i)).idx, 1 + i); 55 | assert!(reloaded.contains_key(&i)); 56 | reloaded.remove(&i); 57 | assert!(!reloaded.contains_key(&i)); 58 | }); 59 | 60 | reloaded.clear(); 61 | 62 | reloaded.insert(&1, &gen_sample(1)); 63 | reloaded.insert(&1, &gen_sample(1)); 64 | reloaded.insert(&10, &gen_sample(10)); 65 | reloaded.insert(&100, &gen_sample(100)); 66 | reloaded.insert(&1000, &gen_sample(1000)); 67 | 68 | assert!(reloaded.range(0..1).next().is_none()); 69 | 70 | assert_eq!(100, reloaded.range(12..999).next().unwrap().1.idx); 71 | assert_eq!(100, reloaded.range(12..=999).next().unwrap().1.idx); 72 | 73 | assert_eq!(100, reloaded.range(100..=999).next().unwrap().1.idx); 74 | assert!( 75 | reloaded 76 | .range((Bound::Excluded(100), Bound::Included(999))) 77 | .next() 78 | .is_none() 79 | ); 80 | 81 | assert_eq!(100, reloaded.get_ge(&99).unwrap().1.idx); 82 | assert_eq!(100, reloaded.get_ge(&100).unwrap().1.idx); 83 | assert_eq!(100, reloaded.get_le(&100).unwrap().1.idx); 84 | assert_eq!(100, reloaded.get_le(&101).unwrap().1.idx); 85 | } 86 | 87 | #[test] 88 | fn negative_int_range() { 89 | macro_rules! run { 90 | ($int: ty) => { 91 | let mut hdr = MapxOrd::<$int, $int>::new(); 92 | (-50..50).for_each(|i| { 93 | hdr.insert(&i, &i); 94 | }); 95 | 96 | hdr.range(..) 97 | .map(|(i, _)| i) 98 | .enumerate() 99 | .for_each(|(idx, i)| { 100 | assert_eq!((idx as $int) - 50, i); 101 | }); 102 | }; 103 | } 104 | 105 | run!(i8); 106 | run!(i16); 107 | run!(i32); 108 | run!(i64); 109 | run!(i128); 110 | run!(isize); 111 | } 112 | -------------------------------------------------------------------------------- /wrappers/src/dagmap/rawkey/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | macro_rules! s { 4 | ($i: expr) => {{ $i.as_bytes().to_vec() }}; 5 | } 6 | 7 | #[test] 8 | fn dagmaprawkey_functions() { 9 | let mut i0 = DagMapRawKey::new(&mut Orphan::new(None)).unwrap(); 10 | i0.insert("k0", &s!("v0")); 11 | assert_eq!(i0.get("k0").unwrap(), s!("v0")); 12 | assert!(i0.get("k1").is_none()); 13 | let mut i0_raw = Orphan::new(Some(i0.into_inner())); 14 | 15 | let mut i1 = DagMapRawKey::new(&mut i0_raw).unwrap(); 16 | i1.insert("k1", &s!("v1")); 17 | assert_eq!(i1.get("k1").unwrap(), s!("v1")); 18 | assert_eq!(i1.get("k0").unwrap(), s!("v0")); 19 | let mut i1_raw = Orphan::new(Some(i1.into_inner())); 20 | 21 | let mut i2 = DagMapRawKey::new(&mut i1_raw).unwrap(); 22 | i2.insert("k2", &s!("v2")); 23 | assert_eq!(i2.get("k2").unwrap(), s!("v2")); 24 | assert_eq!(i2.get("k1").unwrap(), s!("v1")); 25 | assert_eq!(i2.get("k0").unwrap(), s!("v0")); 26 | i2.insert("k2", &s!("v2x")); 27 | assert_eq!(i2.get("k2").unwrap(), s!("v2x")); 28 | assert_eq!(i2.get("k1").unwrap(), s!("v1")); 29 | assert_eq!(i2.get("k0").unwrap(), s!("v0")); 30 | i2.insert("k1", &s!("v1x")); 31 | assert_eq!(i2.get("k2").unwrap(), s!("v2x")); 32 | assert_eq!(i2.get("k1").unwrap(), s!("v1x")); 33 | assert_eq!(i2.get("k0").unwrap(), s!("v0")); 34 | i2.insert("k0", &s!("v0x")); 35 | assert_eq!(i2.get("k2").unwrap(), s!("v2x")); 36 | assert_eq!(i2.get("k1").unwrap(), s!("v1x")); 37 | assert_eq!(i2.get("k0").unwrap(), s!("v0x")); 38 | 39 | assert!(i1_raw.get_value().unwrap().get("k2").is_none()); 40 | assert_eq!( 41 | i1_raw.get_value().unwrap().get("k1").unwrap(), 42 | s!("v1").encode() 43 | ); 44 | assert_eq!( 45 | i1_raw.get_value().unwrap().get("k0").unwrap(), 46 | s!("v0").encode() 47 | ); 48 | 49 | assert!(i0_raw.get_value().unwrap().get("k2").is_none()); 50 | assert!(i0_raw.get_value().unwrap().get("k1").is_none()); 51 | assert_eq!( 52 | i0_raw.get_value().unwrap().get("k0").unwrap(), 53 | s!("v0").encode() 54 | ); 55 | 56 | let mut head = pnk!(i2.prune()); 57 | sleep_ms!(1000); // give some time to the async cleaner 58 | 59 | assert_eq!(head.get("k2").unwrap(), s!("v2x")); 60 | assert_eq!(head.get("k1").unwrap(), s!("v1x")); 61 | assert_eq!(head.get("k0").unwrap(), s!("v0x")); 62 | 63 | assert!(i1_raw.get_value().is_none()); 64 | assert!(i1_raw.get_value().is_none()); 65 | assert!(i1_raw.get_value().is_none()); 66 | 67 | assert!(i0_raw.get_value().is_none()); 68 | assert!(i0_raw.get_value().is_none()); 69 | assert!(i0_raw.get_value().is_none()); 70 | 71 | // prune with deep stack 72 | for i in 10u8..=255 { 73 | head.insert(i.to_be_bytes(), &i.to_be_bytes().to_vec()); 74 | head = DagMapRawKey::new(&mut Orphan::new(Some(head.into_inner()))).unwrap(); 75 | } 76 | 77 | let mut head = pnk!(head.prune()); 78 | sleep_ms!(1000); // give some time to the async cleaner 79 | 80 | for i in 10u8..=255 { 81 | assert_eq!(head.get(i.to_be_bytes()).unwrap(), i.to_be_bytes().to_vec()); 82 | } 83 | 84 | for i in 0u8..=254 { 85 | head.remove(i.to_be_bytes()); 86 | assert!(head.get(i.to_be_bytes()).is_none()); 87 | } 88 | 89 | *(head.get_mut(255u8.to_be_bytes()).unwrap()) = 0u8.to_be_bytes().to_vec(); 90 | assert_eq!( 91 | head.get(255u8.to_be_bytes()).unwrap().as_slice(), 92 | 0u8.to_be_bytes() 93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /wrappers/src/common/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! define_map_wrapper { 3 | ( 4 | $(#[$struct_doc:meta])* 5 | $vis:vis struct $wrapper_name:ident <$($wrapper_generics:tt),*> { 6 | $inner_vis:vis inner: $inner_type:ty, 7 | $phantom_field:ident: $phantom_type:ty, 8 | } 9 | where $($trait_bounds:tt)+ 10 | ) => { 11 | $(#[$struct_doc])* 12 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug)] 13 | #[serde(bound = "")] 14 | $vis struct $wrapper_name<$($wrapper_generics),*> { 15 | $inner_vis inner: $inner_type, 16 | $phantom_field: $phantom_type, 17 | } 18 | 19 | impl<$($wrapper_generics),*> $wrapper_name<$($wrapper_generics),*> 20 | where 21 | $($trait_bounds)+ 22 | { 23 | /// # Safety 24 | /// 25 | /// This function is unsafe because it creates a new wrapper instance that shares the same underlying 26 | /// data source. The caller must ensure that no write operations occur on the original instance 27 | /// while the shadow instance exists, as this could lead to data corruption or undefined behavior. 28 | #[inline(always)] 29 | pub unsafe fn shadow(&self) -> Self { 30 | unsafe { 31 | Self { 32 | inner: self.inner.shadow(), 33 | $phantom_field: std::marker::PhantomData, 34 | } 35 | } 36 | } 37 | 38 | /// # Safety 39 | /// 40 | /// This function is unsafe because it deserializes the data structure from a raw byte slice. 41 | /// The caller must ensure that the provided bytes represent a valid, serialized instance of the 42 | /// data structure. Providing invalid or malicious data can lead to memory unsafety, panics, 43 | /// or other undefined behavior. 44 | #[inline(always)] 45 | pub unsafe fn from_bytes(s: impl AsRef<[u8]>) -> Self { 46 | unsafe { 47 | Self { 48 | inner: <$inner_type>::from_bytes(s), 49 | $phantom_field: std::marker::PhantomData, 50 | } 51 | } 52 | } 53 | 54 | #[inline(always)] 55 | pub fn as_bytes(&self) -> &[u8] { 56 | self.inner.as_bytes() 57 | } 58 | 59 | #[inline(always)] 60 | pub fn new() -> Self { 61 | Self { 62 | inner: <$inner_type>::new(), 63 | $phantom_field: std::marker::PhantomData, 64 | } 65 | } 66 | 67 | #[inline(always)] 68 | pub fn clear(&mut self) { 69 | self.inner.clear(); 70 | } 71 | 72 | #[inline(always)] 73 | pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool { 74 | self.inner.is_the_same_instance(&other_hdr.inner) 75 | } 76 | } 77 | 78 | impl<$($wrapper_generics),*> Clone for $wrapper_name<$($wrapper_generics),*> 79 | { 80 | fn clone(&self) -> Self { 81 | Self { 82 | inner: self.inner.clone(), 83 | $phantom_field: std::marker::PhantomData, 84 | } 85 | } 86 | } 87 | 88 | impl<$($wrapper_generics),*> Default for $wrapper_name<$($wrapper_generics),*> 89 | where 90 | $($trait_bounds)+ 91 | { 92 | fn default() -> Self { 93 | Self::new() 94 | } 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /wrappers/src/dagmap/raw/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn dagmapraw_functions() { 5 | let mut i0 = DagMapRaw::new(&mut Orphan::new(None)).unwrap(); 6 | i0.insert("k0", "v0"); 7 | assert_eq!(i0.get("k0").unwrap().as_slice(), "v0".as_bytes()); 8 | assert!(i0.get("k1").is_none()); 9 | let mut i0 = Orphan::new(Some(i0)); 10 | 11 | let mut i1 = DagMapRaw::new(&mut i0).unwrap(); 12 | i1.insert("k1", "v1"); 13 | assert_eq!(i1.get("k1").unwrap().as_slice(), "v1".as_bytes()); 14 | assert_eq!(i1.get("k0").unwrap().as_slice(), "v0".as_bytes()); 15 | let mut i1 = Orphan::new(Some(i1)); 16 | 17 | let mut i2 = DagMapRaw::new(&mut i1).unwrap(); 18 | i2.insert("k2", "v2"); 19 | assert_eq!(i2.get("k2").unwrap().as_slice(), "v2".as_bytes()); 20 | assert_eq!(i2.get("k1").unwrap().as_slice(), "v1".as_bytes()); 21 | assert_eq!(i2.get("k0").unwrap().as_slice(), "v0".as_bytes()); 22 | i2.insert("k2", "v2x"); 23 | assert_eq!(i2.get("k2").unwrap().as_slice(), "v2x".as_bytes()); 24 | assert_eq!(i2.get("k1").unwrap().as_slice(), "v1".as_bytes()); 25 | assert_eq!(i2.get("k0").unwrap().as_slice(), "v0".as_bytes()); 26 | i2.insert("k1", "v1x"); 27 | assert_eq!(i2.get("k2").unwrap().as_slice(), "v2x".as_bytes()); 28 | assert_eq!(i2.get("k1").unwrap().as_slice(), "v1x".as_bytes()); 29 | assert_eq!(i2.get("k0").unwrap().as_slice(), "v0".as_bytes()); 30 | i2.insert("k0", "v0x"); 31 | assert_eq!(i2.get("k2").unwrap().as_slice(), "v2x".as_bytes()); 32 | assert_eq!(i2.get("k1").unwrap().as_slice(), "v1x".as_bytes()); 33 | assert_eq!(i2.get("k0").unwrap().as_slice(), "v0x".as_bytes()); 34 | 35 | assert!(i1.get_value().unwrap().get("k2").is_none()); 36 | assert_eq!( 37 | i1.get_value().unwrap().get("k1").unwrap().as_slice(), 38 | "v1".as_bytes() 39 | ); 40 | assert_eq!( 41 | i1.get_value().unwrap().get("k0").unwrap().as_slice(), 42 | "v0".as_bytes() 43 | ); 44 | 45 | assert!(i0.get_value().unwrap().get("k2").is_none()); 46 | assert!(i0.get_value().unwrap().get("k1").is_none()); 47 | assert_eq!( 48 | i0.get_value().unwrap().get("k0").unwrap().as_slice(), 49 | "v0".as_bytes() 50 | ); 51 | 52 | let mut head = pnk!(i2.prune()); 53 | sleep_ms!(1000); // give some time to the async cleaner 54 | 55 | assert_eq!(head.get("k2").unwrap().as_slice(), "v2x".as_bytes()); 56 | assert_eq!(head.get("k1").unwrap().as_slice(), "v1x".as_bytes()); 57 | assert_eq!(head.get("k0").unwrap().as_slice(), "v0x".as_bytes()); 58 | 59 | assert!(i1.get_value().is_none()); 60 | assert!(i1.get_value().is_none()); 61 | assert!(i1.get_value().is_none()); 62 | 63 | assert!(i0.get_value().is_none()); 64 | assert!(i0.get_value().is_none()); 65 | assert!(i0.get_value().is_none()); 66 | 67 | // prune with deep stack 68 | for i in 10u8..=255 { 69 | head.insert(i.to_be_bytes(), i.to_be_bytes()); 70 | head = DagMapRaw::new(&mut Orphan::new(Some(head))).unwrap(); 71 | } 72 | 73 | let mut head = pnk!(head.prune()); 74 | sleep_ms!(1000); // give some time to the async cleaner 75 | assert!(head.parent.get_value().is_none()); 76 | assert!(head.children.iter().next().is_none()); 77 | 78 | for i in 10u8..=255 { 79 | assert_eq!( 80 | head.get(i.to_be_bytes()).unwrap().as_slice(), 81 | i.to_be_bytes() 82 | ); 83 | } 84 | 85 | for i in 0u8..=254 { 86 | head.remove(i.to_be_bytes()); 87 | assert!(head.get(i.to_be_bytes()).is_none()); 88 | } 89 | 90 | *(head.get_mut(255u8.to_be_bytes()).unwrap()) = 0u8.to_be_bytes().to_vec(); 91 | assert_eq!( 92 | head.get(255u8.to_be_bytes()).unwrap().as_slice(), 93 | 0u8.to_be_bytes() 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /utils/slot_db/benches/2_slot_db_reverse.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group, criterion_main}; 2 | use rand::random; 3 | use std::hint::black_box; 4 | use vsdb_slot_db::SlotDB; 5 | 6 | const DATA_SIZE: u32 = 100_0000; 7 | 8 | type V = Vec; 9 | 10 | fn slot_db_custom(mn: u64) -> SlotDB { 11 | let mut db = SlotDB::new(mn, false); 12 | 13 | (1..DATA_SIZE).for_each(|i| { 14 | db.insert(i as u64, i.to_be_bytes().to_vec()).unwrap(); 15 | }); 16 | 17 | db 18 | } 19 | 20 | fn query(db: &SlotDB, page_size: u16) { 21 | let page_number = random::() % (DATA_SIZE / (page_size as u32)); 22 | db.get_entries_by_page(page_size, page_number, true); 23 | } 24 | 25 | fn slot_4(c: &mut Criterion) { 26 | let mut db = slot_db_custom(4); 27 | 28 | c.bench_function("slot 4, page size: 10", |b| { 29 | b.iter(|| query(&db, black_box(10))) 30 | }); 31 | c.bench_function("slot 4, page size: 20", |b| { 32 | b.iter(|| query(&db, black_box(20))) 33 | }); 34 | c.bench_function("slot 4, page size: 40", |b| { 35 | b.iter(|| query(&db, black_box(40))) 36 | }); 37 | c.bench_function("slot 4, page size: 80", |b| { 38 | b.iter(|| query(&db, black_box(80))) 39 | }); 40 | 41 | db.clear(); 42 | } 43 | 44 | fn slot_8(c: &mut Criterion) { 45 | let mut db = slot_db_custom(8); 46 | c.bench_function("slot 8, page size: 10", |b| { 47 | b.iter(|| query(&db, black_box(10))) 48 | }); 49 | c.bench_function("slot 8, page size: 20", |b| { 50 | b.iter(|| query(&db, black_box(20))) 51 | }); 52 | c.bench_function("slot 8, page size: 40", |b| { 53 | b.iter(|| query(&db, black_box(40))) 54 | }); 55 | c.bench_function("slot 8, page size: 80", |b| { 56 | b.iter(|| query(&db, black_box(80))) 57 | }); 58 | 59 | db.clear(); 60 | } 61 | 62 | fn slot_16(c: &mut Criterion) { 63 | let mut db = slot_db_custom(16); 64 | c.bench_function("slot 16, page size: 10", |b| { 65 | b.iter(|| query(&db, black_box(10))) 66 | }); 67 | c.bench_function("slot 16, page size: 20", |b| { 68 | b.iter(|| query(&db, black_box(20))) 69 | }); 70 | c.bench_function("slot 16, page size: 40", |b| { 71 | b.iter(|| query(&db, black_box(40))) 72 | }); 73 | c.bench_function("slot 16, page size: 80", |b| { 74 | b.iter(|| query(&db, black_box(80))) 75 | }); 76 | 77 | db.clear(); 78 | } 79 | 80 | fn slot_32(c: &mut Criterion) { 81 | let mut db = slot_db_custom(32); 82 | c.bench_function("slot 32, page size: 10", |b| { 83 | b.iter(|| query(&db, black_box(10))) 84 | }); 85 | c.bench_function("slot 32, page size: 20", |b| { 86 | b.iter(|| query(&db, black_box(20))) 87 | }); 88 | c.bench_function("slot 32, page size: 40", |b| { 89 | b.iter(|| query(&db, black_box(40))) 90 | }); 91 | c.bench_function("slot 32, page size: 80", |b| { 92 | b.iter(|| query(&db, black_box(80))) 93 | }); 94 | 95 | db.clear(); 96 | } 97 | 98 | fn slot_64(c: &mut Criterion) { 99 | let mut db = slot_db_custom(64); 100 | c.bench_function("slot 64, page size: 10", |b| { 101 | b.iter(|| query(&db, black_box(10))) 102 | }); 103 | c.bench_function("slot 64, page size: 20", |b| { 104 | b.iter(|| query(&db, black_box(20))) 105 | }); 106 | c.bench_function("slot 64, page size: 40", |b| { 107 | b.iter(|| query(&db, black_box(40))) 108 | }); 109 | c.bench_function("slot 64, page size: 80", |b| { 110 | b.iter(|| query(&db, black_box(80))) 111 | }); 112 | 113 | db.clear(); 114 | } 115 | 116 | criterion_group!(benches, slot_64, slot_32, slot_16, slot_8, slot_4); 117 | criterion_main!(benches); 118 | -------------------------------------------------------------------------------- /utils/slot_db/benches/0_slot_db_reverse_swap_order.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group, criterion_main}; 2 | use rand::random; 3 | use std::hint::black_box; 4 | use vsdb_slot_db::SlotDB; 5 | 6 | const DATA_SIZE: u32 = 100_0000; 7 | 8 | type V = Vec; 9 | 10 | fn slot_db_custom(mn: u64) -> SlotDB { 11 | let mut db = SlotDB::new(mn, true); 12 | 13 | (1..DATA_SIZE).for_each(|i| { 14 | db.insert(i as u64, i.to_be_bytes().to_vec()).unwrap(); 15 | }); 16 | 17 | db 18 | } 19 | 20 | fn query(db: &SlotDB, page_size: u16) { 21 | let page_number = random::() % (DATA_SIZE / (page_size as u32)); 22 | db.get_entries_by_page(page_size, page_number, true); 23 | } 24 | 25 | fn slot_4(c: &mut Criterion) { 26 | let mut db = slot_db_custom(4); 27 | 28 | c.bench_function("slot 4, page size: 10", |b| { 29 | b.iter(|| query(&db, black_box(10))) 30 | }); 31 | c.bench_function("slot 4, page size: 20", |b| { 32 | b.iter(|| query(&db, black_box(20))) 33 | }); 34 | c.bench_function("slot 4, page size: 40", |b| { 35 | b.iter(|| query(&db, black_box(40))) 36 | }); 37 | c.bench_function("slot 4, page size: 80", |b| { 38 | b.iter(|| query(&db, black_box(80))) 39 | }); 40 | 41 | db.clear(); 42 | } 43 | 44 | fn slot_8(c: &mut Criterion) { 45 | let mut db = slot_db_custom(8); 46 | c.bench_function("slot 8, page size: 10", |b| { 47 | b.iter(|| query(&db, black_box(10))) 48 | }); 49 | c.bench_function("slot 8, page size: 20", |b| { 50 | b.iter(|| query(&db, black_box(20))) 51 | }); 52 | c.bench_function("slot 8, page size: 40", |b| { 53 | b.iter(|| query(&db, black_box(40))) 54 | }); 55 | c.bench_function("slot 8, page size: 80", |b| { 56 | b.iter(|| query(&db, black_box(80))) 57 | }); 58 | 59 | db.clear(); 60 | } 61 | 62 | fn slot_16(c: &mut Criterion) { 63 | let mut db = slot_db_custom(16); 64 | c.bench_function("slot 16, page size: 10", |b| { 65 | b.iter(|| query(&db, black_box(10))) 66 | }); 67 | c.bench_function("slot 16, page size: 20", |b| { 68 | b.iter(|| query(&db, black_box(20))) 69 | }); 70 | c.bench_function("slot 16, page size: 40", |b| { 71 | b.iter(|| query(&db, black_box(40))) 72 | }); 73 | c.bench_function("slot 16, page size: 80", |b| { 74 | b.iter(|| query(&db, black_box(80))) 75 | }); 76 | 77 | db.clear(); 78 | } 79 | 80 | fn slot_32(c: &mut Criterion) { 81 | let mut db = slot_db_custom(32); 82 | c.bench_function("slot 32, page size: 10", |b| { 83 | b.iter(|| query(&db, black_box(10))) 84 | }); 85 | c.bench_function("slot 32, page size: 20", |b| { 86 | b.iter(|| query(&db, black_box(20))) 87 | }); 88 | c.bench_function("slot 32, page size: 40", |b| { 89 | b.iter(|| query(&db, black_box(40))) 90 | }); 91 | c.bench_function("slot 32, page size: 80", |b| { 92 | b.iter(|| query(&db, black_box(80))) 93 | }); 94 | 95 | db.clear(); 96 | } 97 | 98 | fn slot_64(c: &mut Criterion) { 99 | let mut db = slot_db_custom(64); 100 | c.bench_function("slot 64, page size: 10", |b| { 101 | b.iter(|| query(&db, black_box(10))) 102 | }); 103 | c.bench_function("slot 64, page size: 20", |b| { 104 | b.iter(|| query(&db, black_box(20))) 105 | }); 106 | c.bench_function("slot 64, page size: 40", |b| { 107 | b.iter(|| query(&db, black_box(40))) 108 | }); 109 | c.bench_function("slot 64, page size: 80", |b| { 110 | b.iter(|| query(&db, black_box(80))) 111 | }); 112 | 113 | db.clear(); 114 | } 115 | 116 | criterion_group!(benches, slot_64, slot_32, slot_16, slot_8, slot_4); 117 | criterion_main!(benches); 118 | -------------------------------------------------------------------------------- /utils/slot_db/benches/1_slot_db_positive.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, criterion_group, criterion_main}; 2 | use rand::random; 3 | use std::hint::black_box; 4 | use vsdb_slot_db::SlotDB; 5 | 6 | const DATA_SIZE: u32 = 100_0000; 7 | 8 | type V = Vec; 9 | 10 | fn slot_db_custom(mn: u64) -> SlotDB { 11 | let mut db = SlotDB::new(mn, false); 12 | 13 | (0..DATA_SIZE).for_each(|i| { 14 | db.insert(i as u64, i.to_be_bytes().to_vec()).unwrap(); 15 | }); 16 | 17 | db 18 | } 19 | 20 | fn query(db: &SlotDB, page_size: u16) { 21 | let page_number = random::() % (DATA_SIZE / (page_size as u32)); 22 | db.get_entries_by_page(page_size, page_number, false); 23 | } 24 | 25 | fn slot_4(c: &mut Criterion) { 26 | let mut db = slot_db_custom(4); 27 | 28 | c.bench_function("slot 4, page size: 10", |b| { 29 | b.iter(|| query(&db, black_box(10))) 30 | }); 31 | c.bench_function("slot 4, page size: 20", |b| { 32 | b.iter(|| query(&db, black_box(20))) 33 | }); 34 | c.bench_function("slot 4, page size: 40", |b| { 35 | b.iter(|| query(&db, black_box(40))) 36 | }); 37 | c.bench_function("slot 4, page size: 80", |b| { 38 | b.iter(|| query(&db, black_box(80))) 39 | }); 40 | 41 | db.clear(); 42 | } 43 | 44 | fn slot_8(c: &mut Criterion) { 45 | let mut db = slot_db_custom(8); 46 | 47 | c.bench_function("slot 8, page size: 10", |b| { 48 | b.iter(|| query(&db, black_box(10))) 49 | }); 50 | c.bench_function("slot 8, page size: 20", |b| { 51 | b.iter(|| query(&db, black_box(20))) 52 | }); 53 | c.bench_function("slot 8, page size: 40", |b| { 54 | b.iter(|| query(&db, black_box(40))) 55 | }); 56 | c.bench_function("slot 8, page size: 80", |b| { 57 | b.iter(|| query(&db, black_box(80))) 58 | }); 59 | 60 | db.clear(); 61 | } 62 | 63 | fn slot_16(c: &mut Criterion) { 64 | let mut db = slot_db_custom(16); 65 | 66 | c.bench_function("slot 16, page size: 10", |b| { 67 | b.iter(|| query(&db, black_box(10))) 68 | }); 69 | c.bench_function("slot 16, page size: 20", |b| { 70 | b.iter(|| query(&db, black_box(20))) 71 | }); 72 | c.bench_function("slot 16, page size: 40", |b| { 73 | b.iter(|| query(&db, black_box(40))) 74 | }); 75 | c.bench_function("slot 16, page size: 80", |b| { 76 | b.iter(|| query(&db, black_box(80))) 77 | }); 78 | 79 | db.clear(); 80 | } 81 | 82 | fn slot_32(c: &mut Criterion) { 83 | let mut db = slot_db_custom(32); 84 | 85 | c.bench_function("slot 32, page size: 10", |b| { 86 | b.iter(|| query(&db, black_box(10))) 87 | }); 88 | c.bench_function("slot 32, page size: 20", |b| { 89 | b.iter(|| query(&db, black_box(20))) 90 | }); 91 | c.bench_function("slot 32, page size: 40", |b| { 92 | b.iter(|| query(&db, black_box(40))) 93 | }); 94 | c.bench_function("slot 32, page size: 80", |b| { 95 | b.iter(|| query(&db, black_box(80))) 96 | }); 97 | 98 | db.clear(); 99 | } 100 | 101 | fn slot_64(c: &mut Criterion) { 102 | let mut db = slot_db_custom(64); 103 | 104 | c.bench_function("slot 64, page size: 10", |b| { 105 | b.iter(|| query(&db, black_box(10))) 106 | }); 107 | c.bench_function("slot 64, page size: 20", |b| { 108 | b.iter(|| query(&db, black_box(20))) 109 | }); 110 | c.bench_function("slot 64, page size: 40", |b| { 111 | b.iter(|| query(&db, black_box(40))) 112 | }); 113 | c.bench_function("slot 64, page size: 80", |b| { 114 | b.iter(|| query(&db, black_box(80))) 115 | }); 116 | 117 | db.clear(); 118 | } 119 | 120 | criterion_group!(benches, slot_64, slot_32, slot_16, slot_8, slot_4); 121 | criterion_main!(benches); 122 | -------------------------------------------------------------------------------- /wrappers/README.md: -------------------------------------------------------------------------------- 1 | # vsdb 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/vsdb.svg)](https://crates.io/crates/vsdb) 4 | [![Docs.rs](https://docs.rs/vsdb/badge.svg)](https://docs.rs/vsdb) 5 | [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg)](../../LICENSE) 6 | [![Rust](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-util-collections/vsdb/actions/workflows/rust.yml) 7 | 8 | > `vsdb` is a high-performance, embedded database with an API similar to Rust's standard collections. 9 | 10 | This crate provides high-level, typed data structures that are backed by a persistent key-value store. It is the primary crate for end-users. 11 | 12 | This is a simplified version of the original [**vsdb**](https://crates.io/crates/vsdb/0.70.0), retaining only the most practical and stable parts. 13 | 14 | ## Installation 15 | 16 | Add this to your `Cargo.toml`: 17 | 18 | ```toml 19 | [dependencies] 20 | vsdb = "7.1.0" 21 | ``` 22 | 23 | ## Highlights 24 | 25 | For more detailed API examples, see [API Examples](docs/api.md). 26 | 27 | - **Familiar API**: Most APIs are designed to mirror their counterparts in the standard library. 28 | - `Vecx` behaves like `std::collections::Vec`. 29 | - `Mapx` behaves like `std::collections::HashMap`. 30 | - `MapxOrd` behaves like `std::collections::BTreeMap`. 31 | - **Persistent Storage**: Data is automatically saved to disk and loaded on instantiation. 32 | - **Typed Keys and Values**: Keys and values are strongly typed and automatically serialized/deserialized. 33 | 34 | ## Features 35 | 36 | - `parity_backend`: **(Default)** Use `parity-db` as the backend database. Pure Rust implementation. 37 | - `rocks_backend`: Use `rocksdb` as the backend database. C++ implementation. 38 | - `msgpack_codec`: **(Default)** Use `rmp-serde` as the codec for faster performance. 39 | - `json_codec`: Use `serde_json` as the codec for better compatibility. 40 | - `compress`: **(Default)** Enable data compression in the backend database. 41 | 42 | ## Usage 43 | 44 | ### Mapx 45 | 46 | `Mapx` is a persistent, hash map-like data structure. 47 | 48 | ```rust 49 | use vsdb::Mapx; 50 | 51 | let mut map = Mapx::new(); 52 | 53 | // Insert some key-value pairs 54 | map.insert(&"key1", &"value1"); 55 | map.insert(&"key2", &"value2"); 56 | 57 | // Get a value 58 | assert_eq!(map.get(&"key1"), Some("value1".to_string())); 59 | 60 | // Check if a key exists 61 | assert!(map.contains_key(&"key2")); 62 | 63 | // Iterate over the key-value pairs 64 | for (key, value) in map.iter() { 65 | println!("{}: {}", key, value); 66 | } 67 | 68 | // Remove a key-value pair 69 | map.remove(&"key1"); 70 | ``` 71 | 72 | ### MapxOrd 73 | 74 | `MapxOrd` is a persistent, B-tree map-like data structure that keeps keys in sorted order. 75 | 76 | ```rust 77 | use vsdb::MapxOrd; 78 | 79 | let mut map = MapxOrd::new(); 80 | 81 | // Insert some key-value pairs 82 | map.insert(&3, &"three"); 83 | map.insert(&1, &"one"); 84 | map.insert(&2, &"two"); 85 | 86 | // Get a value 87 | assert_eq!(map.get(&1), Some("one".to_string())); 88 | 89 | // Iterate over the key-value pairs in sorted order 90 | for (key, value) in map.iter() { 91 | println!("{}: {}", key, value); 92 | } 93 | 94 | // Get the first and last key-value pairs 95 | assert_eq!(map.first(), Some((1, "one".to_string()))); 96 | assert_eq!(map.last(), Some((3, "three".to_string()))); 97 | ``` 98 | 99 | ## Important Notes 100 | 101 | - The serialized result of a `vsdb` instance cannot be used for distributed consensus. The serialized data contains meta-information (like storage paths) that may differ across environments. The correct approach is to read the required data and then process the raw content. 102 | 103 | ## License 104 | 105 | This project is licensed under the **GPL-3.0** license. 106 | -------------------------------------------------------------------------------- /wrappers/tests/basic_multi_key_mapx_rawkey_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use vsdb::{basic_multi_key::mapx_rawkey::MapxRawKeyMk, vsdb_set_base_dir}; 3 | 4 | #[test] 5 | fn basic_cases() { 6 | info_omit!(vsdb_set_base_dir(&format!( 7 | "/tmp/vsdb_testing/{}", 8 | rand::random::() 9 | ))); 10 | 11 | let mut map = MapxRawKeyMk::new(4); 12 | 13 | // key size mismatch 14 | assert!(map.insert(&[&[1]], &0u32).is_err()); 15 | 16 | assert!(map.insert(&[&[1], &[2], &[3], &[4]], &9).is_ok()); 17 | assert!(map.insert(&[&[1], &[2], &[3], &[40]], &8).is_ok()); 18 | assert!(map.insert(&[&[1], &[2], &[30], &[40]], &7).is_ok()); 19 | assert!(map.insert(&[&[1], &[2], &[30], &[41]], &6).is_ok()); 20 | 21 | assert_eq!(map.get(&[&[1], &[2], &[3], &[4]]).unwrap(), 9); 22 | assert_eq!(map.get(&[&[1], &[2], &[3], &[40]]).unwrap(), 8); 23 | assert_eq!(map.get(&[&[1], &[2], &[30], &[40]]).unwrap(), 7); 24 | assert_eq!(map.get(&[&[1], &[2], &[30], &[41]]).unwrap(), 6); 25 | 26 | // key size mismatch 27 | assert!(map.get(&[&[1], &[2], &[3]]).is_none()); 28 | assert!(map.get(&[&[1], &[2]]).is_none()); 29 | assert!(map.get(&[&[1]]).is_none()); 30 | assert!(map.get(&[]).is_none()); 31 | 32 | // does not exist 33 | map.remove(&[&[1], &[2], &[3], &[200]]).unwrap(); 34 | 35 | map.remove(&[&[1], &[2], &[3], &[40]]).unwrap(); 36 | assert!(map.get(&[&[1], &[2], &[3], &[40]]).is_none()); 37 | 38 | // partial-path remove 39 | map.remove(&[&[1], &[2], &[30]]).unwrap(); // yes, is none 40 | assert!(map.get(&[&[1], &[2], &[30], &[40]]).is_none()); 41 | assert!(map.get(&[&[1], &[2], &[30], &[41]]).is_none()); 42 | 43 | // nothing will be removed by an empty key 44 | map.remove(&[]).unwrap(); 45 | 46 | assert!(map.get(&[&[1], &[2], &[3], &[4]]).is_some()); 47 | map.remove(&[&[1]]).unwrap(); // yes, is none 48 | assert!(map.get(&[&[1], &[2], &[3], &[4]]).is_none()); 49 | 50 | assert!(map.entry(&[]).is_err()); 51 | map.entry(&[&[11], &[12], &[13], &[14]]) 52 | .unwrap() 53 | .or_insert(777); 54 | assert_eq!(map.get(&[&[11], &[12], &[13], &[14]]).unwrap(), 777); 55 | 56 | let mut cnt = 0; 57 | let mut op = |k: &[&[u8]], v: &u32| { 58 | cnt += 1; 59 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 60 | assert_eq!(v, &777); 61 | Ok(()) 62 | }; 63 | 64 | pnk!(map.iter_op(&mut op)); 65 | assert_eq!(cnt, 1); 66 | 67 | map.entry(&[&[11], &[12], &[13], &[15]]) 68 | .unwrap() 69 | .or_insert(888); 70 | assert_eq!(map.get(&[&[11], &[12], &[13], &[15]]).unwrap(), 888); 71 | 72 | let mut cnt = 0; 73 | let mut op = |k: &[&[u8]], v: &u32| { 74 | cnt += 1; 75 | if v == &777 { 76 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 77 | } else { 78 | assert_eq!(k, &[&[11], &[12], &[13], &[15]]); 79 | } 80 | Ok(()) 81 | }; 82 | 83 | // cnt += 2 84 | pnk!(map.iter_op(&mut op)); 85 | // cnt += 2 86 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11]])); 87 | // cnt += 2 88 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12]])); 89 | // cnt += 2 90 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13]])); 91 | // cnt += 1 92 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[14]])); 93 | // cnt += 1 94 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[15]])); 95 | 96 | // cnt += 0 97 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111]])); 98 | // cnt += 0 99 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12]])); 100 | // cnt += 0 101 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13]])); 102 | // cnt += 0 103 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[14]])); 104 | // cnt += 0 105 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[15]])); 106 | 107 | drop(op); 108 | assert_eq!(cnt, 10); 109 | } 110 | -------------------------------------------------------------------------------- /wrappers/tests/basic_multi_key_mapx_raw_test.rs: -------------------------------------------------------------------------------- 1 | use ruc::*; 2 | use vsdb::{basic_multi_key::mapx_raw::MapxRawMk, vsdb_set_base_dir}; 3 | 4 | const NIL: &[u8] = &[]; 5 | 6 | #[test] 7 | fn basic_cases() { 8 | info_omit!(vsdb_set_base_dir(&format!( 9 | "/tmp/vsdb_testing/{}", 10 | rand::random::() 11 | ))); 12 | 13 | let mut map = MapxRawMk::new(4); 14 | 15 | // key size mismatch 16 | assert!(map.insert(&[&[1]], &[]).is_err()); 17 | 18 | assert!(map.insert(&[&[1], &[2], &[3], &[4]], &[9]).is_ok()); 19 | assert!(map.insert(&[&[1], &[2], &[3], &[40]], &[8]).is_ok()); 20 | assert!(map.insert(&[&[1], &[2], &[30], &[40]], &[7]).is_ok()); 21 | assert!(map.insert(&[&[1], &[2], &[30], &[41]], &[6]).is_ok()); 22 | 23 | assert_eq!(&map.get(&[&[1], &[2], &[3], &[4]]).unwrap(), &[9]); 24 | assert_eq!(&map.get(&[&[1], &[2], &[3], &[40]]).unwrap(), &[8]); 25 | assert_eq!(&map.get(&[&[1], &[2], &[30], &[40]]).unwrap(), &[7]); 26 | assert_eq!(&map.get(&[&[1], &[2], &[30], &[41]]).unwrap(), &[6]); 27 | 28 | // key size mismatch 29 | assert!(map.get(&[&[1], &[2], &[3]]).is_none()); 30 | assert!(map.get(&[&[1], &[2]]).is_none()); 31 | assert!(map.get(&[&[1]]).is_none()); 32 | assert!(map.get(&[]).is_none()); 33 | 34 | // does not exist 35 | map.remove(&[&[1], &[2], &[3], &[200]]).unwrap(); 36 | 37 | map.remove(&[&[1], &[2], &[3], &[40]]).unwrap(); 38 | assert!(map.get(&[&[1], &[2], &[3], &[40]]).is_none()); 39 | 40 | // partial-path remove 41 | map.remove(&[&[1], &[2], &[30]]).unwrap(); // yes, is none 42 | assert!(map.get(&[&[1], &[2], &[30], &[40]]).is_none()); 43 | assert!(map.get(&[&[1], &[2], &[30], &[41]]).is_none()); 44 | 45 | // nothing will be removed by an empty key 46 | map.remove(&[]).unwrap(); 47 | 48 | assert!(map.get(&[&[1], &[2], &[3], &[4]]).is_some()); 49 | map.remove(&[&[1]]).unwrap(); // yes, is none 50 | assert!(map.get(&[&[1], &[2], &[3], &[4]]).is_none()); 51 | 52 | assert!(map.entry(&[]).is_err()); 53 | map.entry(&[&[11], &[12], &[13], &[14]]) 54 | .unwrap() 55 | .or_insert(&[]); 56 | assert_eq!(&map.get(&[&[11], &[12], &[13], &[14]]).unwrap(), NIL); 57 | 58 | let mut cnt = 0; 59 | let mut op = |k: &[&[u8]], v: &[u8]| { 60 | cnt += 1; 61 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 62 | assert_eq!(v, NIL); 63 | Ok(()) 64 | }; 65 | 66 | pnk!(map.iter_op(&mut op)); 67 | assert_eq!(cnt, 1); 68 | 69 | map.entry(&[&[11], &[12], &[13], &[15]]) 70 | .unwrap() 71 | .or_insert(&[0]); 72 | assert_eq!(&map.get(&[&[11], &[12], &[13], &[15]]).unwrap(), &[0]); 73 | 74 | let mut cnt = 0; 75 | let mut op = |k: &[&[u8]], v: &[u8]| { 76 | cnt += 1; 77 | if v == NIL { 78 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 79 | } else { 80 | assert_eq!(k, &[&[11], &[12], &[13], &[15]]); 81 | } 82 | Ok(()) 83 | }; 84 | 85 | // cnt += 2 86 | pnk!(map.iter_op(&mut op)); 87 | // cnt += 2 88 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11]])); 89 | // cnt += 2 90 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12]])); 91 | // cnt += 2 92 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13]])); 93 | // cnt += 1 94 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[14]])); 95 | // cnt += 1 96 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[15]])); 97 | 98 | // cnt += 0 99 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111]])); 100 | // cnt += 0 101 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12]])); 102 | // cnt += 0 103 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13]])); 104 | // cnt += 0 105 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[14]])); 106 | // cnt += 0 107 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[15]])); 108 | 109 | drop(op); 110 | assert_eq!(cnt, 10); 111 | } 112 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mapx_raw/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ValueEnDe; 2 | 3 | use super::*; 4 | use ruc::*; 5 | 6 | const NIL: &[u8] = &[]; 7 | 8 | #[test] 9 | fn test_insert() { 10 | let mut hdr = MapxRawMk::new(2); 11 | assert_eq!(2, hdr.key_size()); 12 | let max = 100; 13 | (0..max) 14 | .map(|i: usize| (i.to_be_bytes(), ::encode(&(max + i)))) 15 | .for_each(|(subkey, value)| { 16 | let key: &[&[u8]] = &[&subkey, &subkey]; 17 | assert!(hdr.get(&key).is_none()); 18 | hdr.entry(&key).unwrap().or_insert(&value); 19 | hdr.insert(&key, &value).unwrap(); 20 | assert!(hdr.contains_key(&key)); 21 | assert_eq!(pnk!(hdr.get(&key)), value); 22 | hdr.remove(&key).unwrap(); 23 | assert!(hdr.get(&key).is_none()); 24 | hdr.insert(&key, &value).unwrap(); 25 | }); 26 | hdr.clear(); 27 | (0..max).map(|i: usize| i.to_be_bytes()).for_each(|subkey| { 28 | assert!(hdr.get(&[&subkey, &subkey]).is_none()); 29 | }); 30 | } 31 | 32 | #[test] 33 | fn test_valueende() { 34 | let cnt = 100; 35 | let dehdr = { 36 | let mut hdr = MapxRawMk::new(2); 37 | let max = 100; 38 | (0..max) 39 | .map(|i: usize| (i.to_be_bytes(), ::encode(&i))) 40 | .for_each(|(subkey, value)| { 41 | let key: &[&[u8]] = &[&subkey, &subkey]; 42 | hdr.insert(&key, &value).unwrap(); 43 | }); 44 | ::encode(&hdr) 45 | }; 46 | let mut reloaded = pnk!(::decode(&dehdr)); 47 | 48 | (0..cnt) 49 | .map(|i: usize| (i, i.to_be_bytes())) 50 | .for_each(|(i, subkey)| { 51 | let val = pnk!(::decode(&pnk!( 52 | reloaded.get(&[&subkey, &subkey]) 53 | ))); 54 | assert_eq!(i, val); 55 | }); 56 | } 57 | 58 | #[test] 59 | fn test_iter_op() { 60 | let mut map = MapxRawMk::new(4); 61 | map.entry(&[&[1], &[2], &[3], &[4]]) 62 | .unwrap() 63 | .or_insert(&[0]); 64 | assert_eq!(&map.get(&[&[1], &[2], &[3], &[4]]).unwrap(), &[0]); 65 | 66 | let mut cnt = 0; 67 | pnk!(map.iter_op(&mut |k: &[&[u8]], v: &[u8]| { 68 | cnt += 1; 69 | assert_eq!(k, &[&[1], &[2], &[3], &[4]]); 70 | assert_eq!(v, &[0]); 71 | Ok(()) 72 | })); 73 | assert_eq!(cnt, 1); 74 | } 75 | 76 | #[test] 77 | fn test_iter_op_with_key_prefix() { 78 | let mut map = MapxRawMk::new(4); 79 | map.entry(&[&[11], &[12], &[13], &[14]]) 80 | .unwrap() 81 | .or_insert(&[]); 82 | assert_eq!(&map.get(&[&[11], &[12], &[13], &[14]]).unwrap(), NIL); 83 | map.entry(&[&[11], &[12], &[13], &[15]]) 84 | .unwrap() 85 | .or_insert(&[0]); 86 | assert_eq!(&map.get(&[&[11], &[12], &[13], &[15]]).unwrap(), &[0]); 87 | 88 | let mut cnt = 0; 89 | let mut op = |k: &[&[u8]], v: &[u8]| { 90 | cnt += 1; 91 | println!("cnt = {} v = {:?}", cnt, v); 92 | if v == NIL { 93 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 94 | } else { 95 | assert_eq!(k, &[&[11], &[12], &[13], &[15]]); 96 | } 97 | Ok(()) 98 | }; 99 | 100 | // cnt += 2 101 | pnk!(map.iter_op(&mut op)); 102 | // cnt += 2 103 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11]])); 104 | // // cnt += 2 105 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12]])); 106 | // // cnt += 2 107 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13]])); 108 | // // cnt += 1 109 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[14]])); 110 | // // cnt += 1 111 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[15]])); 112 | 113 | // cnt += 0 114 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111]])); 115 | // cnt += 0 116 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12]])); 117 | // cnt += 0 118 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13]])); 119 | // cnt += 0 120 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[14]])); 121 | // cnt += 0 122 | pnk!(map.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[15]])); 123 | 124 | assert_eq!(cnt, 10); 125 | } 126 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mapx_rawkey/test.rs: -------------------------------------------------------------------------------- 1 | use crate::ValueEnDe; 2 | 3 | use super::*; 4 | use ruc::*; 5 | 6 | #[test] 7 | fn test_insert() { 8 | let mut hdr = MapxRawKeyMk::new(2); 9 | assert_eq!(2, hdr.key_size()); 10 | let max = 100; 11 | (0..max) 12 | .map(|i: usize| (i.to_be_bytes(), ::encode(&(max + i)))) 13 | .for_each(|(subkey, value)| { 14 | let key: &[&[u8]] = &[&subkey, &subkey]; 15 | assert!(hdr.get(&key).is_none()); 16 | hdr.entry(key).unwrap().or_insert(value.clone()); 17 | hdr.insert(&key, &value).unwrap(); 18 | assert!(hdr.contains_key(&key)); 19 | assert_eq!(pnk!(hdr.get(&key)), value); 20 | hdr.remove(&key).unwrap(); 21 | assert!(hdr.get(&key).is_none()); 22 | hdr.insert(&key, &value).unwrap(); 23 | }); 24 | hdr.clear(); 25 | (0..max).map(|i: usize| i.to_be_bytes()).for_each(|subkey| { 26 | assert!(hdr.get(&[&subkey, &subkey]).is_none()); 27 | }); 28 | } 29 | 30 | #[test] 31 | fn test_valueende() { 32 | let cnt = 100; 33 | let dehdr = { 34 | let mut hdr: MapxRawKeyMk> = MapxRawKeyMk::new(2); 35 | let max = 100; 36 | (0..max) 37 | .map(|i: usize| (i.to_be_bytes(), ::encode(&i))) 38 | .for_each(|(subkey, value)| { 39 | let key: &[&[u8]] = &[&subkey, &subkey]; 40 | hdr.insert(&key, &value).unwrap(); 41 | }); 42 | > as ValueEnDe>::encode(&hdr) 43 | }; 44 | let mut reloaded = pnk!(> as ValueEnDe>::decode(&dehdr)); 45 | 46 | (0..cnt) 47 | .map(|i: usize| (i, i.to_be_bytes())) 48 | .for_each(|(i, subkey)| { 49 | let val = pnk!(::decode(&pnk!( 50 | reloaded.get(&[&subkey, &subkey]) 51 | ))); 52 | assert_eq!(i, val); 53 | }); 54 | } 55 | 56 | #[test] 57 | fn test_iter_op() { 58 | let mut hdr = MapxRawKeyMk::new(4); 59 | assert!(hdr.entry(&[]).is_err()); 60 | hdr.entry(&[&[11], &[12], &[13], &[14]]) 61 | .unwrap() 62 | .or_insert(777); 63 | assert_eq!(hdr.get(&[&[11], &[12], &[13], &[14]]).unwrap(), 777); 64 | 65 | let mut cnt = 0; 66 | let mut op = |k: &[&[u8]], v: &u32| { 67 | cnt += 1; 68 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 69 | assert_eq!(v, &777); 70 | Ok(()) 71 | }; 72 | 73 | pnk!(hdr.iter_op(&mut op)); 74 | assert_eq!(cnt, 1); 75 | } 76 | 77 | #[test] 78 | fn test_iter_op_with_key_prefix() { 79 | let mut hdr = MapxRawKeyMk::new(4); 80 | assert!(hdr.entry(&[]).is_err()); 81 | hdr.entry(&[&[11], &[12], &[13], &[14]]) 82 | .unwrap() 83 | .or_insert(777); 84 | assert_eq!(hdr.get(&[&[11], &[12], &[13], &[14]]).unwrap(), 777); 85 | 86 | hdr.entry(&[&[11], &[12], &[13], &[15]]) 87 | .unwrap() 88 | .or_insert(888); 89 | assert_eq!(hdr.get(&[&[11], &[12], &[13], &[15]]).unwrap(), 888); 90 | 91 | let mut cnt = 0; 92 | let mut op = |k: &[&[u8]], v: &u32| { 93 | cnt += 1; 94 | if v == &777 { 95 | assert_eq!(k, &[&[11], &[12], &[13], &[14]]); 96 | } else { 97 | assert_eq!(k, &[&[11], &[12], &[13], &[15]]); 98 | } 99 | Ok(()) 100 | }; 101 | 102 | // cnt += 2 103 | pnk!(hdr.iter_op(&mut op)); 104 | // cnt += 2 105 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[11]])); 106 | // cnt += 2 107 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[11], &[12]])); 108 | // cnt += 2 109 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13]])); 110 | // cnt += 1 111 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[14]])); 112 | // cnt += 1 113 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[11], &[12], &[13], &[15]])); 114 | 115 | // cnt += 0 116 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[111]])); 117 | // cnt += 0 118 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[111], &[12]])); 119 | // cnt += 0 120 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13]])); 121 | // cnt += 0 122 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[14]])); 123 | // cnt += 0 124 | pnk!(hdr.iter_op_with_key_prefix(&mut op, &[&[111], &[12], &[13], &[15]])); 125 | 126 | drop(op); 127 | assert_eq!(cnt, 10); 128 | } 129 | -------------------------------------------------------------------------------- /utils/slot_db/src/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use rand::random; 3 | 4 | #[test] 5 | fn workflow_normal() { 6 | [32, 16, 8].into_iter().for_each(|i| { 7 | slot_db(i, false); 8 | }); 9 | } 10 | 11 | #[test] 12 | fn workflow_swap_order() { 13 | [32, 16, 8].into_iter().for_each(|i| { 14 | slot_db(i, true); 15 | }); 16 | } 17 | 18 | fn slot_db(mn: u64, swap_order: bool) { 19 | let mut db = SlotDB::new(mn, swap_order); 20 | let mut test_db = testdb::TestDB::default(); 21 | 22 | let mut slot_min = Slot::MAX; 23 | let mut slot_max = Slot::MIN; 24 | 25 | (0..siz()).for_each(|i| { 26 | slot_min = i.min(slot_min); 27 | slot_max = i.max(slot_max); 28 | 29 | db.insert(i, i).unwrap(); 30 | test_db.insert(i, i); 31 | }); 32 | 33 | assert_eq!(siz(), db.total()); 34 | 35 | assert_queryable(&db, &test_db, slot_min, slot_max); 36 | 37 | db.clear(); 38 | assert_eq!(0, db.total()); 39 | assert!(db.get_entries_by_page(10, 0, true).is_empty()); 40 | assert!(db.get_entries_by_page(10, 0, false).is_empty()); 41 | } 42 | 43 | fn assert_queryable( 44 | db: &SlotDB, 45 | test_db: &testdb::TestDB, 46 | slot_min: Slot, 47 | slot_max: Slot, 48 | ) { 49 | for _ in 0..16 { 50 | let page_size = 1 + (random::() as u32) % 128; 51 | let max_page = 100 + (db.total() / (page_size as u64)) as u32; 52 | 53 | // Ensure the first page case is covered 54 | let page_number = random::() % max_page; 55 | 56 | let page_size = page_size as u64; 57 | let page_number = page_number as u64; 58 | dbg!(page_number, page_size); 59 | 60 | let a = test_db.get_entries_by_page_slot( 61 | None, 62 | None, 63 | page_size as u16, 64 | page_number as u32, 65 | true, 66 | ); 67 | let b = 68 | db.get_entries_by_page(page_size as u16, page_number as u32, true); 69 | assert_eq!(a, b); 70 | 71 | let a = test_db.get_entries_by_page_slot( 72 | None, 73 | None, 74 | page_size as u16, 75 | page_number as u32, 76 | false, 77 | ); 78 | let b = db.get_entries_by_page( 79 | page_size as u16, 80 | page_number as u32, 81 | false, 82 | ); 83 | assert_eq!(a, b); 84 | 85 | ////////////////////////////////// 86 | // Cases with custom slot range // 87 | ////////////////////////////////// 88 | 89 | let smin = random::() % (slot_min.saturating_add(100)); 90 | let smax = smin 91 | + random::() % ((slot_max - slot_min).saturating_add(100)); 92 | 93 | //////////////////////////////////////// 94 | //////////////////////////////////////// 95 | 96 | let a = test_db.get_entries_by_page_slot( 97 | Some(dbg!(smin)), 98 | Some(dbg!(smax)), 99 | page_size as u16, 100 | page_number as u32, 101 | true, 102 | ); 103 | 104 | let b = db.get_entries_by_page_slot( 105 | Some(smin), 106 | Some(smax), 107 | page_size as u16, 108 | page_number as u32, 109 | true, 110 | ); 111 | assert_eq!(a, b); 112 | 113 | let a = test_db.get_entries_by_page_slot( 114 | Some(smin), 115 | Some(smax), 116 | page_size as u16, 117 | page_number as u32, 118 | false, 119 | ); 120 | 121 | let b = db.get_entries_by_page_slot( 122 | Some(smin), 123 | Some(smax), 124 | page_size as u16, 125 | page_number as u32, 126 | false, 127 | ); 128 | assert_eq!(a, b); 129 | } 130 | } 131 | 132 | const fn siz() -> u64 { 133 | if cfg!(debug_assertions) { 134 | 1_000 135 | } else { 136 | 100_000 137 | } 138 | } 139 | 140 | #[test] 141 | fn data_container() { 142 | let mut db = SlotDB::new(16, false); 143 | 144 | db.insert(0, 0).unwrap(); 145 | 146 | assert!(matches!( 147 | db.data.iter().next().unwrap().1, 148 | DataCtner::Small(_) 149 | )); 150 | 151 | (0..100u32).for_each(|i| { 152 | db.insert(0, i).unwrap(); 153 | }); 154 | 155 | assert!(matches!( 156 | db.data.iter().next().unwrap().1, 157 | DataCtner::Large { .. } 158 | )); 159 | assert_eq!(db.data.iter().count(), 1); 160 | assert_eq!(db.data.first().unwrap().1.len(), 100); 161 | assert_eq!(db.data.first().unwrap().1.iter().next().unwrap(), 0); 162 | assert_eq!(db.data.first().unwrap().1.iter().last().unwrap(), 99); 163 | 164 | db.clear(); 165 | } 166 | 167 | mod testdb { 168 | use super::*; 169 | use std::{ 170 | collections::BTreeMap, 171 | sync::atomic::{AtomicU64, Ordering}, 172 | }; 173 | 174 | static INNER_ID: AtomicU64 = AtomicU64::new(0); 175 | 176 | #[derive(Default)] 177 | pub struct TestDB { 178 | data: BTreeMap<[Slot; 2], T>, 179 | } 180 | 181 | impl TestDB { 182 | pub fn insert(&mut self, slot: Slot, v: T) { 183 | let inner_id = INNER_ID.fetch_add(1, Ordering::Relaxed); 184 | self.data.insert([slot, inner_id], v); 185 | } 186 | 187 | pub fn get_entries_by_page_slot( 188 | &self, 189 | slot_start: Option, 190 | slot_end: Option, 191 | page_size: PageSize, 192 | page_index: PageIndex, 193 | reverse: bool, 194 | ) -> Vec { 195 | let page_size = page_size as usize; 196 | let page_index = page_index as usize; 197 | 198 | let slot_start = slot_start.unwrap_or(Slot::MIN); 199 | let slot_end = slot_end.unwrap_or(Slot::MAX); 200 | 201 | if reverse { 202 | self.data 203 | .range([slot_start, 0]..=[slot_end, Slot::MAX]) 204 | .map(|(_, v)| v) 205 | .rev() 206 | .skip(page_size * page_index) 207 | .take(page_size) 208 | .cloned() 209 | .collect() 210 | } else { 211 | self.data 212 | .range([slot_start, 0]..=[slot_end, Slot::MAX]) 213 | .map(|(_, v)| v) 214 | .skip(page_size * page_index) 215 | .take(page_size) 216 | .cloned() 217 | .collect() 218 | } 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /wrappers/src/dagmap/rawkey/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A raw-key, disk-based, directed acyclic graph (DAG) map. 3 | //! 4 | //! `DagMapRawKey` provides a map-like interface where each instance can have a parent 5 | //! and multiple children, forming a directed acyclic graph. Keys are stored as raw 6 | //! bytes, while values are typed and encoded. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ``` 11 | //! use vsdb::{DagMapRaw, DagMapRawKey, Orphan}; 12 | //! use vsdb::{vsdb_set_base_dir, vsdb_get_base_dir}; 13 | //! use std::fs; 14 | //! 15 | //! // It's recommended to use a temporary directory for testing 16 | //! let dir = format!("/tmp/vsdb_testing/{}", rand::random::()); 17 | //! vsdb_set_base_dir(&dir).unwrap(); 18 | //! 19 | //! let mut parent = Orphan::new(None); 20 | //! let mut dag: DagMapRawKey = DagMapRawKey::new(&mut parent).unwrap(); 21 | //! 22 | //! // Insert a value 23 | //! dag.insert(&[1], &"hello".to_string()); 24 | //! assert_eq!(dag.get(&[1]), Some("hello".to_string())); 25 | //! 26 | //! // Create a child 27 | //! let mut child = DagMapRawKey::new(&mut Orphan::new(Some(dag.into_inner()))).unwrap(); 28 | //! assert_eq!(child.get(&[1]), Some("hello".to_string())); 29 | //! 30 | //! // Clean up the directory 31 | //! fs::remove_dir_all(vsdb_get_base_dir()).unwrap(); 32 | //! ``` 33 | 34 | #[cfg(test)] 35 | mod test; 36 | 37 | use crate::{DagMapId, DagMapRaw, Orphan, ValueEnDe, dagmap::raw}; 38 | use ruc::*; 39 | use serde::{Deserialize, Serialize}; 40 | use std::{ 41 | marker::PhantomData, 42 | ops::{Deref, DerefMut}, 43 | }; 44 | 45 | type DagHead = DagMapRawKey; 46 | 47 | /// A raw-key, disk-based, directed acyclic graph (DAG) map. 48 | #[derive(Clone, Debug, Default, Serialize, Deserialize)] 49 | #[serde(bound = "")] 50 | pub struct DagMapRawKey { 51 | inner: DagMapRaw, 52 | _p: PhantomData, 53 | } 54 | 55 | impl DagMapRawKey 56 | where 57 | V: ValueEnDe, 58 | { 59 | /// Creates a new `DagMapRawKey`. 60 | #[inline(always)] 61 | pub fn new(raw_parent: &mut Orphan>) -> Result { 62 | DagMapRaw::new(raw_parent).c(d!()).map(|inner| Self { 63 | inner, 64 | _p: PhantomData, 65 | }) 66 | } 67 | 68 | /// Consumes the `DagMapRawKey` and returns the inner `DagMapRaw`. 69 | #[inline(always)] 70 | pub fn into_inner(self) -> DagMapRaw { 71 | self.inner 72 | } 73 | 74 | /// Creates a "shadow" copy of the inner `DagMapRaw`. 75 | /// 76 | /// # Safety 77 | /// 78 | /// This API breaks Rust's semantic safety guarantees. Use only in a race-free environment. 79 | #[inline(always)] 80 | pub unsafe fn shadow_inner(&self) -> DagMapRaw { 81 | unsafe { self.inner.shadow() } 82 | } 83 | 84 | /// Creates a "shadow" copy of the `DagMapRawKey` instance. 85 | /// 86 | /// # Safety 87 | /// 88 | /// This API breaks Rust's semantic safety guarantees. Use only in a race-free environment. 89 | #[inline(always)] 90 | pub unsafe fn shadow(&self) -> DagMapRawKey { 91 | unsafe { 92 | Self { 93 | inner: self.shadow_inner(), 94 | _p: PhantomData, 95 | } 96 | } 97 | } 98 | 99 | /// Checks if the DAG map is dead. 100 | #[inline(always)] 101 | pub fn is_dead(&self) -> bool { 102 | self.inner.is_dead() 103 | } 104 | 105 | /// Checks if the DAG map has no children. 106 | #[inline(always)] 107 | pub fn no_children(&self) -> bool { 108 | self.inner.no_children() 109 | } 110 | 111 | /// Retrieves a value from the DAG map. 112 | #[inline(always)] 113 | pub fn get(&self, key: impl AsRef<[u8]>) -> Option { 114 | self.inner.get(key).map(|v| V::decode(&v).unwrap()) 115 | } 116 | 117 | /// Retrieves a mutable reference to a value in the DAG map. 118 | #[inline(always)] 119 | pub fn get_mut(&mut self, key: impl AsRef<[u8]>) -> Option> { 120 | self.inner.get_mut(key.as_ref()).map(|inner| ValueMut { 121 | value: ::decode(&inner).unwrap(), 122 | inner, 123 | }) 124 | } 125 | 126 | /// Inserts a key-value pair into the DAG map. 127 | /// 128 | /// Does not return the old value for performance reasons. 129 | #[inline(always)] 130 | pub fn insert(&mut self, key: impl AsRef<[u8]>, value: &V) { 131 | self.inner.insert(key, value.encode()) 132 | } 133 | 134 | /// Removes a key-value pair from the DAG map. 135 | /// 136 | /// Does not return the old value for performance reasons. 137 | #[inline(always)] 138 | pub fn remove(&mut self, key: impl AsRef<[u8]>) { 139 | self.inner.remove(key) 140 | } 141 | 142 | /// Prunes the DAG, merging all nodes in the mainline into the genesis node. 143 | #[inline(always)] 144 | pub fn prune(self) -> Result> { 145 | self.inner.prune().c(d!()).map(|inner| Self { 146 | inner, 147 | _p: PhantomData, 148 | }) 149 | } 150 | 151 | /// Prunes children that are in the `include_targets` list. 152 | #[inline(always)] 153 | pub fn prune_children_include(&mut self, include_targets: &[impl AsRef]) { 154 | self.inner.prune_children_include(include_targets); 155 | } 156 | 157 | /// Prunes children that are not in the `exclude_targets` list. 158 | #[inline(always)] 159 | pub fn prune_children_exclude(&mut self, exclude_targets: &[impl AsRef]) { 160 | self.inner.prune_children_exclude(exclude_targets); 161 | } 162 | 163 | /// Destroys the DAG map and all its children. 164 | #[inline(always)] 165 | pub fn destroy(&mut self) { 166 | self.inner.destroy(); 167 | } 168 | 169 | /// Checks if this `DagMapRawKey` instance is the same as another. 170 | #[inline(always)] 171 | pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool { 172 | self.inner.is_the_same_instance(&other_hdr.inner) 173 | } 174 | } 175 | 176 | ///////////////////////////////////////////////////////////////////////////// 177 | ///////////////////////////////////////////////////////////////////////////// 178 | 179 | /// A mutable reference to a value in a `DagMapRawKey`. 180 | #[derive(Debug)] 181 | pub struct ValueMut<'a, V> 182 | where 183 | V: ValueEnDe, 184 | { 185 | value: V, 186 | inner: raw::ValueMut<'a>, 187 | } 188 | 189 | impl Drop for ValueMut<'_, V> 190 | where 191 | V: ValueEnDe, 192 | { 193 | fn drop(&mut self) { 194 | *self.inner = self.value.encode(); 195 | } 196 | } 197 | 198 | impl Deref for ValueMut<'_, V> 199 | where 200 | V: ValueEnDe, 201 | { 202 | type Target = V; 203 | fn deref(&self) -> &Self::Target { 204 | &self.value 205 | } 206 | } 207 | 208 | impl DerefMut for ValueMut<'_, V> 209 | where 210 | V: ValueEnDe, 211 | { 212 | fn deref_mut(&mut self) -> &mut Self::Target { 213 | &mut self.value 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /utils/trie_db/src/node/codec.rs: -------------------------------------------------------------------------------- 1 | use super::{Node, NodeHandle}; 2 | use crate::config::HASH_LEN; 3 | use crate::error::{Result, TrieError}; 4 | use crate::nibbles::Nibbles; 5 | 6 | pub struct NodeCodec; 7 | 8 | impl NodeCodec { 9 | pub fn encode(node: &Node) -> Vec { 10 | let mut buf = Vec::new(); 11 | match node { 12 | Node::Null => { 13 | buf.push(0x00); 14 | } 15 | Node::Leaf { path, value } => { 16 | // Tag: 0x01 17 | buf.push(0x01); 18 | encode_path(&mut buf, path); 19 | encode_bytes(&mut buf, value); 20 | } 21 | Node::Extension { path, child } => { 22 | // Tag: 0x02 23 | buf.push(0x02); 24 | encode_path(&mut buf, path); 25 | // Child must be a hash (or cached) 26 | let hash = child 27 | .hash() 28 | .expect("Child must be hashed before encoding extension"); 29 | buf.extend_from_slice(hash); 30 | } 31 | Node::Branch { children, value } => { 32 | // Tag: 0x03 33 | buf.push(0x03); 34 | 35 | // Bitmap 36 | let mut bitmap: u16 = 0; 37 | for (i, child) in children.iter().enumerate() { 38 | if child.is_some() { 39 | bitmap |= 1 << i; 40 | } 41 | } 42 | buf.extend_from_slice(&bitmap.to_le_bytes()); 43 | 44 | // Value 45 | if let Some(v) = value { 46 | buf.push(1); // Has value 47 | encode_bytes(&mut buf, v); 48 | } else { 49 | buf.push(0); // No value 50 | } 51 | 52 | // Children Hashes 53 | for child in children.iter().flatten() { 54 | let hash = child 55 | .hash() 56 | .expect("Child must be hashed before encoding branch"); 57 | buf.extend_from_slice(hash); 58 | } 59 | } 60 | } 61 | buf 62 | } 63 | 64 | pub fn decode(data: &[u8]) -> Result { 65 | if data.is_empty() { 66 | return Err(TrieError::DecodeError("Empty data".into())); 67 | } 68 | let mut cursor = 0; 69 | let tag = data[cursor]; 70 | cursor += 1; 71 | 72 | match tag { 73 | 0x00 => Ok(Node::Null), 74 | 0x01 => { 75 | let path = decode_path(data, &mut cursor)?; 76 | let value = decode_bytes(data, &mut cursor)?; 77 | Ok(Node::Leaf { path, value }) 78 | } 79 | 0x02 => { 80 | let path = decode_path(data, &mut cursor)?; 81 | if cursor + HASH_LEN > data.len() { 82 | return Err(TrieError::DecodeError( 83 | "Not enough bytes for extension child hash".into(), 84 | )); 85 | } 86 | let hash = data[cursor..cursor + HASH_LEN].to_vec(); 87 | Ok(Node::Extension { 88 | path, 89 | child: NodeHandle::Hash(hash), 90 | }) 91 | } 92 | 0x03 => { 93 | if cursor + 2 > data.len() { 94 | return Err(TrieError::DecodeError( 95 | "Not enough bytes for branch bitmap".into(), 96 | )); 97 | } 98 | let bitmap_bytes = &data[cursor..cursor + 2]; 99 | let bitmap = u16::from_le_bytes([bitmap_bytes[0], bitmap_bytes[1]]); 100 | cursor += 2; 101 | 102 | let has_value = if cursor < data.len() { 103 | data[cursor] == 1 104 | } else { 105 | return Err(TrieError::DecodeError("Unexpected EOF".into())); 106 | }; 107 | cursor += 1; 108 | 109 | let value = if has_value { 110 | Some(decode_bytes(data, &mut cursor)?) 111 | } else { 112 | None 113 | }; 114 | 115 | let mut children = Box::new([ 116 | None, None, None, None, None, None, None, None, None, None, None, None, None, 117 | None, None, None, 118 | ]); 119 | 120 | for i in 0..16 { 121 | if (bitmap & (1 << i)) != 0 { 122 | if cursor + HASH_LEN > data.len() { 123 | return Err(TrieError::DecodeError( 124 | "Not enough bytes for branch child hash".into(), 125 | )); 126 | } 127 | let hash = data[cursor..cursor + HASH_LEN].to_vec(); 128 | children[i] = Some(NodeHandle::Hash(hash)); 129 | cursor += HASH_LEN; 130 | } 131 | } 132 | 133 | Ok(Node::Branch { children, value }) 134 | } 135 | _ => Err(TrieError::DecodeError(format!("Unknown tag: {}", tag))), 136 | } 137 | } 138 | } 139 | 140 | // Helpers 141 | 142 | fn encode_varint(buf: &mut Vec, mut n: usize) { 143 | while n >= 0x80 { 144 | buf.push((n as u8) | 0x80); 145 | n >>= 7; 146 | } 147 | buf.push(n as u8); 148 | } 149 | 150 | fn decode_varint(data: &[u8], cursor: &mut usize) -> Result { 151 | let mut n: usize = 0; 152 | let mut shift = 0; 153 | loop { 154 | if *cursor >= data.len() { 155 | return Err(TrieError::DecodeError("Varint unexpected EOF".into())); 156 | } 157 | let b = data[*cursor]; 158 | *cursor += 1; 159 | n |= ((b & 0x7F) as usize) << shift; 160 | if b & 0x80 == 0 { 161 | break; 162 | } 163 | shift += 7; 164 | } 165 | Ok(n) 166 | } 167 | 168 | fn encode_path(buf: &mut Vec, path: &Nibbles) { 169 | let len = path.len(); 170 | encode_varint(buf, len); 171 | let nibbles = path.as_slice(); 172 | for i in (0..len).step_by(2) { 173 | let n1 = nibbles[i]; 174 | let n2 = if i + 1 < len { nibbles[i + 1] } else { 0 }; 175 | buf.push((n1 << 4) | n2); 176 | } 177 | } 178 | 179 | fn decode_path(data: &[u8], cursor: &mut usize) -> Result { 180 | let len = decode_varint(data, cursor)?; 181 | let mut nibbles = Vec::with_capacity(len); 182 | let byte_len = len.div_ceil(2); 183 | if *cursor + byte_len > data.len() { 184 | return Err(TrieError::DecodeError("Path bytes unexpected EOF".into())); 185 | } 186 | 187 | for i in 0..byte_len { 188 | let b = data[*cursor + i]; 189 | nibbles.push(b >> 4); 190 | if nibbles.len() < len { 191 | nibbles.push(b & 0x0F); 192 | } 193 | } 194 | *cursor += byte_len; 195 | Ok(Nibbles::from_nibbles_unsafe(nibbles)) 196 | } 197 | 198 | fn encode_bytes(buf: &mut Vec, bytes: &[u8]) { 199 | encode_varint(buf, bytes.len()); 200 | buf.extend_from_slice(bytes); 201 | } 202 | 203 | fn decode_bytes(data: &[u8], cursor: &mut usize) -> Result> { 204 | let len = decode_varint(data, cursor)?; 205 | if *cursor + len > data.len() { 206 | return Err(TrieError::DecodeError("Bytes unexpected EOF".into())); 207 | } 208 | let bytes = data[*cursor..*cursor + len].to_vec(); 209 | *cursor += len; 210 | Ok(bytes) 211 | } 212 | -------------------------------------------------------------------------------- /wrappers/src/basic_multi_key/mapx_rawkey/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A multi-key map with raw keys and typed values. 3 | //! 4 | //! `MapxRawKeyMk` provides a map-like interface where each value is associated 5 | //! with a sequence of raw byte keys. This is useful for creating nested or 6 | //! hierarchical key structures with typed values. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ``` 11 | //! use vsdb::basic_multi_key::mapx_rawkey::MapxRawKeyMk; 12 | //! use vsdb::{vsdb_set_base_dir, vsdb_get_base_dir}; 13 | //! use std::fs; 14 | //! 15 | //! // It's recommended to use a temporary directory for testing 16 | //! let dir = format!("/tmp/vsdb_testing/{}", rand::random::()); 17 | //! vsdb_set_base_dir(&dir).unwrap(); 18 | //! 19 | //! let mut m: MapxRawKeyMk = MapxRawKeyMk::new(2); // Two keys 20 | //! 21 | //! // Insert a value with a multi-key 22 | //! m.insert(&[&[1], &[10]], &"hello".to_string()).unwrap(); 23 | //! m.insert(&[&[2], &[20]], &"world".to_string()).unwrap(); 24 | //! 25 | //! // Get a value 26 | //! assert_eq!(m.get(&[&[1], &[10]]), Some("hello".to_string())); 27 | //! 28 | //! // Remove a value 29 | //! m.remove(&[&[1], &[10]]).unwrap(); 30 | //! assert!(!m.contains_key(&[&[1], &[10]])); 31 | //! 32 | //! // Clean up the directory 33 | //! fs::remove_dir_all(vsdb_get_base_dir()).unwrap(); 34 | //! ``` 35 | 36 | #[cfg(test)] 37 | mod test; 38 | 39 | use crate::{basic_multi_key::mapx_raw::MapxRawMk, common::ende::ValueEnDe}; 40 | use ruc::*; 41 | use serde::{Deserialize, Serialize}; 42 | use std::{ 43 | marker::PhantomData, 44 | ops::{Deref, DerefMut}, 45 | }; 46 | 47 | /// A multi-key map with raw keys and typed values. 48 | #[derive(Serialize, Deserialize, Debug)] 49 | #[serde(bound = "")] 50 | pub struct MapxRawKeyMk { 51 | inner: MapxRawMk, 52 | p: PhantomData, 53 | } 54 | 55 | impl MapxRawKeyMk { 56 | /// Creates a "shadow" copy of the `MapxRawKeyMk` instance. 57 | /// 58 | /// # Safety 59 | /// 60 | /// This API breaks Rust's semantic safety guarantees. Use only in a race-free environment. 61 | #[inline(always)] 62 | pub unsafe fn shadow(&self) -> Self { 63 | unsafe { 64 | Self { 65 | inner: self.inner.shadow(), 66 | p: PhantomData, 67 | } 68 | } 69 | } 70 | 71 | /// Creates a new `MapxRawKeyMk` with a specified number of keys. 72 | /// 73 | /// # Panics 74 | /// 75 | /// Panics if `key_size` is 0. 76 | #[inline(always)] 77 | pub fn new(key_size: u32) -> Self { 78 | Self { 79 | inner: MapxRawMk::new(key_size), 80 | p: PhantomData, 81 | } 82 | } 83 | 84 | /// Retrieves a value from the map for a given multi-key. 85 | #[inline(always)] 86 | pub fn get(&self, key: &[&[u8]]) -> Option { 87 | self.inner.get(key).map(|v| pnk!(ValueEnDe::decode(&v))) 88 | } 89 | 90 | /// Retrieves a mutable reference to a value in the map. 91 | #[inline(always)] 92 | pub fn get_mut<'a>(&'a mut self, key: &'a [&'a [u8]]) -> Option> { 93 | self.get(key).map(move |v| ValueMut::new(self, key, v)) 94 | } 95 | 96 | /// Mocks a mutable value for a given key. 97 | #[inline(always)] 98 | pub(crate) fn mock_value_mut<'a>( 99 | &'a mut self, 100 | key: &'a [&'a [u8]], 101 | v: V, 102 | ) -> ValueMut<'a, V> { 103 | ValueMut::new(self, key, v) 104 | } 105 | 106 | /// Checks if the map contains a value for the specified multi-key. 107 | #[inline(always)] 108 | pub fn contains_key(&self, key: &[&[u8]]) -> bool { 109 | self.get(key).is_some() 110 | } 111 | 112 | /// Checks if the map is empty. 113 | #[inline(always)] 114 | pub fn is_empty(&self) -> bool { 115 | self.inner.is_empty() 116 | } 117 | 118 | /// Gets an entry for a given key, allowing for in-place modification. 119 | #[inline(always)] 120 | pub fn entry<'a>(&'a mut self, key: &'a [&'a [u8]]) -> Result> { 121 | if key.len() != self.key_size() as usize { 122 | Err(eg!()) 123 | } else { 124 | Ok(Entry { key, hdr: self }) 125 | } 126 | } 127 | 128 | /// Inserts a key-value pair into the map. 129 | #[inline(always)] 130 | pub fn insert(&mut self, key: &[&[u8]], value: &V) -> Result<()> { 131 | let v = value.encode(); 132 | self.inner.insert(key, &v).c(d!()) 133 | } 134 | 135 | /// Removes a key-value pair from the map. Supports batch removal by providing a partial key. 136 | /// 137 | /// Does not return the old value for performance reasons. 138 | #[inline(always)] 139 | pub fn remove(&mut self, key: &[&[u8]]) -> Result<()> { 140 | self.inner.remove(key).c(d!()) 141 | } 142 | 143 | /// Clears the map, removing all key-value pairs. 144 | #[inline(always)] 145 | pub fn clear(&mut self) { 146 | self.inner.clear(); 147 | } 148 | 149 | /// Checks if this `MapxRawKeyMk` instance is the same as another. 150 | #[inline(always)] 151 | pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool { 152 | self.inner.is_the_same_instance(&other_hdr.inner) 153 | } 154 | 155 | /// Returns the number of keys in the map. 156 | #[inline(always)] 157 | pub fn key_size(&self) -> u32 { 158 | self.inner.key_size() 159 | } 160 | 161 | /// Iterates over the map's entries, applying a function to each. 162 | #[inline(always)] 163 | pub fn iter_op(&self, op: &mut F) -> Result<()> 164 | where 165 | F: FnMut(&[&[u8]], &V) -> Result<()>, 166 | { 167 | self.inner.iter_op_typed_value(op).c(d!()) 168 | } 169 | 170 | /// Iterates over the map's entries with a given key prefix, applying a function to each. 171 | #[inline(always)] 172 | pub fn iter_op_with_key_prefix( 173 | &self, 174 | op: &mut F, 175 | key_prefix: &[&[u8]], 176 | ) -> Result<()> 177 | where 178 | F: FnMut(&[&[u8]], &V) -> Result<()>, 179 | { 180 | self.inner 181 | .iter_op_typed_value_with_key_prefix(op, key_prefix) 182 | .c(d!()) 183 | } 184 | 185 | // TODO 186 | // pub fn iter_mut_op 187 | // pub fn iter_mut_op_with_key_prefix 188 | } 189 | 190 | impl Clone for MapxRawKeyMk { 191 | fn clone(&self) -> Self { 192 | Self { 193 | inner: self.inner.clone(), 194 | p: PhantomData, 195 | } 196 | } 197 | } 198 | 199 | /// A mutable reference to a value in a `MapxRawKeyMk`. 200 | #[derive(Debug)] 201 | pub struct ValueMut<'a, V: ValueEnDe> { 202 | hdr: &'a mut MapxRawKeyMk, 203 | key: &'a [&'a [u8]], 204 | value: V, 205 | } 206 | 207 | impl<'a, V: ValueEnDe> ValueMut<'a, V> { 208 | fn new(hdr: &'a mut MapxRawKeyMk, key: &'a [&'a [u8]], value: V) -> Self { 209 | ValueMut { hdr, key, value } 210 | } 211 | } 212 | 213 | impl Drop for ValueMut<'_, V> { 214 | fn drop(&mut self) { 215 | pnk!(self.hdr.insert(self.key, &self.value)); 216 | } 217 | } 218 | 219 | impl Deref for ValueMut<'_, V> { 220 | type Target = V; 221 | fn deref(&self) -> &Self::Target { 222 | &self.value 223 | } 224 | } 225 | 226 | impl DerefMut for ValueMut<'_, V> { 227 | fn deref_mut(&mut self) -> &mut Self::Target { 228 | &mut self.value 229 | } 230 | } 231 | 232 | /// A view into a single entry in a map, which may either be vacant or occupied. 233 | pub struct Entry<'a, V> { 234 | key: &'a [&'a [u8]], 235 | hdr: &'a mut MapxRawKeyMk, 236 | } 237 | 238 | impl<'a, V: ValueEnDe> Entry<'a, V> { 239 | /// Ensures a value is in the entry by inserting the default if empty, 240 | /// and returns a mutable reference to the value. 241 | pub fn or_insert(self, default: V) -> ValueMut<'a, V> { 242 | let hdr = self.hdr as *mut MapxRawKeyMk; 243 | match unsafe { &mut *hdr }.get_mut(self.key) { 244 | Some(v) => v, 245 | _ => unsafe { &mut *hdr }.mock_value_mut(self.key, default), 246 | } 247 | } 248 | } 249 | 250 | ///////////////////////////////////////////////////////////////////////////// 251 | ///////////////////////////////////////////////////////////////////////////// 252 | -------------------------------------------------------------------------------- /core/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Common components 3 | //! 4 | //! This module provides common components and utilities used throughout the VSDB framework. 5 | //! It includes type definitions, constants, macros, and functions for managing the 6 | //! underlying database environment. 7 | //! 8 | 9 | pub(crate) mod engines; 10 | 11 | pub use engines::BatchTrait; 12 | use engines::Engine; 13 | use parking_lot::Mutex; 14 | use ruc::*; 15 | use std::{ 16 | env, fs, 17 | mem::size_of, 18 | path::{Path, PathBuf}, 19 | sync::{ 20 | LazyLock, 21 | atomic::{AtomicBool, Ordering}, 22 | }, 23 | }; 24 | use threadpool::ThreadPool; 25 | 26 | ///////////////////////////////////////////////////////////////////////////// 27 | ///////////////////////////////////////////////////////////////////////////// 28 | 29 | /// A constant representing a null or empty byte slice. 30 | pub const NULL: &[u8] = &[]; 31 | 32 | /// A type alias for a vector of bytes, commonly used for raw data. 33 | pub type RawBytes = Vec; 34 | /// A type alias for a raw key, represented as a vector of bytes. 35 | pub type RawKey = RawBytes; 36 | /// A type alias for a raw value, represented as a vector of bytes. 37 | pub type RawValue = RawBytes; 38 | 39 | /// A type alias for a prefix, represented as a `u64`. 40 | pub type Pre = u64; 41 | /// The size of a prefix in bytes. 42 | pub const PREFIX_SIZE: usize = size_of::
();
 43 | /// A type alias for a prefix represented as a byte array.
 44 | pub type PreBytes = [u8; PREFIX_SIZE];
 45 | 
 46 | /// A constant representing 1 kilobyte in bytes.
 47 | pub const KB: u64 = 1 << 10;
 48 | /// A constant representing 1 megabyte in bytes.
 49 | pub const MB: u64 = 1 << 20;
 50 | /// A constant representing 1 gigabyte in bytes.
 51 | pub const GB: u64 = 1 << 30;
 52 | 
 53 | /// The number of reserved IDs.
 54 | const RESERVED_ID_CNT: Pre = 4096_0000;
 55 | /// The biggest reserved ID.
 56 | pub const BIGGEST_RESERVED_ID: Pre = RESERVED_ID_CNT - 1;
 57 | 
 58 | /////////////////////////////////////////////////////////////////////////////
 59 | /////////////////////////////////////////////////////////////////////////////
 60 | 
 61 | const BASE_DIR_VAR: &str = "VSDB_BASE_DIR";
 62 | 
 63 | static VSDB_BASE_DIR: LazyLock> =
 64 |     LazyLock::new(|| Mutex::new(gen_data_dir()));
 65 | 
 66 | static VSDB_CUSTOM_DIR: LazyLock = LazyLock::new(|| {
 67 |     let mut d = VSDB_BASE_DIR.lock().clone();
 68 |     d.push("__CUSTOM__");
 69 |     pnk!(fs::create_dir_all(&d));
 70 |     unsafe { env::set_var("VSDB_CUSTOM_DIR", d.as_os_str()) }
 71 |     d
 72 | });
 73 | 
 74 | /// The global instance of the VsDB database.
 75 | ///
 76 | /// This static variable is lazily initialized and provides a single point of
 77 | /// access to the underlying database. The backend is determined by the
 78 | /// feature flags passed at compile time.
 79 | #[cfg(feature = "rocks_backend")]
 80 | pub static VSDB: LazyLock> = LazyLock::new(|| pnk!(VsDB::new()));
 81 | 
 82 | /// The global instance of the VsDB database.
 83 | ///
 84 | /// This static variable is lazily initialized and provides a single point of
 85 | /// access to the underlying database. The backend is determined by the
 86 | /// feature flags passed at compile time.
 87 | #[cfg(feature = "parity_backend")]
 88 | pub static VSDB: LazyLock> = LazyLock::new(|| pnk!(VsDB::new()));
 89 | 
 90 | /// A thread pool for cleaning up orphan instances in the background.
 91 | ///
 92 | /// This static variable is lazily initialized and provides a thread pool
 93 | /// with a single thread and a large stack size to handle background cleanup tasks.
 94 | pub static TRASH_CLEANER: LazyLock> = LazyLock::new(|| {
 95 |     let pool = threadpool::Builder::new()
 96 |         .num_threads(1)
 97 |         .thread_stack_size(512 * MB as usize) // use large stack size
 98 |         .build();
 99 |     Mutex::new(pool)
100 | });
101 | 
102 | /////////////////////////////////////////////////////////////////////////////
103 | /////////////////////////////////////////////////////////////////////////////
104 | 
105 | /// A macro to parse a byte slice into a specified integer type.
106 | ///
107 | /// # Arguments
108 | ///
109 | /// * `$bytes` - The byte slice to parse.
110 | /// * `$ty` - The integer type to parse the bytes into.
111 | ///
112 | /// # Panics
113 | ///
114 | /// This macro will panic if the byte slice cannot be converted into the specified integer type.
115 | #[macro_export]
116 | macro_rules! parse_int {
117 |     ($bytes: expr, $ty: ty) => {{
118 |         let array: [u8; std::mem::size_of::<$ty>()] = $bytes[..].try_into().unwrap();
119 |         <$ty>::from_be_bytes(array)
120 |     }};
121 | }
122 | 
123 | /// A macro to parse a byte slice into a `Pre` type.
124 | ///
125 | /// # Arguments
126 | ///
127 | /// * `$bytes` - The byte slice to parse.
128 | ///
129 | /// # Panics
130 | ///
131 | /// This macro will panic if the byte slice cannot be converted into a `Pre` type.
132 | #[macro_export]
133 | macro_rules! parse_prefix {
134 |     ($bytes: expr) => {
135 |         $crate::parse_int!($bytes, $crate::common::Pre)
136 |     };
137 | }
138 | 
139 | /////////////////////////////////////////////////////////////////////////////
140 | /////////////////////////////////////////////////////////////////////////////
141 | 
142 | /// A struct representing the VsDB database.
143 | ///
144 | /// This struct encapsulates the underlying database engine and provides a
145 | /// high-level interface for interacting with the database.
146 | pub struct VsDB {
147 |     db: T,
148 | }
149 | 
150 | impl VsDB {
151 |     #[inline(always)]
152 |     fn new() -> Result {
153 |         Ok(Self {
154 |             db: T::new().c(d!())?,
155 |         })
156 |     }
157 | 
158 |     #[inline(always)]
159 |     fn flush(&self) {
160 |         self.db.flush()
161 |     }
162 | }
163 | 
164 | /////////////////////////////////////////////////////////////////////////////
165 | /////////////////////////////////////////////////////////////////////////////
166 | 
167 | #[inline(always)]
168 | fn gen_data_dir() -> PathBuf {
169 |     // Compatible with Windows OS?
170 |     let d = env::var(BASE_DIR_VAR)
171 |         .or_else(|_| env::var("HOME").map(|h| format!("{h}/.vsdb")))
172 |         .unwrap_or_else(|_| "/tmp/.vsdb".to_owned());
173 |     pnk!(fs::create_dir_all(&d));
174 |     PathBuf::from(d)
175 | }
176 | 
177 | /// Returns the custom directory path for VSDB.
178 | ///
179 | /// This function returns a static reference to the path of the custom directory,
180 | /// which is set by the `VSDB_CUSTOM_DIR` environment variable.
181 | ///
182 | /// # Returns
183 | ///
184 | /// A `&'static Path` to the custom directory.
185 | #[inline(always)]
186 | pub fn vsdb_get_custom_dir() -> &'static Path {
187 |     VSDB_CUSTOM_DIR.as_path()
188 | }
189 | 
190 | /// Returns the base directory path for VSDB.
191 | ///
192 | /// This function returns the path of the base directory, which is determined
193 | /// by the `VSDB_BASE_DIR` environment variable, the `HOME` environment variable,
194 | /// or a default path of `/tmp/.vsdb`.
195 | ///
196 | /// # Returns
197 | ///
198 | /// A `PathBuf` to the base directory.
199 | #[inline(always)]
200 | pub fn vsdb_get_base_dir() -> PathBuf {
201 |     VSDB_BASE_DIR.lock().clone()
202 | }
203 | 
204 | /// Sets the base directory path for VSDB manually.
205 | ///
206 | /// This function allows you to programmatically set the base directory for VSDB.
207 | /// It can only be called once, before the database is initialized.
208 | ///
209 | /// # Arguments
210 | ///
211 | /// * `dir` - An object that can be converted into a `Path`.
212 | ///
213 | /// # Errors
214 | ///
215 | /// This function will return an error if the base directory has already been initialized.
216 | #[inline(always)]
217 | pub fn vsdb_set_base_dir(dir: impl AsRef) -> Result<()> {
218 |     static HAS_INITED: AtomicBool = AtomicBool::new(false);
219 | 
220 |     if HAS_INITED.swap(true, Ordering::Relaxed) {
221 |         Err(eg!("VSDB has been initialized !!"))
222 |     } else {
223 |         unsafe { env::set_var(BASE_DIR_VAR, dir.as_ref().as_os_str()) }
224 |         *VSDB_BASE_DIR.lock() = dir.as_ref().to_path_buf();
225 |         Ok(())
226 |     }
227 | }
228 | 
229 | /// Flushes all data to disk.
230 | ///
231 | /// This function triggers a flush operation on the underlying database,
232 | /// ensuring that all pending writes are persisted to disk. This operation
233 | /// may take a long time to complete, depending on the amount of data to be flushed.
234 | #[inline(always)]
235 | pub fn vsdb_flush() {
236 |     VSDB.flush();
237 | }
238 | 


--------------------------------------------------------------------------------
/wrappers/src/basic_multi_key/mapx_double_key/mod.rs:
--------------------------------------------------------------------------------
  1 | //!
  2 | //! A `Mapx` with a two-level key structure.
  3 | //!
  4 | //! `MapxDk` (Mapx Double Key) provides a map-like interface where each value is
  5 | //! associated with a pair of keys (K1, K2). This is useful for creating
  6 | //! hierarchical or composite key structures.
  7 | //!
  8 | //! # Examples
  9 | //!
 10 | //! ```
 11 | //! use vsdb::basic_multi_key::mapx_double_key::MapxDk;
 12 | //! use vsdb::{vsdb_set_base_dir, vsdb_get_base_dir};
 13 | //! use std::fs;
 14 | //!
 15 | //! // It's recommended to use a temporary directory for testing
 16 | //! let dir = format!("/tmp/vsdb_testing/{}", rand::random::());
 17 | //! vsdb_set_base_dir(&dir).unwrap();
 18 | //!
 19 | //! let mut m: MapxDk = MapxDk::new();
 20 | //!
 21 | //! // Insert a value with a double key
 22 | //! m.insert(&(&1, &10), &"hello".to_string());
 23 | //! m.insert(&(&2, &20), &"world".to_string());
 24 | //!
 25 | //! // Get a value
 26 | //! assert_eq!(m.get(&(&1, &10)), Some("hello".to_string()));
 27 | //!
 28 | //! // Remove values
 29 | //! m.remove(&(&1, Some(&10)));
 30 | //! assert!(!m.contains_key(&(&1, &10)));
 31 | //!
 32 | //! // Clean up the directory
 33 | //! fs::remove_dir_all(vsdb_get_base_dir()).unwrap();
 34 | //! ```
 35 | 
 36 | #[cfg(test)]
 37 | mod test;
 38 | 
 39 | use crate::{
 40 |     basic_multi_key::mapx_raw::MapxRawMk,
 41 |     common::ende::{KeyEnDe, ValueEnDe},
 42 | };
 43 | use ruc::*;
 44 | use serde::{Deserialize, Serialize};
 45 | use std::{
 46 |     marker::PhantomData,
 47 |     ops::{Deref, DerefMut},
 48 | };
 49 | 
 50 | const KEY_SIZE: u32 = 2;
 51 | 
 52 | /// A map structure with two-level keys.
 53 | #[derive(Serialize, Deserialize, Debug)]
 54 | #[serde(bound = "")]
 55 | pub struct MapxDk {
 56 |     inner: MapxRawMk,
 57 |     p: PhantomData<(K1, K2, V)>,
 58 | }
 59 | 
 60 | impl MapxDk
 61 | where
 62 |     K1: KeyEnDe,
 63 |     K2: KeyEnDe,
 64 |     V: ValueEnDe,
 65 | {
 66 |     /// Creates a "shadow" copy of the `MapxDk` instance.
 67 |     ///
 68 |     /// # Safety
 69 |     ///
 70 |     /// This API breaks Rust's semantic safety guarantees. Use only in a race-free environment.
 71 |     #[inline(always)]
 72 |     pub unsafe fn shadow(&self) -> Self {
 73 |         unsafe {
 74 |             Self {
 75 |                 inner: self.inner.shadow(),
 76 |                 p: PhantomData,
 77 |             }
 78 |         }
 79 |     }
 80 | 
 81 |     /// Creates a new, empty `MapxDk`.
 82 |     #[inline(always)]
 83 |     pub fn new() -> Self {
 84 |         Self {
 85 |             inner: MapxRawMk::new(KEY_SIZE),
 86 |             p: PhantomData,
 87 |         }
 88 |     }
 89 | 
 90 |     /// Retrieves a value from the map for a given double key.
 91 |     #[inline(always)]
 92 |     pub fn get(&self, key: &(&K1, &K2)) -> Option {
 93 |         let k1 = key.0.encode();
 94 |         let k2 = key.1.encode();
 95 |         self.inner
 96 |             .get(&[&k1, &k2])
 97 |             .map(|v| pnk!(ValueEnDe::decode(&v)))
 98 |     }
 99 | 
100 |     /// Retrieves a mutable reference to a value in the map.
101 |     #[inline(always)]
102 |     pub fn get_mut<'a>(
103 |         &'a mut self,
104 |         key: &'a (&'a K1, &'a K2),
105 |     ) -> Option> {
106 |         self.get(key).map(move |v| ValueMut::new(self, key, v))
107 |     }
108 | 
109 |     /// Mocks a mutable value for a given key.
110 |     #[inline(always)]
111 |     pub fn mock_value_mut<'a>(
112 |         &'a mut self,
113 |         key: &'a (&'a K1, &'a K2),
114 |         v: V,
115 |     ) -> ValueMut<'a, K1, K2, V> {
116 |         ValueMut::new(self, key, v)
117 |     }
118 | 
119 |     /// Checks if the map contains a value for the specified double key.
120 |     #[inline(always)]
121 |     pub fn contains_key(&self, key: &(&K1, &K2)) -> bool {
122 |         self.get(key).is_some()
123 |     }
124 | 
125 |     /// Checks if the map is empty.
126 |     #[inline(always)]
127 |     pub fn is_empty(&self) -> bool {
128 |         self.inner.is_empty()
129 |     }
130 | 
131 |     /// Gets an entry for a given key, allowing for in-place modification.
132 |     #[inline(always)]
133 |     pub fn entry<'a>(&'a mut self, key: &'a (&'a K1, &'a K2)) -> Entry<'a, K1, K2, V> {
134 |         Entry { key, hdr: self }
135 |     }
136 | 
137 |     /// Inserts a key-value pair into the map.
138 |     ///
139 |     /// Does not return the old value for performance reasons.
140 |     #[inline(always)]
141 |     pub fn insert(&mut self, key: &(&K1, &K2), value: &V) {
142 |         let k1 = key.0.encode();
143 |         let k2 = key.1.encode();
144 |         let v = value.encode();
145 |         pnk!(self.inner.insert(&[&k1, &k2], &v));
146 |     }
147 | 
148 |     /// Removes a key-value pair from the map. Supports batch removal by omitting the second key.
149 |     ///
150 |     /// Does not return the old value for performance reasons.
151 |     #[inline(always)]
152 |     pub fn remove(&mut self, key: &(&K1, Option<&K2>)) {
153 |         let k1 = key.0.encode();
154 |         let k2 = key.1.map(|k2| k2.encode());
155 |         let k = if let Some(k2) = k2.as_ref() {
156 |             vec![&k1[..], &k2[..]]
157 |         } else {
158 |             vec![&k1[..]]
159 |         };
160 |         pnk!(self.inner.remove(k.as_slice()));
161 |     }
162 | 
163 |     /// Clears the map, removing all key-value pairs.
164 |     #[inline(always)]
165 |     pub fn clear(&mut self) {
166 |         self.inner.clear();
167 |     }
168 | 
169 |     /// Checks if this `MapxDk` instance is the same as another.
170 |     #[inline(always)]
171 |     pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool {
172 |         self.inner.is_the_same_instance(&other_hdr.inner)
173 |     }
174 | 
175 |     /// Returns the number of keys in the map.
176 |     #[inline(always)]
177 |     pub fn key_size(&self) -> u32 {
178 |         self.inner.key_size()
179 |     }
180 | 
181 |     // TODO
182 |     // pub fn iter_op
183 |     // pub fn iter_op_with_key_prefix
184 |     // pub fn iter_mut_op
185 |     // pub fn iter_mut_op_with_key_prefix
186 | }
187 | 
188 | impl Clone for MapxDk {
189 |     fn clone(&self) -> Self {
190 |         Self {
191 |             inner: self.inner.clone(),
192 |             p: PhantomData,
193 |         }
194 |     }
195 | }
196 | 
197 | impl Default for MapxDk
198 | where
199 |     K1: KeyEnDe,
200 |     K2: KeyEnDe,
201 |     V: ValueEnDe,
202 | {
203 |     fn default() -> Self {
204 |         Self::new()
205 |     }
206 | }
207 | 
208 | /// A mutable reference to a value in a `MapxDk`.
209 | #[derive(Debug)]
210 | pub struct ValueMut<'a, K1, K2, V>
211 | where
212 |     K1: KeyEnDe,
213 |     K2: KeyEnDe,
214 |     V: ValueEnDe,
215 | {
216 |     hdr: &'a mut MapxDk,
217 |     key: &'a (&'a K1, &'a K2),
218 |     value: V,
219 | }
220 | 
221 | impl<'a, K1, K2, V> ValueMut<'a, K1, K2, V>
222 | where
223 |     K1: KeyEnDe,
224 |     K2: KeyEnDe,
225 |     V: ValueEnDe,
226 | {
227 |     fn new(hdr: &'a mut MapxDk, key: &'a (&'a K1, &'a K2), value: V) -> Self {
228 |         ValueMut { hdr, key, value }
229 |     }
230 | }
231 | 
232 | impl Drop for ValueMut<'_, K1, K2, V>
233 | where
234 |     K1: KeyEnDe,
235 |     K2: KeyEnDe,
236 |     V: ValueEnDe,
237 | {
238 |     fn drop(&mut self) {
239 |         self.hdr.insert(self.key, &self.value);
240 |     }
241 | }
242 | 
243 | impl Deref for ValueMut<'_, K1, K2, V>
244 | where
245 |     K1: KeyEnDe,
246 |     K2: KeyEnDe,
247 |     V: ValueEnDe,
248 | {
249 |     type Target = V;
250 |     fn deref(&self) -> &Self::Target {
251 |         &self.value
252 |     }
253 | }
254 | 
255 | impl DerefMut for ValueMut<'_, K1, K2, V>
256 | where
257 |     K1: KeyEnDe,
258 |     K2: KeyEnDe,
259 |     V: ValueEnDe,
260 | {
261 |     fn deref_mut(&mut self) -> &mut Self::Target {
262 |         &mut self.value
263 |     }
264 | }
265 | 
266 | /// A view into a single entry in a map, which may either be vacant or occupied.
267 | pub struct Entry<'a, K1, K2, V> {
268 |     hdr: &'a mut MapxDk,
269 |     key: &'a (&'a K1, &'a K2),
270 | }
271 | 
272 | impl<'a, K1, K2, V> Entry<'a, K1, K2, V>
273 | where
274 |     K1: KeyEnDe,
275 |     K2: KeyEnDe,
276 |     V: ValueEnDe,
277 | {
278 |     /// Ensures a value is in the entry by inserting the default if empty,
279 |     /// and returns a mutable reference to the value.
280 |     pub fn or_insert(self, default: V) -> ValueMut<'a, K1, K2, V> {
281 |         let hdr = self.hdr as *mut MapxDk;
282 |         match unsafe { &mut *hdr }.get_mut(self.key) {
283 |             Some(v) => v,
284 |             _ => unsafe { &mut *hdr }.mock_value_mut(self.key, default),
285 |         }
286 |     }
287 | }
288 | 
289 | /////////////////////////////////////////////////////////////////////////////
290 | /////////////////////////////////////////////////////////////////////////////
291 | 


--------------------------------------------------------------------------------
/wrappers/src/basic_multi_key/mapx_triple_key/mod.rs:
--------------------------------------------------------------------------------
  1 | //!
  2 | //! A `Mapx` with a three-level key structure.
  3 | //!
  4 | //! `MapxTk` (Mapx Triple Key) provides a map-like interface where each value is
  5 | //! associated with a triplet of keys (K1, K2, K3). This is useful for creating
  6 | //! complex hierarchical or composite key structures.
  7 | //!
  8 | //! # Examples
  9 | //!
 10 | //! ```
 11 | //! use vsdb::basic_multi_key::mapx_triple_key::MapxTk;
 12 | //! use vsdb::{vsdb_set_base_dir, vsdb_get_base_dir};
 13 | //! use std::fs;
 14 | //!
 15 | //! // It's recommended to use a temporary directory for testing
 16 | //! let dir = format!("/tmp/vsdb_testing/{}", rand::random::());
 17 | //! vsdb_set_base_dir(&dir).unwrap();
 18 | //!
 19 | //! let mut m: MapxTk = MapxTk::new();
 20 | //!
 21 | //! // Insert a value with a triple key
 22 | //! m.insert(&(&1, &10, &100), &"hello".to_string());
 23 | //! m.insert(&(&2, &20, &200), &"world".to_string());
 24 | //!
 25 | //! // Get a value
 26 | //! assert_eq!(m.get(&(&1, &10, &100)), Some("hello".to_string()));
 27 | //!
 28 | //! // Remove values
 29 | //! m.remove(&(&1, Some((&10, Some(&100)))));
 30 | //! assert!(!m.contains_key(&(&1, &10, &100)));
 31 | //!
 32 | //! // Clean up the directory
 33 | //! fs::remove_dir_all(vsdb_get_base_dir()).unwrap();
 34 | //! ```
 35 | 
 36 | #[cfg(test)]
 37 | mod test;
 38 | 
 39 | use crate::{
 40 |     basic_multi_key::mapx_raw::MapxRawMk,
 41 |     common::ende::{KeyEnDe, ValueEnDe},
 42 | };
 43 | use ruc::*;
 44 | use serde::{Deserialize, Serialize};
 45 | use std::{
 46 |     marker::PhantomData,
 47 |     ops::{Deref, DerefMut},
 48 | };
 49 | 
 50 | const KEY_SIZE: u32 = 3;
 51 | 
 52 | /// A map structure with three-level keys.
 53 | #[derive(Serialize, Deserialize, Debug)]
 54 | #[serde(bound = "")]
 55 | pub struct MapxTk {
 56 |     inner: MapxRawMk,
 57 |     p: PhantomData<(K1, K2, K3, V)>,
 58 | }
 59 | 
 60 | impl MapxTk
 61 | where
 62 |     K1: KeyEnDe,
 63 |     K2: KeyEnDe,
 64 |     K3: KeyEnDe,
 65 |     V: ValueEnDe,
 66 | {
 67 |     /// Creates a "shadow" copy of the `MapxTk` instance.
 68 |     ///
 69 |     /// # Safety
 70 |     ///
 71 |     /// This API breaks Rust's semantic safety guarantees. Use only in a race-free environment.
 72 |     #[inline(always)]
 73 |     pub unsafe fn shadow(&self) -> Self {
 74 |         unsafe {
 75 |             Self {
 76 |                 inner: self.inner.shadow(),
 77 |                 p: PhantomData,
 78 |             }
 79 |         }
 80 |     }
 81 | 
 82 |     /// Creates a new, empty `MapxTk`.
 83 |     #[inline(always)]
 84 |     pub fn new() -> Self {
 85 |         Self {
 86 |             inner: MapxRawMk::new(KEY_SIZE),
 87 |             p: PhantomData,
 88 |         }
 89 |     }
 90 | 
 91 |     /// Retrieves a value from the map for a given triple key.
 92 |     #[inline(always)]
 93 |     pub fn get(&self, key: &(&K1, &K2, &K3)) -> Option {
 94 |         let k1 = key.0.encode();
 95 |         let k2 = key.1.encode();
 96 |         let k3 = key.2.encode();
 97 |         self.inner
 98 |             .get(&[&k1, &k2, &k3])
 99 |             .map(|v| pnk!(ValueEnDe::decode(&v)))
100 |     }
101 | 
102 |     /// Retrieves a mutable reference to a value in the map.
103 |     #[inline(always)]
104 |     pub fn get_mut<'a>(
105 |         &'a mut self,
106 |         key: &'a (&'a K1, &'a K2, &'a K3),
107 |     ) -> Option> {
108 |         self.get(key).map(move |v| ValueMut::new(self, key, v))
109 |     }
110 | 
111 |     /// Mocks a mutable value for a given key.
112 |     #[inline(always)]
113 |     pub fn mock_value_mut<'a>(
114 |         &'a mut self,
115 |         key: &'a (&'a K1, &'a K2, &'a K3),
116 |         v: V,
117 |     ) -> ValueMut<'a, K1, K2, K3, V> {
118 |         ValueMut::new(self, key, v)
119 |     }
120 | 
121 |     /// Checks if the map contains a value for the specified triple key.
122 |     #[inline(always)]
123 |     pub fn contains_key(&self, key: &(&K1, &K2, &K3)) -> bool {
124 |         self.get(key).is_some()
125 |     }
126 | 
127 |     /// Checks if the map is empty.
128 |     #[inline(always)]
129 |     pub fn is_empty(&self) -> bool {
130 |         self.inner.is_empty()
131 |     }
132 | 
133 |     /// Gets an entry for a given key, allowing for in-place modification.
134 |     #[inline(always)]
135 |     pub fn entry<'a>(
136 |         &'a mut self,
137 |         key: &'a (&'a K1, &'a K2, &'a K3),
138 |     ) -> Entry<'a, K1, K2, K3, V> {
139 |         Entry { key, hdr: self }
140 |     }
141 | 
142 |     /// Inserts a key-value pair into the map.
143 |     ///
144 |     /// Does not return the old value for performance reasons.
145 |     #[inline(always)]
146 |     pub fn insert(&mut self, key: &(&K1, &K2, &K3), value: &V) {
147 |         let k1 = key.0.encode();
148 |         let k2 = key.1.encode();
149 |         let k3 = key.2.encode();
150 |         let v = value.encode();
151 |         pnk!(self.inner.insert(&[&k1, &k2, &k3], &v));
152 |     }
153 | 
154 |     /// Removes a key-value pair from the map. Supports batch removal by omitting keys.
155 |     ///
156 |     /// Does not return the old value for performance reasons.
157 |     #[inline(always)]
158 |     pub fn remove(&mut self, key: &(&K1, Option<(&K2, Option<&K3>)>)) {
159 |         let k1 = key.0.encode();
160 |         let k2_k3 = key
161 |             .1
162 |             .map(|(k2, k3)| (k2.encode(), k3.map(|k3| k3.encode())));
163 |         let k = if let Some((k2, k3)) = k2_k3.as_ref() {
164 |             let mut res = vec![&k1[..], &k2[..]];
165 |             if let Some(k3) = k3.as_ref() {
166 |                 res.push(&k3[..]);
167 |             }
168 |             res
169 |         } else {
170 |             vec![&k1[..]]
171 |         };
172 | 
173 |         pnk!(self.inner.remove(k.as_slice()));
174 |     }
175 | 
176 |     /// Clears the map, removing all key-value pairs.
177 |     #[inline(always)]
178 |     pub fn clear(&mut self) {
179 |         self.inner.clear();
180 |     }
181 | 
182 |     /// Checks if this `MapxTk` instance is the same as another.
183 |     #[inline(always)]
184 |     pub fn is_the_same_instance(&self, other_hdr: &Self) -> bool {
185 |         self.inner.is_the_same_instance(&other_hdr.inner)
186 |     }
187 | 
188 |     /// Returns the number of keys in the map.
189 |     #[inline(always)]
190 |     pub fn key_size(&self) -> u32 {
191 |         self.inner.key_size()
192 |     }
193 | 
194 |     // TODO
195 |     // pub fn iter_op
196 |     // pub fn iter_op_with_key_prefix
197 |     // pub fn iter_mut_op
198 |     // pub fn iter_mut_op_with_key_prefix
199 | }
200 | 
201 | impl Clone for MapxTk {
202 |     fn clone(&self) -> Self {
203 |         Self {
204 |             inner: self.inner.clone(),
205 |             p: PhantomData,
206 |         }
207 |     }
208 | }
209 | 
210 | impl Default for MapxTk
211 | where
212 |     K1: KeyEnDe,
213 |     K2: KeyEnDe,
214 |     K3: KeyEnDe,
215 |     V: ValueEnDe,
216 | {
217 |     fn default() -> Self {
218 |         Self::new()
219 |     }
220 | }
221 | 
222 | /// A mutable reference to a value in a `MapxTk`.
223 | #[derive(Debug)]
224 | pub struct ValueMut<'a, K1, K2, K3, V>
225 | where
226 |     K1: KeyEnDe,
227 |     K2: KeyEnDe,
228 |     K3: KeyEnDe,
229 |     V: ValueEnDe,
230 | {
231 |     hdr: &'a mut MapxTk,
232 |     key: &'a (&'a K1, &'a K2, &'a K3),
233 |     value: V,
234 | }
235 | 
236 | impl<'a, K1, K2, K3, V> ValueMut<'a, K1, K2, K3, V>
237 | where
238 |     K1: KeyEnDe,
239 |     K2: KeyEnDe,
240 |     K3: KeyEnDe,
241 |     V: ValueEnDe,
242 | {
243 |     fn new(
244 |         hdr: &'a mut MapxTk,
245 |         key: &'a (&'a K1, &'a K2, &'a K3),
246 |         value: V,
247 |     ) -> Self {
248 |         ValueMut { hdr, key, value }
249 |     }
250 | }
251 | 
252 | impl Drop for ValueMut<'_, K1, K2, K3, V>
253 | where
254 |     K1: KeyEnDe,
255 |     K2: KeyEnDe,
256 |     K3: KeyEnDe,
257 |     V: ValueEnDe,
258 | {
259 |     fn drop(&mut self) {
260 |         self.hdr.insert(self.key, &self.value);
261 |     }
262 | }
263 | 
264 | impl Deref for ValueMut<'_, K1, K2, K3, V>
265 | where
266 |     K1: KeyEnDe,
267 |     K2: KeyEnDe,
268 |     K3: KeyEnDe,
269 |     V: ValueEnDe,
270 | {
271 |     type Target = V;
272 |     fn deref(&self) -> &Self::Target {
273 |         &self.value
274 |     }
275 | }
276 | 
277 | impl DerefMut for ValueMut<'_, K1, K2, K3, V>
278 | where
279 |     K1: KeyEnDe,
280 |     K2: KeyEnDe,
281 |     K3: KeyEnDe,
282 |     V: ValueEnDe,
283 | {
284 |     fn deref_mut(&mut self) -> &mut Self::Target {
285 |         &mut self.value
286 |     }
287 | }
288 | 
289 | /// A view into a single entry in a map, which may either be vacant or occupied.
290 | pub struct Entry<'a, K1, K2, K3, V> {
291 |     hdr: &'a mut MapxTk,
292 |     key: &'a (&'a K1, &'a K2, &'a K3),
293 | }
294 | 
295 | impl<'a, K1, K2, K3, V> Entry<'a, K1, K2, K3, V>
296 | where
297 |     K1: KeyEnDe,
298 |     K2: KeyEnDe,
299 |     K3: KeyEnDe,
300 |     V: ValueEnDe,
301 | {
302 |     /// Ensures a value is in the entry by inserting the default if empty,
303 |     /// and returns a mutable reference to the value.
304 |     pub fn or_insert(self, default: V) -> ValueMut<'a, K1, K2, K3, V> {
305 |         let hdr = self.hdr as *mut MapxTk;
306 |         match unsafe { &mut *hdr }.get_mut(self.key) {
307 |             Some(v) => v,
308 |             _ => unsafe { &mut *hdr }.mock_value_mut(self.key, default),
309 |         }
310 |     }
311 | }
312 | 
313 | /////////////////////////////////////////////////////////////////////////////
314 | /////////////////////////////////////////////////////////////////////////////
315 | 


--------------------------------------------------------------------------------