1use alloc::vec::Vec;
2
3use crate::msgpack::{self, Value};
4use crate::constants::{RESOURCE_HASHMAP_MAX_LEN, RESOURCE_MAPHASH_LEN};
5use super::types::{AdvFlags, ResourceError};
6
7#[derive(Debug, Clone)]
9pub struct ResourceAdvertisement {
10 pub transfer_size: u64,
12 pub data_size: u64,
14 pub num_parts: u64,
16 pub resource_hash: Vec<u8>,
18 pub random_hash: Vec<u8>,
20 pub original_hash: Vec<u8>,
22 pub hashmap: Vec<u8>,
24 pub flags: AdvFlags,
26 pub segment_index: u64,
28 pub total_segments: u64,
30 pub request_id: Option<Vec<u8>>,
32}
33
34impl ResourceAdvertisement {
35 pub fn pack(&self, segment: usize) -> Vec<u8> {
38 let hashmap_start = segment * RESOURCE_HASHMAP_MAX_LEN * RESOURCE_MAPHASH_LEN;
39 let max_end = (segment + 1) * RESOURCE_HASHMAP_MAX_LEN * RESOURCE_MAPHASH_LEN;
40 let hashmap_end = core::cmp::min(max_end, self.hashmap.len());
41 let hashmap_segment = if hashmap_start < self.hashmap.len() {
42 &self.hashmap[hashmap_start..hashmap_end]
43 } else {
44 &[]
45 };
46
47 let q_value = match &self.request_id {
48 Some(id) => Value::Bin(id.clone()),
49 None => Value::Nil,
50 };
51
52 let entries: Vec<(&str, Value)> = vec![
54 ("t", Value::UInt(self.transfer_size)),
55 ("d", Value::UInt(self.data_size)),
56 ("n", Value::UInt(self.num_parts)),
57 ("h", Value::Bin(self.resource_hash.clone())),
58 ("r", Value::Bin(self.random_hash.clone())),
59 ("o", Value::Bin(self.original_hash.clone())),
60 ("i", Value::UInt(self.segment_index)),
61 ("l", Value::UInt(self.total_segments)),
62 ("q", q_value),
63 ("f", Value::UInt(self.flags.to_byte() as u64)),
64 ("m", Value::Bin(hashmap_segment.to_vec())),
65 ];
66
67 msgpack::pack_str_map(&entries)
68 }
69
70 pub fn unpack(data: &[u8]) -> Result<Self, ResourceError> {
72 let value = msgpack::unpack_exact(data).map_err(|_| ResourceError::InvalidAdvertisement)?;
73
74 let t = value.map_get("t")
75 .and_then(|v| v.as_uint())
76 .ok_or(ResourceError::InvalidAdvertisement)?;
77 let d = value.map_get("d")
78 .and_then(|v| v.as_uint())
79 .ok_or(ResourceError::InvalidAdvertisement)?;
80 let n = value.map_get("n")
81 .and_then(|v| v.as_uint())
82 .ok_or(ResourceError::InvalidAdvertisement)?;
83 let h = value.map_get("h")
84 .and_then(|v| v.as_bin())
85 .ok_or(ResourceError::InvalidAdvertisement)?
86 .to_vec();
87 let r = value.map_get("r")
88 .and_then(|v| v.as_bin())
89 .ok_or(ResourceError::InvalidAdvertisement)?
90 .to_vec();
91 let o = value.map_get("o")
92 .and_then(|v| v.as_bin())
93 .ok_or(ResourceError::InvalidAdvertisement)?
94 .to_vec();
95 let m = value.map_get("m")
96 .and_then(|v| v.as_bin())
97 .ok_or(ResourceError::InvalidAdvertisement)?
98 .to_vec();
99 let f = value.map_get("f")
100 .and_then(|v| v.as_uint())
101 .ok_or(ResourceError::InvalidAdvertisement)? as u8;
102 let i = value.map_get("i")
103 .and_then(|v| v.as_uint())
104 .ok_or(ResourceError::InvalidAdvertisement)?;
105 let l = value.map_get("l")
106 .and_then(|v| v.as_uint())
107 .ok_or(ResourceError::InvalidAdvertisement)?;
108
109 let q_val = value.map_get("q").ok_or(ResourceError::InvalidAdvertisement)?;
110 let request_id = if q_val.is_nil() {
111 None
112 } else {
113 Some(q_val.as_bin().ok_or(ResourceError::InvalidAdvertisement)?.to_vec())
114 };
115
116 Ok(ResourceAdvertisement {
117 transfer_size: t,
118 data_size: d,
119 num_parts: n,
120 resource_hash: h,
121 random_hash: r,
122 original_hash: o,
123 hashmap: m,
124 flags: AdvFlags::from_byte(f),
125 segment_index: i,
126 total_segments: l,
127 request_id,
128 })
129 }
130
131 pub fn is_request(&self) -> bool {
133 self.request_id.is_some() && self.flags.is_request
134 }
135
136 pub fn is_response(&self) -> bool {
138 self.request_id.is_some() && self.flags.is_response
139 }
140
141 pub fn hashmap_segments(&self) -> usize {
143 let total_hashes = self.num_parts as usize;
144 if total_hashes == 0 {
145 return 1;
146 }
147 (total_hashes + RESOURCE_HASHMAP_MAX_LEN - 1) / RESOURCE_HASHMAP_MAX_LEN
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 fn make_adv(flags: AdvFlags) -> ResourceAdvertisement {
156 ResourceAdvertisement {
157 transfer_size: 1000,
158 data_size: 950,
159 num_parts: 3,
160 resource_hash: vec![0x11; 32],
161 random_hash: vec![0xAA, 0xBB, 0xCC, 0xDD],
162 original_hash: vec![0x22; 32],
163 hashmap: vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C],
164 flags,
165 segment_index: 1,
166 total_segments: 1,
167 request_id: None,
168 }
169 }
170
171 #[test]
172 fn test_pack_unpack_roundtrip() {
173 let flags = AdvFlags {
174 encrypted: true,
175 compressed: false,
176 split: false,
177 is_request: false,
178 is_response: false,
179 has_metadata: false,
180 };
181 let adv = make_adv(flags);
182 let packed = adv.pack(0);
183 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
184
185 assert_eq!(unpacked.transfer_size, 1000);
186 assert_eq!(unpacked.data_size, 950);
187 assert_eq!(unpacked.num_parts, 3);
188 assert_eq!(unpacked.resource_hash, vec![0x11; 32]);
189 assert_eq!(unpacked.random_hash, vec![0xAA, 0xBB, 0xCC, 0xDD]);
190 assert_eq!(unpacked.original_hash, vec![0x22; 32]);
191 assert_eq!(unpacked.flags, flags);
192 assert_eq!(unpacked.segment_index, 1);
193 assert_eq!(unpacked.total_segments, 1);
194 assert!(unpacked.request_id.is_none());
195 }
196
197 #[test]
198 fn test_flags_encrypted_compressed() {
199 let flags = AdvFlags {
200 encrypted: true,
201 compressed: true,
202 split: false,
203 is_request: false,
204 is_response: false,
205 has_metadata: false,
206 };
207 let adv = make_adv(flags);
208 let packed = adv.pack(0);
209 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
210 assert!(unpacked.flags.encrypted);
211 assert!(unpacked.flags.compressed);
212 assert!(!unpacked.flags.split);
213 }
214
215 #[test]
216 fn test_flags_with_metadata() {
217 let flags = AdvFlags {
218 encrypted: true,
219 compressed: false,
220 split: false,
221 is_request: false,
222 is_response: false,
223 has_metadata: true,
224 };
225 let adv = make_adv(flags);
226 let packed = adv.pack(0);
227 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
228 assert!(unpacked.flags.has_metadata);
229 }
230
231 #[test]
232 fn test_multi_segment() {
233 let flags = AdvFlags {
234 encrypted: true,
235 compressed: false,
236 split: true,
237 is_request: false,
238 is_response: false,
239 has_metadata: false,
240 };
241 let mut adv = make_adv(flags);
242 adv.segment_index = 2;
243 adv.total_segments = 5;
244 let packed = adv.pack(0);
245 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
246 assert!(unpacked.flags.split);
247 assert_eq!(unpacked.segment_index, 2);
248 assert_eq!(unpacked.total_segments, 5);
249 }
250
251 #[test]
252 fn test_with_request_id() {
253 let flags = AdvFlags {
254 encrypted: true,
255 compressed: false,
256 split: false,
257 is_request: true,
258 is_response: false,
259 has_metadata: false,
260 };
261 let mut adv = make_adv(flags);
262 adv.request_id = Some(vec![0xDE, 0xAD, 0xBE, 0xEF]);
263 let packed = adv.pack(0);
264 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
265 assert!(unpacked.is_request());
266 assert!(!unpacked.is_response());
267 assert_eq!(unpacked.request_id, Some(vec![0xDE, 0xAD, 0xBE, 0xEF]));
268 }
269
270 #[test]
271 fn test_is_response() {
272 let flags = AdvFlags {
273 encrypted: true,
274 compressed: false,
275 split: false,
276 is_request: false,
277 is_response: true,
278 has_metadata: false,
279 };
280 let mut adv = make_adv(flags);
281 adv.request_id = Some(vec![0x42; 16]);
282 assert!(adv.is_response());
283 assert!(!adv.is_request());
284 }
285
286 #[test]
287 fn test_nil_request_id() {
288 let flags = AdvFlags {
289 encrypted: true,
290 compressed: false,
291 split: false,
292 is_request: false,
293 is_response: false,
294 has_metadata: false,
295 };
296 let adv = make_adv(flags);
297 let packed = adv.pack(0);
298 let unpacked = ResourceAdvertisement::unpack(&packed).unwrap();
299 assert!(unpacked.request_id.is_none());
300 assert!(!unpacked.is_request());
301 assert!(!unpacked.is_response());
302 }
303
304 #[test]
305 fn test_hashmap_segmentation() {
306 let num_hashes = 100;
308 let hashmap: Vec<u8> = (0..num_hashes).flat_map(|i| vec![i as u8; 4]).collect();
309
310 let flags = AdvFlags {
311 encrypted: true,
312 compressed: false,
313 split: false,
314 is_request: false,
315 is_response: false,
316 has_metadata: false,
317 };
318 let adv = ResourceAdvertisement {
319 transfer_size: 50000,
320 data_size: 48000,
321 num_parts: num_hashes,
322 resource_hash: vec![0x11; 32],
323 random_hash: vec![0xAA; 4],
324 original_hash: vec![0x22; 32],
325 hashmap: hashmap.clone(),
326 flags,
327 segment_index: 1,
328 total_segments: 1,
329 request_id: None,
330 };
331
332 let packed0 = adv.pack(0);
334 let unpacked0 = ResourceAdvertisement::unpack(&packed0).unwrap();
335 assert_eq!(unpacked0.hashmap.len(), 74 * 4);
336
337 let packed1 = adv.pack(1);
339 let unpacked1 = ResourceAdvertisement::unpack(&packed1).unwrap();
340 assert_eq!(unpacked1.hashmap.len(), 26 * 4);
341 }
342
343 #[test]
344 fn test_hashmap_segments_count() {
345 let flags = AdvFlags {
346 encrypted: true, compressed: false, split: false,
347 is_request: false, is_response: false, has_metadata: false,
348 };
349 let mut adv = make_adv(flags);
350
351 adv.num_parts = 74; assert_eq!(adv.hashmap_segments(), 1);
353
354 adv.num_parts = 75;
355 assert_eq!(adv.hashmap_segments(), 2);
356
357 adv.num_parts = 148;
358 assert_eq!(adv.hashmap_segments(), 2);
359
360 adv.num_parts = 149;
361 assert_eq!(adv.hashmap_segments(), 3);
362 }
363
364 #[test]
365 fn test_unpack_invalid_data() {
366 assert!(ResourceAdvertisement::unpack(&[]).is_err());
367 assert!(ResourceAdvertisement::unpack(&[0xc0]).is_err()); assert!(ResourceAdvertisement::unpack(&[0x01, 0x02]).is_err()); }
370}