[package]
name = "coordinode-lsm-tree"
description = "Embedded LSM-tree storage engine: BuRR filters, zstd dictionary compression, MVCC, range tombstones, merge operators, K/V separation, AES-256-GCM at rest."
license = "Apache-2.0"
version = "5.2.1"
edition = "2024"
rust-version = "1.92"
readme = "README.md"
include = ["src/**/*", "build.rs", "LICENSE-APACHE", "README.md", "CHANGELOG.md"]
repository = "https://github.com/structured-world/coordinode-lsm-tree"
homepage = "https://github.com/structured-world/coordinode-lsm-tree"
documentation = "https://docs.rs/coordinode-lsm-tree"
keywords = ["lsm-tree", "storage", "database", "embedded", "key-value"]
categories = ["data-structures", "database-implementations", "filesystem", "compression"]

[package.metadata.docs.rs]
# Build the docs.rs page with every feature enabled so the rendered
# crate page exposes the full public API surface (zstd dictionary
# compression, encryption, io-uring, bytes integration, metrics,
# ribbon-serde). cfg(docsrs) is set so #[cfg_attr(docsrs, ...)] items
# render their feature/availability badges.
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu"]

[lib]
name = "lsm_tree"
path = "src/lib.rs"

[features]
# `std` is the canonical platform feature. Default-on so every existing
# downstream consumer keeps the current build profile (file I/O, threads,
# `std::sync` primitives, system clock). Turning it OFF puts the crate
# into no-std + alloc mode — currently in progress: only foundational
# modules compile without `std`, the rest still imports `std::*` directly
# and is gated to migrate incrementally. The `cargo check
# --no-default-features --features alloc` CI job tracks migration progress
# (expected to fail until each module is ported). New code added to this
# crate MUST be no-std-friendly: prefer `core::*` / `alloc::*`, gate
# std-only primitives behind `#[cfg(feature = "std")]`, and pick external
# crates that support no-std.
default = ["std", "parallel"]
std = [
    "dep:arc-swap",
    "dep:parking_lot",
    "dep:tempfile",
    "rustc-hash/std",
    "varint-rs/std",
]
# Parallel block-compression pipeline (default executor: rayon thread pool).
# std-only — there are no threads below std. The pipeline itself is reached
# through the `CompactionSpawner` trait (available with just `std`), so callers
# can inject a custom executor without this feature; `parallel` only adds the
# built-in rayon-backed `RayonSpawner` default.
parallel = ["std", "dep:rayon"]
# `alloc` is the minimal hard requirement — the crate uses `Arc`,
# `Vec`, `Box` everywhere. Listed explicitly so consumers can pick it up
# without enabling `std` once the no-std migration completes per-module.
alloc = []
# Every feature below currently pulls in modules that use `std::*`
# directly (file I/O, threading, system clock, etc.). Make each one
# transitively depend on `std` so that `--no-default-features
# --features <X>` is automatically coherent — without this, a consumer
# would have to remember to spell `--features std,X` or hit a wall of
# `unresolved module std` errors. As individual modules are ported to
# no-std + alloc (tracked in the migration epic), the `std`
# transitive dep can be lifted on a per-feature basis.
io-uring = ["dep:io-uring", "std"]
lz4 = ["dep:lz4_flex", "std"]
# The previous `zstd-pure = ["zstd"]` alias was removed. It was
# documented as deprecated when there were two candidate zstd backends
# on the roadmap; only structured-zstd remains, so the alias serves no
# purpose and is dropped per the standard deprecation lifecycle. The
# removal is signalled to release tooling as a breaking change via the
# conventional `!` markers on this PR's breaking commits (BuRR filter
# wire format, V5 manifest gate); release-plz raises the crate's major
# version on the next release tag accordingly.
zstd = ["dep:structured-zstd", "std"]
encryption = ["dep:aes-gcm", "dep:chacha20poly1305", "dep:rand_chacha", "std"]
# Per-block Reed-Solomon Page ECC. When enabled and `Config::page_ecc(true)`
# is set on a tree, each block gets a Reed-Solomon parity trailer
# computed over the on-disk payload (post-compression / post-encryption,
# the same bytes XXH3 covers). On read, if the block-level XXH3
# disagrees with the on-disk bytes, the reader attempts RS recovery
# before surfacing the corruption. Recovers up to two corrupted shards
# out of six (RS(4,2)). Disabled by default — pulls in the
# `reed-solomon-simd` dep only when set.
page_ecc = ["dep:reed-solomon-simd", "std"]
bytes_1 = ["dep:bytes"]
# Optional CRC32C (Castagnoli) checksum algorithm for block-level and
# per-KV checksums. Without this feature the `Xxh3_64` (default) and
# `Xxh3Low32` algorithms still work; selecting `ChecksumAlgorithm::Crc32c`
# at runtime returns "not compiled in" so callers can fall back or error.
crc32c = ["dep:crc32c"]
metrics = []
# Vendored Ribbon filter retains its `#[cfg(feature = "ribbon-serde")]`
# guards (renamed from upstream's bare `serde` feature to avoid clashing
# with any future top-level serde feature in this crate). We do not
# consume the serde repr from inside this crate — the BuRR on-disk
# format is byteorder-encoded — but the feature wires `serde` as an
# optional dep so `--all-features` builds and a future extraction back
# into a standalone crate compile cleanly.
ribbon-serde = ["dep:serde"]
# RocksDB head-to-head bench lives in a STANDALONE crate at
# `tools/compare-rocksdb/` — NOT a feature here. Keeping `rocksdb`
# out of the main crate's feature graph means `cargo build
# --all-features` / `cargo clippy --all-features` / docs.rs
# (`all-features = true`) do not have to pull in librocksdb's C
# dependency tree (libsnappy, liblz4, libbz2, librocksdb itself)
# nor bindgen + libclang. Build / run the comparison harness with:
#
#     cd tools/compare-rocksdb && cargo bench

