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>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
//! GF(2⁸) classic Reed–Solomon 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)
|
||||
}
|
||||
Reference in New Issue
Block a user