Files
punktfunk/crates/lumen-core/src/fec/gf8.rs
T
enricobuehler a988909196 feat: M1 lumen-core (FEC/crypto/packet/session + C ABI) and workspace scaffold
Ground-up low-latency streaming stack per docs/implementation-plan.md. M1 is
complete and tested; Linux host backends are cfg-gated stubs to be filled in on
real hardware (M0/M2).

lumen-core (built + tested on macOS/aarch64 — 21 tests):
- fec: ErasureCoder over GF(2^8) (reed-solomon-erasure, Moonlight-compatible)
  and GF(2^16) Leopard-RS (reed-solomon-simd, the >1 Gbps wall-breaker); proptested
- packet: zero-copy #[repr(C)] framing, multi-block, FEC-aware reassembly
- crypto: AES-128-GCM with per-direction nonce salts + sequence-as-AAD
- session: host submit / client poll hot paths + input; loopback & UDP transports
- abi: opaque handles, versioned LumenConfig, panic guards; cbindgen-generated header
- acceptance: Rust loopback+proptest and a C harness that links the staticlib

Scaffold (compiles green on all platforms): lumen-host (vdisplay/capture/encode/
inject/web/pipeline seams under cfg(linux)), lumen-client-rs, tools/{loss-harness,
latency-probe}, Apple/Android client stubs, Gitea CI, docs.

Hardened against a multi-agent adversarial review (13 verified findings fixed,
regression-tested): reassembler memory-DoS bounds + block-consistency validation,
GCM nonce-reuse direction separation, ABI struct_size guard + range checks, FEC
shard-length guards, shard_payload datagram bound, key zeroization + Debug redaction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 00:02:52 +02:00

72 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! GF(2⁸) classic ReedSolomon backend (`reed-solomon-erasure`), equivalent to the
//! `nanors` library Moonlight uses. Hard ceiling: data + recovery ≤ 255 shards/block.
use super::{validate_block_shape, validate_encode_shape, ErasureCoder, FecError};
use crate::config::FecScheme;
use reed_solomon_erasure::galois_8::ReedSolomon;
pub struct Gf8Coder;
impl ErasureCoder for Gf8Coder {
fn scheme(&self) -> FecScheme {
FecScheme::Gf8
}
fn encode(&self, data: &[Vec<u8>], recovery_count: usize) -> Result<Vec<Vec<u8>>, FecError> {
if recovery_count == 0 {
return Ok(Vec::new());
}
validate_encode_shape(data)?;
let k = data.len();
let shard_len = data[0].len();
let rs = ReedSolomon::new(k, recovery_count)
.map_err(|_| FecError::Config("invalid GF(2^8) shard counts"))?;
// reed-solomon-erasure fills parity in place: shards = data || zeroed parity.
let mut shards: Vec<Vec<u8>> = Vec::with_capacity(k + recovery_count);
shards.extend_from_slice(data);
shards.resize_with(k + recovery_count, || vec![0u8; shard_len]);
rs.encode(&mut shards)
.map_err(|_| FecError::Backend("gf8 encode"))?;
Ok(shards.split_off(k))
}
fn reconstruct(
&self,
data_count: usize,
recovery_count: usize,
received: &mut [Option<Vec<u8>>],
) -> Result<Vec<Vec<u8>>, FecError> {
validate_block_shape(received, data_count, recovery_count)?;
let present = received.iter().filter(|s| s.is_some()).count();
if present < data_count {
return Err(FecError::TooFewShards {
have: present,
need: data_count,
});
}
if recovery_count == 0 {
// No FEC: every original must already be present.
return collect_originals(received, data_count);
}
let rs = ReedSolomon::new(data_count, recovery_count)
.map_err(|_| FecError::Config("invalid GF(2^8) shard counts"))?;
rs.reconstruct_data(received)
.map_err(|_| FecError::Backend("gf8 reconstruct"))?;
collect_originals(received, data_count)
}
}
fn collect_originals(
received: &[Option<Vec<u8>>],
data_count: usize,
) -> Result<Vec<Vec<u8>>, FecError> {
let mut out = Vec::with_capacity(data_count);
for slot in received.iter().take(data_count) {
out.push(
slot.clone()
.ok_or(FecError::Backend("reconstruction left an original missing"))?,
);
}
Ok(out)
}