bedrock-leveldb
bedrock-leveldb is a pure Rust raw key/value storage library for
Minecraft Bedrock world databases. The performance target is benchmark-backed
zero-copy where possible, lock-free read hot paths after a short state snapshot,
and explicit owned allocation when callers request it. It focuses on the storage layer only:
chunk, actor, player, and NBT semantics are intentionally out of scope and
belong in application code or domain-specific layers.
The crate can read native Bedrock/LevelDB manifests, WAL records, and table
files. v0.2 writes standard LevelDB WAL batches, flushes native .ldb tables,
and persists manifest version edits. Older BWLDB... files remain readable for
migration/backward compatibility only.
Maintainers and contributors should also read the development guide.
Quick Start
use ;
For read-only analysis of real Bedrock worlds, set OpenOptions::read_only = true and create_if_missing = false. Read-only handles never initialize,
repair, flush, or write to the database directory.
Supported Surface
| Area | Status |
|---|---|
| Native LevelDB manifest replay | Implemented for the metadata needed to find tables |
| Native LevelDB WAL replay | Implemented for write batches |
| Native LevelDB table reads | Footer, index block, data blocks, restart arrays, internal key trailer |
| Compression reads | Snappy, zlib, and Bedrock raw deflate when features are enabled |
| Lazy point lookup | Implemented with manifest range filtering and seeked block reads |
| Visitor scans | Key, entry, prefix, sequential, and table-parallel modes |
| Native block cache | Bounded decoded block cache |
| Bedrock chunk key helpers | Parse and encode documented LevelDB chunk keys |
Legacy LegacyTerrain values |
Validate and expose the 83,200-byte early LevelDB terrain layout, including [biome_id, red, green, blue] biome samples |
| Legacy subchunk values | Classify paletted subchunks and expose pre-paletted block ID/metadata arrays |
| Batch exact reads | Db::get_many_owned preserves input order for legacy and modern render keys |
| Native writes by this crate | WAL batch append, native .ldb flush, manifest edit persistence |
| Production LevelDB compaction | Correctness-first native range compaction |
| Arbitrary corrupt database repair | Partial, writes native recovered output from readable data |
| Pre-LevelDB worlds | Not supported; chunks.dat and entities.dat are outside this crate |
mmap read path |
Feature-gated callback scans can borrow uncompressed custom/native table values |
API Notes
Db::open(path, OpenOptions)loadsCURRENT, manifest metadata, and the WAL overlay. It does not eagerly materialize every native table value.Db::get(key)is the compatibility owned/shared read path.Db::get_refandDb::get_with_refreturnValueRef, which can represent borrowed, shared, or explicitly owned values. Cross-function point lookups stay shared or owned so they cannot return dangling table slices.Db::for_each_entry_refandDb::for_each_prefix_refare the true borrowed-first APIs. WithReadStrategy::Borrowedand sequential scan mode, uncompressed native LevelDB blocks returnValueRef::Borrowedinside the visitor callback. Compressed blocks, WAL/overlay values, and non-callback point reads returnShared/Owned. Enabling themmapfeature maps table files read-only so those borrowed slices are backed by the mapping for the duration of the callback.Db::get_many_owned(keys, ReadOptions)is the preferred renderer path for exact chunk records such asLegacyTerrain(0x30),Data2D, subchunks, and block entities. It preserves input order and avoids prefix scans during tile rendering. It returns raw values byte-for-byte; X/Y/Z coordinate interpretation and legacy biome priority are intentionally tested and implemented inbedrock-world/bedrock-render.- With the default
asyncfeature,Arc<Db>now provides owned async read helpers:get_async,get_with_async,collect_keys_owned_async,collect_prefix_keys_owned_async, andcollect_prefix_owned_async. They use Tokiospawn_blockingand are intended for GUI or server runtimes that must keep foreground tasks responsive. Db::collect_keys_owned,Db::collect_prefix_keys_owned, andDb::collect_prefix_ownedreturn owned data without forcing callers to write visitor glue for common indexing paths.Db::write_batch_native,Db::flush_memtable,Db::compact_range_native, andDb::recover_nativeare the explicit v0.2 native write/recovery entry points.Db::write,Db::flush,Db::compact_range, andDb::repairdelegate to the same native paths.ReadOptions::cache_policydefaults toBypass, so normal reads do not contend on the shared block cache. Set it toUseonly when cross-request block reuse is worth the lock cost.ReadOptions::pipelineconfigures local Rayon scan scheduling.queue_depth,table_batch_size, andprogress_intervaluse automatic defaults when set to zero.ScanOutcomereportstables_scanned,worker_threads,queue_wait_ms, andcancel_checksso renderers can tune without fixed machine-specific timing thresholds.- Old LevelDB worlds are still LevelDB databases. This crate reads native zlib
compression tag
2, Bedrock raw deflate tag4, WAL +.ldboverlays, and exactLegacyTerrainkeys; pre-LevelDBchunks.datparsing intentionally lives inbedrock-world. Db::for_each_key,Db::for_each_entry, andDb::for_each_prefixstream borrowed keys andBytesvalues to visitors.Db::for_each_prefix_keyis the preferred render-index path when callers only need keys. It avoids value callbacks and lets native table scans seek directly into the requested prefix range.- Visitors return
VisitorControl::ContinueorVisitorControl::Stop; normal early termination is reported inScanOutcome, not as an error. stats_fast()is metadata/overlay-only.stats_full(), snapshots, materialized iterators, repair, and compaction are explicit expensive paths.
Migration: full prefix values to key-only scans
Old render index code often read every chunk value just to discover whether a chunk had renderable records:
let mut keys = Vecnew;
db.for_each_prefix?;
Prefer the key-only API for viewport and region indexes:
let mut keys = Vecnew;
db.for_each_prefix_key?;
Async callers should share the database handle instead of reopening it for each request:
let db = new;
let keys = db
.clone
.collect_prefix_keys_owned_async
.await?;
Bedrock Record Helpers
The database APIs stay raw key/value APIs. For old Bedrock LevelDB worlds, the crate also provides storage-level helpers for documented record families:
use ;
#
The helpers cover the LevelDB-era legacy layouts described by the Bedrock
format history, including LegacyTerrain and old SubChunkPrefix payload
families. They intentionally do not parse pre-LevelDB chunks.dat /
entities.dat worlds, NBT payloads, actor records, or gameplay-level chunk
semantics.
Logging
This is a library crate, so it only emits diagnostics through the standard
log facade. It does not initialize a global logger and never calls
println! or eprintln!. Applications can connect any compatible backend:
Log events are intentionally low-noise and avoid raw values. Useful events are
emitted around database open, manifest/WAL replay, table scans, custom flushes,
repair paths that discard unreadable files, parallel table workers, cancellation,
and key-only prefix scans. Applications using tracing can bridge these events
with tracing_log::LogTracer.
Errors
All fallible APIs return bedrock_leveldb::Result<T>, an alias for
Result<T, LevelDbError>. LevelDbError is structured; prefer matching
ErrorKind and using path() instead of parsing display strings:
use ;
let err = open
.expect_err;
assert_eq!;
assert!;
Cooperative scan cancellation returns ErrorKind::Cancelled. Read-only handles
return ErrorKind::ReadOnly for writes, flushes, repair, and compaction.
Features
| Feature | Default | Meaning |
|---|---|---|
zlib |
yes | Enables zlib and Bedrock raw-deflate decompression/compression |
snappy |
yes | Enables Snappy table decompression/compression |
async |
yes | Adds Db::open_async through Tokio spawn_blocking |
mmap |
no | Reserved for a future mapped read path |
repair-tools |
no | Reserved for expanded repair tooling |
bench |
no | Reserved for benchmark-only code paths |
docs.rs builds with all features enabled, so the hosted API reference includes
async helpers, compression backends, mapped scan types, and repair-tool entry
points. The crates.io package includes the English and Chinese READMEs, the
guide documents under docs/, the changelog, licenses, source, tests, and
benchmarks.
MSRV is Rust 1.87.
Testing And Benchmarks
Release checks used before the first public commit:
cargo fmt --check
cargo clippy --all-features --all-targets -- -D warnings
cargo rustdoc --all-features -- -D missing_docs
cargo test --all-features
cargo test --no-default-features
cargo test --no-default-features --features zlib
cargo test --no-default-features --features snappy
cargo test --no-default-features --features async
cargo test --no-default-features --features mmap
cargo doc --all-features --no-deps
cargo package --allow-dirty
cargo bench --all-features
The Criterion suite is synthetic. It separates overlay hot reads, flushed native table reads, native table point/prefix reads, WAL recovery, and sequential versus table-parallel scans. Large-world behavior should still be validated with real Bedrock fixtures in higher-level crates because this crate does not interpret world keys or NBT payloads. Latest local numbers are tracked in docs/BENCHMARKS.md.
License
Licensed under either of:
- Apache License, Version 2.0
- MIT license