Ctrl K
ComunidadGuías…

Cargo.toml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[workspace]
members = [
    "crates/nzbkit-base",
    "crates/nzbkit",
    "crates/nzbfast-core",
    "crates/nzbfast-unpack",
    "crates/nzbfast-meta",
    "crates/nzbfast-engine",
    "crates/nzbfast-daemon",
    "crates/nzbfast-tasks",
    "crates/nzbfast-api",
    "crates/nzbfast",
    "crates/nzbfast-ffi",
    "crates/nzbtray",
    "crates/postfast",
    "crates/parfast",
    "vendor/rars",
    "vendor/tiny_http",
    "vendor/lzma-rust2",
]
# nzbfast-ffi is deliberately NOT a default member. Two reasons, and the
# FIRST of them ended on 2 Sep 2026 with the crate-split step 4.5 - read
# the history below before citing it.
#
# What is left: it is a `staticlib`, so a workspace-wide BUILD pays a
# whole-archive link for a target only the iOS app consumes, and it turns
# on nzbfast's `ffi` feature, which unifies features (tokio, rusqlite)
# wider than any shipped build resolves. Build it explicitly:
# `cargo build -p nzbfast-ffi --target aarch64-apple-ios-sim`.
#
# WHAT ENDED: the DUPLICATE COMPILE. Until step 4.5, `feature = "ffi"`
# had exactly one site - `crates/nzbfast/src/lib.rs`'s crate-root
# `#![cfg]` - so turning it on gave the package a SECOND root over the
# same module files, and a workspace-wide run compiled the whole tree
# twice and ran ~1,774 tests that were a strict subset of the bin root's.
# Measured cold on an 18-core box on 23 Aug 2026 with `--workspace` as
# the only variable: archive build 50.9 s -> 64.7 s wall, 381 s -> 455 s
# CPU, cached target/ 3.30 GB -> 5.53 GB, archive artifact 159 MB ->
# 244 MB, test set 4,701 -> 6,464. lib.rs is now the package's ONE root
# and carries the tree in every configuration, so none of that doubling
# exists any more; `ffi` gates three `embedded_*` functions.
#
# `--workspace` still OVERRIDES this line, so a command that passes it
# gets none of the protection above - and the flag has been added by
# accident before (ci-private's `cargo nextest archive` carried it from
# the day it was written until 23 Aug 2026, while this comment said it
# did not). Before adding `--workspace` to anything that BUILDS (as
# opposed to `cargo metadata`, `cargo fmt` or another command that emits
# no artifacts), price it - and prefer naming the package, which is how
# the two clippy gates reach nzbfast-ffi today without dragging the ffi
# feature through a link step.
default-members = [
    "crates/nzbkit-base",
    "crates/nzbkit",
    "crates/nzbfast-core",
    "crates/nzbfast-unpack",
    "crates/nzbfast-meta",
    "crates/nzbfast-engine",
    "crates/nzbfast-daemon",
    "crates/nzbfast-tasks",
    "crates/nzbfast-api",
    "crates/nzbfast",
    "crates/nzbtray",
    "crates/postfast",
    "crates/parfast",
    "vendor/rars",
    "vendor/tiny_http",
    "vendor/lzma-rust2",
]
resolver = "2"

[workspace.package]
edition = "2024"
license = "GPL-3.0-or-later"
repository = "https://github.com/nzbfast/nzbfast"

