cbvault 0.1.2

Read ChessBase databases as moves2 streams: Database, GameIter, PGN writer, and .cbv/.cbz archives that unarchive in full
Documentation
# cbvault

Read ChessBase databases as `moves2` streams, and unarchive `.cbv`/`.cbz`
containers in full. MIT licensed, read-only, and built for throughput.

This is the **consumer-facing** crate. It ties together
[`cbvault-format`](https://crates.io/crates/cbvault-format) (bytes) and
[`cbvault-chess`](https://crates.io/crates/cbvault-chess) (`gigachess`) into one
API: open a database, stream its games, read annotations, write PGN, or unarchive
a container.

## Open and read

```rust
use cbvault::bridge::Database;

let db = Database::open("Mega Database 2025/Mega Database 2025")?;
for header in db.headers().iter().take(5) {
    println!("{} {}", header.id, header.white);
}
```

Listing never opens the moves file, so inspecting a database header does not pay
for its gigabytes.

## Convert through a sink

`for_each_game` walks the database and hands each game to a sink that declares
what it needs. Asking for less makes the walk cheaper: no keys means the fast
move generator, keys means the hash-maintaining one, annotations off means the
annotations file is never opened.

```rust
use cbvault::bridge::{for_each_game, Database, GameRef, GameSink};

struct Counted(u64);
impl GameSink for Counted {
    fn game(&mut self, _: GameRef<'_>) {
        self.0 += 1;
    }
}

let db = Database::open("Mega Database 2025/Mega Database 2025")?;
let mut count = Counted(0);
for_each_game(&db, &mut count)?;
println!("{} games", count.0);
```

`convert_parallel` is the Rayon-parallel form. **Both are byte-identical**, and
that is a test rather than a claim: the same sink sees the same games in the same
order at every thread count.

## Export PGN

```rust
use cbvault::pgn::PgnWriter;

let mut writer = PgnWriter::new();
writer.write_game(&mut out, &header, &entities, &game, annotations)?;
```

`cbvault::pgn::export_parallel` is the Rayon-parallel form and produces
**byte-identical** output to the sequential one, which is a test rather than a
claim.

## Unarchive a `.cbv`

```rust
use cbvault_format::archive::Archive;

let archive = Archive::open("Mega.cbv")?;
archive.extract_parallel("./out", 4)?;
```

All four block modes decode: **3,871 of 3,871 members and 100 % of the
reference archive's 3.61 GB**, every member byte-identical to the reference
extractor. `.cbz` opens with a password.

## Design notes

- **Read-only, permanently.** Nothing opens a database for writing; no extraction
  ever writes bytes the reader did not decode.
- **Zero allocation in hot paths.** Games stream through caller-owned buffers.
- **One chess core.** `gigachess`, and only `gigachess`.
- **Malformed input never panics.** Every failure is a typed error.

## Not supported

- **2CBH.** `cbvault_format::twocbh` reads the container's framing for
  inspection, but the `.2cbg` move codec is not decoded. `Database::open` refuses
  a 2CBH set with a typed error rather than returning a half-read database.
- **Writing.** This project never writes ChessBase data.
- **Derived accelerators** (`.cko`, `.cpo`) are tolerated and ignored.

## Licence

MIT. The `.cbh` readers are ported from `cbformat` in `oschess-cb-bridge` (MIT,
"Copyright (c) 2026 the oschess-cb-bridge contributors") with attribution; the
`.cbv`/`.cbz` codec was written from the published format facts. No ChessBase
code is used and no ChessBase data is redistributed.
ChessBase is a trademark of its owner, used only to name the formats read here.