Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# mdbx Crate Notes

Include updates to the notes here if you discover important details while
working, or if the notes become outdated.

## Crate Overview

Rust bindings for libmdbx (MDBX database). Crate name: `signet-libmdbx`.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "signet-libmdbx"
description = "Idiomatic and safe MDBX wrapper"
version = "0.3.1"
version = "0.4.0"
edition = "2024"
rust-version = "1.92"
license = "MIT OR Apache-2.0"
Expand Down
17 changes: 14 additions & 3 deletions benches/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ fn bench_get_seq_iter(c: &mut Criterion) {
let (_dir, env) = setup_bench_db(n);
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
// Note: setup_bench_db creates a named database which adds metadata to the
// main database, so actual item count is n + 1
let actual_items = n + 1;
c.bench_function("bench_get_seq_iter", |b| {
b.iter(|| {
let mut cursor = txn.cursor(db).unwrap();
Expand Down Expand Up @@ -42,7 +45,8 @@ fn bench_get_seq_iter(c: &mut Criterion) {
iterate(&mut cursor).unwrap();

black_box(i);
assert_eq!(count, n);
// Both loops iterate all items since iter() repositions exhausted cursors
assert_eq!(count, actual_items * 2);
})
});
}
Expand All @@ -53,6 +57,9 @@ fn bench_get_seq_cursor(c: &mut Criterion) {
let (_dir, env) = setup_bench_db(n);
let txn = env.begin_ro_txn().unwrap();
let db = txn.open_db(None).unwrap();
// Note: setup_bench_db creates a named database which adds metadata to the
// main database, so actual item count is n + 1
let actual_items = n + 1;
c.bench_function("bench_get_seq_cursor", |b| {
b.iter(|| {
let (i, count) = txn
Expand All @@ -63,7 +70,7 @@ fn bench_get_seq_cursor(c: &mut Criterion) {
.fold((0, 0), |(i, count), (key, val)| (i + *key + *val, count + 1));

black_box(i);
assert_eq!(count, n);
assert_eq!(count, actual_items);
})
});
}
Expand All @@ -80,6 +87,10 @@ fn bench_get_seq_raw(c: &mut Criterion) {
let mut data = MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let mut cursor: *mut MDBX_cursor = ptr::null_mut();

// Note: setup_bench_db creates a named database which adds metadata to the
// main database, so actual item count is n + 1
let actual_items = n + 1;

c.bench_function("bench_get_seq_raw", |b| {
b.iter(|| unsafe {
txn.txn_execute(|txn| {
Expand All @@ -93,7 +104,7 @@ fn bench_get_seq_raw(c: &mut Criterion) {
}

black_box(i);
assert_eq!(count, n);
assert_eq!(count, actual_items);
mdbx_cursor_close(cursor);
})
.unwrap();
Expand Down
7 changes: 1 addition & 6 deletions mdbx-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
[package]
name = "signet-mdbx-sys"
description = "Raw bindings for libmdbx"
version = "0.0.1"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "Apache-2.0"
homepage = "https://github.com/init4tech/signet-mdbx"
repository = "https://github.com/init4tech/signet-mdbx"

[features]
default = []
bindgen = ["dep:bindgen"]

[build-dependencies]
cc = "1.2.15"
bindgen = { version = "0.72", default-features = false, optional = true }
6 changes: 6 additions & 0 deletions mdbx-sys/Dockerfile.bindgen
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM rust:latest
RUN apt-get update && apt-get install -y libclang-dev && rm -rf /var/lib/apt/lists/*
RUN rustup component add rustfmt
RUN cargo install bindgen-cli
WORKDIR /work
ENTRYPOINT ["bindgen"]
67 changes: 67 additions & 0 deletions mdbx-sys/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# signet-mdbx-sys

Raw FFI bindings for [libmdbx].

## Bindings

Platform-specific bindings are pre-generated and committed:

- `src/bindings_macos.rs` - macOS and other Unix-like systems
- `src/bindings_linux.rs` - Linux
- `src/bindings_windows.rs` - Windows

### Regenerating Bindings

When updating libmdbx, regenerate bindings on each target platform.

#### Linux (via Docker)

Generate bindings:

```bash
# First build the Docker image
docker build -t mdbx-bindgen -f Dockerfile.bindgen .

# Then run bindgen inside the container
docker run --rm -v "$(pwd)":/work mdbx-bindgen \
libmdbx/mdbx.h \
--allowlist-var "^(MDBX|mdbx)_.*" \
--allowlist-type "^(MDBX|mdbx)_.*" \
--allowlist-function "^(MDBX|mdbx)_.*" \
--no-layout-tests \
--no-doc-comments \
--no-prepend-enum-name \
--merge-extern-blocks \
-o src/bindings_linux.rs
```

#### macOS / Windows / Linux (locally)

Install bindgen-cli and run directly:

```bash
# Install bindgen-cli if not already installed
cargo install bindgen-cli

# Generate bindings for your platform
bindgen libmdbx/mdbx.h \
--allowlist-var "^(MDBX|mdbx)_.*" \
--allowlist-type "^(MDBX|mdbx)_.*" \
--allowlist-function "^(MDBX|mdbx)_.*" \
--no-layout-tests \
--no-doc-comments \
--no-prepend-enum-name \
--merge-extern-blocks \
-o src/bindings_macos.rs
# or src/bindings_windows.rs
# or src/bindings_linux.rs
```

Requires libclang. On macOS:

```bash
brew install llvm
export LIBCLANG_PATH=$(brew --prefix llvm)/lib
```

[libmdbx]: https://github.com/erthink/libmdbx
103 changes: 2 additions & 101 deletions mdbx-sys/build.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
use std::{env, path::PathBuf};

#[cfg(feature = "bindgen")]
use std::path::Path;

fn main() {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let mdbx = manifest_dir.join("libmdbx");

println!("cargo:rerun-if-changed={}", mdbx.display());

// Binding regeneration requires both the `bindgen` feature AND the
// `MDBX_REGENERATE_BINDINGS` env var. This prevents accidental regeneration
// when running `cargo build --all-features` on the workspace.
//
// To regenerate bindings intentionally:
// MDBX_REGENERATE_BINDINGS=1 cargo build -p signet-mdbx-sys --features bindgen
#[cfg(feature = "bindgen")]
if env::var("MDBX_REGENERATE_BINDINGS").is_ok() {
let bindings = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
generate_bindings(&mdbx, &bindings);

// Also write to src/bindings.rs for easy commit
let src_bindings = manifest_dir.join("src").join("bindings.rs");
std::fs::copy(&bindings, &src_bindings).expect("Failed to copy bindings to src/");
println!("cargo:warning=Regenerated bindings. Don't forget to commit src/bindings.rs");
}

let mut cc = cc::Build::new();
cc.flag_if_supported("-Wno-unused-parameter").flag_if_supported("-Wuninitialized");

Expand All @@ -36,97 +16,18 @@ fn main() {
let flags = format!("{:?}", cc.get_compiler().cflags_env());
cc.define("MDBX_BUILD_FLAGS", flags.as_str()).define("MDBX_TXN_CHECKOWNER", "0");

// Enable debugging on debug builds
#[cfg(debug_assertions)]
cc.define("MDBX_DEBUG", "1").define("MDBX_ENABLE_PROFGC", "1");

// Disables debug logging on optimized builds
#[cfg(not(debug_assertions))]
cc.define("MDBX_DEBUG", "0").define("NDEBUG", None);

// Propagate `-C target-cpu=native`
let rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();
let rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap_or_default();
if rustflags.contains("target-cpu=native")
&& env::var("CARGO_CFG_TARGET_ENV").unwrap() != "msvc"
&& env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default() != "msvc"
{
cc.flag("-march=native");
}

cc.file(mdbx.join("mdbx.c")).compile("libmdbx.a");
}

#[cfg(feature = "bindgen")]
fn generate_bindings(mdbx: &Path, out_file: &Path) {
use bindgen::{
Formatter,
callbacks::{IntKind, ParseCallbacks},
};

#[derive(Debug)]
struct Callbacks;

impl ParseCallbacks for Callbacks {
fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
match name {
"MDBX_SUCCESS"
| "MDBX_KEYEXIST"
| "MDBX_NOTFOUND"
| "MDBX_PAGE_NOTFOUND"
| "MDBX_CORRUPTED"
| "MDBX_PANIC"
| "MDBX_VERSION_MISMATCH"
| "MDBX_INVALID"
| "MDBX_MAP_FULL"
| "MDBX_DBS_FULL"
| "MDBX_READERS_FULL"
| "MDBX_TLS_FULL"
| "MDBX_TXN_FULL"
| "MDBX_CURSOR_FULL"
| "MDBX_PAGE_FULL"
| "MDBX_MAP_RESIZED"
| "MDBX_INCOMPATIBLE"
| "MDBX_BAD_RSLOT"
| "MDBX_BAD_TXN"
| "MDBX_BAD_VALSIZE"
| "MDBX_BAD_DBI"
| "MDBX_LOG_DONTCHANGE"
| "MDBX_DBG_DONTCHANGE"
| "MDBX_RESULT_TRUE"
| "MDBX_UNABLE_EXTEND_MAPSIZE"
| "MDBX_PROBLEM"
| "MDBX_LAST_LMDB_ERRCODE"
| "MDBX_BUSY"
| "MDBX_EMULTIVAL"
| "MDBX_EBADSIGN"
| "MDBX_WANNA_RECOVERY"
| "MDBX_EKEYMISMATCH"
| "MDBX_TOO_LARGE"
| "MDBX_THREAD_MISMATCH"
| "MDBX_TXN_OVERLAPPING"
| "MDBX_LAST_ERRCODE" => Some(IntKind::Int),
_ => Some(IntKind::UInt),
}
}
}

let bindings = bindgen::Builder::default()
.header(mdbx.join("mdbx.h").to_string_lossy())
.allowlist_var("^(MDBX|mdbx)_.*")
.allowlist_type("^(MDBX|mdbx)_.*")
.allowlist_function("^(MDBX|mdbx)_.*")
.size_t_is_usize(true)
.merge_extern_blocks(true)
.parse_callbacks(Box::new(Callbacks))
.layout_tests(false)
.prepend_enum_name(false)
.generate_comments(false)
.formatter(Formatter::Rustfmt)
.generate()
.expect(
"Unable to generate bindings. Ensure LIBCLANG_PATH is set.\n\
On macOS: export LIBCLANG_PATH=$(brew --prefix llvm)/lib\n\
export DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix llvm)/lib\n\
On Linux: apt-get install libclang-dev",
);
bindings.write_to_file(out_file).expect("Couldn't write bindings!");
}
Loading
Loading