[dependencies]
bytes = { version = "1", optional = true }
# Lockless atomic snapshot for RuntimeConfig (see src/runtime_config/).
# Lock-free atomic Arc swap. `arc-swap` 1.x is NOT `#![no_std]` (it requires
# `std`), so it stays an optional dependency enabled only by the `std`
# feature. Both its consumers — `RuntimeConfigHandle` and the version
# latest-`SuperVersion` mirror on the point-read fast path — gate their use
# behind `#[cfg(feature = "std")]` so the no-std-check job never sees it.
arc-swap = { version = "1.9", optional = true }
enum_dispatch = "0.3.13"
log = "0.4.32"
# `spin` provides a no_std-compatible Mutex. Used by
# `deletion_pause` so that module's only std footprint comes from the
# `Fs` trait it interacts with, not from its own synchronisation.
spin = { version = "0.12", default-features = false, features = ["mutex", "spin_mutex", "rwlock"] }
# In-tree sharded block cache (#429): `hashbrown` is the no_std-capable hash map
# backing each shard (alloc-only, no std dependency), replacing quick_cache's
# std-bound map. `parking_lot::RwLock` is the per-shard lock under `std` (small,
# userspace fast-path, concurrent readers); `spin::RwLock` is the no_std lock.
hashbrown = { version = "0.16", default-features = false, features = ["default-hasher"] }
parking_lot = { version = "0.12", optional = true }
lz4_flex = { version = "0.13.0", optional = true, default-features = false }
structured-zstd = { version = "0.0.34", optional = true, default-features = false, features = ["std", "lsm"] }
# `once_cell::race::OnceBox` — the no-std + alloc one-shot primitive.
# We pick it over `std::sync::OnceLock` (which has both `set` and the
# stabilised `get_or_try_init` on our 1.92 MSRV) because OnceLock is
# std-only: it can't compile on the `cargo check
# --no-default-features --features alloc` CI target, and the crate's
# direction is no-std-friendly. Unconditional dep with
# `default-features = false, features = ["race"]` so the crate stays
# no-std-friendly even with `--no-default-features`. Used by:
#   - zstd `ZstdDictionary::prepared_handle` (canonical single-parse
#     across racers, no auxiliary `Mutex`)
#   - `deletion_pause` slot on tables / blob files (one-shot install of
#     the shared `Arc<DeletionPause>` after recovery / compaction).
once_cell = { version = "1", default-features = false, features = ["race", "alloc"] }
rustc-hash = { version = "2.1.1", default-features = false }
# `portable-atomic` supplies `AtomicU64` on targets without native 64-bit
# atomics (e.g. `thumbv7em-none-eabihf`, where the seqno counter would not
# otherwise compile). On `std` / 64-bit targets it resolves to the native
# atomic with zero overhead, so the std hot path is unchanged. The default
# `fallback` feature implements the wide atomic via the target's native
# 32-bit CAS, so no `critical-section` impl is required to compile-check.
portable-atomic = { version = "1", default-features = false, features = ["fallback"] }
# Pure-Rust libm — supplies f64 ceil/round on `no_std` targets, which lack the
# float-math intrinsics std provides. Used only on the no_std path (filter
# sizing); std keeps the native `f64::ceil` / `f64::round`.
libm = { version = "0.2", default-features = false }
self_cell = "1.2.0"
# Optional — only pulled in by the vendored Ribbon filter under the
# `ribbon-serde` feature flag (off by default).
serde = { version = "1", optional = true, features = ["derive"] }
# sfa is vendored under src/sfa/ (upstream does not accept PRs; pinning
# the format to in-tree code lets us evolve it locally without waiting
# for a release we cannot influence). License files preserved alongside.
# Only used by std-bound test fixtures and the `get_tmp_folder` helper
# (which itself uses `std::env`). Optional + gated behind the `std`
# feature so the no-std build does not pull in `fastrand` (which
# transitively does `extern crate std`).
tempfile = { version = "3.20.0", optional = true }
varint-rs = { version = "2.2.1", default-features = false }
xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
# CRC32C (Castagnoli) for the optional `Crc32c` checksum algorithm.
# Hardware-accelerated (SSE4.2 `_mm_crc32` on x86, CRC32 instructions on
# ARM) with a safe scalar fallback; runtime-detected by the crate. Only
# pulled in behind the `crc32c` feature — the default `Xxh3_64` algorithm
# needs only `xxhash-rust`.
crc32c = { version = "0.6", optional = true }
# Exact pin on a prerelease — RC API can churn between releases, so locking
# the exact version is intentional. The 0.11 surface we depend on:
#   - aes_gcm::aead::{AeadInOut, Generate, Nonce, Tag}
#   - AeadInOut::{encrypt_inout_detached, decrypt_inout_detached}
#   - Nonce::generate_from_rng (via Generate)
#   - Nonce::try_from / Tag::try_from
# rand_core re-exported via aes-gcm's `rand_core` feature so `aead::rand_core`
# exposes the rand_core 0.10 trait family (no version-skew).
# TODO: bump to stable aes-gcm 0.11.0 once released — at that point this pin
# becomes a normal "^0.11" caret requirement and the API list above can be
# treated as the public 0.11 surface, not a prerelease snapshot.
aes-gcm = { version = "=0.11.0-rc.4", optional = true, features = ["aes", "rand_core"] }
# Exact pin on the matching prerelease — chacha20poly1305 0.11.0-rc.3 lines
# up with aes-gcm 0.11.0-rc.3 on the shared `aead` 0.6 surface, so both
# suites can share the same AeadInOut / Nonce / Tag types in the
# AEAD-dispatch module. Bumps in lockstep with aes-gcm when the stable
# 0.11.0 lands.
chacha20poly1305 = { version = "=0.11.0-rc.3", optional = true, features = ["rand_core"] }
rand_chacha = { version = "0.10", optional = true }
# Pure-Rust Reed-Solomon erasure coding, SIMD-accelerated on x86_64 / aarch64.
# Used by the optional `page_ecc` feature for per-block parity. Apache-2.0 /
# MIT, matches our license stance.
reed-solomon-simd = { version = "3", optional = true, default-features = false }
# Work-stealing thread pool backing the default parallel block-compression
# pipeline (the `parallel` feature). std-only by nature — there are no threads
# below `std`; the pipeline is reached through the `CompactionSpawner` seam, so
# rayon is just the default executor, not a hard dependency. Optional + gated so
# minimal / no-std builds drop it.
rayon = { version = "1.10", optional = true }

