1extern crate alloc;
6
7use alloc::vec::Vec;
8
9use broadcast_common::{Parse, Serialize};
10
11use crate::ber::{ber_length_size, decode_ber_length, encode_ber_length};
12use crate::error::{Error, Result};
13use crate::types::{UlBytes, parse_uid_batch, serialize_uid_batch, ul_bytes_from_prefix};
14
15const PARTITION_KEY_PREFIX: [u8; 7] = [0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01];
20const PARTITION_KEY_MID: [u8; 4] = [0x0D, 0x01, 0x02, 0x01];
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[non_exhaustive]
27pub enum PartitionKind {
28 Header,
30 Body,
32 Footer,
34}
35
36impl PartitionKind {
37 #[must_use]
39 pub fn name(&self) -> &'static str {
40 match self {
41 Self::Header => "Header Partition",
42 Self::Body => "Body Partition",
43 Self::Footer => "Footer Partition",
44 }
45 }
46
47 fn from_byte(b: u8) -> Result<Self> {
48 match b {
49 0x02 => Ok(Self::Header),
50 0x03 => Ok(Self::Body),
51 0x04 => Ok(Self::Footer),
52 other => Err(Error::UnknownPartitionKind { byte: other }),
53 }
54 }
55
56 fn to_byte(self) -> u8 {
57 match self {
58 Self::Header => 0x02,
59 Self::Body => 0x03,
60 Self::Footer => 0x04,
61 }
62 }
63}
64
65broadcast_common::impl_spec_display!(PartitionKind);
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[non_exhaustive]
72pub enum PartitionStatus {
73 OpenIncomplete,
75 ClosedIncomplete,
77 OpenComplete,
79 ClosedComplete,
81}
82
83impl PartitionStatus {
84 #[must_use]
86 pub fn name(&self) -> &'static str {
87 match self {
88 Self::OpenIncomplete => "open and incomplete",
89 Self::ClosedIncomplete => "closed and incomplete",
90 Self::OpenComplete => "open and complete",
91 Self::ClosedComplete => "closed and complete",
92 }
93 }
94
95 #[must_use]
97 pub fn is_open(&self) -> bool {
98 matches!(self, Self::OpenIncomplete | Self::OpenComplete)
99 }
100
101 fn from_byte(b: u8) -> Result<Self> {
102 match b {
103 0x01 => Ok(Self::OpenIncomplete),
104 0x02 => Ok(Self::ClosedIncomplete),
105 0x03 => Ok(Self::OpenComplete),
106 0x04 => Ok(Self::ClosedComplete),
107 other => Err(Error::UnknownPartitionStatus { byte: other }),
108 }
109 }
110
111 fn to_byte(self) -> u8 {
112 match self {
113 Self::OpenIncomplete => 0x01,
114 Self::ClosedIncomplete => 0x02,
115 Self::OpenComplete => 0x03,
116 Self::ClosedComplete => 0x04,
117 }
118 }
119}
120
121broadcast_common::impl_spec_display!(PartitionStatus);
122
123#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct PartitionPack {
128 pub kind: PartitionKind,
130 pub status: PartitionStatus,
132 pub major_version: u16,
134 pub minor_version: u16,
136 pub kag_size: u32,
139 pub this_partition: u64,
142 pub previous_partition: u64,
145 pub footer_partition: u64,
147 pub header_byte_count: u64,
150 pub index_byte_count: u64,
152 pub index_sid: u32,
154 pub body_offset: u64,
157 pub body_sid: u32,
159 pub operational_pattern: UlBytes,
161 pub essence_containers: Vec<UlBytes>,
163}
164
165impl PartitionPack {
166 #[must_use]
176 pub fn key(kind: PartitionKind, status: PartitionStatus) -> UlBytes {
177 let mut key = [0u8; 16];
178 key[0..7].copy_from_slice(&PARTITION_KEY_PREFIX);
179 key[7] = 0x01;
180 key[8..12].copy_from_slice(&PARTITION_KEY_MID);
181 key[12] = 0x01; key[13] = kind.to_byte();
183 key[14] = status.to_byte();
184 key[15] = 0x00;
185 key
186 }
187
188 #[must_use]
201 pub fn is_partition_key(key: &UlBytes) -> bool {
202 key[0..7] == PARTITION_KEY_PREFIX
203 && key[8..12] == PARTITION_KEY_MID
204 && key[12] == 0x01
205 && PartitionKind::from_byte(key[13]).is_ok()
206 }
207
208 fn parse_key(key: &UlBytes) -> Result<(PartitionKind, PartitionStatus)> {
209 if key[0..7] != PARTITION_KEY_PREFIX || key[8..12] != PARTITION_KEY_MID || key[12] != 0x01 {
210 return Err(Error::KeyPrefixMismatch {
211 what: "Partition Pack (Table 4)",
212 });
213 }
214 let kind = PartitionKind::from_byte(key[13])?;
215 let status = PartitionStatus::from_byte(key[14])?;
216 if kind == PartitionKind::Footer && status.is_open() {
217 return Err(Error::OpenFooterPartition { byte: key[14] });
218 }
219 Ok((kind, status))
220 }
221}
222
223impl<'a> Parse<'a> for PartitionPack {
224 type Error = Error;
225
226 fn parse(bytes: &'a [u8]) -> Result<Self> {
227 if bytes.len() < 16 {
228 return Err(Error::BufferTooShort {
229 need: 16,
230 have: bytes.len(),
231 what: "Partition Pack key",
232 });
233 }
234 let key: UlBytes = ul_bytes_from_prefix(bytes);
235 let (kind, status) = Self::parse_key(&key)?;
236
237 let (len, len_size) = decode_ber_length(&bytes[16..])?;
238 let value_start = 16 + len_size;
239 let len = len as usize;
240 let value_end = value_start.checked_add(len).ok_or(Error::BufferTooShort {
241 need: usize::MAX,
242 have: bytes.len(),
243 what: "Partition Pack value (length overflow)",
244 })?;
245 if bytes.len() < value_end {
246 return Err(Error::BufferTooShort {
247 need: value_end,
248 have: bytes.len(),
249 what: "Partition Pack value",
250 });
251 }
252 let v = &bytes[value_start..value_end];
253
254 const FIXED_FIELDS_LEN: usize = 2 + 2 + 4 + 8 + 8 + 8 + 8 + 8 + 4 + 8 + 4 + 16;
255 if v.len() < FIXED_FIELDS_LEN {
256 return Err(Error::BufferTooShort {
257 need: FIXED_FIELDS_LEN,
258 have: v.len(),
259 what: "Partition Pack fixed fields",
260 });
261 }
262 let u16_at = |o: usize| u16::from_be_bytes([v[o], v[o + 1]]);
263 let u32_at = |o: usize| u32::from_be_bytes([v[o], v[o + 1], v[o + 2], v[o + 3]]);
264 let u64_at = |o: usize| {
265 u64::from_be_bytes([
266 v[o],
267 v[o + 1],
268 v[o + 2],
269 v[o + 3],
270 v[o + 4],
271 v[o + 5],
272 v[o + 6],
273 v[o + 7],
274 ])
275 };
276
277 let major_version = u16_at(0);
278 let minor_version = u16_at(2);
279 let kag_size = u32_at(4);
280 let this_partition = u64_at(8);
281 let previous_partition = u64_at(16);
282 let footer_partition = u64_at(24);
283 let header_byte_count = u64_at(32);
284 let index_byte_count = u64_at(40);
285 let index_sid = u32_at(48);
286 let body_offset = u64_at(52);
287 let body_sid = u32_at(60);
288 let operational_pattern: UlBytes = ul_bytes_from_prefix(&v[64..]);
289 let essence_containers = parse_uid_batch(&v[80..])?;
290
291 Ok(PartitionPack {
292 kind,
293 status,
294 major_version,
295 minor_version,
296 kag_size,
297 this_partition,
298 previous_partition,
299 footer_partition,
300 header_byte_count,
301 index_byte_count,
302 index_sid,
303 body_offset,
304 body_sid,
305 operational_pattern,
306 essence_containers,
307 })
308 }
309}
310
311impl Serialize for PartitionPack {
312 type Error = Error;
313
314 fn serialized_len(&self) -> usize {
315 let value_len =
316 2 + 2 + 4 + 8 + 8 + 8 + 8 + 8 + 4 + 8 + 4 + 16 + 8 + self.essence_containers.len() * 16;
317 16 + ber_length_size(value_len as u64) + value_len
318 }
319
320 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
321 if self.kind == PartitionKind::Footer && self.status.is_open() {
322 return Err(Error::OpenFooterPartition {
323 byte: self.status.to_byte(),
324 });
325 }
326 let total = self.serialized_len();
327 if buf.len() < total {
328 return Err(Error::BufferTooShort {
329 need: total,
330 have: buf.len(),
331 what: "Partition Pack",
332 });
333 }
334 buf[0..16].copy_from_slice(&Self::key(self.kind, self.status));
335 let batch = serialize_uid_batch(&self.essence_containers);
336 let value_len = 2 + 2 + 4 + 8 + 8 + 8 + 8 + 8 + 4 + 8 + 4 + 16 + batch.len();
337 let len_size = encode_ber_length(value_len as u64, &mut buf[16..])?;
338 let mut pos = 16 + len_size;
339
340 buf[pos..pos + 2].copy_from_slice(&self.major_version.to_be_bytes());
341 pos += 2;
342 buf[pos..pos + 2].copy_from_slice(&self.minor_version.to_be_bytes());
343 pos += 2;
344 buf[pos..pos + 4].copy_from_slice(&self.kag_size.to_be_bytes());
345 pos += 4;
346 buf[pos..pos + 8].copy_from_slice(&self.this_partition.to_be_bytes());
347 pos += 8;
348 buf[pos..pos + 8].copy_from_slice(&self.previous_partition.to_be_bytes());
349 pos += 8;
350 buf[pos..pos + 8].copy_from_slice(&self.footer_partition.to_be_bytes());
351 pos += 8;
352 buf[pos..pos + 8].copy_from_slice(&self.header_byte_count.to_be_bytes());
353 pos += 8;
354 buf[pos..pos + 8].copy_from_slice(&self.index_byte_count.to_be_bytes());
355 pos += 8;
356 buf[pos..pos + 4].copy_from_slice(&self.index_sid.to_be_bytes());
357 pos += 4;
358 buf[pos..pos + 8].copy_from_slice(&self.body_offset.to_be_bytes());
359 pos += 8;
360 buf[pos..pos + 4].copy_from_slice(&self.body_sid.to_be_bytes());
361 pos += 4;
362 buf[pos..pos + 16].copy_from_slice(&self.operational_pattern);
363 pos += 16;
364 buf[pos..pos + batch.len()].copy_from_slice(&batch);
365 pos += batch.len();
366 Ok(pos)
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 fn sample(kind: PartitionKind, status: PartitionStatus) -> PartitionPack {
375 PartitionPack {
376 kind,
377 status,
378 major_version: 1,
379 minor_version: 3,
380 kag_size: 512,
381 this_partition: 0,
382 previous_partition: 0,
383 footer_partition: 12345,
384 header_byte_count: 1000,
385 index_byte_count: 0,
386 index_sid: 0,
387 body_offset: 0,
388 body_sid: 1,
389 operational_pattern: [0xAA; 16],
390 essence_containers: alloc::vec![[0xBBu8; 16]],
391 }
392 }
393
394 #[test]
395 fn header_partition_round_trip() {
396 let pp = sample(PartitionKind::Header, PartitionStatus::ClosedComplete);
397 let mut buf = alloc::vec![0u8; pp.serialized_len()];
398 pp.serialize_into(&mut buf).unwrap();
399 let parsed = PartitionPack::parse(&buf).unwrap();
400 assert_eq!(parsed, pp);
401 }
402
403 #[test]
404 fn body_partition_round_trip_empty_essence_containers() {
405 let mut pp = sample(PartitionKind::Body, PartitionStatus::OpenIncomplete);
406 pp.essence_containers.clear();
407 let mut buf = alloc::vec![0u8; pp.serialized_len()];
408 pp.serialize_into(&mut buf).unwrap();
409 let parsed = PartitionPack::parse(&buf).unwrap();
410 assert_eq!(parsed, pp);
411 assert!(parsed.essence_containers.is_empty());
412 }
413
414 #[test]
415 fn footer_partition_cannot_be_open() {
416 let pp = sample(PartitionKind::Footer, PartitionStatus::OpenComplete);
417 let mut buf = alloc::vec![0u8; pp.serialized_len()];
418 assert!(matches!(
419 pp.serialize_into(&mut buf),
420 Err(Error::OpenFooterPartition { .. })
421 ));
422 }
423
424 #[test]
425 fn parse_rejects_open_footer_key() {
426 let key = PartitionPack::key(PartitionKind::Footer, PartitionStatus::OpenComplete);
427 let mut bytes = alloc::vec::Vec::new();
428 bytes.extend_from_slice(&key);
429 bytes.push(0); assert!(matches!(
431 PartitionPack::parse(&bytes),
432 Err(Error::OpenFooterPartition { .. })
433 ));
434 }
435
436 #[test]
437 fn unknown_kind_byte_rejected() {
438 assert!(matches!(
439 PartitionKind::from_byte(0xFF),
440 Err(Error::UnknownPartitionKind { byte: 0xFF })
441 ));
442 }
443}