# Workspace-wide lints; each first-party crate opts in with
# `[lints] workspace = true`. vendor/ crates keep their own tables, and
# nothing here reaches them. All four first-party crates opt in since
# 26 Aug 2026 - nzbtray was the last holdout, and it is nearly all Win32
# `unsafe`, so it was the crate that most wanted the two below.
[workspace.lints.rust]
clashing_extern_declarations = "deny"
# The unsafe-policy ratchet, on since 26 Aug 2026 (TODO 307 item 3).
#
# WHAT `deny` BUYS OVER THE DEFAULT, since the honest answer is "less
# than it looks and still worth having": in edition 2024 this lint is
# already warn-by-default (measured, not assumed - a probe crate at
# edition 2024 with a bare deref in an `unsafe fn` warns with nothing
# configured), and CI's clippy gate runs `-D warnings`, so the tree was
# already held to it by that ONE job. Denying it here moves the refusal
# into every ordinary `cargo build` / `cargo check` / `cargo test`, so a
# lane sees it at the moment it writes the code rather than at the gate
# - and it survives an edition that ever relaxes the default.
unsafe_op_in_unsafe_fn = "deny"
# `--cfg fuzzing` is set by cargo-fuzz for every crate in its build, and
# par2repair reads it to lower the verify pool's byte gate so
# par2_verify_diff can reach the parallel branch. Declared here or the
# ordinary build warns it is unexpected, and CI's clippy runs -D warnings.
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }

# Burned down 1 Aug 2026 from 32 categories to these. CI runs clippy with
# -D warnings and no -A escapes at all, so anything not listed here blocks
# - including dead_code and unused_assignments, which used to be waived on
# the command line. What is left splits in two: everything above the
# divider is still debt (fix one, delete its line); everything below it
# is house style, is expected to stay, and says why.
[workspace.lints.clippy]
# The other half of the 26 Aug 2026 unsafe ratchet, and the half that
# actually found something: every `unsafe` block, `unsafe impl` and
# `unsafe fn` body must carry a `// SAFETY:` line on the line above
# saying WHY it is sound. 53 first-party sites had no such line when
# this went on - 49 the host clippy gate reaches, plus four that ONLY
# `--target x86_64-pc-windows-gnu` can see (three in nzbkit's logtee
# log-capper, one in the GetDiskFreeSpaceExW arm of diskfree). Every one
# was documented rather than rewritten: the deliverable was the argument
# and the ratchet, not a refactor of working platform code.
#
# ONE CLIPPY RUN SHOWS ONE LAYER, which is the same trap CLAUDE.md's
# SIXTEENTH gate entry writes up for the Windows recipe and it applies
# to the host run just as hard: a lib that fails to compile is never
# followed by the bin and test targets that depend on it, so four
# errors in nzbkit's lib hid 38 more behind them. Turning either of
# these on somewhere new means ITERATING to exit 0, not fixing the
# batch the first run named.
#
# A `// SAFETY:` note states the INVARIANT that makes the operation
# sound - which pointer is live, which contract the caller already owes,
# why the bit pattern is valid. "This is safe" is not one. Where the
# obligation is the caller's, say so and point at the `# Safety` clause
# that puts it there.
undocumented_unsafe_blocks = "deny"
too_many_arguments = "allow"
type_complexity = "allow"
large_enum_variant = "allow"
ptr_arg = "allow"
assertions_on_constants = "allow"
unnecessary_unwrap = "allow"

# --- below this line: kept on purpose, not debt ---
# The two doc lints below are a house-style clash, not debt. Copy rules ban
# em-dashes, so an aside is written as a spaced hyphen, and reflowing
# puts one at the start of a line - which markdown then reads as a
# bullet, making the rest of the paragraph a
# "lazy continuation". Clippy's fix indents the paragraph under the
# phantom bullet, which entrenches the misparse instead of fixing it.
# Neither crate publishes rustdoc, so the rendering is not worth
# reflowing 126 comments over.
doc_lazy_continuation = "allow"
doc_overindented_list_items = "allow"
# Also style, not debt: the 15 remaining sites are numeric kernels and
# fixtures where the index IS the value - gf16's log/exp tables, RAR5's
# `<< (7 * i)` vint, the PAR2 matrix product, `format!("big.7z.{i+1}")`.
# enumerate()/zip() reads worse in every one.
needless_range_loop = "allow"

[profile.release]
lto = "thin"
codegen-units = 1
# Drop symbol tables: smaller binaries, and removes build-host-varying
# symbol data - part of making release builds attestable/reproducible.
strip = "symbols"

