aboutsummaryrefslogtreecommitdiff
path: root/snapshot/src/receiver.rs
diff options
context:
space:
mode:
Diffstat (limited to 'snapshot/src/receiver.rs')
-rw-r--r--snapshot/src/receiver.rs245
1 files changed, 245 insertions, 0 deletions
diff --git a/snapshot/src/receiver.rs b/snapshot/src/receiver.rs
new file mode 100644
index 0000000..49833d1
--- /dev/null
+++ b/snapshot/src/receiver.rs
@@ -0,0 +1,245 @@
+use common::num::Cast;
+use gamenet::msg::system;
+use std::ops;
+use to_usize;
+use vec_map::VecMap;
+use warn::Warn;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Warning {
+ DuplicateSnap,
+ DifferingAttributes,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Error {
+ OldDelta,
+ InvalidNumParts,
+ InvalidPart,
+ DuplicatePart,
+}
+
+// TODO: How to handle `tick` overflowing?
+#[derive(Clone, Debug)]
+struct CurrentDelta {
+ tick: i32,
+ delta_tick: i32,
+ num_parts: i32,
+ crc: i32,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct ReceivedDelta<'a> {
+ pub delta_tick: i32,
+ pub tick: i32,
+ pub data_and_crc: Option<(&'a [u8], i32)>,
+}
+
+#[derive(Clone, Default)]
+pub struct DeltaReceiver {
+ previous_tick: Option<i32>,
+ current: Option<CurrentDelta>,
+ // `parts` points into `receive_buf`.
+ parts: VecMap<ops::Range<u32>>,
+ receive_buf: Vec<u8>,
+ result: Vec<u8>,
+}
+
+impl DeltaReceiver {
+ pub fn new() -> DeltaReceiver {
+ Default::default()
+ }
+ pub fn reset(&mut self) {
+ self.previous_tick = None;
+ self.current = None;
+ }
+ fn can_receive(&self, tick: i32) -> bool {
+ self.current.as_ref().map(|c| c.tick <= tick)
+ .or(self.previous_tick.map(|t| t < tick))
+ .unwrap_or(true)
+ }
+ fn init_delta(&mut self) {
+ self.parts.clear();
+ self.receive_buf.clear();
+ self.result.clear();
+ }
+ fn finish_delta(&mut self, tick: i32) {
+ self.current = None;
+ self.previous_tick = Some(tick);
+ }
+ pub fn snap_empty<W>(&mut self, warn: &mut W, snap: system::SnapEmpty)
+ -> Result<Option<ReceivedDelta>, Error>
+ where W: Warn<Warning>,
+ {
+ if !self.can_receive(snap.tick) {
+ return Err(Error::OldDelta);
+ }
+ if self.current.as_ref().map(|c| c.tick == snap.tick).unwrap_or(false) {
+ warn.warn(Warning::DuplicateSnap);
+ }
+ self.init_delta();
+ self.finish_delta(snap.tick);
+ Ok(Some(ReceivedDelta {
+ delta_tick: snap.tick.wrapping_sub(snap.delta_tick),
+ tick: snap.tick,
+ data_and_crc: None,
+ }))
+ }
+ pub fn snap_single<W>(&mut self, warn: &mut W, snap: system::SnapSingle)
+ -> Result<Option<ReceivedDelta>, Error>
+ where W: Warn<Warning>,
+ {
+ if !self.can_receive(snap.tick) {
+ return Err(Error::OldDelta);
+ }
+ if self.current.as_ref().map(|c| c.tick == snap.tick).unwrap_or(false) {
+ warn.warn(Warning::DuplicateSnap);
+ }
+ self.init_delta();
+ self.finish_delta(snap.tick);
+ self.result.extend(snap.data);
+ Ok(Some(ReceivedDelta {
+ delta_tick: snap.tick.wrapping_sub(snap.delta_tick),
+ tick: snap.tick,
+ data_and_crc: Some((&self.result, snap.crc)),
+ }))
+ }
+ pub fn snap<W>(&mut self, warn: &mut W, snap: system::Snap)
+ -> Result<Option<ReceivedDelta>, Error>
+ where W: Warn<Warning>,
+ {
+ if !self.can_receive(snap.tick) {
+ return Err(Error::OldDelta);
+ }
+ if !(0 <= snap.num_parts && snap.num_parts <= 32) {
+ return Err(Error::InvalidNumParts);
+ }
+ if !(0 <= snap.part && snap.part < snap.num_parts) {
+ return Err(Error::InvalidPart);
+ }
+ if self.current.as_ref().map(|c| c.tick != snap.tick).unwrap_or(false) {
+ self.current = None;
+ }
+ if let None = self.current {
+ self.init_delta();
+ self.current = Some(CurrentDelta {
+ tick: snap.tick,
+ delta_tick: snap.tick.wrapping_sub(snap.delta_tick),
+ num_parts: snap.num_parts,
+ crc: snap.crc,
+ });
+
+ // Checked above.
+ let num_parts = snap.num_parts.assert_usize();
+ self.parts.reserve_len(num_parts);
+ }
+ let delta_tick;
+ let tick;
+ let crc;
+ let num_parts;
+ {
+ let current: &mut CurrentDelta = self.current.as_mut().unwrap();
+ if snap.delta_tick != current.delta_tick
+ || snap.num_parts != current.num_parts
+ || snap.crc != current.crc
+ {
+ warn.warn(Warning::DifferingAttributes);
+ }
+ delta_tick = current.delta_tick;
+ tick = current.tick;
+ crc = current.crc;
+ num_parts = current.num_parts;
+ }
+ let part = snap.part.assert_usize();
+ if self.parts.contains_key(part) {
+ return Err(Error::DuplicatePart);
+ }
+ let start = self.receive_buf.len().assert_u32();
+ let end = (self.receive_buf.len() + snap.data.len()).assert_u32();
+ self.receive_buf.extend(snap.data);
+ assert!(self.parts.insert(part, start..end).is_none());
+
+ if self.parts.len().assert_i32() != num_parts {
+ return Ok(None);
+ }
+
+ self.finish_delta(tick);
+ self.result.reserve(self.receive_buf.len());
+ for range in self.parts.values() {
+ self.result.extend(&self.receive_buf[to_usize(range.clone())]);
+ }
+
+ Ok(Some(ReceivedDelta {
+ delta_tick: delta_tick,
+ tick: tick,
+ data_and_crc: Some((&self.result, crc)),
+ }))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use common::num::Cast;
+ use gamenet::msg::system::Snap;
+ use gamenet::msg::system::SnapEmpty;
+ use gamenet::msg::system::SnapSingle;
+ use super::DeltaReceiver;
+ use super::Error;
+ use super::ReceivedDelta;
+ use warn::Panic;
+
+ #[test]
+ fn old() {
+ let mut receiver = DeltaReceiver::new();
+ {
+ let result = receiver.snap_empty(&mut Panic, SnapEmpty {
+ tick: 1,
+ delta_tick: 2,
+ }).unwrap();
+
+ assert_eq!(result, Some(ReceivedDelta {
+ delta_tick: -1,
+ tick: 1,
+ data_and_crc: None,
+ }));
+ }
+
+ assert_eq!(receiver.snap_single(&mut Panic, SnapSingle {
+ tick: 0,
+ delta_tick: 0,
+ data: b"123",
+ crc: 0,
+ }).unwrap_err(), Error::OldDelta);
+ }
+
+ #[test]
+ fn reorder() {
+ let mut receiver = DeltaReceiver::new();
+ let chunks: &[(i32, &[u8])] = &[
+ (3, b"3"),
+ (2, b"2"),
+ (4, b"4_"),
+ (1, b"1__"),
+ (0, b"0"),
+ ];
+ for &(i, c) in chunks {
+ let result = receiver.snap(&mut Panic, Snap {
+ tick: 2,
+ delta_tick: 1,
+ num_parts: chunks.len().assert_i32(),
+ part: i,
+ crc: 3,
+ data: c,
+ }).unwrap();
+ if i != 0 {
+ assert_eq!(result, None);
+ } else {
+ assert_eq!(result, Some(ReceivedDelta {
+ delta_tick: 1,
+ tick: 2,
+ data_and_crc: Some((b"01__234_", 3)),
+ }));
+ }
+ }
+ }
+}