heddle_object_model/compact/
blob.rs1use super::{
4 Result,
5 io::{Reader, Writer},
6 limits::{MAX_COMPACT_COUNT, MIN_BLOB_ITEM_BYTES},
7};
8use crate::object::ContentHash;
9
10const BLOB_MAGIC: &[u8; 4] = b"HCB2";
11
12pub type DecodedBlob<'a> = (ContentHash, &'a [u8]);
14
15pub fn is_blob_frame(bytes: &[u8]) -> bool {
17 bytes.starts_with(BLOB_MAGIC)
18}
19
20pub fn encode_blob_frame(blobs: &[&[u8]]) -> Result<Vec<u8>> {
25 if blobs.len() > MAX_COMPACT_COUNT {
26 return Err(super::invalid(format!(
27 "blob frame count {} exceeds maximum {MAX_COMPACT_COUNT}",
28 blobs.len()
29 )));
30 }
31 let mut output = Writer::new(BLOB_MAGIC);
32 output.put_u64(blobs.len() as u64);
33 if let Some((first, rest)) = blobs.split_first() {
34 output.put_u64(first.len() as u64);
35 let mut previous = i64::try_from(first.len())
36 .map_err(|_| super::invalid("blob length exceeds signed delta range"))?;
37 for blob in rest {
38 let current = i64::try_from(blob.len())
39 .map_err(|_| super::invalid("blob length exceeds signed delta range"))?;
40 output.put_i64(current - previous);
41 previous = current;
42 }
43 }
44 for blob in blobs {
45 output.put_fixed(blob);
46 }
47 Ok(output.finish())
48}
49
50pub fn decode_blob_frame(bytes: &[u8]) -> Result<Vec<DecodedBlob<'_>>> {
52 let mut input = Reader::verified(bytes, BLOB_MAGIC)?;
53 let count = input.get_count("blob frame", MIN_BLOB_ITEM_BYTES)?;
54 let mut lengths = Vec::with_capacity(count);
55 if count > 0 {
56 let first = input.get_u64()?;
57 lengths.push(checked_length(first)?);
58 let mut previous = i64::try_from(first)
59 .map_err(|_| super::invalid("blob length exceeds signed delta range"))?;
60 for _ in 1..count {
61 let current = previous
62 .checked_add(input.get_i64()?)
63 .ok_or_else(|| super::invalid("blob length delta overflow"))?;
64 if current < 0 {
65 return Err(super::invalid("blob length delta became negative"));
66 }
67 lengths.push(checked_length(current as u64)?);
68 previous = current;
69 }
70 }
71 let minimum_remaining = lengths
72 .iter()
73 .try_fold(0usize, |total, len| total.checked_add(*len))
74 .ok_or_else(|| super::invalid("blob frame length overflow"))?;
75 if input.remaining() != minimum_remaining {
76 return Err(super::invalid(format!(
77 "blob lengths total {minimum_remaining}, frame has {} body bytes",
78 input.remaining()
79 )));
80 }
81 let mut blobs = Vec::with_capacity(count);
82 for len in lengths {
83 let body = input.take(len)?;
84 blobs.push((ContentHash::compute_typed("blob", body), body));
85 }
86 input.finish()?;
87 Ok(blobs)
88}
89
90fn checked_length(value: u64) -> Result<usize> {
91 usize::try_from(value).map_err(|_| super::invalid("blob length exceeds platform limits"))
92}