Skip to content

Commit ec79d58

Browse files
committed
Refactor chunk_codec
1 parent 57ffa79 commit ec79d58

11 files changed

Lines changed: 298 additions & 137 deletions

File tree

.github/workflows/rust.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
jobs:
1010
clippy:
11-
name: Format & Clippy
11+
name: Cargo Format & Clippy & Test
1212
runs-on: ubuntu-latest
1313
steps:
1414
- name: Checkout
@@ -31,6 +31,7 @@ jobs:
3131
pushd $t
3232
cargo fmt --check
3333
cargo clippy -- -D warnings
34+
cargo test --no-fail-fast --all-features
3435
popd
3536
done
3637

pixie-server/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixie-server/src/ping.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
state::{State, UnitSelector},
66
};
77
use anyhow::{bail, Result};
8-
use pixie_shared::{PACKET_LEN, PING_PORT};
8+
use pixie_shared::{PING_PORT, UDP_BODY_LEN};
99
use std::{
1010
net::{IpAddr, Ipv4Addr},
1111
sync::Arc,
@@ -17,7 +17,7 @@ pub async fn main(state: Arc<State>) -> Result<()> {
1717
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, PING_PORT)).await?;
1818
log::info!("Listening on {}", socket.local_addr()?);
1919

20-
let mut buf = [0; PACKET_LEN];
20+
let mut buf = [0; UDP_BODY_LEN];
2121
loop {
2222
let (len, peer_addr) = tokio::select! {
2323
x = socket.recv_from(&mut buf) => x?,

pixie-server/src/udp.rs

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::{
66
};
77
use anyhow::{bail, ensure, Context, Result};
88
use pixie_shared::{
9-
ChunkHash, HintPacket, RegistrationInfo, UdpRequest, ACTION_PORT, BODY_LEN, CHUNKS_PORT,
10-
HINT_PORT, PACKET_LEN,
9+
chunk_codec::Encoder, ChunkHash, HintPacket, RegistrationInfo, UdpRequest, ACTION_PORT,
10+
CHUNKS_PORT, HINT_PORT, UDP_BODY_LEN,
1111
};
1212
use std::{
1313
collections::BTreeSet,
@@ -28,7 +28,7 @@ async fn broadcast_chunks(
2828
mut rx: Receiver<ChunkHash>,
2929
) -> Result<()> {
3030
let mut queue = BTreeSet::<ChunkHash>::new();
31-
let mut write_buf = [0; PACKET_LEN];
31+
let mut write_buf = [0; UDP_BODY_LEN];
3232
let mut wait_for = Instant::now();
3333
let mut index = [0; 32];
3434

@@ -67,48 +67,24 @@ async fn broadcast_chunks(
6767
_ = state.cancel_token.cancelled() => break,
6868
};
6969

70-
let Some(data) = state
70+
let Some(cdata) = state
7171
.get_chunk_cdata(index)
7272
.with_context(|| format!("get chunk {}", hex::encode(index)))?
7373
else {
7474
log::warn!("Chunk {} not found", hex::encode(index));
7575
continue;
7676
};
7777

78-
let num_packets = data.len().div_ceil(BODY_LEN);
79-
write_buf[..32].clone_from_slice(&index);
80-
81-
let mut xor = [[0; BODY_LEN]; 32];
82-
8378
let hosts_cfg = &state.config.hosts;
8479
let chunks_addr = SocketAddrV4::new(ip, CHUNKS_PORT);
8580

86-
for index in 0..num_packets {
87-
write_buf[32..34].clone_from_slice(&(index as u16).to_le_bytes());
88-
let start = index * BODY_LEN;
89-
let len = BODY_LEN.min(data.len() - start);
90-
let body = &data[start..start + len];
91-
let group = index & 31;
92-
body.iter()
93-
.zip(xor[group].iter_mut())
94-
.for_each(|(a, b)| *b ^= a);
95-
write_buf[34..34 + len].clone_from_slice(body);
96-
81+
let mut encoder = Encoder::new(cdata);
82+
write_buf[..32].clone_from_slice(&index);
83+
while let Some(len) = encoder.next_packet(&mut write_buf[32..]) {
9784
time::sleep_until(wait_for).await;
9885

99-
let sent_len = socket.send_to(&write_buf[..34 + len], chunks_addr).await?;
100-
ensure!(sent_len == 34 + len, "Could not send packet");
101-
wait_for += 8 * (sent_len as u32) * Duration::from_secs(1) / hosts_cfg.broadcast_speed;
102-
}
103-
104-
for (index, body) in xor.iter().enumerate().take(num_packets) {
105-
write_buf[32..34].clone_from_slice(&(index as u16).wrapping_sub(32).to_le_bytes());
106-
let len = BODY_LEN;
107-
write_buf[34..34 + len].clone_from_slice(body);
108-
109-
time::sleep_until(wait_for).await;
110-
let sent_len = socket.send_to(&write_buf[..34 + len], chunks_addr).await?;
111-
ensure!(sent_len == 34 + len, "Could not send packet");
86+
let sent_len = socket.send_to(&write_buf[..32 + len], chunks_addr).await?;
87+
ensure!(sent_len == 32 + len, "Could not send packet");
11288
wait_for += 8 * (sent_len as u32) * Duration::from_secs(1) / hosts_cfg.broadcast_speed;
11389
}
11490
}
@@ -190,7 +166,7 @@ async fn broadcast_hint(state: &State, socket: &UdpSocket, ip: Ipv4Addr) -> Resu
190166
}
191167

192168
async fn handle_requests(state: &State, socket: &UdpSocket, tx: Sender<[u8; 32]>) -> Result<()> {
193-
let mut buf = [0; PACKET_LEN];
169+
let mut buf = [0; UDP_BODY_LEN];
194170
loop {
195171
let (len, addr) = tokio::select! {
196172
x = socket.recv_from(&mut buf) => x?,

pixie-shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ blake3 = { version = "1", default-features = false }
88
ipnet = { version = "2.9.0", optional = true, features = ["serde"] }
99
macaddr = { version = "1.0.1", optional = true, features = ["serde"] }
1010
serde = { version = "1.0.193", default-features = false, features = ["derive", "alloc"] }
11+
thiserror = { version = "2.0.12", default-features = false }
1112

1213
[features]
1314
std = ["ipnet", "macaddr", "serde/std"]

pixie-shared/src/chunk_codec.rs

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
use crate::UDP_BODY_LEN;
2+
use alloc::{vec, vec::Vec};
3+
use thiserror::Error;
4+
5+
const PACKET_LEN: usize = UDP_BODY_LEN - 32;
6+
const HEADER_LEN: usize = 2;
7+
const BODY_LEN: usize = PACKET_LEN - HEADER_LEN;
8+
9+
const MIN_SIZE: usize = HEADER_LEN;
10+
const MAX_SIZE: usize = PACKET_LEN;
11+
12+
#[derive(Error, Debug)]
13+
pub enum DecoderError {
14+
#[error("Packet too small; got {0} bytes, expected at least {MIN_SIZE} bytes")]
15+
PacketTooSmall(usize),
16+
#[error("Packet too big; got {0} bytes, expected at most {MAX_SIZE} bytes")]
17+
PacketTooBig(usize),
18+
#[error("Invalid index: 0x{0:04x}")]
19+
InvalidIndex(u16),
20+
}
21+
22+
pub struct Decoder {
23+
data: Vec<u8>,
24+
missing_packet: Vec<bool>,
25+
missing_packets_per_group: [u16; 32],
26+
missing_groups: u16,
27+
}
28+
29+
impl Decoder {
30+
pub fn new(size: usize) -> Self {
31+
let num_packets = size.div_ceil(BODY_LEN);
32+
let data = vec![0; 32 * BODY_LEN + size];
33+
let missing_packet = vec![true; 32 + num_packets];
34+
let missing_packets_per_group: [u16; 32] = (0..32)
35+
.map(|i| ((num_packets + 31 - i) / 32) as u16)
36+
.collect::<Vec<_>>()
37+
.try_into()
38+
.unwrap();
39+
let missing_groups = missing_packets_per_group
40+
.iter()
41+
.map(|&x| (x != 0) as u16)
42+
.sum();
43+
Decoder {
44+
data,
45+
missing_packet,
46+
missing_packets_per_group,
47+
missing_groups,
48+
}
49+
}
50+
51+
pub fn add_packet(&mut self, buf: &[u8]) -> Result<(), DecoderError> {
52+
if buf.len() < MIN_SIZE {
53+
return Err(DecoderError::PacketTooSmall(buf.len()));
54+
}
55+
if buf.len() > MAX_SIZE {
56+
return Err(DecoderError::PacketTooBig(buf.len()));
57+
}
58+
59+
let index = u16::from_le_bytes(buf[..2].try_into().unwrap());
60+
61+
let rot_index = index.wrapping_add(32) as usize;
62+
let missing = self
63+
.missing_packet
64+
.get_mut(rot_index)
65+
.ok_or(DecoderError::InvalidIndex(index))?;
66+
match missing {
67+
false => return Ok(()),
68+
x @ true => *x = false,
69+
}
70+
71+
let start = rot_index * BODY_LEN;
72+
self.data[start..start + buf.len() - 2].clone_from_slice(&buf[2..]);
73+
74+
let group = index & 31;
75+
match &mut self.missing_packets_per_group[group as usize] {
76+
0 => return Ok(()),
77+
x @ 1 => *x = 0,
78+
x @ 2.. => {
79+
*x -= 1;
80+
return Ok(());
81+
}
82+
}
83+
84+
match &mut self.missing_groups {
85+
0 => unreachable!(),
86+
x @ 1.. => *x -= 1,
87+
}
88+
89+
Ok(())
90+
}
91+
92+
pub fn finish(&mut self) -> Option<Vec<u8>> {
93+
if self.missing_groups != 0 {
94+
return None;
95+
}
96+
97+
let mut xor = [[0; BODY_LEN]; 32];
98+
for packet in 0..self.missing_packet.len() {
99+
if !self.missing_packet[packet] {
100+
let group = packet & 31;
101+
self.data[BODY_LEN * packet..]
102+
.iter()
103+
.zip(xor[group].iter_mut())
104+
.for_each(|(a, b)| *b ^= a);
105+
}
106+
}
107+
for packet in 0..self.missing_packet.len() {
108+
if self.missing_packet[packet] {
109+
let group = packet & 31;
110+
self.data[BODY_LEN * packet..]
111+
.iter_mut()
112+
.zip(xor[group].iter())
113+
.for_each(|(a, b)| *a = *b);
114+
}
115+
}
116+
Some(self.data[32 * BODY_LEN..].to_vec())
117+
}
118+
}
119+
120+
pub struct Encoder {
121+
data: Vec<u8>,
122+
groups: usize,
123+
idx: usize,
124+
}
125+
126+
impl Encoder {
127+
pub fn new(data: Vec<u8>) -> Self {
128+
let idx = 0;
129+
let groups = data.len().div_ceil(BODY_LEN).min(32);
130+
Encoder { data, groups, idx }
131+
}
132+
133+
pub fn next_packet(&mut self, out_buf: &mut [u8]) -> Option<usize> {
134+
let start = self.idx * BODY_LEN;
135+
if start < self.data.len() {
136+
let end = self.data.len().min(start + BODY_LEN);
137+
let len = end - start;
138+
out_buf[0..2].copy_from_slice(&(self.idx as u16).to_le_bytes());
139+
out_buf[2..2 + len].copy_from_slice(&self.data[start..end]);
140+
self.idx += 1;
141+
Some(2 + len)
142+
} else if self.groups > 0 {
143+
self.groups -= 1;
144+
out_buf[0..2].copy_from_slice(&(self.groups as u16).wrapping_sub(32).to_le_bytes());
145+
out_buf[2..].fill(0);
146+
(0..)
147+
.map(|x| (x * 32 + self.groups) * BODY_LEN)
148+
.take_while(|x| *x < self.data.len())
149+
.for_each(|start| {
150+
let end = self.data.len().min(start + BODY_LEN);
151+
out_buf[2..2 + end - start]
152+
.iter_mut()
153+
.zip(self.data[start..end].iter())
154+
.for_each(|(a, b)| *a ^= *b);
155+
});
156+
Some(2 + BODY_LEN.min(self.data.len() - self.groups * BODY_LEN))
157+
} else {
158+
None
159+
}
160+
}
161+
}
162+
163+
#[cfg(test)]
164+
mod tests {
165+
use super::*;
166+
use crate::UDP_BODY_LEN;
167+
168+
fn test_chunk_skip_packet(chunk: &[u8]) {
169+
let mut encoder = Encoder::new(chunk.to_vec());
170+
let mut packets = Vec::new();
171+
let mut buf = [0u8; UDP_BODY_LEN];
172+
while let Some(len) = encoder.next_packet(&mut buf) {
173+
packets.push(buf[..len].to_vec());
174+
}
175+
176+
packets.sort_by_key(|p| {
177+
p.iter().take(6).fold(0u64, |acc, &x| {
178+
acc.wrapping_mul(0x5DEECE66D).wrapping_add(x as u64)
179+
})
180+
});
181+
182+
for skip_idx in 0..packets.len() {
183+
let mut decoder = Decoder::new(chunk.len());
184+
for (idx, packet) in packets.iter().enumerate() {
185+
if idx != skip_idx {
186+
decoder.add_packet(packet).expect("Failed to add packet");
187+
}
188+
}
189+
let decoded = decoder.finish().expect("Failed to decode chunk");
190+
assert_eq!(
191+
decoded, chunk,
192+
"Failed to decode chunk with skip index {skip_idx}"
193+
);
194+
}
195+
196+
let mut decoder = Decoder::new(chunk.len());
197+
for (idx, packet) in packets.iter().enumerate() {
198+
if !(idx.is_multiple_of(33) && idx / 33 < 32) {
199+
decoder.add_packet(packet).expect("Failed to add packet");
200+
}
201+
}
202+
let decoded = decoder.finish().expect("Failed to decode chunk");
203+
assert_eq!(decoded, chunk, "Failed to decode chunk with multiple skips");
204+
}
205+
206+
#[test]
207+
fn test_small_chunk() {
208+
let mut chunk = vec![0u8; 20];
209+
let mut val = u64::MAX / 5;
210+
for x in &mut chunk {
211+
val = val.wrapping_mul(0x5DEECE66D).wrapping_add(0xB);
212+
*x = val.to_be_bytes()[0];
213+
}
214+
test_chunk_skip_packet(&chunk);
215+
}
216+
217+
#[test]
218+
fn test_big_chunk() {
219+
let mut chunk = vec![0u8; 200 << 10];
220+
let mut val = u64::MAX / 5;
221+
for x in &mut chunk {
222+
val = val.wrapping_mul(0x5DEECE66D).wrapping_add(0xB);
223+
*x = val.to_be_bytes()[0];
224+
}
225+
test_chunk_skip_packet(&chunk);
226+
}
227+
}

pixie-shared/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
extern crate alloc;
44

55
pub mod bijection;
6+
pub mod chunk_codec;
67
#[cfg(feature = "std")]
78
pub mod config;
89

@@ -110,9 +111,7 @@ pub struct HintPacket {
110111
}
111112

112113
/// The maximum number of bytes in a udp packet with mtu 1500
113-
pub const PACKET_LEN: usize = 1472;
114-
pub const HEADER_LEN: usize = 34;
115-
pub const BODY_LEN: usize = PACKET_LEN - HEADER_LEN;
114+
pub const UDP_BODY_LEN: usize = 1472;
116115

117116
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
118117
#[serde(rename_all = "lowercase")]

0 commit comments

Comments
 (0)