sparsemap 5.6.0

A sparse, compressed bitmap with run-length encoding, optimized for long runs of consecutive bits. 100% safe Rust, no_std, zero dependencies; reads the C sparsemap library's serialized format.
Documentation
# Releasing the `sparsemap` crate

The crate is release-ready: `cargo publish --dry-run` succeeds and
`cargo package` produces `target/package/sparsemap-<version>.crate`.
Publishing needs a crates.io API token (see step 3) and push access to
the forges.

Versions are kept in lockstep with the C library and the Python
binding, so a crate release exists even when the crate itself is
functionally unchanged; `scripts/check_version_consistency.sh` verifies
all eight sources agree before you tag.  Note that not every C release
reaches crates.io -- 5.4.0 and 5.5.0 were lockstep-only bumps and the
crate went 5.1.0 straight to 5.5.1 -- which is fine, but means
`max_version` on crates.io is not a reliable read of what the C library
is at.

## 1. Pre-flight (already green in CI)

```sh
cd rust
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test                      # unit + proptest + read-direction wire fixtures
sh ci/wire_compat.sh            # write-direction: C reads Rust's bytes (needs a C toolchain)
cargo build --no-default-features   # no_std
cargo publish --dry-run
```

## 2. Tag

Tags are namespaced because the C library and the Rust crate share one
repository and version line:

- `v3.0.0`      — the C library release.
- `rust-v3.0.0` — the Rust crate release.

Both annotated tags already exist locally:

```sh
git push origin v3.0.0 rust-v3.0.0
```

## 3. Publish to crates.io

```sh
cd rust
cargo publish
```

Authentication comes from the `CARGO_REGISTRY_TOKEN` environment
variable when it is set, so no `cargo login` and no `--token` flag is
needed on a machine that already exports it.  Otherwise run
`cargo login` once and paste a crates.io API token.

Use a recent cargo: the pinned dev shell's toolchain is old enough that
a transitive dev-dependency fails to parse (`feature 'edition2024' is
required`), which looks like a problem with this crate but is not.
`nix shell nixpkgs#cargo nixpkgs#rustc` works for `cargo test`,
`cargo publish` and everything else here.

Verify afterwards that the registry agrees, rather than trusting the
upload message:

```sh
curl -s https://crates.io/api/v1/crates/sparsemap |
    python3 -c 'import json,sys; print(json.load(sys.stdin)["crate"]["max_version"])'
```

A stronger check is to depend on the freshly published version from a
throwaway project and run one assertion against it; that catches a
package that uploaded but does not resolve or build for consumers.

`docs.rs` builds the documentation automatically after publish.  The
crate is pure Rust with no build script, so the docs.rs build needs no
C toolchain.

## 4. Release packages on GitHub and Codeberg

Attach the packaged crate as a release asset on both forges:

```sh
ART=rust/target/package/sparsemap-3.0.0.crate

# Codeberg (Forgejo) — primary
#   Web UI: Releases -> New release -> tag rust-v3.0.0 -> attach $ART
# or with the Forgejo `tea` CLI:
tea release create --repo gregburd/sparsemap --tag rust-v3.0.0 \
    --title "sparsemap (Rust) 3.0.0" --note-file rust/CHANGELOG.md \
    --asset "$ART"

# GitHub mirror
gh release create rust-v3.0.0 "$ART" \
    --title "sparsemap (Rust) 3.0.0" --notes-file rust/CHANGELOG.md
```

The C library's `v3.0.0` release is created the same way against the
`v3.0.0` tag; its source tarball is the repository at that tag (the
library is just `sm.h` + `sm.c`).

## Version bumps

Keep the C and Rust versions aligned.  Update, in lock-step:

- `meson.build` `version` and `sm.h` `SM_VERSION_*` (C);
- `rust/Cargo.toml` `version` and `rust/CHANGELOG.md` (Rust).

`scripts/check_version_consistency.sh` guards the C side in CI.