Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
bitcoinleveldb-compat
Low-level Rust bindings that emulate the original C++ LevelDB port and Snappy interfaces used by Bitcoin Core, with tracing, safety checks, and modern Rust infrastructure under the hood.
This crate is intentionally narrow in scope: it exists to make it straightforward to plug Bitcoin's LevelDB-dependent code into Rust with minimal semantic drift. It provides
- C/FFI-friendly Snappy compression helpers with LevelDB-style contracts
- CRC32C acceleration in the same shape as LevelDB's
crc32chelpers - A
portmodule implementing LevelDB-compatible mutexes and condition variables on top of Rust primitives - Stubs for heap profiling to satisfy link-time expectations without adding heavy dependencies
All interfaces are designed to be close to the C++ originals (pointer-based, explicit contracts, and boolean success flags) while being implemented in Rust with logging and modern concurrency primitives.
Features at a Glance
-
Snappy compatibility
snappy_compress(input, length, output) -> boolsnappy_uncompress(input, length, output) -> boolsnappy_get_uncompressed_length(input, length, result) -> bool- Behavior gated behind the
leveldb_snappyCargo feature; without it, the functions are present but always returnfalse, matching LevelDB's conditional Snappy support.
-
CRC32C acceleration
acceleratedcrc32c(crc, buf, size) -> u32wraps a performant CRC32C implementation and mirrors LevelDB's accelerated CRC API.
-
Synchronization primitives (
portmodule)port::Mutex: thin wrapper overparking_lot::RawMutex, with explicitlock(),unlock(), andassert_held()semantics aligned with LevelDB'sport::Mutex.port::CondVar: condition variable that works withport::Mutex, mirroring LevelDB'sCondVarsemantics (wait,signal,signal_all).
-
Heap profiling stub
get_heap_profile(func, arg) -> boolsatisfies LevelDB's expected symbol but always returnsfalse, indicating that heap profiling is not supported in this build.
-
Observability
- All public operations are instrumented using
tracing, producing detailed spans and events attrace,debug, anderrorlevels for diagnosis of concurrency and compression behavior.
- All public operations are instrumented using
Installation
[]
= "0.1.19"
# Optional: to enable Snappy support
[]
# In your own crate, just enable this feature for bitcoinleveldb-compat
# bitcoinleveldb-compat = { version = "0.1.19", features = ["leveldb_snappy"] }
This crate targets Rust edition 2021 and is licensed under MIT.
The code lives in the bitcoin-rs monorepo:
- Repository: https://github.com/klebs6/bitcoin-rs
Snappy API
The Snappy interface is deliberately low-level and pointer-based to approximate the C++ implementation.
Feature gating
All Snappy functions are compiled in both with and without Snappy support:
- With
leveldb_snappyfeature enabled:- Functions perform real Snappy compression/decompression using the
snapcrate.
- Functions perform real Snappy compression/decompression using the
- Without
leveldb_snappyfeature:- Functions are no-ops that return
falseand log that Snappy is disabled, mirroring LevelDB's optional Snappy integration.
- Functions are no-ops that return
snappy_compress
- Input:
input[0..length)is treated as a raw byte slice. - Output: On success,
*outputis overwritten with aStringwhose underlying bytes are the raw compressed Snappy buffer. - Return:
trueon success,falseon failure or invalid pointers.
Important properties:
- The function treats
Stringpurely as an owned byte container, usingString::from_utf8_unchecked. The compressed Snappy stream is not guaranteed to be UTF-8. This is intentional to mimic the originalstd::stringusage in LevelDB, which is a generic byte container. - Callers must never rely on UTF-8 validity of the resulting
String; treat it as opaque binary data.
Safety: This function is unsafe to call from a strict Rust perspective because it takes and dereferences raw pointers. In idiomatic Rust code, you should wrap it with a safe abstraction that ensures:
inputis non-null and references at leastlengthbytesoutputis non-null and points to a validStringinstance
Example usage inside unsafe context:
use snappy_compress;
unsafe
snappy_uncompress
- Input:
input[0..length)is a Snappy-compressed buffer. - Output: Raw bytes are written starting at
output. - Return:
trueon successful decompression,falseon invalid or corrupt input.
Constraints:
- The caller must ensure that
outputpoints to a writable buffer of at leastnbytes, wherenis the value returned by a successfulsnappy_get_uncompressed_lengthcall on the sameinput. - The function uses
ptr::copy_nonoverlappingto write the decompressed data.
Example pattern:
use ;
unsafe
snappy_get_uncompressed_length
This function attempts to uncompress the buffer internally to determine the output length and writes it into *result.
- Success path: returns
trueand sets*resultto the decompressed length. - Failure: returns
falseand leaves*resultunspecified.
Note that the implementation currently does full decompression to compute the length instead of using Snappy's cheap-length-query primitive. This is acceptable for compatibility but may be less efficient; consult the repository if efficiency is critical and consider contributing an optimized implementation.
CRC32C Acceleration
- If
bufis null orsize == 0, the function returns the originalcrcunchanged. - Otherwise, it computes an extended CRC32C over
buf[0..size)viacrc32c_extend(crc, data)and returns the new CRC.
This corresponds to the mathematical operation:
[ \text{new_crc} = \operatorname{CRC32C_extend}(\text{crc}, \text{buf}) ]
where CRC32C is the Castagnoli polynomial-based CRC with well-defined compositional properties (you can extend a prefix CRC with suffix bytes without recomputing over the entire concatenated buffer).
Example:
use acceleratedcrc32c;
unsafe
port Module: Mutex and Condition Variable
The port module re-implements the LevelDB port::Mutex and port::CondVar APIs, but on top of Rust's synchronization primitives (parking_lot::RawMutex and std::sync::Condvar), preserving the original semantics.
port::Mutex
Key behaviors:
lock()blocks until the mutex is obtained, then flipsis_lockedtotrue.unlock()setsis_lockedtofalseand releases the underlyingRawMutex.assert_held()issues adebug_assert!thatis_lockedistrue, used for internal invariants consistent with LevelDB's expectations.
This mutex is marked Send and Sync, so it can be shared across threads. It is explicitly not a RAII-guarded mutex; you must call lock()/unlock() manually, which is closer to how the C++ code interacts with port::Mutex.
Example:
use Mutex;
let mut mu = new;
mu.lock;
// critical section
mu.assert_held;
mu.unlock;
port::CondVar
Semantics mirror LevelDB:
CondVar::new(mu)associates the condition variable with a specificport::Mutexpointer. The pointer is assumed to outlive theCondVar.wait()must be called while holdingmu:- Registers the caller as a waiter (increments waiter count).
- Releases
mu. - Blocks on the internal
Condvar. - On wakeup, decrements waiter count and re-acquires
mubefore returning.
signal()wakes a single waiting thread (if any).signal_all()wakes all waiters.
There is a small internal StdMutex<usize> tracking waiters; poisoning is detected and logged but recovered from by taking the inner state.
Example usage:
use ;
use Arc;
use thread;
let mu = new;
let cond = new;
let ready = new;
// Producer
// Consumer
mu.lock;
while !ready.load
mu.unlock;
This pattern closely tracks the LevelDB idiom: check predicate under lock, call wait() to sleep if not satisfied, and re-evaluate after wake-up.
Heap Profiling Stub
This function always logs that heap profiling is not supported and returns false. It exists solely to satisfy link-time expectations from LevelDB/Bitcoin code that may conditionally call into get_heap_profile.
If you need real heap profiling, you should layer a dedicated solution over or in place of this stub and adjust your integration accordingly.
Tracing and Diagnostics
All public functions are annotated with #[instrument] from the tracing ecosystem. This means:
- Every call generates a span with structured fields (e.g.,
length,size, pointer values). - Errors and exceptional conditions are logged with
debug!,warn!, orerror!levels.
In production systems, this makes it significantly easier to diagnose concurrency issues, mis-sized buffers, or misconfigured features (e.g., Snappy disabled when expected).
To consume these logs, wire up a tracing subscriber in your binary:
use FmtSubscriber;
Safety and FFI Considerations
This crate presents a C-like API surface for the sake of Bitcoin/LevelDB compatibility:
- Raw pointers (
*const u8,*mut u8,*mut String,*mut usize) - Manual memory management responsibilities for the caller
- No lifetime tracking in the type system
When used from Rust, prefer to encapsulate these calls behind safe wrappers that:
- Own and manage the input/output buffers
- Avoid null pointers
- Guarantee length invariants
When used from C/C++ via FFI, ensure that the ABI and calling conventions are matched, and that the Rust side is compiled with a stable layout and visibility for the symbols in question.
License
This crate is distributed under the terms of the MIT license.