# ---------------------------------------------------------------------
# The vendored RAR engine is a FIXTURE BUILDER for most of this repo's
# tests, and at opt-level 0 it was the single largest cost in CI.
#
# ~32 sites across four test files (extract/{chase_shape,chase,nested,
# sfx}_tests.rs) build REAL compressed RAR volumes with
# `Rar50VolumeWriter` - an actual LZ bitstream, not a header shell -
# because a chase test that fed a hand-crafted fixture would prove
# nothing about the decoder. That compression is rars code, and an
# unoptimized LZ inner loop is 7-10x off its own release speed.
#
# What that cost, measured 23 Aug 2026 on run 32653896109:
#
#   nzbkit extract::chase::chase_shape_tests::
#     a_chase_survives_trimming_the_volume_its_deferred_walk_resumes_in
#       272.2s on windows-unit shard 4 (of that shard's 451.7s)
#       177.4s on linux-tests  shard 3 (of that shard's 309.5s)
#
# It is ONE test, and locally it was the entire nzbkit lib suite's wall
# clock: 85.575s for the suite, 85.509s of it that test, with the other
# 1,768 tests finishing inside it. Split with a standalone probe, 76.4s
# of its 109.5s was `Rar50VolumeWriter` compressing the 37 MB fixture -
# no nzbfast code involved, and nothing the test is there to prove.
#
# The fixture cannot simply be made smaller: the test needs three
# volumes EACH over `HOLDS_CAP_FLOOR` (8 MiB) or the finish volume never
# trims and the regression it pins (TODO 220/250, the deferred header
# walk re-reading a trimmed prefix) cannot occur at all. The size is the
# field condition, so the answer is to make the engine fast, not the
# fixture weak.
#
# Measured with this override, same box, same tests:
#
#   fixture build (37 MB -> 3 volumes)   76.4s -> 10.2s   7.5x
#   the test above                      109.6s -> 10.8s  10.1x
#   whole nzbkit lib suite (1769 tests)  85.6s -> 47.0s   -45%
#
# All 1,769 pass either way, identical volume sizes out of the writer.
# The suite's new floor is `pool::fault_rigs`, which paces real sleeps
# and is not a codegen cost - correctly untouched by this.
#
# On CI, rars-attributable work was 34% / 37% / 42% / 59% of measured
# test time on the four windows-unit shards, so this is the lever on the
# slowest shard rather than a saving spread thinly.
#
# THE COST, measured: rars recompiles in 5.2s instead of 1.8s, once per
# build job - and it recompiles on every run regardless, because git
# checkout stamps its sources newer than any cached artifact. 3.4
# seconds on the build leg to take minutes off the test legs.
#
# CONFIRMED ON CI, 23 Aug 2026, after this landed: windows-unit's
# slowest shard went 486s to ~316s (mean of 2 runs), and the build step
# was unchanged within noise (817s to ~827s). So the prediction above
# held on the leg it was about. Note the whole of that win is THIS
# change: the nzbkit test-target merge that landed right after it moved
# the same number not at all, and its own header carries that
# correction.
#
# `debug-assertions` and `overflow-checks` are NOT touched and stay ON -
# verified with `cargo build -v`, which shows `-C opt-level=2` and
# `-C debug-assertions=on` together. This changes codegen only, so every
# runtime check the suite relies on (and tools/overflow-gate.py's whole
# subject) still fires. Optimizing is not the same as trusting.
#
# NOT extended to `package."*"` (all dependencies), which was tried and
# is NOT justified here: the other slow CI tests (index::evict,
# index::segmig, par2ntt) are 2-4s locally against 19-30s on a Windows
# runner, so they are bound by that runner's cores and filesystem, not
# by unoptimized dependency code - optimizing every dependency measured
# no faster on them and would buy a large cold-build cost for nothing.
#
# `dev` covers rars as a DEPENDENCY of nzbkit's tests; `test` covers
# rars's own test target. Both, or half the win is missing.
[profile.dev.package.rars]
opt-level = 2

