Onset: version control where nobody waits and nothing gets lost. The on command, its core and onset-server.
| 6 |
| //! end: 32 zero bytes |
| 7 | //! ``` |
| 8 | //! |
| 9 | //! The codec says how the payload is stored (0 raw, 1 zstd); hosts compress |
| 10 | //! and decompress. A receiver checks every object against its ID. |
| 11 |
| 12 | use crate::object::{Kind, Object}; |
| 13 | use crate::{Id, Result, corrupt}; |
| 14 |
| 15 | pub const MAGIC: &[u8] = b"VCSB1\n"; |
| 16 | pub const RAW: u8 = 0; |
| 17 | pub const ZSTD: u8 = 1; |
| 18 | /// No single object may claim to be bigger than this. |
| 19 | pub const MAX_OBJECT: u64 = 256 << 20; |
| 20 |
| 21 | /// One stored object as it travels: possibly compressed. |
| 22 | #[derive(Debug, Clone)] |
| 23 | pub struct Frame { |
| 24 | pub id: Id, |
| 25 | pub kind: Kind, |
| 26 | pub codec: u8, |
| 27 | pub raw_len: u64, |
| 28 | pub payload: Vec<u8>, |
| 29 | } |
| 30 |
| 31 | impl Frame { |
| 32 | pub fn raw(object: &Object) -> Frame { |
| 33 | Frame { id: object.id(), kind: object.kind, codec: RAW, raw_len: object.data.len() as u64, payload: object.data.clone() } |
| 34 | } |
| 35 | } |
| 36 |
| 37 | pub fn write_header(out: &mut Vec<u8>) { |
| 38 | out.extend_from_slice(MAGIC); |
| 39 | } |
| 40 |
| 41 | pub fn write_frame(out: &mut Vec<u8>, f: &Frame) { |
| 42 | out.extend_from_slice(&f.id.0); |
| 43 | out.push(f.kind as u8); |
| 44 | out.push(f.codec); |
| 45 | out.extend_from_slice(&f.raw_len.to_le_bytes()); |
| 46 | out.extend_from_slice(&(f.payload.len() as u64).to_le_bytes()); |
| 47 | out.extend_from_slice(&f.payload); |
| 48 | } |
| 49 |
| 50 | pub fn write_end(out: &mut Vec<u8>) { |
| 51 | out.extend_from_slice(&[0u8; 32]); |
| 52 | } |
| 53 |
| 54 | /// Reads frames from a complete bundle. |
| 55 | pub fn read(bytes: &[u8]) -> Result<Vec<Frame>> { |
| 56 | if !bytes.starts_with(MAGIC) { |
| 57 | return Err(corrupt("not an Onset bundle")); |
| 58 | } |
| 59 | let mut at = MAGIC.len(); |
| 60 | let mut out = Vec::new(); |
| 61 | loop { |
| 62 | let id_bytes = bytes.get(at..at + 32).ok_or_else(|| corrupt("bundle ends early"))?; |
| 63 | if id_bytes.iter().all(|b| *b == 0) { |
| 64 | break; |
| 65 | } |
| 66 | let id = Id::from_slice(id_bytes).unwrap(); |
| 67 | let header = bytes.get(at + 32..at + 50).ok_or_else(|| corrupt("bundle ends early"))?; |
| 68 | let kind = Kind::from_u8(header[0]).ok_or_else(|| corrupt("bad object kind in bundle"))?; |
| 69 | let codec = header[1]; |
| 70 | if codec != RAW && codec != ZSTD { |
| 71 | return Err(corrupt("unknown codec in bundle")); |
| 72 | } |
| 73 | let raw_len = u64::from_le_bytes(header[2..10].try_into().unwrap()); |
| 74 | let payload_len = u64::from_le_bytes(header[10..18].try_into().unwrap()); |
| 75 | if raw_len > MAX_OBJECT || payload_len > MAX_OBJECT { |
| 76 | return Err(crate::Error::Limit("an object in the bundle is too big".into())); |
| 77 | } |
| 78 | let start = at + 50; |
| 79 | let end = start + payload_len as usize; |
| 80 | let payload = bytes.get(start..end).ok_or_else(|| corrupt("bundle ends early"))?.to_vec(); |
| 81 | if codec == RAW && payload_len != raw_len { |
| 82 | return Err(corrupt("raw payload length mismatch")); |
| 83 | } |
| 84 | out.push(Frame { id, kind, codec, raw_len, payload }); |
| 85 | at = end; |
| 86 | } |
| 87 | Ok(out) |
| 88 | } |
| 89 |
| 90 | /// Checks a decoded object against the ID its frame claimed. |
| 91 | pub fn verify(frame: &Frame, data: &[u8]) -> Result<()> { |
| 92 | if data.len() as u64 != frame.raw_len { |
| 93 | return Err(corrupt(format!("object {} has the wrong length", frame.id.short()))); |
| 94 | } |
| 95 | if Id::of(frame.kind as u8, data) != frame.id { |
| 96 | return Err(corrupt(format!("object {} doesn't match its ID", frame.id.short()))); |
| 97 | } |
| 98 | Ok(()) |
| 99 | } |
| 100 |
| 101 | #[cfg(test)] |
| 102 | mod tests { |
| 103 | use super::*; |
| 104 |
| 105 | #[test] |
| 106 | fn round_trip() { |
| 107 | let a = Object::blob(b"hello".to_vec()); |
| 108 | let b = Object::new(Kind::Tree, Vec::new()); |
| 109 | let mut out = Vec::new(); |
| 110 | write_header(&mut out); |
| 111 | write_frame(&mut out, &Frame::raw(&a)); |
| 112 | write_frame(&mut out, &Frame::raw(&b)); |
| 113 | write_end(&mut out); |
| 114 | let frames = read(&out).unwrap(); |
| 115 | assert_eq!(frames.len(), 2); |
| 116 | verify(&frames[0], &frames[0].payload).unwrap(); |
| 117 | assert!(verify(&frames[0], b"other").is_err()); |
| 118 | assert_eq!(frames[1].kind, Kind::Tree); |
| 119 | } |
| 120 | } |
| 121 |