[target.'cfg(target_os = "linux")'.dependencies]
io-uring = { version = "0.7", optional = true }

[target.'cfg(unix)'.dependencies]
# Unix syscall surface that std does not expose:
#   - macOS: plain `fsync` for SyncMode::Normal (std's `File::sync_all` issues
#     the much slower `fcntl(F_FULLFSYNC)`), plus `statfs`/`clonefile` for
#     FS-capability detection + O(1) APFS reflink.
#   - Linux: `statfs` f_type detection (Btrfs/XFS), `ioctl(FICLONE)` reflink,
#     and `ioctl(FS_IOC_SETFLAGS|FS_NOCOW_FL)` CoW disable on Btrfs SSTs.
# libc provides the arch-correct struct layouts and ioctl constants that are
# otherwise error-prone to hand-declare per architecture.
libc = "0.2"

[dev-dependencies]
# Integration tests assemble their own wire fixtures with byteorder; the
# library itself no longer depends on it (its `ReadBytesExt`/`WriteBytesExt`
# now live in the in-tree, no_std-capable `crate::io`).
byteorder = { package = "byteorder-lite", version = "0.1.0" }
criterion = { version = "0.8.0", features = ["html_reports"] }
fs_extra = "1.3.0"
nanoid = "0.5.0"
proptest = "1"
rand = "0.10.1"
# Used by the vendored ribbon-filter's #[cfg(feature = "ribbon-serde")]
# round-trip tests. Dev-only; production code does not depend on it.
serde_json = "1"
strum = { version = "0.28.0", features = ["derive"] }
test-log = "0.2.18"

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
  'cfg(coverage,coverage_nightly)',
  'cfg(zstd_any)',
] }

[package.metadata.cargo-all-features]
denylist = []

[[bench]]
name = "merge"
harness = false
path = "benches/merge.rs"
required-features = []

[[bench]]
name = "memtable"
harness = false
path = "benches/memtable.rs"
required-features = []

[[bench]]
name = "bloom"
harness = false
path = "benches/bloom.rs"
required-features = []

[[bench]]
name = "index_block"
harness = false
path = "benches/index_block.rs"
required-features = []

[[bench]]
name = "block_cache"
harness = false
path = "benches/block_cache.rs"
required-features = []

[[bench]]
name = "batch_ops"
harness = false
path = "benches/batch_ops.rs"
required-features = []

[[bench]]
name = "zstd_dict"
harness = false
path = "benches/zstd_dict.rs"
required-features = []

[[bench]]
name = "lsp"
harness = false
path = "benches/lsp.rs"
required-features = []

[[bench]]
name = "ecc"
harness = false
path = "benches/ecc.rs"
required-features = ["page_ecc"]

[[bench]]
name = "compaction"
harness = false
path = "benches/compaction.rs"
required-features = ["zstd"]

[[bench]]
name = "partial_decode"
harness = false
path = "benches/partial_decode.rs"
required-features = ["zstd"]