# THREE MORE FIXTURE BUILDERS, same argument as rars above and measured
# the same way (research/TEST-SWEEP-SLOW-TESTS-2026-09-01.md, class C):
# `aes` and `cbc` encrypt the two 24 MB RAR-crypt fixtures at opt-level 0
# in the sweep, and `rusqlite` runs every index test's SQLite through an
# unoptimised binding. None of the three is in any timing assertion - the
# pool fault rigs measure nzbkit's OWN throughput, and a blanket
# `package."*"` override was tried and rejected for exactly that reason
# (see the rars note above). Anything wider than these three is a
# decision about the rigs, not about fixtures.
[profile.dev.package.aes]
opt-level = 2
[profile.dev.package.cbc]
opt-level = 2
[profile.dev.package.rusqlite]
opt-level = 2
[profile.dev.package.libsqlite3-sys]
opt-level = 2

# nzbkit-base ITSELF, and unlike the five above this one is not a fixture
# builder - it is the crate under test. Measured 20 Sep 2026, claim
# `profile-dev-package-opt-level-ci-keys-20sep`, which took the third
# bullet of section 3 of the 18 Sep 2026 forney/par2ntt slow-class
# writeup (claim `nzbkit-base-forney-slowclass-18sep-r2`) - that one
# named this "the one lever that would move every row at once" and left
# it as a cross-cutting item because of the cache-key trap the paragraph
# further down answers. Both writeups are private; grep either claim id.
#
# THE SUBJECT. That handoff measured seven
# `par2repair::forney::joint` / `par2ntt` tests as ~336-395 s of
# `unit-one-process`'s 555 s nzbkit-base row, the largest single row of
# the job on the slow runner class, and proved that NOT ONE of them
# shrinks without spending the property it proves: the stripe widths are
# already minimal on both the admitted and the refused arm, four of the
# `m` values are pinned to `JOINT_FACTOR_MIN_M` on purpose so a
# recalibration reds there, and `prepare_impl` is ~1.3 s of the ~53 s so
# the shared-fixture lever does not apply either. So the TESTS are not
# the cost. The CODEGEN is: `gf16`, `par2ntt` and `par2repair` are
# GF(2^16) and NTT bit-arithmetic, the shape unoptimised rustc punishes
# hardest, and all three live IN this crate - which is why not one of
# the five dependency overrides above can reach them.
#
# MEASURED, dev Mac, `--features test-support`, ONE process, serial, and
# the same-load control arm run from the same target dir rather than
# quoted from another box. Load was 169-328 load1 against 186-257
# load15, so WALL IS NOT THE RESULT HERE and CPU is, per CLAUDE.md's
# rising/drained rule and the handoff's own "price them in CPU" - the
# forney solves are internally rayon-parallel (`per_stripe` takes
# `cpu_workers()`), so a loaded box loses the intra-test parallelism as
# well as the core speed:
#
#   the seven tests, user CPU:   142.93 s -> 3.41 s    (42x)
#   whole crate suite, user CPU: 286.86 s -> 34.93 s   (8.2x)
#   whole crate suite, wall:    1213.35 s -> 310.46 s  (3.9x)
#
# 1,656 passed / 0 failed / 8 ignored on BOTH arms. The opt-0 control's
# 286.86 s agrees within 5% with the 273.10 s the handoff measured on a
# quieter 18-core box, which is the cross-check that the CPU figure is
# the robust one; and the opt-2 arm was taken at HIGHER load (load1 328
# against 209), so the ratio above UNDERSTATES the win.
#
# opt-level 1 WAS MEASURED AND IS NOT ENOUGH, which is the reason this
# says 2 rather than inheriting the rars precedent: the seven cost
# 31.54 s of CPU at 1 against 3.41 s at 2, so 1 leaves 9x on the table
# to save 35 s of one-time build CPU (116 s against 151 s for the test
# harness) - and the archive is built ONCE and then run by
# `unit-one-process`, every `linux-tests` shard and `windows-unit`.
#
# WHY THIS IS SAFE ON A CRATE WHOSE OWN THROUGHPUT ITS RIGS MEASURE,
# which is the question the rars note above does not have to ask. Six
# assertions in the crate read a clock, and their DIRECTIONS are what
# matters: four are upper bounds (`< 60 s` in par2gen/digest_cache_tests,
# `< 2 s` in live/tests, `< 100 ms` in disk/tests) which faster code
# makes strictly safer, and the two lower bounds
# (`>= stall`, `>= budget` in nntp/unit_tests) are sleep-paced, so
# codegen cannot shorten the wait they assert. Nothing in
# `par2repair/`, `par2ntt*` or `gf16*` asserts a clock at all. The
# 1,656-green run above is the empirical half of that.
#
# BOTH PROFILES, for the reason the rars note gives ("`dev` covers it as
# a DEPENDENCY, `test` covers its own test target") and one more that is
# specific to a workspace member this big: if `dev` and `test` disagree
# on opt-level then nzbkit-base is built TWICE per target dir - once at
# 0 as a dependency of every other crate's tests, once at 2 as its own
# test harness - which is a second full artifact set of the largest
# crate in the tree, exactly the cost CLAUDE.md's "Build artifacts and
# disk" section is about. Setting both keeps it at one. Session env
# (`CARGO_PROFILE_DEV_DEBUG=1` in .claude/settings.json) does not add a
# split either: it maps to `profile.dev.debug`, `test` inherits it, so
# the two profiles still agree on every fingerprint input.
#
# WHY THIS NEEDED NO CI CACHE-KEY BUMP. State this precisely, because
# the general rule is real, it is the reason this was a separate item,
# and this is a genuine exception rather than a lapse. The rule (TODO
# 116 and the `-v2` note in ci-private.yml): a profile change moves
# rustc fingerprints, the target-cache keys hash the NORMALISED
# `Cargo.lock` and never `Cargo.toml`, and actions/cache saves only on
# an exact-key miss - so a GLOBAL profile flag landed without bumping
# every key makes every run rebuild cold forever behind a green
# cache-hit line. That trap needs a CACHED artifact's fingerprint to
# move. A `package.<one workspace member>` override does not move one:
# verified on this tree by flipping each half and counting what cargo
# rebuilds, which is `Compiling nzbkit-base` and NOTHING else - zero
# third-party units - on the dev rlib and on the test harness alike.
# ci-private.yml's own cache note states the other half: "the workspace
# crates' own artifacts are stale in it by construction and always were;
# they are rebuilt every push regardless". So every key still hits,
# every dependency still arrives warm, and the single unit that
# recompiles is one CI recompiles on every push anyway. A needless bump
# is NOT the safe direction here: the repo is at 11.85 GB against
# GitHub's 10 GB per-repo limit with LRU eviction active (same note), so
# nine cold multi-GB archives would evict the nightly caches to buy
# nothing. The census, for whoever DOES land a global flag: 10 key
# families over 34 target-bearing cache steps in 6 workflow files
# (ci-private 5, nightly 17 across five families, pr-check 4, fuzz 4,
# coverage 2, journeys 2) - the "nine caches" the older notes quote is
# stale, and fuzz-cargo-v2 caches only the DETACHED fuzz workspace's own
# target, which a root-profile change cannot reach at all.
#
# WHAT IT COSTS, MEASURED AS A DELTA - and it is 5.5x, not the "couple
# of minutes" the first version of this note implied. That version quoted
# bare wall figures for one opt-2 build with no opt-0 comparator and no
# load stated, which is an absolute masquerading as a cost and is the
# thing CLAUDE.md's rising/drained section refuses. Re-measured properly:
# an EDIT-TRIGGERED harness rebuild (touch lib.rs, same target dir, the
# two arms back to back), load1 186-242 against load15 229-248:
#
#   opt-0:  153.58 s wall,  27.44 s user CPU
#   opt-2:  205.59 s wall, 149.70 s user CPU
#   delta:  +52 s wall (+34%), +122.3 s CPU (5.5x)
#
# The wall column flatters this change rather than the other way round:
# at 27.44 s of CPU under 153.58 s of wall the opt-0 rebuild is almost
# entirely WAITING on an oversubscribed box, so the +34% is largely a
# queueing artifact. On an idle box expect the gap to be wider than
# +34%, because rustc's parallelism inside ONE crate is bounded by its
# codegen units - magnitude NOT measured here, so do not quote one.
#
# SO WHO PAYS. One run of this crate's suite saves 251.9 s of CPU
# (286.86 -> 34.93), so an edit followed by the SUITE repays the rebuild
# about twice over; an edit followed by ONE narrow non-par2 test is a
# straight loss of ~122 s of CPU. A lane doing tight local iteration on
# nzbkit-base is therefore worse off, and that is a real cost this entry
# accepts rather than hides.
#
# WHERE THE WIN ACTUALLY LANDS, measured 21 Sep 2026 because the first
# version of this entry implied it was everywhere and that is WRONG. The
# win is large where test execution is SERIAL and small where it is
# PARALLEL, and the reason is arithmetic rather than anything subtle:
# this removes ~252 s of CPU, and a parallel runner spreads that over the
# box's cores, so on 32 cores it is ~8 s of wall.
#
#   SERIAL, and this is the case worth having:
#     `cargo test -p nzbkit-base --lib --features test-support`
#     1213.35 s -> 310.46 s of wall, 3.9x. One of CLAUDE.md's twelve
#     one-process lines, and `unit-one-process` is the CI job of the same
#     shape - its ~555 s nzbkit-base row is what GAP 4 was about.
#
#   PARALLEL, and this is the case NOT to promise anyone: the whole
#     workspace nextest sweep, BOTH ARMS PREBUILT, box drained to load1
#     22-58 - 133.38 s at opt-0 against 117.66 s at opt-2, so 11.8%, and
#     that is an UPPER BOUND because the opt-0 arm ran at load1 58 and
#     RISING against 22 drained. The same applies to CI's `linux-tests`
#     and `windows-unit` shards, which are parallel too.
#
# The longest pole DID move and it still does not buy wall: in the opt-0
# arm five of the eight slowest tests in the whole sweep were
# nzbkit-base, topping at 162.1 s, and in the opt-2 arm NONE of the
# slowest eight were, the top being postfast at 164.6 s. A pole that
# moves while the wall does not is the signature of a throughput-bound
# run, and it is why "the slowest test got 30x faster" is not a wall
# claim.
#
# METHOD WARNING for whoever re-measures this, because the obvious
# experiment is wrong: do NOT compare outer wall across a profile flip.
# The flip invalidates this crate, so the arm you flip INTO pays a
# rebuild the other does not - measured here at 366.6 s of build against
# 38.9 s, which swamped the test difference and read as a 1.63x win that
# does not exist. Prebuild both arms (`--no-run`), time only the run, and
# state the load of each arm.
#
# CI IS STILL WHY THIS SHIPS, on the serial row rather than the sharded
# ones: the nextest archive is built ONCE and then run by
# `unit-one-process`, every `linux-tests` shard and `windows-unit`, so
# +122 s of CPU once is paid once. Setting BOTH profiles is also what keeps
# that number at 122 s: with `dev` left at 0 and only `test` at 2 the
# crate compiles TWICE per target dir (27 s + 150 s) instead of once, so
# the split everyone reaches for first is DEARER for the archive than
# this is, as well as costing a second artifact set.
#
# THERE IS NO PER-INVOCATION ESCAPE, checked rather than assumed: cargo's
# config-env mapping is `CARGO_PROFILE_<profile>_<key>` and cannot express
# a package override, so
# `CARGO_PROFILE_TEST_PACKAGE_nzbkit-base_OPT_LEVEL=0` is silently
# IGNORED - verified by fingerprint, the build staying on the opt-2 hash
# and finishing as a 1.47 s no-op. Opting out locally means editing this
# tracked file, which shows up in `git status` in a shared checkout.
# If the local cost bites, the documented retreat is opt-level 1 further
# up, and the condition to revisit this entry is a lane iterating hard on
# nzbkit-base rather than a number in this comment.
#
# The rars note above can call its own cost free only because git
# checkout restamps rars anyway; this crate is under active development
# and has no such excuse. `debug-assertions` and
# `overflow-checks` are untouched and stay ON, so every runtime check
# the suite relies on (and tools/overflow-gate.py's whole subject) still
# fires; optimising is not the same as trusting. The debuggability cost
# is real: frames in this crate now inline, and `frame variable` already
# needed `CARGO_PROFILE_DEV_DEBUG=2` per CLAUDE.md.
#
# WHAT THIS ALREADY COVERS, because the first version of this comment got
# it backwards and a reader acting on that would add a redundant entry:
# `nzbkit`'s own tests DO get the optimised nzbkit-base, by the `dev` half
# above and for exactly the reason the rars note gives - `dev` covers a
# crate as a DEPENDENCY of nzbkit's tests, `test` covers its own test
# target. Every crate above nzbkit-base inherits this the same way, so
# parfast and postfast need nothing either.
#
# What no entry here touches is nzbkit's OWN code, and the prior for
# wanting it is weak rather than unmeasured: the facade is orchestration -
# extraction, the connection pool, media probing, the indexer - and the
# heavy codecs beneath it are vendored crates with their own entries
# (rars above; lzma-rust2 and sevenz-rust2 are separate packages). There
# is no GF(2^16) or NTT arithmetic in it, which is the shape that made
# this entry worth 42x. Reopen it only on evidence of the same kind:
# a slow row in nzbkit's own suite that profiles into nzbkit's own
# functions rather than into nzbkit-base or a vendored codec. GAP 4's
# ranking named nzbkit-base as the largest row and nothing has named
# nzbkit.
[profile.dev.package.nzbkit-base]
opt-level = 2

