hotshot_types/data/
ns_table.rs1use std::ops::Range;
10
11const NUM_NSS_BYTE_LEN: usize = 4;
13const NS_OFFSET_BYTE_LEN: usize = 4;
14const NS_ID_BYTE_LEN: usize = 4;
15
16#[allow(clippy::single_range_in_vec_init)]
20pub fn parse_ns_table(payload_byte_len: usize, bytes: &[u8]) -> Vec<Range<usize>> {
21 let Some(offsets) = ns_table_offsets(bytes) else {
22 return single_ns_table(payload_byte_len);
23 };
24 if offsets.is_empty() && payload_byte_len == 0 {
26 return vec![(0..0)];
27 }
28 if offsets.last() != Some(&payload_byte_len) {
29 return single_ns_table(payload_byte_len);
30 }
31 let mut start = 0;
32 offsets
33 .into_iter()
34 .map(|end| {
35 let range = start..end;
36 start = end;
37 range
38 })
39 .collect()
40}
41
42pub fn ns_table_payload_byte_len(bytes: &[u8]) -> Option<usize> {
52 ns_table_offsets(bytes).map(|offsets| offsets.last().copied().unwrap_or(0))
53}
54
55fn ns_table_offsets(bytes: &[u8]) -> Option<Vec<usize>> {
59 const ENTRY_BYTE_LEN: usize = NS_ID_BYTE_LEN + NS_OFFSET_BYTE_LEN;
60 if bytes.len() < NUM_NSS_BYTE_LEN
61 || !(bytes.len() - NUM_NSS_BYTE_LEN).is_multiple_of(ENTRY_BYTE_LEN)
62 {
63 return None;
64 }
65 let num_entries = u32::from_le_bytes(bytes[..NUM_NSS_BYTE_LEN].try_into().unwrap()) as usize;
66 if num_entries != (bytes.len() - NUM_NSS_BYTE_LEN) / ENTRY_BYTE_LEN {
67 return None;
68 }
69 let mut offsets = Vec::with_capacity(num_entries);
70 let mut last = 0;
71 for i in 0..num_entries {
72 let offset = NUM_NSS_BYTE_LEN + i * ENTRY_BYTE_LEN + NS_ID_BYTE_LEN;
73 let end = u32::from_le_bytes(
74 bytes[offset..offset + NS_OFFSET_BYTE_LEN]
75 .try_into()
76 .unwrap(),
77 ) as usize;
78 if end < last {
79 return None;
80 }
81 offsets.push(end);
82 last = end;
83 }
84 Some(offsets)
85}
86
87#[allow(clippy::single_range_in_vec_init)]
88fn single_ns_table(payload_byte_len: usize) -> Vec<Range<usize>> {
89 tracing::debug!(
90 "Failed to parse the metadata as namespace table. Use a single namespace table instead."
91 );
92 vec![(0..payload_byte_len)]
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 fn ns_table(offsets: &[u32]) -> Vec<u8> {
101 let mut bytes = (offsets.len() as u32).to_le_bytes().to_vec();
102 for (id, offset) in offsets.iter().enumerate() {
103 bytes.extend_from_slice(&(id as u32).to_le_bytes());
104 bytes.extend_from_slice(&offset.to_le_bytes());
105 }
106 bytes
107 }
108
109 fn miscounted() -> Vec<u8> {
111 let mut bytes = ns_table(&[30, 100]);
112 bytes[..NUM_NSS_BYTE_LEN].copy_from_slice(&1u32.to_le_bytes());
113 bytes
114 }
115
116 #[test]
117 fn parse_ns_table_slices_a_table_sized_for_the_payload() {
118 assert_eq!(
119 parse_ns_table(100, &ns_table(&[30, 100])),
120 vec![0..30, 30..100]
121 );
122 assert_eq!(
123 parse_ns_table(100, &ns_table(&[0, 100])),
124 vec![0..0, 0..100]
125 );
126 assert_eq!(parse_ns_table(100, &ns_table(&[100])), vec![0..100]);
127 assert_eq!(parse_ns_table(0, &ns_table(&[])), vec![0..0]);
128 }
129
130 #[test]
131 fn parse_ns_table_falls_back_to_one_namespace() {
132 assert_eq!(parse_ns_table(100, &[]), vec![0..100]);
134 assert_eq!(
135 parse_ns_table(100, &ns_table(&[30, 100])[..9]),
136 vec![0..100]
137 );
138 assert_eq!(parse_ns_table(100, &miscounted()), vec![0..100]);
139 assert_eq!(parse_ns_table(100, &ns_table(&[50, 30])), vec![0..100]);
140 assert_eq!(parse_ns_table(100, &ns_table(&[30, 90])), vec![0..100]);
142 assert_eq!(parse_ns_table(100, &ns_table(&[30, 110])), vec![0..100]);
143 assert_eq!(parse_ns_table(100, &ns_table(&[])), vec![0..100]);
144 }
145
146 #[test]
147 fn ns_table_payload_byte_len_is_the_final_offset_of_a_table() {
148 assert_eq!(ns_table_payload_byte_len(&ns_table(&[30, 90])), Some(90));
149 assert_eq!(ns_table_payload_byte_len(&ns_table(&[100])), Some(100));
150 assert_eq!(ns_table_payload_byte_len(&ns_table(&[])), Some(0));
151 assert_eq!(ns_table_payload_byte_len(&[]), None);
152 assert_eq!(ns_table_payload_byte_len(&ns_table(&[30, 100])[..9]), None);
153 assert_eq!(ns_table_payload_byte_len(&miscounted()), None);
154 assert_eq!(ns_table_payload_byte_len(&ns_table(&[50, 30])), None);
155 }
156}