ic_memory/ledger/
payload.rs1use super::{CURRENT_LEDGER_SCHEMA_VERSION, CURRENT_PHYSICAL_FORMAT_ID};
2
3const LEDGER_PAYLOAD_MAGIC: &[u8; 8] = b"ICMEMLED";
4const LEDGER_PAYLOAD_HEADER_LEN: usize = 8 + 2 + 4 + 4 + 8;
5
6pub const CURRENT_LEDGER_PAYLOAD_ENVELOPE_VERSION: u16 = 1;
8
9#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct LedgerPayloadEnvelope {
23 envelope_version: u16,
24 ledger_schema_version: u32,
25 physical_format_id: u32,
26 payload: Vec<u8>,
27}
28
29impl LedgerPayloadEnvelope {
30 #[must_use]
32 pub const fn current(payload: Vec<u8>) -> Self {
33 Self {
34 envelope_version: CURRENT_LEDGER_PAYLOAD_ENVELOPE_VERSION,
35 ledger_schema_version: CURRENT_LEDGER_SCHEMA_VERSION,
36 physical_format_id: CURRENT_PHYSICAL_FORMAT_ID,
37 payload,
38 }
39 }
40
41 #[cfg(test)]
42 pub(crate) const fn from_parts(
43 envelope_version: u16,
44 ledger_schema_version: u32,
45 physical_format_id: u32,
46 payload: Vec<u8>,
47 ) -> Self {
48 Self {
49 envelope_version,
50 ledger_schema_version,
51 physical_format_id,
52 payload,
53 }
54 }
55
56 #[must_use]
58 pub fn encode(&self) -> Vec<u8> {
59 let mut bytes = Vec::with_capacity(LEDGER_PAYLOAD_HEADER_LEN + self.payload.len());
60 bytes.extend_from_slice(LEDGER_PAYLOAD_MAGIC);
61 bytes.extend_from_slice(&self.envelope_version.to_le_bytes());
62 bytes.extend_from_slice(&self.ledger_schema_version.to_le_bytes());
63 bytes.extend_from_slice(&self.physical_format_id.to_le_bytes());
64 bytes.extend_from_slice(
65 &u64::try_from(self.payload.len())
66 .expect("payload length does not fit in u64")
67 .to_le_bytes(),
68 );
69 bytes.extend_from_slice(&self.payload);
70 bytes
71 }
72
73 pub fn decode(bytes: &[u8]) -> Result<Self, LedgerPayloadEnvelopeError> {
75 if bytes.len() < LEDGER_PAYLOAD_HEADER_LEN {
76 return Err(LedgerPayloadEnvelopeError::Truncated {
77 actual: bytes.len(),
78 minimum: LEDGER_PAYLOAD_HEADER_LEN,
79 });
80 }
81
82 let magic = <[u8; 8]>::try_from(&bytes[0..8]).expect("magic slice length");
83 if &magic != LEDGER_PAYLOAD_MAGIC {
84 return Err(LedgerPayloadEnvelopeError::BadMagic { found: magic });
85 }
86
87 let envelope_version = u16::from_le_bytes(bytes[8..10].try_into().expect("u16 slice"));
88 let ledger_schema_version =
89 u32::from_le_bytes(bytes[10..14].try_into().expect("u32 slice"));
90 let physical_format_id = u32::from_le_bytes(bytes[14..18].try_into().expect("u32 slice"));
91 let payload_len = u64::from_le_bytes(bytes[18..26].try_into().expect("u64 slice"));
92 let payload_len = usize::try_from(payload_len)
93 .map_err(|_| LedgerPayloadEnvelopeError::PayloadTooLarge { len: payload_len })?;
94 let expected_len = LEDGER_PAYLOAD_HEADER_LEN
95 .checked_add(payload_len)
96 .ok_or(LedgerPayloadEnvelopeError::PayloadLengthOverflow { len: payload_len })?;
97 if bytes.len() != expected_len {
98 return Err(LedgerPayloadEnvelopeError::LengthMismatch {
99 declared: payload_len,
100 actual: bytes.len().saturating_sub(LEDGER_PAYLOAD_HEADER_LEN),
101 });
102 }
103
104 Ok(Self {
105 envelope_version,
106 ledger_schema_version,
107 physical_format_id,
108 payload: bytes[LEDGER_PAYLOAD_HEADER_LEN..].to_vec(),
109 })
110 }
111
112 #[must_use]
114 pub const fn envelope_version(&self) -> u16 {
115 self.envelope_version
116 }
117
118 #[must_use]
120 pub const fn ledger_schema_version(&self) -> u32 {
121 self.ledger_schema_version
122 }
123
124 #[must_use]
126 pub const fn physical_format_id(&self) -> u32 {
127 self.physical_format_id
128 }
129
130 #[must_use]
132 pub fn payload(&self) -> &[u8] {
133 &self.payload
134 }
135}
136
137#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
142pub enum LedgerPayloadEnvelopeError {
143 #[error("ledger payload envelope is truncated: {actual} bytes, need at least {minimum}")]
145 Truncated {
146 actual: usize,
148 minimum: usize,
150 },
151 #[error("ledger payload envelope has bad magic {found:?}")]
153 BadMagic {
154 found: [u8; 8],
156 },
157 #[error("ledger payload envelope length {len} is too large")]
159 PayloadTooLarge {
160 len: u64,
162 },
163 #[error("ledger payload envelope length {len} overflows total length")]
165 PayloadLengthOverflow {
166 len: usize,
168 },
169 #[error("ledger payload envelope declared {declared} payload bytes but contained {actual}")]
171 LengthMismatch {
172 declared: usize,
174 actual: usize,
176 },
177}