Blockers 4 & 5: browser getUserMedia → JPEG IPC → Rust I420 pipeline; remote video strip renders decoded frames via canvas; EncryptingTransport wraps QuinnTransport so WZP AEAD is applied to all media (C2 fix). Test fixes: HandshakeResult.session destructuring across relay/client/crypto integration tests; video_codecs field added to all CallOffer/CallAnswer structs; wzp-video pipeline_roundtrip integration tests added. PRD docs: five Kimi-ready specs for E2E encryption, Android NDK 0.9 migration, quality upgrade flow, wire-format hardening, and clippy debt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.8 KiB
PRD: Fix wzp-codec Clippy Lint Debt
Status: proposed Resolves: 9 pre-existing clippy lints in
crates/wzp-codec/src/that causecargo clippy --workspace -D warningsto fail, breaking any strict-CI configuration. Depends on: Nothing — all changes are incrates/wzp-codec/src/.
Problem
cargo clippy -p wzp-codec -- -D warnings fails with 9 lints across 5 files.
These are pre-existing code patterns that were never flagged during development
because the CI flag was not set. They have no runtime impact today but prevent
adding -D warnings to CI without first cleaning them up.
The 3 errors in deps/featherchat are in a submodule — do NOT touch them.
warzone_protocol clippy errors are accepted debt (not our code).
Goals
cargo clippy -p wzp-codec -- -D warningsexits 0.- No behavior changes — every fix is a semantically equivalent rewrite.
- No changes outside
crates/wzp-codec/src/.
Non-goals
- Fixing clippy lints in any crate other than
wzp-codec. - Adding new functionality.
- Touching the
deps/featherchatsubmodule.
Design
Lint inventory
| Lint | Count | File | Approx line | Fix |
|---|---|---|---|---|
implicit_saturating_sub |
1 | aec.rs |
117–119 | saturating_sub |
needless_range_loop |
2 | aec.rs:164, resample.rs:51 |
— | iterate with iter().enumerate() or direct iter |
manual_div_ceil |
2 | codec2_dec.rs:48, codec2_enc.rs:48 |
— | div_ceil |
manual_clamp |
2 | denoise.rs:59, opus_enc.rs:250 |
— | .clamp(min, max) |
manual_ascii_check |
1 | opus_enc.rs:104 |
— | .eq_ignore_ascii_case() |
same_item_push |
1 | resample.rs:184 |
— | vec.resize or extend(repeat) |
Fix details
1. implicit_saturating_sub — aec.rs line ~117
Current code:
fn delay_available(&self) -> usize {
let buffered = self.delay_write - self.delay_read;
if buffered > self.delay_samples {
buffered - self.delay_samples
} else {
0
}
}
Clippy wants saturating_sub because the subtraction can underflow if
buffered < self.delay_samples:
fn delay_available(&self) -> usize {
let buffered = self.delay_write - self.delay_read;
buffered.saturating_sub(self.delay_samples)
}
This is semantically identical (both return 0 when buffered <= delay_samples).
2a. needless_range_loop — aec.rs line ~164
Current code:
for i in 0..n {
let near_f = nearend[i] as f32;
let base = (self.far_pos + fl * ((n / fl) + 2) + i - n) % fl;
...
}
i is used both to index nearend[i] and in arithmetic (+ i - n).
Clippy fires because nearend[i] could use .iter().enumerate().
Convert to enumerate:
for (i, &sample) in nearend.iter().enumerate() {
let near_f = sample as f32;
let base = (self.far_pos + fl * ((n / fl) + 2) + i - n) % fl;
...
}
Make sure to update any references to nearend[i] inside the loop body
to use sample (or near_f directly). Also update the NLMS adaptation
sub-loop if it references nearend[i].
2b. needless_range_loop — resample.rs line ~51
Current code:
for i in 0..FIR_TAPS {
let n = i as f64 - m / 2.0;
let sinc = ...;
let t = 2.0 * i as f64 / m - 1.0;
let kaiser = ...;
kernel[i] = sinc * kaiser;
}
i is used both as an index (kernel[i]) and in arithmetic. Use
iter_mut().enumerate():
for (i, slot) in kernel.iter_mut().enumerate() {
let n = i as f64 - m / 2.0;
let sinc = ...;
let t = 2.0 * i as f64 / m - 1.0;
let kaiser = ...;
*slot = sinc * kaiser;
}
3a. manual_div_ceil — codec2_dec.rs line ~48
Current code:
fn bytes_per_frame(&self) -> usize {
(self.inner.bits_per_frame() + 7) / 8
}
Replace with:
fn bytes_per_frame(&self) -> usize {
self.inner.bits_per_frame().div_ceil(8)
}
div_ceil is stable as of Rust 1.73. The builder uses a recent enough
toolchain. If bits_per_frame() returns usize, the method is available.
If it returns a different integer type, cast accordingly.
3b. manual_div_ceil — codec2_enc.rs line ~48
Same pattern, same fix:
fn bytes_per_frame(&self) -> usize {
self.inner.bits_per_frame().div_ceil(8)
}
4a. manual_clamp — denoise.rs line ~59
Current code:
let clamped = val.max(-32768.0).min(32767.0);
Replace with:
let clamped = val.clamp(-32768.0_f32, 32767.0_f32);
Note: .clamp() on f32 requires both bounds to be the same type. If val
is already f32, no extra cast is needed. Verify the type of val in
context (it is f32 per the output array type [f32; 480]).
4b. manual_clamp — opus_enc.rs line ~252
Read the surrounding code for the exact pattern. It will be something like:
let v = if x < min_val { min_val } else if x > max_val { max_val } else { x };
or the .max().min() chain. Replace with x.clamp(min_val, max_val).
5. manual_ascii_check — opus_enc.rs line ~104
Current code:
Ok(v) => !v.is_empty() && v != "0" && v.to_ascii_lowercase() != "false",
Clippy wants .eq_ignore_ascii_case() instead of lowercasing the whole string:
Ok(v) => !v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("false"),
6. same_item_push — resample.rs line ~183
Current code:
for _ in 1..RATIO {
work.push(0.0);
}
This pushes the same 0.0 value (RATIO - 1) times. Replace with:
work.resize(work.len() + (RATIO - 1), 0.0f64);
Or equivalently:
work.extend(std::iter::repeat(0.0f64).take(RATIO - 1));
Note: RATIO is a const usize. Verify work is Vec<f64> in context
(it is — work.push(s as f64) immediately before).
Implementation steps
- Read each file at the line numbers listed above to confirm the exact current code before editing (line numbers may shift slightly due to prior edits).
- Apply all 9 fixes. They are independent — no ordering requirement.
- Run
cargo clippy -p wzp-codec -- -D warningslocally or via the CI command. - If any lint persists, re-read that file section and adjust.
Files to read before implementing
crates/wzp-codec/src/aec.rslines 114–200crates/wzp-codec/src/resample.rslines 45–70 and 178–190crates/wzp-codec/src/codec2_dec.rslines 40–55crates/wzp-codec/src/codec2_enc.rslines 40–55crates/wzp-codec/src/denoise.rslines 45–65crates/wzp-codec/src/opus_enc.rslines 96–110 and 244–260
Verify
cargo clippy -p wzp-codec -- -D warnings
Expected: exits 0 with no warnings.
Also run to confirm no regressions:
cargo test -p wzp-codec
Done when
cargo clippy -p wzp-codec -- -D warnings exits 0. All 9 lints are gone.
cargo test -p wzp-codec passes. No changes outside crates/wzp-codec/src/.