[profile.test.package.rars]
opt-level = 2

# nzbkit-base's own test harness. The measurement, the opt-1 comparison
# and the cache-key reasoning are all on the `profile.dev` entry above;
# BOTH are needed or the crate builds twice per target dir.
[profile.test.package.nzbkit-base]
opt-level = 2

# sevenz-rust2 0.22.2 vendored from crates.io (Apache-2.0) for TWO changes
# now. The first: its AES decoder read its input 512 bytes at a time
# through a stack buffer and copied every plaintext byte through an
# intermediate,
# which held an encrypted 1 GiB Copy-method member at 0.47 s of user
# CPU against 7-Zip's 0.26 on an M1 Ultra
# (research/RAR-PERF-AUDIT-2026-09-02.md, round 10). The second: a
# caller-supplied entropy source (src/write_entropy.rs), because
# AesEncoderOptions drew its salt and IV from the OS, so no encrypted 7z
# the posting-layout generator wrote could be reproduced from a seed -
# the same blocker rars::Entropy answered for the RAR writers. Os is
# still the default in both crates and must stay that way. See
# vendor/sevenz-rust2/README-nzbfast.md; re-apply both on the next bump.
# lzma-rust2 0.20.0 vendored (Apache-2.0). It started as ONE change - its
# multi-threaded LZMA2 reader only spawned a worker once every existing
# worker had marked itself active, which the dispatcher outran, so a
# 16-block stream decoded on one or two threads (8.1 s vs 2.7 s fixed on
# an 8-vCPU EPYC) - and now carries four, including a bit-tree fastpath
# in the decoder (-2.4% single-thread on x86) and the differential test
# that fastpath is carried by. vendor/lzma-rust2/README-nzbfast.md is the
# whole list, with the measurement for each; re-apply them on a bump.
[patch.crates-io]
sevenz-rust2 = { path = "vendor/sevenz-rust2" }
lzma-rust2 = { path = "vendor/lzma-rust2" }