1use std::borrow::Borrow;
9use std::cmp::Ordering;
10use std::fmt::{self, Debug};
11use std::ops::{
12 Add, AddAssign, Div, DivAssign, Mul, MulAssign, Range, Rem, RemAssign, Sub, SubAssign,
13};
14
15#[cfg(target_os = "fuchsia")]
16mod page;
17#[cfg(target_os = "fuchsia")]
18pub use crate::page::*;
19
20pub trait BlockSizeSpec: Copy + Clone + Debug + Eq + PartialEq {
24 fn size(self) -> u64;
26
27 fn mask(self) -> u64;
29
30 fn shift(self) -> u32;
32}
33
34#[derive(Copy, Clone, Debug, Eq)]
39pub struct GenericBlockSize<T: BlockSizeSpec>(T);
40
41impl<T: BlockSizeSpec> GenericBlockSize<T> {
42 #[inline(always)]
43 pub fn from_spec(spec: T) -> Self {
44 Self(spec)
45 }
46
47 #[inline(always)]
49 pub fn get(self) -> u64 {
50 self.0.size()
51 }
52
53 #[inline(always)]
55 pub fn mask(self) -> u64 {
56 self.0.mask()
57 }
58
59 #[inline(always)]
61 pub fn shift(self) -> u32 {
62 self.0.shift()
63 }
64
65 #[inline(always)]
67 pub fn is_aligned(self, value: impl IsAligned<T>) -> bool {
68 value.is_aligned(self)
69 }
70
71 #[inline(always)]
73 pub fn align_up(self, bytes: u64) -> Option<u64> {
74 match bytes.checked_add(self.mask()) {
75 Some(bytes) => Some(self.align_down(bytes)),
76 None => None,
77 }
78 }
79
80 #[inline(always)]
82 pub fn align_down(self, bytes: u64) -> u64 {
83 bytes & !self.mask()
84 }
85
86 #[inline(always)]
88 pub fn align_up_to_blocks(self, bytes: u64) -> u64 {
89 bytes / self + ((bytes % self != 0) as u64)
90 }
91
92 #[inline(always)]
96 pub fn align_range_outwards(self, range: impl Borrow<Range<u64>>) -> Option<Range<u64>> {
97 let range = range.borrow();
98 let start = self.align_down(range.start);
99 let end = self.align_up(range.end)?;
100 Some(start..end)
101 }
102
103 #[inline(always)]
105 pub fn checked_mul(self, blocks: u64) -> Option<u64> {
106 let shift = self.shift();
107 if blocks.leading_zeros() >= shift { Some(blocks << shift) } else { None }
108 }
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq)]
157pub struct MaskShiftSpec(u64);
158
159impl BlockSizeSpec for MaskShiftSpec {
160 #[inline(always)]
161 fn size(self) -> u64 {
162 self.mask() + 1
163 }
164
165 #[inline(always)]
166 fn mask(self) -> u64 {
167 self.0 >> 32
168 }
169
170 #[inline(always)]
171 fn shift(self) -> u32 {
172 self.0 as u32
173 }
174}
175
176impl MaskShiftSpec {
177 #[inline(always)]
183 const fn new(block_size: u64) -> Self {
184 debug_assert!(block_size.is_power_of_two() && block_size <= (1 << 32));
185 let mask = (block_size - 1) as u32 as u64;
186 let shift = (block_size - 1).trailing_ones();
187 Self((mask << 32) | (shift as u64))
188 }
189}
190
191pub type BlockSize = GenericBlockSize<MaskShiftSpec>;
193
194impl BlockSize {
195 pub const SIZE_512B: Self = Self::new(1 << 9).unwrap();
196 pub const SIZE_1KIB: Self = Self::new(1 << 10).unwrap();
197 pub const SIZE_2KIB: Self = Self::new(1 << 11).unwrap();
198 pub const SIZE_4KIB: Self = Self::new(1 << 12).unwrap();
199 pub const SIZE_8KIB: Self = Self::new(1 << 13).unwrap();
200 pub const SIZE_16KIB: Self = Self::new(1 << 14).unwrap();
201 pub const SIZE_32KIB: Self = Self::new(1 << 15).unwrap();
202 pub const SIZE_64KIB: Self = Self::new(1 << 16).unwrap();
203 pub const SIZE_128KIB: Self = Self::new(1 << 17).unwrap();
204 pub const SIZE_256KIB: Self = Self::new(1 << 18).unwrap();
205 pub const SIZE_512KIB: Self = Self::new(1 << 19).unwrap();
206 pub const SIZE_1MIB: Self = Self::new(1 << 20).unwrap();
207
208 #[inline(always)]
210 pub const fn new(block_size: u32) -> Option<Self> {
211 Self::from_u64(block_size as u64)
212 }
213
214 #[inline(always)]
218 pub const fn from_u64(block_size: u64) -> Option<Self> {
219 if block_size.is_power_of_two() && block_size <= (1 << 32) {
220 Some(GenericBlockSize(MaskShiftSpec::new(block_size)))
221 } else {
222 None
223 }
224 }
225
226 #[inline(always)]
232 pub const fn size(self) -> u64 {
233 (self.0.0 >> 32) + 1
234 }
235}
236
237macro_rules! impl_binary_op {
238 ($trait:ident, $method:ident, $lhs:ty, $rhs:ty, |$a:ident, $b:ident| $expr:expr) => {
239 impl<T: BlockSizeSpec> $trait<$rhs> for $lhs {
240 type Output = u64;
241 #[inline(always)]
242 fn $method(self, other: $rhs) -> u64 {
243 let $a = self;
244 let $b = other;
245 $expr
246 }
247 }
248 };
249}
250
251impl_binary_op!(Add, add, GenericBlockSize<T>, u64, |bs, val| bs.get() + val);
253impl_binary_op!(Add, add, GenericBlockSize<T>, &u64, |bs, val| bs.get() + *val);
254impl_binary_op!(Add, add, &GenericBlockSize<T>, u64, |bs, val| bs.get() + val);
255impl_binary_op!(Add, add, &GenericBlockSize<T>, &u64, |bs, val| bs.get() + *val);
256
257impl_binary_op!(Add, add, u64, GenericBlockSize<T>, |val, bs| val + bs.get());
258impl_binary_op!(Add, add, u64, &GenericBlockSize<T>, |val, bs| val + bs.get());
259impl_binary_op!(Add, add, &u64, GenericBlockSize<T>, |val, bs| *val + bs.get());
260impl_binary_op!(Add, add, &u64, &GenericBlockSize<T>, |val, bs| *val + bs.get());
261
262impl_binary_op!(Sub, sub, GenericBlockSize<T>, u64, |bs, val| bs.get() - val);
264impl_binary_op!(Sub, sub, GenericBlockSize<T>, &u64, |bs, val| bs.get() - *val);
265impl_binary_op!(Sub, sub, &GenericBlockSize<T>, u64, |bs, val| bs.get() - val);
266impl_binary_op!(Sub, sub, &GenericBlockSize<T>, &u64, |bs, val| bs.get() - *val);
267
268impl_binary_op!(Sub, sub, u64, GenericBlockSize<T>, |val, bs| val - bs.get());
269impl_binary_op!(Sub, sub, u64, &GenericBlockSize<T>, |val, bs| val - bs.get());
270impl_binary_op!(Sub, sub, &u64, GenericBlockSize<T>, |val, bs| *val - bs.get());
271impl_binary_op!(Sub, sub, &u64, &GenericBlockSize<T>, |val, bs| *val - bs.get());
272
273#[inline(always)]
274fn mul_block_size<T: BlockSizeSpec>(val: u64, bs: GenericBlockSize<T>) -> u64 {
275 let shift = bs.shift();
276 debug_assert!(val.leading_zeros() >= shift, "attempt to multiply with overflow");
278 val << shift
279}
280
281impl_binary_op!(Mul, mul, GenericBlockSize<T>, u64, |bs, val| mul_block_size(val, bs));
283impl_binary_op!(Mul, mul, GenericBlockSize<T>, &u64, |bs, val| mul_block_size(*val, bs));
284impl_binary_op!(Mul, mul, &GenericBlockSize<T>, u64, |bs, val| mul_block_size(val, *bs));
285impl_binary_op!(Mul, mul, &GenericBlockSize<T>, &u64, |bs, val| mul_block_size(*val, *bs));
286
287impl_binary_op!(Mul, mul, u64, GenericBlockSize<T>, |val, bs| mul_block_size(val, bs));
288impl_binary_op!(Mul, mul, u64, &GenericBlockSize<T>, |val, bs| mul_block_size(val, *bs));
289impl_binary_op!(Mul, mul, &u64, GenericBlockSize<T>, |val, bs| mul_block_size(*val, bs));
290impl_binary_op!(Mul, mul, &u64, &GenericBlockSize<T>, |val, bs| mul_block_size(*val, *bs));
291
292impl_binary_op!(Div, div, u64, GenericBlockSize<T>, |val, bs| val >> bs.shift());
294impl_binary_op!(Div, div, u64, &GenericBlockSize<T>, |val, bs| val >> bs.shift());
295impl_binary_op!(Div, div, &u64, GenericBlockSize<T>, |val, bs| *val >> bs.shift());
296impl_binary_op!(Div, div, &u64, &GenericBlockSize<T>, |val, bs| *val >> bs.shift());
297
298impl_binary_op!(Rem, rem, u64, GenericBlockSize<T>, |val, bs| val & bs.mask());
300impl_binary_op!(Rem, rem, u64, &GenericBlockSize<T>, |val, bs| val & bs.mask());
301impl_binary_op!(Rem, rem, &u64, GenericBlockSize<T>, |val, bs| *val & bs.mask());
302impl_binary_op!(Rem, rem, &u64, &GenericBlockSize<T>, |val, bs| *val & bs.mask());
303
304macro_rules! impl_assign_op {
305 ($trait:ident, $method:ident, $rhs:ty, |$a:ident, $b:ident| $expr:expr) => {
306 impl<T: BlockSizeSpec> $trait<$rhs> for u64 {
307 #[inline(always)]
308 fn $method(&mut self, other: $rhs) {
309 let $a = self;
310 let $b = other;
311 $expr
312 }
313 }
314 };
315}
316
317impl_assign_op!(AddAssign, add_assign, GenericBlockSize<T>, |val, bs| *val += bs.get());
319impl_assign_op!(AddAssign, add_assign, &GenericBlockSize<T>, |val, bs| *val += bs.get());
320
321impl_assign_op!(SubAssign, sub_assign, GenericBlockSize<T>, |val, bs| *val -= bs.get());
322impl_assign_op!(SubAssign, sub_assign, &GenericBlockSize<T>, |val, bs| *val -= bs.get());
323
324impl_assign_op!(MulAssign, mul_assign, GenericBlockSize<T>, |val, bs| *val =
325 mul_block_size(*val, bs));
326impl_assign_op!(MulAssign, mul_assign, &GenericBlockSize<T>, |val, bs| *val =
327 mul_block_size(*val, *bs));
328
329impl_assign_op!(DivAssign, div_assign, GenericBlockSize<T>, |val, bs| *val >>= bs.shift());
330impl_assign_op!(DivAssign, div_assign, &GenericBlockSize<T>, |val, bs| *val >>= bs.shift());
331
332impl_assign_op!(RemAssign, rem_assign, GenericBlockSize<T>, |val, bs| *val &= bs.mask());
333impl_assign_op!(RemAssign, rem_assign, &GenericBlockSize<T>, |val, bs| *val &= bs.mask());
334
335macro_rules! impl_partial_eq_ord {
336 ($lhs:ty, $rhs:ty, |$a:ident, $b:ident| ($expr_a:expr, $expr_b:expr)) => {
337 impl<T: BlockSizeSpec> PartialEq<$rhs> for $lhs {
338 #[inline(always)]
339 fn eq(&self, other: &$rhs) -> bool {
340 let $a = self;
341 let $b = other;
342 $expr_a == $expr_b
343 }
344 }
345
346 impl<T: BlockSizeSpec> PartialOrd<$rhs> for $lhs {
347 #[inline(always)]
348 fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
349 let $a = self;
350 let $b = other;
351 $expr_a.partial_cmp(&$expr_b)
352 }
353 }
354 };
355}
356
357impl_partial_eq_ord!(GenericBlockSize<T>, u64, |bs, val| (bs.get(), *val));
359impl_partial_eq_ord!(GenericBlockSize<T>, &u64, |bs, val| (bs.get(), **val));
360impl_partial_eq_ord!(&GenericBlockSize<T>, u64, |bs, val| (bs.get(), *val));
361impl_partial_eq_ord!(u64, GenericBlockSize<T>, |val, bs| (*val, bs.get()));
362impl_partial_eq_ord!(u64, &GenericBlockSize<T>, |val, bs| (*val, bs.get()));
363impl_partial_eq_ord!(&u64, GenericBlockSize<T>, |val, bs| (**val, bs.get()));
364
365impl<T: BlockSizeSpec, U: BlockSizeSpec> PartialEq<GenericBlockSize<T>> for GenericBlockSize<U> {
366 #[inline(always)]
367 fn eq(&self, other: &GenericBlockSize<T>) -> bool {
368 self.get() == other.get()
369 }
370}
371
372impl<T: BlockSizeSpec, U: BlockSizeSpec> PartialOrd<GenericBlockSize<T>> for GenericBlockSize<U> {
373 #[inline(always)]
374 fn partial_cmp(&self, other: &GenericBlockSize<T>) -> Option<Ordering> {
375 self.get().partial_cmp(&other.get())
376 }
377}
378
379impl<T: BlockSizeSpec> Ord for GenericBlockSize<T> {
380 #[inline(always)]
381 fn cmp(&self, other: &Self) -> Ordering {
382 self.get().cmp(&other.get())
383 }
384}
385
386impl<T: BlockSizeSpec> std::hash::Hash for GenericBlockSize<T> {
387 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
388 self.get().hash(state);
389 }
390}
391
392macro_rules! impl_fmt {
393 ($($trait:ident),*) => {
394 $(
395 impl<T: BlockSizeSpec> fmt::$trait for GenericBlockSize<T> {
396 #[inline(always)]
397 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398 fmt::$trait::fmt(&self.get(), f)
399 }
400 }
401 )*
402 };
403}
404
405impl_fmt!(Display, Binary, LowerHex, UpperHex, Octal);
407
408pub trait IsAligned<T: BlockSizeSpec> {
410 fn is_aligned(self, block_size: GenericBlockSize<T>) -> bool;
411}
412
413impl<T: BlockSizeSpec> IsAligned<T> for u64 {
414 #[inline(always)]
415 fn is_aligned(self, block_size: GenericBlockSize<T>) -> bool {
416 self % block_size == 0
417 }
418}
419
420impl<T: BlockSizeSpec> IsAligned<T> for &u64 {
421 #[inline(always)]
422 fn is_aligned(self, block_size: GenericBlockSize<T>) -> bool {
423 block_size.is_aligned(*self)
424 }
425}
426
427impl<T: BlockSizeSpec> IsAligned<T> for Range<u64> {
428 #[inline(always)]
429 fn is_aligned(self, block_size: GenericBlockSize<T>) -> bool {
430 block_size.is_aligned(&self)
431 }
432}
433
434impl<T: BlockSizeSpec> IsAligned<T> for &Range<u64> {
435 #[inline(always)]
436 fn is_aligned(self, block_size: GenericBlockSize<T>) -> bool {
437 (self.start | self.end) % block_size == 0
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444
445 #[fuchsia::test]
446 fn test_new() {
447 assert!(BlockSize::new(0).is_none());
448 assert_eq!(BlockSize::new(1).unwrap().get(), 1);
449 assert!(BlockSize::new(3).is_none());
450 assert_eq!(BlockSize::new(512).unwrap().get(), 512);
451 assert_eq!(BlockSize::new(4096).unwrap().get(), 4096);
452 assert!(BlockSize::new(u32::MAX).is_none());
453
454 assert!(BlockSize::from_u64(0).is_none());
455 assert_eq!(BlockSize::from_u64(1).unwrap().get(), 1);
456 assert!(BlockSize::from_u64(3).is_none());
457 assert_eq!(BlockSize::from_u64(4096).unwrap().get(), 4096);
458 assert_eq!(BlockSize::from_u64(u32::MAX as u64 + 1).unwrap().get(), 1 << 32);
459 assert!(BlockSize::from_u64((1 << 32) + 1).is_none());
460 assert!(BlockSize::from_u64(1 << 33).is_none());
461 assert!(BlockSize::from_u64(u64::MAX).is_none());
462 }
463
464 #[fuchsia::test]
465 fn test_getters_and_constants() {
466 let bs = BlockSize::SIZE_4KIB;
467 assert_eq!(bs.get(), 4096);
468 assert_eq!(bs.size(), 4096);
469 assert_eq!(bs.mask(), 4095);
470 assert_eq!(bs.shift(), 12);
471
472 assert_eq!(BlockSize::SIZE_512B.get(), 512);
473 assert_eq!(BlockSize::SIZE_1KIB.get(), 1024);
474 assert_eq!(BlockSize::SIZE_2KIB.get(), 2048);
475 assert_eq!(BlockSize::SIZE_4KIB.get(), 4096);
476 assert_eq!(BlockSize::SIZE_8KIB.get(), 8192);
477 assert_eq!(BlockSize::SIZE_16KIB.get(), 16384);
478 assert_eq!(BlockSize::SIZE_32KIB.get(), 32768);
479 assert_eq!(BlockSize::SIZE_64KIB.get(), 65536);
480 assert_eq!(BlockSize::SIZE_128KIB.get(), 131072);
481 assert_eq!(BlockSize::SIZE_256KIB.get(), 262144);
482 assert_eq!(BlockSize::SIZE_512KIB.get(), 524288);
483 assert_eq!(BlockSize::SIZE_1MIB.get(), 1048576);
484 }
485
486 #[fuchsia::test]
487 fn test_alignment_helpers() {
488 let bs = BlockSize::SIZE_4KIB;
489
490 assert!(bs.is_aligned(0u64));
491 assert!(bs.is_aligned(4096u64));
492 assert!(bs.is_aligned(&4096u64));
493 assert!(!bs.is_aligned(4095u64));
494 assert!(!bs.is_aligned(4097u64));
495
496 assert!(bs.is_aligned(0..4096));
497 assert!(bs.is_aligned(&(4096..8192)));
498 assert!(!bs.is_aligned(0..4095));
499 assert!(!bs.is_aligned(1..4096));
500 assert!(!bs.is_aligned(1..4095));
501
502 assert_eq!(bs.align_down(0), 0);
503 assert_eq!(bs.align_down(4095), 0);
504 assert_eq!(bs.align_down(4096), 4096);
505 assert_eq!(bs.align_down(4097), 4096);
506
507 assert_eq!(bs.align_up(0), Some(0));
508 assert_eq!(bs.align_up(1), Some(4096));
509 assert_eq!(bs.align_up(4095), Some(4096));
510 assert_eq!(bs.align_up(4096), Some(4096));
511 assert_eq!(bs.align_up(u64::MAX - 4095), Some(u64::MAX - 4095));
513 assert_eq!(bs.align_up(u64::MAX - 4094), None);
514 assert_eq!(bs.align_up(u64::MAX), None);
515
516 assert_eq!(bs.align_up_to_blocks(0), 0);
517 assert_eq!(bs.align_up_to_blocks(1), 1);
518 assert_eq!(bs.align_up_to_blocks(4095), 1);
519 assert_eq!(bs.align_up_to_blocks(4096), 1);
520 assert_eq!(bs.align_up_to_blocks(4097), 2);
521 assert_eq!(bs.align_up_to_blocks(u64::MAX), 1 << 52);
522
523 assert_eq!(bs.align_range_outwards(1..4095), Some(0..4096));
524 assert_eq!(bs.align_range_outwards(&(0..4096)), Some(0..4096));
525 assert_eq!(bs.align_range_outwards(4096..4096), Some(4096..4096));
526 assert_eq!(bs.align_range_outwards(10..10), Some(0..4096));
527 assert_eq!(bs.align_range_outwards(0..u64::MAX), None);
528 }
529
530 #[fuchsia::test]
531 fn test_arithmetic_ops() {
532 let bs = BlockSize::SIZE_4KIB;
533 let bs_ref = &bs;
534 let val = 8192u64;
535 let val_ref = &val;
536
537 assert_eq!(bs + val, 12288);
539 assert_eq!(bs + val_ref, 12288);
540 assert_eq!(bs_ref + val, 12288);
541 assert_eq!(bs_ref + val_ref, 12288);
542 assert_eq!(val + bs, 12288);
543 assert_eq!(val + bs_ref, 12288);
544 assert_eq!(val_ref + bs, 12288);
545 assert_eq!(val_ref + bs_ref, 12288);
546
547 assert_eq!(val - bs, 4096);
549 assert_eq!(val - bs_ref, 4096);
550 assert_eq!(val_ref - bs, 4096);
551 assert_eq!(val_ref - bs_ref, 4096);
552 assert_eq!(bs - 100u64, 3996);
553 assert_eq!(bs - &100u64, 3996);
554 assert_eq!(bs_ref - 100u64, 3996);
555 assert_eq!(bs_ref - &100u64, 3996);
556
557 assert_eq!(bs * 3u64, 12288);
559 assert_eq!(bs * &3u64, 12288);
560 assert_eq!(bs_ref * 3u64, 12288);
561 assert_eq!(bs_ref * &3u64, 12288);
562 assert_eq!(3u64 * bs, 12288);
563 assert_eq!(3u64 * bs_ref, 12288);
564 assert_eq!(&3u64 * bs, 12288);
565 assert_eq!(&3u64 * bs_ref, 12288);
566
567 assert_eq!(bs.checked_mul(0), Some(0));
568 assert_eq!(bs.checked_mul(3), Some(12288));
569 assert_eq!(bs.checked_mul((1 << 52) - 1), Some(u64::MAX - 4095));
570 assert_eq!(bs.checked_mul(1 << 52), None);
571 assert_eq!(bs.checked_mul(u64::MAX), None);
572
573 assert_eq!(val / bs, 2);
575 assert_eq!(val / bs_ref, 2);
576 assert_eq!(val_ref / bs, 2);
577 assert_eq!(val_ref / bs_ref, 2);
578
579 assert_eq!(5000u64 % bs, 904);
581 assert_eq!(5000u64 % bs_ref, 904);
582 assert_eq!(&5000u64 % bs, 904);
583 assert_eq!(&5000u64 % bs_ref, 904);
584 }
585
586 #[fuchsia::test]
587 fn test_assign_ops() {
588 let bs = BlockSize::SIZE_4KIB;
589
590 let mut v = 100u64;
591 v += bs;
592 assert_eq!(v, 4196);
593 v += &bs;
594 assert_eq!(v, 8292);
595
596 v -= bs;
597 assert_eq!(v, 4196);
598 v -= &bs;
599 assert_eq!(v, 100);
600
601 let mut blocks = 3u64;
602 blocks *= bs;
603 assert_eq!(blocks, 12288);
604 let mut blocks2 = 3u64;
605 blocks2 *= &bs;
606 assert_eq!(blocks2, 12288);
607
608 let mut bytes = 12288u64;
609 bytes /= bs;
610 assert_eq!(bytes, 3);
611 let mut bytes2 = 12288u64;
612 bytes2 /= &bs;
613 assert_eq!(bytes2, 3);
614
615 let mut rem = 5000u64;
616 rem %= bs;
617 assert_eq!(rem, 904);
618 let mut rem2 = 5000u64;
619 rem2 %= &bs;
620 assert_eq!(rem2, 904);
621 }
622
623 #[fuchsia::test]
624 fn test_comparisons_and_hash() {
625 use std::collections::hash_map::DefaultHasher;
626 use std::hash::{Hash, Hasher};
627
628 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
629 struct Custom4KiBSpec;
630 impl BlockSizeSpec for Custom4KiBSpec {
631 fn size(self) -> u64 {
632 4096
633 }
634 fn mask(self) -> u64 {
635 4095
636 }
637 fn shift(self) -> u32 {
638 12
639 }
640 }
641 let custom_bs = GenericBlockSize(Custom4KiBSpec);
642
643 let bs1 = BlockSize::SIZE_512B;
644 let bs2 = BlockSize::SIZE_4KIB;
645
646 assert!(bs1 < bs2);
647 assert!(bs2 > bs1);
648 assert_eq!(bs1.min(bs2), bs1);
649 assert_eq!(bs1.max(bs2), bs2);
650
651 assert_eq!(bs2, custom_bs);
653 assert_eq!(custom_bs, bs2);
654 assert!(bs1 < custom_bs);
655 assert!(custom_bs > bs1);
656
657 fn hash_val<H: Hash>(v: &H) -> u64 {
658 let mut hasher = DefaultHasher::new();
659 v.hash(&mut hasher);
660 hasher.finish()
661 }
662 assert_eq!(hash_val(&bs2), hash_val(&custom_bs));
663 assert_ne!(hash_val(&bs1), hash_val(&bs2));
664
665 assert_eq!(bs2, 4096u64);
666 assert_eq!(bs2, &4096u64);
667 assert_eq!(&bs2, 4096u64);
668 assert_eq!(&bs2, &4096u64);
669 assert_eq!(4096u64, bs2);
670 assert_eq!(4096u64, &bs2);
671 assert_eq!(&4096u64, bs2);
672 assert_eq!(&4096u64, &bs2);
673
674 assert!(bs2 > 512u64);
675 assert!(bs2 > &512u64);
676 assert!(&bs2 > 512u64);
677 assert!(&bs2 > &512u64);
678 assert!(512u64 < bs2);
679 assert!(512u64 < &bs2);
680 assert!(&512u64 < bs2);
681 assert!(&512u64 < &bs2);
682 }
683
684 #[cfg(debug_assertions)]
685 #[fuchsia::test]
686 #[should_panic(expected = "attempt to multiply with overflow")]
687 fn test_mul_overflow() {
688 let _ = BlockSize::SIZE_4KIB * (1u64 << 60);
689 }
690
691 #[cfg(debug_assertions)]
692 #[fuchsia::test]
693 #[should_panic(expected = "attempt to multiply with overflow")]
694 fn test_mul_assign_overflow() {
695 let mut v = 1u64 << 60;
696 v *= BlockSize::SIZE_4KIB;
697 }
698
699 #[fuchsia::test]
700 fn test_formatting() {
701 let bs = BlockSize::SIZE_4KIB;
702 assert_eq!(format!("{}", bs), "4096");
703 assert_eq!(format!("{:x}", bs), "1000");
704 assert_eq!(format!("{:X}", bs), "1000");
705 assert_eq!(format!("{:b}", bs), "1000000000000");
706 assert_eq!(format!("{:o}", bs), "10000");
707 }
708
709 #[fuchsia::test]
710 fn test_block_size_one() {
711 let block_size = BlockSize::new(1).unwrap();
712
713 assert_eq!(block_size.align_up(0), Some(0));
714 assert_eq!(block_size.align_up(20), Some(20));
715 assert_eq!(block_size.align_up(u64::MAX), Some(u64::MAX));
716
717 assert_eq!(block_size.align_down(0), 0);
718 assert_eq!(block_size.align_down(20), 20);
719 assert_eq!(block_size.align_down(u64::MAX), u64::MAX);
720
721 assert_eq!(block_size * 0, 0);
722 assert_eq!(block_size * 20, 20);
723 assert_eq!(block_size * u64::MAX, u64::MAX);
724 assert_eq!(block_size.checked_mul(0), Some(0));
725 assert_eq!(block_size.checked_mul(20), Some(20));
726 assert_eq!(block_size.checked_mul(u64::MAX), Some(u64::MAX));
727
728 assert_eq!(0 / block_size, 0);
729 assert_eq!(20 / block_size, 20);
730 assert_eq!(u64::MAX / block_size, u64::MAX);
731
732 assert_eq!(0 % block_size, 0);
733 assert_eq!(20 % block_size, 0);
734 assert_eq!(u64::MAX % block_size, 0);
735 }
736
737 #[fuchsia::test]
738 fn test_block_size_max_4gib() {
739 let bs = BlockSize::from_u64(1 << 32).unwrap();
740 assert_eq!(bs.get(), 1 << 32);
741 assert_eq!(bs.mask(), u32::MAX as u64);
742 assert_eq!(bs.shift(), 32);
743
744 assert!(bs.is_aligned(0u64));
745 assert!(bs.is_aligned(1u64 << 32));
746 assert!(!bs.is_aligned((1u64 << 32) - 1));
747
748 assert_eq!(bs.align_up(1), Some(1 << 32));
749 assert_eq!(bs.align_down((1 << 32) + 123), 1 << 32);
750 assert_eq!(bs * 3u64, 3 << 32);
751 assert_eq!(bs.checked_mul(3), Some(3 << 32));
752 assert_eq!(bs.checked_mul(u32::MAX as u64), Some((u32::MAX as u64) << 32));
753 assert_eq!(bs.checked_mul(1 << 32), None);
754 assert_eq!((3u64 << 32) / bs, 3);
755 assert_eq!(((3u64 << 32) + 55) % bs, 55);
756 }
757}