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::ul_bytes_from_prefix;
14
15const RIP_KEY_PREFIX: [u8; 7] = [0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01];
16const RIP_KEY_MID: [u8; 4] = [0x0D, 0x01, 0x02, 0x01];
17const RIP_KEY_TAIL: [u8; 2] = [0x11, 0x01];
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct PartitionLocation {
24 pub body_sid: u32,
26 pub byte_offset: u64,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Default)]
38pub struct RandomIndexPack {
39 pub partitions: Vec<PartitionLocation>,
41}
42
43impl RandomIndexPack {
44 #[must_use]
46 pub fn key() -> crate::types::UlBytes {
47 let mut key = [0u8; 16];
48 key[0..7].copy_from_slice(&RIP_KEY_PREFIX);
49 key[7] = 0x01; key[8..12].copy_from_slice(&RIP_KEY_MID);
51 key[12] = 0x01; key[13..15].copy_from_slice(&RIP_KEY_TAIL);
53 key[15] = 0x00; key
55 }
56
57 #[must_use]
60 pub fn is_rip_key(key: &[u8; 16]) -> bool {
61 key[0..7] == RIP_KEY_PREFIX
62 && key[8..12] == RIP_KEY_MID
63 && key[12] == 0x01
64 && key[13..15] == RIP_KEY_TAIL
65 }
66
67 fn check_key(key: &[u8; 16]) -> Result<()> {
68 if Self::is_rip_key(key) {
69 Ok(())
70 } else {
71 Err(Error::KeyPrefixMismatch {
72 what: "Random Index Pack (Table 29)",
73 })
74 }
75 }
76
77 pub fn parse_prefix(bytes: &[u8]) -> Result<(Self, usize)> {
80 if bytes.len() < 16 {
81 return Err(Error::BufferTooShort {
82 need: 16,
83 have: bytes.len(),
84 what: "Random Index Pack key",
85 });
86 }
87 let key: [u8; 16] = ul_bytes_from_prefix(bytes);
88 Self::check_key(&key)?;
89
90 let (len, len_size) = decode_ber_length(&bytes[16..])?;
91 let value_start = 16 + len_size;
92 let len = len as usize;
93 let value_end = value_start.checked_add(len).ok_or(Error::BufferTooShort {
94 need: usize::MAX,
95 have: bytes.len(),
96 what: "Random Index Pack value (length overflow)",
97 })?;
98 if bytes.len() < value_end {
99 return Err(Error::BufferTooShort {
100 need: value_end,
101 have: bytes.len(),
102 what: "Random Index Pack value",
103 });
104 }
105 let v = &bytes[value_start..value_end];
106 if v.len() < 4 || !(v.len() - 4).is_multiple_of(12) {
109 return Err(Error::InvalidBatchHeader {
110 count: 0,
111 item_len: 12,
112 buffer_len: v.len(),
113 });
114 }
115 let pairs_len = v.len() - 4;
116 let mut partitions = Vec::with_capacity(pairs_len / 12);
117 for chunk in v[..pairs_len].chunks_exact(12) {
118 let body_sid = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
119 let byte_offset = u64::from_be_bytes([
120 chunk[4], chunk[5], chunk[6], chunk[7], chunk[8], chunk[9], chunk[10], chunk[11],
121 ]);
122 partitions.push(PartitionLocation {
123 body_sid,
124 byte_offset,
125 });
126 }
127 let trailing = u32::from_be_bytes([
131 v[pairs_len],
132 v[pairs_len + 1],
133 v[pairs_len + 2],
134 v[pairs_len + 3],
135 ]);
136 let actual_total = value_end as u64;
137 if u64::from(trailing) != actual_total {
138 return Err(Error::InvalidPropertyLength {
139 tag: 0,
140 name: "Random Index Pack trailing Length",
141 found: trailing as usize,
142 expected: actual_total as usize,
143 });
144 }
145
146 Ok((RandomIndexPack { partitions }, value_end))
147 }
148}
149
150impl<'a> Parse<'a> for RandomIndexPack {
151 type Error = Error;
152
153 fn parse(bytes: &'a [u8]) -> Result<Self> {
154 let (rip, consumed) = Self::parse_prefix(bytes)?;
155 if consumed != bytes.len() {
156 return Err(Error::BufferTooShort {
157 need: consumed,
158 have: bytes.len(),
159 what: "Random Index Pack (trailing bytes after exact-fit parse)",
160 });
161 }
162 Ok(rip)
163 }
164}
165
166impl Serialize for RandomIndexPack {
167 type Error = Error;
168
169 fn serialized_len(&self) -> usize {
170 let value_len = self.partitions.len() * 12 + 4;
171 16 + ber_length_size(value_len as u64) + value_len
172 }
173
174 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
175 let total = self.serialized_len();
176 if buf.len() < total {
177 return Err(Error::BufferTooShort {
178 need: total,
179 have: buf.len(),
180 what: "Random Index Pack",
181 });
182 }
183 buf[0..16].copy_from_slice(&Self::key());
184 let value_len = self.partitions.len() * 12 + 4;
185 let len_size = encode_ber_length(value_len as u64, &mut buf[16..])?;
186 let mut pos = 16 + len_size;
187 for p in &self.partitions {
188 buf[pos..pos + 4].copy_from_slice(&p.body_sid.to_be_bytes());
189 pos += 4;
190 buf[pos..pos + 8].copy_from_slice(&p.byte_offset.to_be_bytes());
191 pos += 8;
192 }
193 buf[pos..pos + 4].copy_from_slice(&(total as u32).to_be_bytes());
194 pos += 4;
195 Ok(pos)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn rip_round_trip() {
205 let rip = RandomIndexPack {
206 partitions: alloc::vec![
207 PartitionLocation {
208 body_sid: 0,
209 byte_offset: 0,
210 },
211 PartitionLocation {
212 body_sid: 1,
213 byte_offset: 65536,
214 },
215 PartitionLocation {
216 body_sid: 0,
217 byte_offset: 131072,
218 },
219 ],
220 };
221 let mut buf = alloc::vec![0u8; rip.serialized_len()];
222 rip.serialize_into(&mut buf).unwrap();
223 let parsed = RandomIndexPack::parse(&buf).unwrap();
224 assert_eq!(parsed, rip);
225 let trailing_len = u32::from_be_bytes(buf[buf.len() - 4..].try_into().unwrap());
227 assert_eq!(trailing_len as usize, buf.len());
228 }
229
230 #[test]
231 fn empty_rip_round_trip() {
232 let rip = RandomIndexPack::default();
233 let mut buf = alloc::vec![0u8; rip.serialized_len()];
234 rip.serialize_into(&mut buf).unwrap();
235 assert_eq!(RandomIndexPack::parse(&buf).unwrap(), rip);
236 }
237
238 #[test]
239 fn wrong_trailing_length_rejected() {
240 let rip = RandomIndexPack {
241 partitions: alloc::vec![PartitionLocation {
242 body_sid: 1,
243 byte_offset: 100,
244 }],
245 };
246 let mut buf = alloc::vec![0u8; rip.serialized_len()];
247 rip.serialize_into(&mut buf).unwrap();
248 let last = buf.len() - 1;
249 buf[last] ^= 0xFF; assert!(matches!(
251 RandomIndexPack::parse(&buf),
252 Err(Error::InvalidPropertyLength { .. })
253 ));
254 }
255}