1use crate::Extents;
6use crate::reader::{BlockService, read_aligned_range};
7use delivery_blob::DataBuffer;
8use delivery_blob::compression::{CompressionInfo, StreamingDecompressor};
9use fuchsia_sync::Mutex;
10use std::cmp::min;
11use std::collections::HashMap;
12use std::ops::{ControlFlow, Range};
13use std::sync::Arc;
14
15pub struct Blob {
17 extents: Extents,
18 uncompressed_size: u64,
19 compression_info: Option<Arc<CompressionInfo>>,
20}
21
22impl Blob {
23 pub fn new(
24 extents: Extents,
25 uncompressed_size: u64,
26 compression_info: Option<CompressionInfo>,
27 ) -> Self {
28 Self { extents, uncompressed_size, compression_info: compression_info.map(Arc::new) }
29 }
30
31 pub fn extents(&self) -> &Extents {
33 &self.extents
34 }
35
36 pub fn uncompressed_size(&self) -> u64 {
38 self.uncompressed_size
39 }
40
41 pub fn compression_info(&self) -> Option<&CompressionInfo> {
43 self.compression_info.as_deref()
44 }
45
46 pub fn read_range(
53 &self,
54 range: Range<u64>,
55 service: &(impl BlockService + ?Sized),
56 mut dest_buf: impl DataBuffer,
57 ) {
58 if range.is_empty() {
59 return;
60 }
61
62 match &self.compression_info {
63 None => {
64 let mut current_offset = range.start;
65 let uncompressed_size = self.uncompressed_size;
66
67 read_aligned_range(&self.extents, range, service, move |res| {
68 let buffer = match res {
69 Ok(buf) => buf,
70 Err(_) => {
71 return ControlFlow::Break(());
72 }
73 };
74 let valid_len =
75 min(buffer.len() as u64, uncompressed_size.saturating_sub(current_offset))
76 as usize;
77 if valid_len > 0 {
78 dest_buf
79 .mut_ptr_slice()
80 .subslice_mut(0..valid_len)
81 .copy_from_ptr_slice(buffer.as_ptr_slice().subslice(0..valid_len));
82 if dest_buf.commit(valid_len).is_err() {
83 return ControlFlow::Break(());
84 }
85 }
86 current_offset += buffer.len() as u64;
87 ControlFlow::Continue(())
88 });
89 }
90 Some(info) => {
91 let info = Arc::clone(info);
92 let Ok((mut decompressor, aligned_range)) =
93 StreamingDecompressor::new(info, range, self.uncompressed_size, dest_buf)
94 else {
95 return;
98 };
99
100 read_aligned_range(&self.extents, aligned_range, service, move |res| {
101 let buffer = match res {
102 Ok(buf) => buf,
103 Err(_) => {
104 return ControlFlow::Break(());
105 }
106 };
107 if decompressor.push(buffer.as_ptr_slice()).is_err() {
108 return ControlFlow::Break(());
109 }
110 ControlFlow::Continue(())
111 });
112 }
113 }
114 }
115}
116
117#[derive(Default)]
119pub struct Blobs {
120 map: Mutex<HashMap<u64, Arc<Blob>>>,
121}
122
123impl Blobs {
124 pub fn new() -> Self {
126 Self::default()
127 }
128
129 pub fn insert(&self, key: u64, blob: Arc<Blob>) -> Option<Arc<Blob>> {
131 self.map.lock().insert(key, blob)
132 }
133
134 pub fn get(&self, key: u64) -> Option<Arc<Blob>> {
136 self.map.lock().get(&key).cloned()
137 }
138
139 pub fn remove(&self, key: u64) -> Option<Arc<Blob>> {
141 self.map.lock().remove(&key)
142 }
143
144 pub fn len(&self) -> usize {
146 self.map.lock().len()
147 }
148
149 pub fn is_empty(&self) -> bool {
151 self.map.lock().is_empty()
152 }
153
154 pub fn clear(&self) {
156 self.map.lock().clear();
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::reader::tests::FakeBlockService;
164 use crate::testing::TestVecBuffer;
165 use crate::{BLOCK_SIZE, Extent};
166 use anyhow::Error;
167 use delivery_blob::compression::{ChunkedArchiveOptions, CompressionAlgorithm};
168 use std::sync::Arc;
169
170 #[test]
171 fn test_read_range_uncompressed() {
172 let block_count = 8;
173 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
174 for (i, byte) in expected_data.iter_mut().enumerate() {
175 *byte = (i % 255) as u8;
176 }
177 let service = FakeBlockService::new(expected_data.clone());
178
179 let extents = Extents::encode_extents(&[Extent::new(0..(8 * BLOCK_SIZE), Some(0))]);
180 let extents = Extents::from_encoded(&extents).unwrap();
181 let blob = Arc::new(Blob::new(extents, 8 * BLOCK_SIZE, None));
182
183 let (dest_buf, rx) = TestVecBuffer::new(expected_data.len());
184 blob.read_range(0..(8 * BLOCK_SIZE), &service, dest_buf);
185
186 assert_eq!(rx.commits(), vec![(0, (8 * BLOCK_SIZE) as usize)]);
187 assert_eq!(rx.output(), expected_data);
188 }
189
190 #[test]
191 fn test_read_range_compressed_zstd() {
192 let uncompressed_size = 32768 * 2 + 1024;
193 let mut uncompressed_data = vec![0u8; uncompressed_size];
194 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
195 *byte = ((i * 7) % 255) as u8;
196 }
197
198 let options =
199 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
200 let archive =
201 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
202
203 let mut compressed_offsets = vec![0];
204 let mut compressed_data = vec![];
205 for chunk in archive.chunks() {
206 compressed_data.extend_from_slice(&chunk.compressed_data);
207 compressed_offsets.push(compressed_data.len() as u64);
208 }
209 compressed_offsets.pop();
210
211 let chunk_size = archive.chunk_size();
212 let stored_size = compressed_data.len() as u64;
213 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
214 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
215 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
216 let service = FakeBlockService::new(device_data);
217
218 let extents =
219 Extents::encode_extents(&[Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))]);
220 let extents = Extents::from_encoded(&extents).unwrap();
221 let compression_info = CompressionInfo::new(
222 chunk_size as u64,
223 stored_size,
224 &compressed_offsets,
225 CompressionAlgorithm::Zstd,
226 )
227 .unwrap();
228 let blob = Arc::new(Blob::new(extents, uncompressed_size as u64, Some(compression_info)));
229
230 let dest_alloc_size = uncompressed_size.next_multiple_of(chunk_size);
231 let (dest_buf, rx) = TestVecBuffer::new(dest_alloc_size);
232 blob.read_range(0..(uncompressed_size as u64), &service, dest_buf);
233
234 assert_eq!(
235 rx.commits(),
236 vec![(0, chunk_size), (chunk_size as u64, chunk_size), (chunk_size as u64 * 2, 1024)]
237 );
238 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
239 }
240
241 #[test]
242 fn test_read_range_compressed_lz4_split_across_buffers() {
243 let uncompressed_size = 32768 * 2;
244 let mut uncompressed_data = vec![0u8; uncompressed_size];
245 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
246 *byte = ((i * 13) % 255) as u8;
247 }
248
249 let options =
250 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Lz4 };
251 let archive =
252 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
253
254 let mut compressed_offsets = vec![0];
255 let mut compressed_data = vec![];
256 for chunk in archive.chunks() {
257 compressed_data.extend_from_slice(&chunk.compressed_data);
258 compressed_offsets.push(compressed_data.len() as u64);
259 }
260 compressed_offsets.pop();
261
262 let chunk_size = archive.chunk_size();
263 let stored_size = compressed_data.len() as u64;
264 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
265 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
266 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
267
268 let service = FakeBlockService::new_with_cap(device_data, Some(4096));
271
272 let extents =
273 Extents::encode_extents(&[Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))]);
274 let extents = Extents::from_encoded(&extents).unwrap();
275 let compression_info = CompressionInfo::new(
276 chunk_size as u64,
277 stored_size,
278 &compressed_offsets,
279 CompressionAlgorithm::Lz4,
280 )
281 .unwrap();
282 let blob = Arc::new(Blob::new(extents, uncompressed_size as u64, Some(compression_info)));
283
284 let (dest_buf, rx) = TestVecBuffer::new(uncompressed_size);
285 blob.read_range(0..(uncompressed_size as u64), &service, dest_buf);
286
287 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, chunk_size)]);
288 assert_eq!(rx.output(), uncompressed_data);
289 }
290
291 #[test]
292 fn test_read_range_invalid_range_noop() {
293 let service = FakeBlockService::new(vec![0u8; 8192]);
294 let extents = Extents::encode_extents(&[Extent::new(0..8192, Some(0))]);
295 let extents = Extents::from_encoded(&extents).unwrap();
296 let blob = Arc::new(Blob::new(extents, 8192, None));
297
298 let (dest_buf, rx) = TestVecBuffer::new_with_offset(0, 4096);
299 blob.read_range(4096..4096, &service, dest_buf);
301 assert_eq!(rx.commits().len(), 0);
302 }
303
304 #[test]
305 fn test_blob_getters() {
306 let extents_raw = Extents::encode_extents(&[Extent::new(0..8192, Some(0))]);
307 let extents = Extents::from_encoded(&extents_raw).unwrap();
308 let uncompressed_size = 8192u64;
309
310 let blob_uncompressed = Blob::new(extents, uncompressed_size, None);
311 assert_eq!(blob_uncompressed.uncompressed_size(), 8192);
312 assert!(blob_uncompressed.compression_info().is_none());
313
314 let compression_info =
315 CompressionInfo::new(32768, 4096, &[0], CompressionAlgorithm::Zstd).unwrap();
316 let blob_compressed = Blob::new(
317 Extents::from_encoded(&extents_raw).unwrap(),
318 uncompressed_size,
319 Some(compression_info),
320 );
321 assert!(blob_compressed.compression_info().is_some());
322 }
323
324 #[test]
325 fn test_read_range_block_service_error_returns_err() {
326 struct FailingBlockService;
327 impl BlockService for FailingBlockService {
328 fn allocate_buffer(&self, max_len: usize) -> storage_device::buffer::OwnedBuffer {
329 FakeBlockService::new(vec![0u8; max_len]).allocate_buffer(max_len)
330 }
331 fn read_blocks(
332 &self,
333 _device_offset: u64,
334 _dest_buffer: storage_device::buffer::OwnedBuffer,
335 _on_complete: Box<
336 dyn FnOnce(Result<storage_device::buffer::OwnedBuffer, Error>) + Send,
337 >,
338 ) -> Result<(), Error> {
339 Err(anyhow::anyhow!("block read failure"))
340 }
341 }
342
343 let extents = Extents::encode_extents(&[Extent::new(0..8192, Some(0))]);
344 let extents = Extents::from_encoded(&extents).unwrap();
345 let blob = Blob::new(extents, 8192, None);
346
347 let (dest_buf, rx) = TestVecBuffer::new(8192);
348
349 blob.read_range(0..8192, &FailingBlockService, dest_buf);
350 assert_eq!(rx.commits().len(), 0);
351 }
352
353 #[test]
354 fn test_read_range_uncompressed_multi_chunk() {
355 let block_count = 4;
356 let mut expected_data = vec![0u8; (block_count as u64 * BLOCK_SIZE) as usize];
357 for (i, byte) in expected_data.iter_mut().enumerate() {
358 *byte = ((i * 11) % 255) as u8;
359 }
360 let service = FakeBlockService::new_with_cap(expected_data.clone(), Some(4096));
363
364 let extents =
365 Extents::encode_extents(&[Extent::new(0..(block_count * BLOCK_SIZE), Some(0))]);
366 let extents = Extents::from_encoded(&extents).unwrap();
367 let blob = Blob::new(extents, block_count * BLOCK_SIZE, None);
368
369 let (dest_buf, rx) = TestVecBuffer::new(expected_data.len());
370 blob.read_range(0..(block_count * BLOCK_SIZE), &service, dest_buf);
371
372 assert_eq!(rx.commits().len(), 4);
373 assert_eq!(rx.output(), expected_data);
374 }
375
376 #[test]
377 fn test_read_range_uncompressed_unaligned_uncompressed_size() {
378 let uncompressed_size = 5000u64;
379 let mut expected_data = vec![0u8; 8192];
380 for (i, byte) in expected_data.iter_mut().enumerate() {
381 *byte = (i % 251) as u8;
382 }
383 let service = FakeBlockService::new(expected_data.clone());
384
385 let extents = Extents::encode_extents(&[Extent::new(0..8192, Some(0))]);
386 let extents = Extents::from_encoded(&extents).unwrap();
387 let blob = Blob::new(extents, uncompressed_size, None);
388
389 let (dest_buf, rx) = TestVecBuffer::new(8192);
390 blob.read_range(0..8192, &service, dest_buf);
391
392 assert_eq!(rx.commits(), vec![(0, 5000)]);
393 assert_eq!(&rx.output()[..5000], &expected_data[..5000]);
394 }
395
396 #[test]
397 fn test_read_range_compressed_tail_chunk_only() {
398 let uncompressed_size = 32768 * 2 + 1024;
399 let mut uncompressed_data = vec![0u8; uncompressed_size];
400 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
401 *byte = ((i * 7) % 251) as u8;
402 }
403
404 let options =
405 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
406 let archive =
407 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
408
409 let mut compressed_offsets = vec![0];
410 let mut compressed_data = vec![];
411 for chunk in archive.chunks() {
412 compressed_data.extend_from_slice(&chunk.compressed_data);
413 compressed_offsets.push(compressed_data.len() as u64);
414 }
415 compressed_offsets.pop();
416
417 let chunk_size = archive.chunk_size();
418 let stored_size = compressed_data.len() as u64;
419 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
420 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
421 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
422 let service = FakeBlockService::new(device_data);
423
424 let extents =
425 Extents::encode_extents(&[Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))]);
426 let extents = Extents::from_encoded(&extents).unwrap();
427 let compression_info = CompressionInfo::new(
428 chunk_size as u64,
429 stored_size,
430 &compressed_offsets,
431 CompressionAlgorithm::Zstd,
432 )
433 .unwrap();
434 let blob = Blob::new(extents, uncompressed_size as u64, Some(compression_info));
435
436 let tail_start = chunk_size as u64 * 2;
437 let (dest_buf, rx) = TestVecBuffer::new_with_offset(32768, tail_start);
438 blob.read_range(tail_start..(uncompressed_size as u64), &service, dest_buf);
439
440 assert_eq!(rx.commits(), vec![(tail_start, 1024)]);
441 assert_eq!(&rx.output()[..1024], &uncompressed_data[65536..]);
442 }
443
444 #[test]
445 fn test_read_range_compressed_partial_final_chunk_zero_tail() {
446 let uncompressed_size = 32768 + 1024;
447 let mut uncompressed_data = vec![0u8; uncompressed_size];
448 for (i, byte) in uncompressed_data.iter_mut().enumerate() {
449 *byte = ((i * 13) % 251) as u8;
450 }
451
452 let options =
453 ChunkedArchiveOptions::V3 { compression_algorithm: CompressionAlgorithm::Zstd };
454 let archive =
455 delivery_blob::compression::ChunkedArchive::new(&uncompressed_data, options).unwrap();
456
457 let mut compressed_offsets = vec![0];
458 let mut compressed_data = vec![];
459 for chunk in archive.chunks() {
460 compressed_data.extend_from_slice(&chunk.compressed_data);
461 compressed_offsets.push(compressed_data.len() as u64);
462 }
463 compressed_offsets.pop();
464
465 let chunk_size = archive.chunk_size();
466 let stored_size = compressed_data.len() as u64;
467 let stored_blocks = stored_size.div_ceil(BLOCK_SIZE);
468 let mut device_data = vec![0u8; (stored_blocks * BLOCK_SIZE) as usize];
469 device_data[..compressed_data.len()].copy_from_slice(&compressed_data);
470 let service = FakeBlockService::new(device_data);
471
472 let extents =
473 Extents::encode_extents(&[Extent::new(0..(stored_blocks * BLOCK_SIZE), Some(0))]);
474 let extents = Extents::from_encoded(&extents).unwrap();
475 let compression_info = CompressionInfo::new(
476 chunk_size as u64,
477 stored_size,
478 &compressed_offsets,
479 CompressionAlgorithm::Zstd,
480 )
481 .unwrap();
482 let blob = Blob::new(extents, uncompressed_size as u64, Some(compression_info));
483
484 let (mut dest_buf, rx) = TestVecBuffer::new(65536);
486 dest_buf.data.fill(0xFF);
487 blob.read_range(0..(uncompressed_size as u64), &service, dest_buf);
488
489 assert_eq!(rx.commits(), vec![(0, chunk_size), (chunk_size as u64, 1024)]);
490 assert_eq!(&rx.output()[..uncompressed_size], &uncompressed_data[..]);
491 assert_eq!(&rx.output()[uncompressed_size..65536], &[0u8; 31744]);
492 }
493
494 #[test]
495 fn test_blobs_registry() {
496 let extents = Extents::encode_extents(&[Extent::new(0..4096, Some(0))]);
497 let extents = Extents::from_encoded(&extents).unwrap();
498 let blob = Arc::new(Blob::new(extents, 4096, None));
499 let blobs = Blobs::new();
500
501 assert!(blobs.is_empty());
502 assert_eq!(blobs.len(), 0);
503 assert!(blobs.get(100).is_none());
504
505 blobs.insert(100, blob.clone());
506 assert_eq!(blobs.len(), 1);
507 assert!(!blobs.is_empty());
508 assert!(blobs.get(100).is_some());
509
510 blobs.remove(100);
511 assert!(blobs.is_empty());
512 }
513}