1use crate::BLOCK_SIZE;
6use anyhow::{Error, anyhow, ensure};
7use std::borrow::Borrow;
8use std::ops::Range;
9
10const TYPE_MASK: u64 = 0xc0000000_00000000;
11const REGULAR: u64 = 0x00000000_00000000;
12const INLINE_TAIL: u64 = 0x40000000_00000000;
13const SPARSE: u64 = 0x80000000_00000000;
14
15const MAX_REGULAR_EXTENT_BLOCKS: u64 = 0x3fff_ffff;
22
23const MAX_INLINE_TAIL_OFFSET: u64 = 0xfff_ffff_ffff;
26
27const MAX_SPARSE_EXTENT_BLOCKS: u64 = !TYPE_MASK;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum DeviceExtent {
33 Sparse,
35 Block(u64),
37 InlineTail(u64),
39}
40
41impl From<Option<u64>> for DeviceExtent {
42 fn from(offset: Option<u64>) -> Self {
43 match offset {
44 Some(offset) => Self::Block(offset),
45 None => Self::Sparse,
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Extent {
54 logical_range: Range<u64>,
55 device_extent: DeviceExtent,
56}
57
58impl Extent {
59 pub fn logical_range(&self) -> Range<u64> {
61 self.logical_range.clone()
62 }
63
64 pub fn device_extent(&self) -> DeviceExtent {
66 self.device_extent
67 }
68
69 pub fn device_offset(&self) -> Option<u64> {
71 match self.device_extent {
72 DeviceExtent::Sparse => None,
73 DeviceExtent::Block(offset) | DeviceExtent::InlineTail(offset) => Some(offset),
74 }
75 }
76
77 pub fn is_sparse(&self) -> bool {
79 matches!(self.device_extent, DeviceExtent::Sparse)
80 }
81
82 pub fn len(&self) -> u64 {
84 self.logical_range.end - self.logical_range.start
85 }
86
87 pub fn new(logical_range: Range<u64>, device_extent: impl Into<DeviceExtent>) -> Self {
95 Self::try_new(logical_range, device_extent).unwrap()
96 }
97
98 pub fn try_new(
100 logical_range: Range<u64>,
101 device_extent: impl Into<DeviceExtent>,
102 ) -> Result<Self, Error> {
103 let device_extent = device_extent.into();
104 ensure!(
105 logical_range.start % BLOCK_SIZE == 0 && logical_range.end % BLOCK_SIZE == 0,
106 "logical_range boundaries must be a multiple of BLOCK_SIZE (4096 bytes), got {:?}",
107 logical_range
108 );
109 ensure!(
110 logical_range.start <= logical_range.end,
111 "logical_range.start must be <= logical_range.end, got {:?}",
112 logical_range
113 );
114 let len = logical_range.end - logical_range.start;
115 match device_extent {
116 DeviceExtent::Sparse => {}
117 DeviceExtent::Block(dev_offset) => {
118 ensure!(
119 dev_offset.checked_add(len).is_some(),
120 "device_offset + extent length overflows u64"
121 );
122 }
123 DeviceExtent::InlineTail(dev_offset) => {
124 ensure!(
125 len == BLOCK_SIZE,
126 "Inline tail extent must be exactly 1 block ({BLOCK_SIZE} bytes), got {len}"
127 );
128 ensure!(
129 dev_offset.checked_add(BLOCK_SIZE).is_some(),
130 "device_offset + BLOCK_SIZE overflows u64"
131 );
132 }
133 }
134 Ok(Self { logical_range, device_extent })
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142struct ExtentEntry {
143 end_logical_offset: u64,
144 device_offset: u64, }
146
147impl ExtentEntry {
148 const SPARSE_DEVICE_OFFSET: u64 = u64::MAX;
149
150 fn is_sparse(&self) -> bool {
151 self.device_offset == Self::SPARSE_DEVICE_OFFSET
152 }
153}
154
155fn encode_regular(length_blocks: u32, target_block: u32) -> u64 {
156 REGULAR | ((length_blocks as u64 & MAX_REGULAR_EXTENT_BLOCKS) << 32) | (target_block as u64)
157}
158
159fn encode_inline_tail(relative_offset: u64) -> u64 {
160 INLINE_TAIL | (relative_offset & MAX_INLINE_TAIL_OFFSET)
161}
162
163fn encode_sparse(length_blocks: u64) -> u64 {
164 SPARSE | (length_blocks & !TYPE_MASK)
165}
166
167#[derive(Debug, Clone)]
169pub struct ExtentsIterator<'a> {
170 extents: &'a Extents,
171 index: usize,
172}
173
174impl<'a> Iterator for ExtentsIterator<'a> {
175 type Item = Extent;
176
177 fn next(&mut self) -> Option<Self::Item> {
178 if self.index < self.extents.entries.len() {
179 let res = self.extents.entry_to_result(self.index);
180 self.index += 1;
181 Some(res)
182 } else {
183 None
184 }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Default)]
192pub struct Extents {
193 base_device_offset: u64,
194 entries: Box<[ExtentEntry]>,
195}
196
197impl Extents {
198 pub fn base_device_offset(&self) -> u64 {
200 self.base_device_offset
201 }
202
203 pub fn len(&self) -> usize {
205 self.entries.len()
206 }
207
208 pub fn is_empty(&self) -> bool {
210 self.entries.is_empty()
211 }
212
213 pub fn try_new(
216 extents: impl IntoIterator<Item = impl Borrow<Extent>>,
217 base_device_offset: u64,
218 ) -> Result<Self, Error> {
219 let iter = extents.into_iter();
220 let (lower_bound, _) = iter.size_hint();
221 let mut entries = Vec::with_capacity(lower_bound);
222 let mut current_logical_offset = 0u64;
223
224 for extent in iter {
225 let extent = extent.borrow();
226 ensure!(
227 extent.logical_range.start == current_logical_offset,
228 "Extents must be contiguous and start at 0: expected start \
229 {current_logical_offset}, got {}",
230 extent.logical_range.start
231 );
232 ensure!(
233 extent.logical_range.start < extent.logical_range.end,
234 "Extent logical range must be non-empty, got {:?}",
235 extent.logical_range
236 );
237 ensure!(
238 extent.logical_range.start % BLOCK_SIZE == 0
239 && extent.logical_range.end % BLOCK_SIZE == 0,
240 "logical_range boundaries must be a multiple of BLOCK_SIZE ({BLOCK_SIZE} bytes), \
241 got {:?}",
242 extent.logical_range
243 );
244 let length_blocks = extent.len() / BLOCK_SIZE;
245 match extent.device_extent {
246 DeviceExtent::InlineTail(dev_offset) => {
247 ensure!(
248 dev_offset >= base_device_offset,
249 "device_offset ({dev_offset}) must be >= base_device_offset \
250 ({base_device_offset})"
251 );
252 ensure!(
253 length_blocks == 1,
254 "Inline tail extent must be exactly 1 block ({BLOCK_SIZE} bytes)"
255 );
256 let relative_offset = dev_offset - base_device_offset;
257 ensure!(
258 relative_offset <= MAX_INLINE_TAIL_OFFSET,
259 "Relative device byte offset exceeds 44-bit maximum"
260 );
261 current_logical_offset += BLOCK_SIZE;
262 entries.push(ExtentEntry {
263 end_logical_offset: current_logical_offset,
264 device_offset: dev_offset,
265 });
266 }
267 DeviceExtent::Block(dev_offset) => {
268 ensure!(
269 dev_offset >= base_device_offset,
270 "device_offset ({dev_offset}) must be >= base_device_offset \
271 ({base_device_offset})"
272 );
273 let relative_offset = dev_offset - base_device_offset;
274 ensure!(
275 relative_offset % BLOCK_SIZE == 0,
276 "Relative device offset ({dev_offset} - {base_device_offset} = \
277 {relative_offset}) must be a multiple of BLOCK_SIZE ({BLOCK_SIZE} bytes)"
278 );
279 let mut remaining_blocks = length_blocks;
280 let mut current_dev_offset = dev_offset;
281 while remaining_blocks > 0 {
282 let entry_blocks =
283 std::cmp::min(remaining_blocks, MAX_REGULAR_EXTENT_BLOCKS);
284 let entry_bytes = entry_blocks * BLOCK_SIZE;
285 let entry_relative_offset = current_dev_offset - base_device_offset;
286 let target_block = entry_relative_offset / BLOCK_SIZE;
287 ensure!(
288 target_block <= u32::MAX as u64,
289 "Relative device offset block index exceeds u32::MAX"
290 );
291 current_logical_offset += entry_bytes;
292 entries.push(ExtentEntry {
293 end_logical_offset: current_logical_offset,
294 device_offset: current_dev_offset,
295 });
296 current_dev_offset =
297 current_dev_offset.checked_add(entry_bytes).ok_or_else(|| {
298 anyhow!("device_offset + extent length overflows u64")
299 })?;
300 remaining_blocks -= entry_blocks;
301 }
302 }
303 DeviceExtent::Sparse => {
304 ensure!(
305 length_blocks <= MAX_SPARSE_EXTENT_BLOCKS,
306 "Extent length bounds exceed maximum encodeable length"
307 );
308 current_logical_offset = extent.logical_range.end;
309 entries.push(ExtentEntry {
310 end_logical_offset: current_logical_offset,
311 device_offset: ExtentEntry::SPARSE_DEVICE_OFFSET,
312 });
313 }
314 }
315 }
316
317 Ok(Self { base_device_offset, entries: entries.into_boxed_slice() })
318 }
319
320 pub fn encode(&self) -> impl Iterator<Item = u64> + '_ {
323 let mut prev_logical = 0u64;
324 self.entries.iter().map(move |entry| {
325 let length_blocks = (entry.end_logical_offset - prev_logical) / BLOCK_SIZE;
326 prev_logical = entry.end_logical_offset;
327 if entry.is_sparse() {
328 encode_sparse(length_blocks)
329 } else {
330 let relative_offset = entry.device_offset - self.base_device_offset;
331 if relative_offset % BLOCK_SIZE != 0 {
332 encode_inline_tail(relative_offset)
333 } else {
334 let target_block = (relative_offset / BLOCK_SIZE) as u32;
335 encode_regular(length_blocks as u32, target_block)
336 }
337 }
338 })
339 }
340
341 pub fn encode_extents(extents: &Extents) -> impl Iterator<Item = u64> + '_ {
343 extents.encode()
344 }
345
346 pub fn encode_extents_with_base_offset(extents: &Extents) -> impl Iterator<Item = u64> + '_ {
349 extents.encode()
350 }
351
352 pub fn from_encoded(
357 encoded: impl IntoIterator<Item = u64>,
358 base_device_offset: u64,
359 ) -> Option<Self> {
360 let iter = encoded.into_iter();
361 let (lower_bound, _) = iter.size_hint();
362 let mut entries = Vec::with_capacity(lower_bound);
363 let mut current_logical_offset = 0u64;
364
365 for val in iter {
366 let kind = val & TYPE_MASK;
367 if kind == REGULAR {
368 let length_blocks = ((val & !TYPE_MASK) >> 32) as u64;
369 let target_block = (val & 0xffff_ffff) as u64;
370 let length_bytes = length_blocks.checked_mul(BLOCK_SIZE)?;
371 current_logical_offset = current_logical_offset.checked_add(length_bytes)?;
372 let device_offset =
373 base_device_offset.checked_add(target_block.checked_mul(BLOCK_SIZE)?)?;
374 entries.push(ExtentEntry {
375 end_logical_offset: current_logical_offset,
376 device_offset,
377 });
378 } else if kind == INLINE_TAIL {
379 let relative_offset = val & !TYPE_MASK;
380 if relative_offset % BLOCK_SIZE == 0 || relative_offset > MAX_INLINE_TAIL_OFFSET {
381 return None;
382 }
383 current_logical_offset = current_logical_offset.checked_add(BLOCK_SIZE)?;
384 let device_offset = base_device_offset.checked_add(relative_offset)?;
385 entries.push(ExtentEntry {
386 end_logical_offset: current_logical_offset,
387 device_offset,
388 });
389 } else if kind == SPARSE {
390 let length_blocks = (val & !TYPE_MASK) as u64;
391 let length_bytes = length_blocks.checked_mul(BLOCK_SIZE)?;
392 current_logical_offset = current_logical_offset.checked_add(length_bytes)?;
393 entries.push(ExtentEntry {
394 end_logical_offset: current_logical_offset,
395 device_offset: ExtentEntry::SPARSE_DEVICE_OFFSET,
396 });
397 } else {
398 return None;
399 }
400 }
401
402 Some(Self { base_device_offset, entries: entries.into_boxed_slice() })
403 }
404
405 pub fn iter_extents(&self, start_offset: u64) -> ExtentsIterator<'_> {
408 let index = self.entries.partition_point(|e| e.end_logical_offset <= start_offset);
409 ExtentsIterator { extents: self, index }
410 }
411
412 pub fn map(&self, offset: u64) -> Option<Extent> {
419 assert!(
420 offset % BLOCK_SIZE == 0,
421 "offset must be a multiple of BLOCK_SIZE (4096 bytes), got {offset}"
422 );
423 let idx = self.entries.partition_point(|e| e.end_logical_offset <= offset);
424 if idx >= self.entries.len() {
425 return None;
426 }
427
428 let entry = &self.entries[idx];
429 let start_logical = self.entry_start_offset(idx);
430 let end_logical = entry.end_logical_offset;
431
432 let offset_within = offset.checked_sub(start_logical)?;
433 let device_extent = if entry.is_sparse() {
434 DeviceExtent::Sparse
435 } else {
436 let dev_offset = entry.device_offset.checked_add(offset_within)?;
437 if (dev_offset - self.base_device_offset) % BLOCK_SIZE != 0 {
438 DeviceExtent::InlineTail(dev_offset)
439 } else {
440 DeviceExtent::Block(dev_offset)
441 }
442 };
443
444 Some(Extent { logical_range: offset..end_logical, device_extent })
445 }
446
447 pub fn mappings(&self) -> Vec<Extent> {
449 (0..self.entries.len()).map(|i| self.entry_to_result(i)).collect()
450 }
451
452 fn entry_start_offset(&self, idx: usize) -> u64 {
453 if idx == 0 { 0 } else { self.entries[idx - 1].end_logical_offset }
454 }
455
456 fn entry_to_result(&self, idx: usize) -> Extent {
457 let entry = &self.entries[idx];
458 let start_logical = self.entry_start_offset(idx);
459 let end_logical = entry.end_logical_offset;
460 let device_extent = if entry.is_sparse() {
461 DeviceExtent::Sparse
462 } else if (entry.device_offset - self.base_device_offset) % BLOCK_SIZE != 0 {
463 DeviceExtent::InlineTail(entry.device_offset)
464 } else {
465 DeviceExtent::Block(entry.device_offset)
466 };
467 Extent { logical_range: start_logical..end_logical, device_extent }
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 #[test]
476 fn test_encode_decode_regular() {
477 let extents = Extents::try_new(
478 [
479 Extent::new(0..(4 * BLOCK_SIZE), Some(10 * BLOCK_SIZE)),
480 Extent::new((4 * BLOCK_SIZE)..(6 * BLOCK_SIZE), Some(100 * BLOCK_SIZE)),
481 ],
482 0,
483 )
484 .unwrap();
485 let encoded = Extents::encode_extents(&extents);
486 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
487
488 let decoded = extents_container.mappings();
489 assert_eq!(decoded.len(), 2);
490
491 assert_eq!(decoded[0].logical_range, 0..(4 * BLOCK_SIZE));
492 assert_eq!(decoded[0].device_offset(), Some(10 * BLOCK_SIZE));
493
494 assert_eq!(decoded[1].logical_range, (4 * BLOCK_SIZE)..(6 * BLOCK_SIZE));
495 assert_eq!(decoded[1].device_offset(), Some(100 * BLOCK_SIZE));
496 }
497
498 #[test]
499 fn test_encode_decode_sparse() {
500 let extents = Extents::try_new(
501 [
502 Extent::new(0..(2 * BLOCK_SIZE), Some(50 * BLOCK_SIZE)),
503 Extent::new((2 * BLOCK_SIZE)..(5 * BLOCK_SIZE), None),
504 Extent::new((5 * BLOCK_SIZE)..(6 * BLOCK_SIZE), Some(200 * BLOCK_SIZE)),
505 ],
506 0,
507 )
508 .unwrap();
509 let encoded = Extents::encode_extents(&extents);
510 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
511
512 let decoded = extents_container.mappings();
513 assert_eq!(decoded.len(), 3);
514
515 assert_eq!(decoded[0].logical_range, 0..(2 * BLOCK_SIZE));
516 assert_eq!(decoded[0].device_offset(), Some(50 * BLOCK_SIZE));
517
518 assert_eq!(decoded[1].logical_range, (2 * BLOCK_SIZE)..(5 * BLOCK_SIZE));
519 assert_eq!(decoded[1].device_offset(), None);
520
521 assert_eq!(decoded[2].logical_range, (5 * BLOCK_SIZE)..(6 * BLOCK_SIZE));
522 assert_eq!(decoded[2].device_offset(), Some(200 * BLOCK_SIZE));
523 }
524
525 #[test]
526 fn test_encode_decode_non_aligned_start() {
527 let base_device_offset = 17408u64; let extents = Extents::try_new(
529 [
530 Extent::new(0..(4 * BLOCK_SIZE), Some(base_device_offset)),
531 Extent::new(
532 (4 * BLOCK_SIZE)..(6 * BLOCK_SIZE),
533 Some(base_device_offset + 100 * BLOCK_SIZE),
534 ),
535 ],
536 base_device_offset,
537 )
538 .unwrap();
539 let encoded = Extents::encode_extents_with_base_offset(&extents);
540 let extents_container =
541 Extents::from_encoded(encoded, base_device_offset).expect("from_encoded failed");
542
543 let decoded = extents_container.mappings();
544 assert_eq!(decoded.len(), 2);
545 assert_eq!(decoded[0].logical_range, 0..(4 * BLOCK_SIZE));
546 assert_eq!(decoded[0].device_offset(), Some(base_device_offset));
547 assert_eq!(decoded[1].logical_range, (4 * BLOCK_SIZE)..(6 * BLOCK_SIZE));
548 assert_eq!(decoded[1].device_offset(), Some(base_device_offset + 100 * BLOCK_SIZE));
549
550 let mapped = extents_container.map(0).expect("should map at 0");
551 assert_eq!(mapped.device_offset(), Some(base_device_offset));
552
553 let mapped_next = extents_container.map(4 * BLOCK_SIZE).expect("should map next");
554 assert_eq!(mapped_next.device_offset(), Some(base_device_offset + 100 * BLOCK_SIZE));
555 }
556
557 #[test]
558 fn test_extents_validation_relative_offset_unaligned_fails() {
559 let base_device_offset = 17408u64;
560 let result = Extents::try_new(
562 [Extent::new(0..BLOCK_SIZE, Some(base_device_offset + 500))],
563 base_device_offset,
564 );
565 assert!(result.is_err());
566 assert!(
567 result.unwrap_err().to_string().contains("Relative device offset"),
568 "Error should mention relative device offset"
569 );
570 }
571
572 #[test]
573 fn test_extents_validation_device_offset_smaller_than_base_fails() {
574 let base_device_offset = 17408u64;
575 let result = Extents::try_new([Extent::new(0..BLOCK_SIZE, Some(0))], base_device_offset);
576 assert!(result.is_err());
577 assert!(
578 result.unwrap_err().to_string().contains("must be >= base_device_offset"),
579 "Error should mention dev_offset >= base_device_offset"
580 );
581 }
582
583 #[test]
584 fn test_extents_validation_non_contiguous_logical_fails() {
585 let result = Extents::try_new(
586 [
587 Extent::new(0..BLOCK_SIZE, Some(0)),
588 Extent::new((2 * BLOCK_SIZE)..(3 * BLOCK_SIZE), Some(BLOCK_SIZE)),
589 ],
590 0,
591 );
592 assert!(result.is_err());
593 assert!(
594 result.unwrap_err().to_string().contains("must be contiguous and start at 0"),
595 "Error should mention non-contiguous start"
596 );
597 }
598
599 #[test]
600 fn test_binary_search_map_logical_offset() {
601 let extents = Extents::try_new(
602 [
603 Extent::new(0..(10 * BLOCK_SIZE), Some(100 * BLOCK_SIZE)),
604 Extent::new((10 * BLOCK_SIZE)..(20 * BLOCK_SIZE), Some(200 * BLOCK_SIZE)),
605 Extent::new((20 * BLOCK_SIZE)..(30 * BLOCK_SIZE), Some(300 * BLOCK_SIZE)),
606 ],
607 0,
608 )
609 .unwrap();
610 let encoded = Extents::encode_extents(&extents);
611 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
612
613 let mapped = extents_container.map(0).expect("should map at offset 0");
614 assert_eq!(mapped.logical_range, 0..(10 * BLOCK_SIZE));
615 assert_eq!(mapped.device_offset(), Some(100 * BLOCK_SIZE));
616
617 let mapped_mid = extents_container
618 .map(12 * BLOCK_SIZE)
619 .expect("should map inside second extent via binary search");
620 assert_eq!(mapped_mid.logical_range, (12 * BLOCK_SIZE)..(20 * BLOCK_SIZE));
621 assert_eq!(mapped_mid.device_offset(), Some(202 * BLOCK_SIZE));
622 }
623
624 #[test]
625 fn test_map_out_of_bounds() {
626 let extents =
627 Extents::try_new([Extent::new(0..(2 * BLOCK_SIZE), Some(10 * BLOCK_SIZE))], 0).unwrap();
628 let encoded = Extents::encode_extents(&extents);
629 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
630
631 assert!(extents_container.map(2 * BLOCK_SIZE).is_none());
632 assert!(extents_container.map(100 * BLOCK_SIZE).is_none());
633 }
634
635 #[test]
636 fn test_binary_search_iter_extents() {
637 let extents = Extents::try_new(
638 [
639 Extent::new(0..(2 * BLOCK_SIZE), Some(10 * BLOCK_SIZE)),
640 Extent::new((2 * BLOCK_SIZE)..(4 * BLOCK_SIZE), Some(20 * BLOCK_SIZE)),
641 Extent::new((4 * BLOCK_SIZE)..(6 * BLOCK_SIZE), Some(30 * BLOCK_SIZE)),
642 ],
643 0,
644 )
645 .unwrap();
646 let encoded = Extents::encode_extents(&extents);
647 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
648
649 let results: Vec<_> = extents_container.iter_extents(3 * BLOCK_SIZE).collect();
650 assert_eq!(results.len(), 2);
651 assert_eq!(results[0].logical_range, (2 * BLOCK_SIZE)..(4 * BLOCK_SIZE));
652 assert_eq!(results[1].logical_range, (4 * BLOCK_SIZE)..(6 * BLOCK_SIZE));
653 }
654
655 #[test]
656 fn test_exact_boundary_queries() {
657 let extents = Extents::try_new(
658 [
659 Extent::new(0..(10 * BLOCK_SIZE), Some(100 * BLOCK_SIZE)),
660 Extent::new((10 * BLOCK_SIZE)..(20 * BLOCK_SIZE), Some(200 * BLOCK_SIZE)),
661 ],
662 0,
663 )
664 .unwrap();
665 let encoded = Extents::encode_extents(&extents);
666 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
667
668 let mapped = extents_container.map(10 * BLOCK_SIZE).expect("should map at exact boundary");
669 assert_eq!(mapped.logical_range, (10 * BLOCK_SIZE)..(20 * BLOCK_SIZE));
670 assert_eq!(mapped.device_offset(), Some(200 * BLOCK_SIZE));
671
672 let results: Vec<_> = extents_container.iter_extents(10 * BLOCK_SIZE).collect();
673 assert_eq!(results.len(), 1);
674 assert_eq!(results[0].logical_range, (10 * BLOCK_SIZE)..(20 * BLOCK_SIZE));
675 }
676
677 #[test]
678 #[should_panic(expected = "multiple of BLOCK_SIZE")]
679 fn test_extent_new_unaligned_logical_start_panics() {
680 Extent::new(1..(2 * BLOCK_SIZE), Some(10 * BLOCK_SIZE));
681 }
682
683 #[test]
684 #[should_panic(expected = "multiple of BLOCK_SIZE")]
685 fn test_extent_new_unaligned_logical_end_panics() {
686 Extent::new(0..(2 * BLOCK_SIZE + 1), Some(10 * BLOCK_SIZE));
687 }
688
689 #[test]
690 #[should_panic(expected = "multiple of BLOCK_SIZE")]
691 fn test_map_unaligned_offset_panics() {
692 Extents::default().map(500);
693 }
694
695 #[test]
696 fn test_iter_extents_unaligned_start_offset() {
697 let extents = Extents::try_new(
698 [
699 Extent::new(0..(10 * BLOCK_SIZE), Some(100 * BLOCK_SIZE)),
700 Extent::new((10 * BLOCK_SIZE)..(20 * BLOCK_SIZE), Some(200 * BLOCK_SIZE)),
701 ],
702 0,
703 )
704 .unwrap();
705 let encoded = Extents::encode_extents(&extents);
706 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
707 let results: Vec<_> = extents_container.iter_extents(500).collect();
708 assert_eq!(results.len(), 2);
709 assert_eq!(results[0].logical_range, 0..(10 * BLOCK_SIZE));
710 }
711
712 #[test]
713 fn test_encode_decode_large_regular_extent() {
714 let entry_bytes = MAX_REGULAR_EXTENT_BLOCKS * BLOCK_SIZE;
715 let large_len = 2 * entry_bytes + 5 * BLOCK_SIZE;
716 let start_dev = 10 * BLOCK_SIZE;
717 let next_len = 3 * BLOCK_SIZE;
718 let next_dev = 50 * BLOCK_SIZE;
719
720 let extents = Extents::try_new(
721 [
722 Extent::new(0..large_len, Some(start_dev)),
723 Extent::new(large_len..(large_len + next_len), Some(next_dev)),
724 ],
725 0,
726 )
727 .expect("Extents::try_new should split large regular extent");
728
729 let encoded: Vec<u64> = Extents::encode_extents(&extents).collect();
730 assert_eq!(encoded.len(), 4);
731
732 let extents_container = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
733 assert_eq!(extents_container, extents);
734
735 let decoded = extents_container.mappings();
736 assert_eq!(
737 decoded,
738 vec![
739 Extent::new(0..entry_bytes, Some(start_dev)),
740 Extent::new(entry_bytes..(2 * entry_bytes), Some(start_dev + entry_bytes)),
741 Extent::new((2 * entry_bytes)..large_len, Some(start_dev + 2 * entry_bytes)),
742 Extent::new(large_len..(large_len + next_len), Some(next_dev)),
743 ]
744 );
745
746 let mapped_second_entry =
747 extents_container.map(entry_bytes + BLOCK_SIZE).expect("should map in second entry");
748 assert_eq!(
749 mapped_second_entry.logical_range,
750 (entry_bytes + BLOCK_SIZE)..(2 * entry_bytes)
751 );
752 assert_eq!(mapped_second_entry.device_offset(), Some(start_dev + entry_bytes + BLOCK_SIZE));
753 }
754
755 #[test]
756 fn test_large_regular_extent_target_block_overflow_errors() {
757 let large_len = (MAX_REGULAR_EXTENT_BLOCKS + 1) * BLOCK_SIZE;
758 let start_dev = (u32::MAX as u64 - 10) * BLOCK_SIZE;
759 let extent = Extent::try_new(0..large_len, Some(start_dev))
760 .expect("Extent::try_new should allow large length");
761 let result = Extents::try_new([extent], 0);
762 assert!(result.is_err());
763 assert!(
764 result
765 .unwrap_err()
766 .to_string()
767 .contains("Relative device offset block index exceeds u32::MAX"),
768 "Second split entry exceeding u32::MAX target_block should fail"
769 );
770 }
771
772 #[test]
773 fn test_from_encoded_unknown_kind_returns_none() {
774 let unknown_descriptor = 0x40000000_00000000;
775 assert!(Extents::from_encoded([unknown_descriptor], 0).is_none());
776 let unknown_descriptor_2 = 0xc0000000_00000000;
777 assert!(Extents::from_encoded([unknown_descriptor_2], 0).is_none());
778 }
779
780 #[test]
781 fn test_encode_decode_inline_tail() {
782 let extents = Extents::try_new(
783 [
784 Extent::new(0..(2 * BLOCK_SIZE), DeviceExtent::Block(10 * BLOCK_SIZE)),
785 Extent::new(
786 (2 * BLOCK_SIZE)..(3 * BLOCK_SIZE),
787 DeviceExtent::InlineTail(3 * BLOCK_SIZE + 64),
788 ),
789 ],
790 0,
791 )
792 .unwrap();
793 let encoded: Vec<u64> = Extents::encode_extents(&extents).collect();
794 assert_eq!(encoded.len(), 2);
795 let decoded = Extents::from_encoded(encoded, 0).expect("from_encoded failed");
796 assert_eq!(decoded, extents);
797 let mapped = decoded.map(2 * BLOCK_SIZE).expect("should map inline tail");
798 assert_eq!(mapped.logical_range, (2 * BLOCK_SIZE)..(3 * BLOCK_SIZE));
799 assert_eq!(mapped.device_extent(), DeviceExtent::InlineTail(3 * BLOCK_SIZE + 64));
800 assert_eq!(mapped.device_offset(), Some(3 * BLOCK_SIZE + 64));
801 }
802}