Skip to main content

rfc9162_tiles/
bundle.rs

1//! Entry bundles, `tile/entries/<N>[.p/<W>]`: a sequence of big-endian uint16
2//! length-prefixed entries, 256 to a full bundle. Entry `i` of bundle `N`
3//! hashes to hash `i` of level-0 tile `N`.
4
5use crate::error::{Error, Result};
6use crate::layout::MAX_ENTRY_LEN;
7
8/// The bytes of a bundle over `entries`; an entry over 65535 bytes is refused.
9pub fn encode_bundle<E: AsRef<[u8]>>(entries: &[E]) -> Result<Vec<u8>> {
10    let mut out = Vec::new();
11    for entry in entries {
12        let entry = entry.as_ref();
13        if entry.len() > MAX_ENTRY_LEN {
14            return Err(Error::EntryTooLong(entry.len()));
15        }
16        out.extend_from_slice(&(entry.len() as u16).to_be_bytes());
17        out.extend_from_slice(entry);
18    }
19    Ok(out)
20}
21
22/// The entries of a bundle's bytes; refused when a length prefix runs past
23/// the end or bytes trail the last entry.
24pub fn decode_bundle(path: &str, bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
25    let mut entries = Vec::new();
26    let mut at = 0usize;
27    while at < bytes.len() {
28        let Some(prefix) = bytes.get(at..at + 2) else {
29            return Err(Error::Corrupt {
30                path: path.to_string(),
31                reason: format!("a length prefix is cut at byte {at}"),
32            });
33        };
34        let len = usize::from(u16::from_be_bytes([prefix[0], prefix[1]]));
35        at += 2;
36        let Some(entry) = bytes.get(at..at + len) else {
37            return Err(Error::Corrupt {
38                path: path.to_string(),
39                reason: format!("an entry of {len} bytes is cut at byte {at}"),
40            });
41        };
42        entries.push(entry.to_vec());
43        at += len;
44    }
45    Ok(entries)
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn a_bundle_round_trips_including_an_empty_entry() {
54        let entries: Vec<Vec<u8>> = vec![b"one".to_vec(), Vec::new(), vec![7u8; 65535]];
55        let bytes = encode_bundle(&entries).unwrap();
56        assert_eq!(bytes.len(), 2 + 3 + 2 + 2 + 65535);
57        assert_eq!(decode_bundle("b", &bytes).unwrap(), entries);
58        assert!(decode_bundle("b", &bytes[..bytes.len() - 1]).is_err());
59        assert!(decode_bundle("b", &[0]).is_err());
60        assert!(decode_bundle("b", &[]).unwrap().is_empty());
61    }
62
63    #[test]
64    fn an_entry_past_the_prefix_is_refused() {
65        let long = vec![0u8; 65536];
66        assert!(matches!(
67            encode_bundle(&[long]),
68            Err(Error::EntryTooLong(65536))
69        ));
70    }
71}