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