1#[cfg(target_pointer_width = "64")]
2use std::num::{NonZeroI64, NonZeroU64};
3use std::{
4 borrow::Cow,
5 num::{
6 NonZeroI16, NonZeroI32, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32, NonZeroU8,
7 NonZeroUsize,
8 },
9 rc::Rc,
10 sync::Arc,
11};
12
13pub use exhaustive_map_macros::Finite;
14use exhaustive_map_macros::__impl_tuples;
15
16pub trait Finite: Sized {
43 const INHABITANTS: usize;
45
46 #[must_use]
48 fn to_usize(&self) -> usize;
49
50 #[must_use]
54 fn from_usize(i: usize) -> Option<Self>;
55}
56
57pub trait FiniteExt: Finite {
59 fn iter_all() -> IterAll<Self> {
61 IterAll((0..Self::INHABITANTS).map(|i| {
62 Self::from_usize(i).expect("unexpected None returned from Finite::from_usize in range")
63 }))
64 }
65}
66
67impl<T: Finite> FiniteExt for T {}
68
69#[must_use = "iterators are lazy and do nothing unless consumed"]
73pub struct IterAll<T>(std::iter::Map<std::ops::Range<usize>, fn(usize) -> T>);
74
75impl<T> Iterator for IterAll<T> {
76 type Item = T;
77
78 fn next(&mut self) -> Option<Self::Item> {
79 self.0.next()
80 }
81}
82
83impl<T: ?Sized> Finite for std::marker::PhantomData<T> {
84 const INHABITANTS: usize = 1;
85
86 fn to_usize(&self) -> usize {
87 0
88 }
89
90 fn from_usize(i: usize) -> Option<Self> {
91 match i {
92 0 => Some(Self),
93 _ => None,
94 }
95 }
96}
97
98impl Finite for bool {
99 const INHABITANTS: usize = 2;
100
101 fn to_usize(&self) -> usize {
102 usize::from(*self)
103 }
104
105 fn from_usize(i: usize) -> Option<Self> {
106 match i {
107 0 => Some(false),
108 1 => Some(true),
109 _ => None,
110 }
111 }
112}
113
114macro_rules! impl_uprim {
115 ($type:path) => {
116 impl Finite for $type {
117 const INHABITANTS: usize = <$type>::MAX as usize + 1;
118
119 fn to_usize(&self) -> usize {
120 *self as usize
121 }
122
123 fn from_usize(i: usize) -> Option<Self> {
124 i.try_into().ok()
125 }
126 }
127 };
128}
129
130impl_uprim!(u8);
131impl_uprim!(u16);
132#[cfg(target_pointer_width = "64")]
133impl_uprim!(u32);
134
135macro_rules! impl_iprim {
136 ($itype:path, $utype:path) => {
137 impl Finite for $itype {
138 const INHABITANTS: usize = <$utype as Finite>::INHABITANTS;
139
140 fn to_usize(&self) -> usize {
141 #[allow(clippy::cast_sign_loss)]
142 (*self as $utype).to_usize()
143 }
144
145 fn from_usize(i: usize) -> Option<Self> {
146 #[allow(clippy::cast_possible_wrap)]
147 <$utype as Finite>::from_usize(i).map(|v| v as Self)
148 }
149 }
150 };
151}
152
153impl_iprim!(i8, u8);
154impl_iprim!(i16, u16);
155#[cfg(target_pointer_width = "64")]
156impl_iprim!(i32, u32);
157
158const fn pow(a: usize, b: usize) -> usize {
159 if a <= 1 {
160 return a;
161 }
162
163 assert!(b <= u32::MAX as usize, "doesn't fit in a usize");
164 #[allow(clippy::cast_possible_truncation)]
165 let b = b as u32;
166
167 a.pow(b)
168}
169
170const fn pow_minus_one(a: usize, b: usize) -> usize {
171 assert!(a != 0, "-1 doesn't fit in a usize");
172 if a == 1 {
173 return 0;
174 }
175
176 assert!(b <= u32::MAX as usize, "doesn't fit in a usize");
177 #[allow(clippy::cast_possible_truncation)]
178 let b = b as u32;
179
180 let res = (a as u128).pow(b) - 1;
181 assert!(res <= usize::MAX as u128, "doesn't fit in a usize");
182 res as usize
183}
184
185macro_rules! impl_unonzero {
186 ($type:path) => {
187 impl Finite for $type {
188 const INHABITANTS: usize = pow_minus_one(2, std::mem::size_of::<$type>() * 8);
189
190 fn to_usize(&self) -> usize {
191 usize::try_from(self.get()).unwrap() - 1
192 }
193
194 fn from_usize(i: usize) -> Option<Self> {
195 <$type>::new((i.checked_add(1)?).try_into().ok()?)
196 }
197 }
198 };
199}
200
201impl_unonzero!(NonZeroU8);
202impl_unonzero!(NonZeroU16);
203impl_unonzero!(NonZeroU32);
204#[cfg(target_pointer_width = "64")]
205impl_unonzero!(NonZeroU64);
206impl_unonzero!(NonZeroUsize);
207
208macro_rules! impl_inonzero {
209 ($nonzero_type:path, $itype:path, $utype:path) => {
210 impl Finite for $nonzero_type {
211 const INHABITANTS: usize = pow_minus_one(2, std::mem::size_of::<$nonzero_type>() * 8);
212
213 #[allow(clippy::cast_sign_loss)]
214 fn to_usize(&self) -> usize {
215 usize::try_from(self.get() as $utype).unwrap() - 1
216 }
217
218 #[allow(clippy::cast_possible_wrap)]
219 fn from_usize(i: usize) -> Option<Self> {
220 <$nonzero_type>::new(
221 <$utype>::try_from(i.checked_add(1)?)
222 .map(|v| v as $itype)
223 .ok()?,
224 )
225 }
226 }
227 };
228}
229
230impl_inonzero!(NonZeroI8, i8, u8);
231impl_inonzero!(NonZeroI16, i16, u16);
232impl_inonzero!(NonZeroI32, i32, u32);
233#[cfg(target_pointer_width = "64")]
234impl_inonzero!(NonZeroI64, i64, u64);
235impl_inonzero!(NonZeroIsize, isize, usize);
236
237const CHAR_GAP_START: usize = 0xD800;
238const CHAR_GAP_END: usize = 0xDFFF;
239const CHAR_GAP_SIZE: usize = CHAR_GAP_END - CHAR_GAP_START + 1;
240impl Finite for char {
241 const INHABITANTS: usize = char::MAX as usize + 1 - CHAR_GAP_SIZE;
242
243 fn to_usize(&self) -> usize {
244 let mut v = *self as usize;
245 if v > CHAR_GAP_END {
246 v -= CHAR_GAP_SIZE;
247 }
248 v
249 }
250
251 fn from_usize(mut i: usize) -> Option<Self> {
252 if i >= CHAR_GAP_START {
253 i = i.checked_add(CHAR_GAP_SIZE)?;
254 }
255 char::from_u32(i.try_into().ok()?)
256 }
257}
258
259#[cfg(target_pointer_width = "64")]
260impl Finite for f32 {
261 const INHABITANTS: usize = u32::INHABITANTS;
262
263 fn to_usize(&self) -> usize {
264 self.to_bits().to_usize()
265 }
266
267 fn from_usize(i: usize) -> Option<Self> {
268 u32::from_usize(i).map(Self::from_bits)
269 }
270}
271
272#[cfg(target_pointer_width = "64")]
273macro_rules! impl_from {
274 ($type:path, $from:path) => {
275 impl Finite for $type {
276 const INHABITANTS: usize = <$from as Finite>::INHABITANTS;
277
278 fn to_usize(&self) -> usize {
279 <$from>::from(*self).to_usize()
280 }
281
282 fn from_usize(i: usize) -> Option<Self> {
283 Self::try_from(<$from>::from_usize(i)?).ok()
284 }
285 }
286 };
287}
288
289#[cfg(target_pointer_width = "64")]
290impl_from!(std::net::Ipv4Addr, u32);
291
292impl<const N: usize, T: Finite> Finite for [T; N] {
293 const INHABITANTS: usize = pow(T::INHABITANTS, N);
294
295 fn to_usize(&self) -> usize {
296 let mut res = 0;
297 for v in self.iter().rev() {
298 res *= T::INHABITANTS;
299 res += v.to_usize();
300 }
301 res
302 }
303
304 fn from_usize(mut i: usize) -> Option<Self> {
305 if i >= Self::INHABITANTS {
306 None
307 } else {
308 let arr = std::array::from_fn(|_| {
309 let v = T::from_usize(i % T::INHABITANTS).unwrap();
310 i /= T::INHABITANTS;
311 v
312 });
313 Some(arr)
314 }
315 }
316}
317
318__impl_tuples!(16);
319
320macro_rules! impl_deref {
321 ($type:path) => {
322 impl<T: Finite> Finite for $type {
323 const INHABITANTS: usize = T::INHABITANTS;
324
325 fn to_usize(&self) -> usize {
326 (**self).to_usize()
327 }
328
329 fn from_usize(i: usize) -> Option<Self> {
330 Some(T::from_usize(i)?.into())
331 }
332 }
333 };
334}
335
336impl_deref!(Box<T>);
337impl_deref!(Rc<T>);
338impl_deref!(Arc<T>);
339
340impl<T: Finite + Clone> Finite for Cow<'_, T> {
341 const INHABITANTS: usize = T::INHABITANTS;
342
343 fn to_usize(&self) -> usize {
344 (**self).to_usize()
345 }
346
347 fn from_usize(i: usize) -> Option<Self> {
348 Some(Cow::Owned(T::from_usize(i)?))
349 }
350}
351
352#[derive(Finite)]
353#[__finite_foreign(std::convert::Infallible)]
354enum _Infallible {}
355
356#[derive(Finite)]
357#[__finite_foreign(std::alloc::System)]
358struct _System;
359
360#[derive(Finite)]
361#[__finite_foreign(std::marker::PhantomPinned)]
362struct _PhantomPinned;
363
364#[derive(Finite)]
365#[__finite_foreign(std::cmp::Ordering)]
366enum _Ordering {
367 Less,
368 Equal,
369 Greater,
370}
371
372#[derive(Finite)]
373#[__finite_foreign(std::net::Shutdown)]
374enum _Shutdown {
375 Read,
376 Write,
377 Both,
378}
379
380#[derive(Finite)]
381#[__finite_foreign(std::num::FpCategory)]
382enum _FpCategory {
383 Nan,
384 Infinite,
385 Zero,
386 Subnormal,
387 Normal,
388}
389
390#[derive(Finite)]
391#[__finite_foreign(std::sync::mpsc::RecvTimeoutError)]
392enum _RecvTimeoutError {
393 Timeout,
394 Disconnected,
395}
396
397#[derive(Finite)]
398#[__finite_foreign(std::sync::mpsc::TryRecvError)]
399enum _TryRecvError {
400 Empty,
401 Disconnected,
402}
403
404#[derive(Finite)]
405#[__finite_foreign(std::fmt::Alignment)]
406enum _Alignment {
407 Left,
408 Right,
409 Center,
410}
411
412#[derive(Finite)]
413#[__finite_foreign(Option)]
414enum _Option<T> {
415 None,
416 Some(T),
417}
418
419#[derive(Finite)]
420#[__finite_foreign(Result)]
421enum _Result<T, E> {
422 Ok(T),
423 Err(E),
424}
425
426#[derive(Finite)]
427#[__finite_foreign(std::task::Poll)]
428enum _Poll<T> {
429 Ready(T),
430 Pending,
431}
432
433#[derive(Finite)]
434#[__finite_foreign(std::ops::Bound)]
435enum _Bound<T> {
436 Included(T),
437 Excluded(T),
438 Unbounded,
439}
440
441#[derive(Finite)]
442#[__finite_foreign(std::ops::ControlFlow)]
443enum _ControlFlow<B, C> {
444 Continue(C),
445 Break(B),
446}
447
448#[derive(Finite)]
449#[__finite_foreign(std::ops::Range)]
450struct _Range<Idx> {
451 start: Idx,
452 end: Idx,
453}
454
455#[derive(Finite)]
456#[__finite_foreign(std::ops::RangeFrom)]
457struct _RangeFrom<Idx> {
458 start: Idx,
459}
460
461#[derive(Finite)]
462#[__finite_foreign(std::ops::RangeTo)]
463struct _RangeTo<Idx> {
464 end: Idx,
465}
466
467#[derive(Finite)]
468#[__finite_foreign(std::ops::RangeToInclusive)]
469struct _RangeToInclusive<Idx> {
470 end: Idx,
471}
472
473#[derive(Finite)]
474#[__finite_foreign(std::ops::RangeFull)]
475struct _RangeFull;
476
477#[cfg(test)]
478mod test {
479 use std::{
480 fmt::Debug,
481 marker::PhantomData,
482 num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU8},
483 };
484
485 use super::*;
486
487 fn test_some<T: Finite + Debug + PartialEq>(expected_elements: usize) {
488 assert_eq!(T::INHABITANTS, expected_elements);
489
490 let mut values: Vec<_> = (0..1000).collect();
491 for base in [usize::MAX, T::INHABITANTS] {
492 for offset in -10..=10 {
493 if let Some(i) = base.checked_add_signed(offset) {
494 values.push(i);
495 }
496 }
497 }
498
499 for i in values {
500 match T::from_usize(i) {
501 None => {
502 assert!(
503 i >= T::INHABITANTS,
504 "Got {i}usize -> None, but INHABITANTS={}",
505 T::INHABITANTS
506 );
507 }
508 Some(v) => {
509 assert!(
510 i < T::INHABITANTS,
511 "Got {i}usize -> {v:?}, but INHABITANTS={}",
512 T::INHABITANTS
513 );
514 let i2 = v.to_usize();
515 assert_eq!(i2, i, "{i}usize -> {v:?} -> {i2}usize");
516 }
517 }
518 }
519 }
520
521 fn test_all<T: Finite + Debug + PartialEq>(expected_elements: usize) {
522 test_some::<T>(expected_elements);
523
524 for i in 0..T::INHABITANTS {
525 let v = T::from_usize(i).unwrap();
526 let i2 = v.to_usize();
527 assert_eq!(i2, i, "{i}usize -> {v:?} -> {i2}usize");
528 }
529
530 for k in [8, 16, 32, 64] {
531 for k in [k - 1, k, k + 1] {
532 let Some(n) = 2usize.checked_pow(k) else {
533 continue;
534 };
535 for i in [n - 1, n, n + 1] {
536 if i >= T::INHABITANTS {
537 assert_eq!(
538 T::from_usize(i),
539 None,
540 "expected None from T::from_usize({i})"
541 );
542 }
543 }
544 }
545 }
546 }
547
548 #[test]
549 fn test_infallible() {
550 test_all::<std::convert::Infallible>(0);
551 }
552
553 #[test]
554 fn test_unit() {
555 test_all::<()>(1);
556 }
557
558 #[test]
559 fn test_bool() {
560 test_all::<bool>(2);
561 }
562
563 #[test]
564 fn test_u8() {
565 test_all::<u8>(256);
566 }
567
568 #[test]
569 fn test_u16() {
570 test_all::<u16>(256 * 256);
571 }
572
573 #[test]
574 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
575 #[cfg(target_pointer_width = "64")]
576 fn test_u32() {
577 test_all::<u32>(256 * 256 * 256 * 256);
578 }
579
580 #[test]
581 fn test_i8() {
582 test_all::<i8>(256);
583 }
584
585 #[test]
586 fn test_i16() {
587 test_all::<i16>(256 * 256);
588 }
589
590 #[test]
591 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
592 #[cfg(target_pointer_width = "64")]
593 fn test_i32() {
594 test_all::<i32>(256 * 256 * 256 * 256);
595 }
596
597 #[test]
598 fn test_nonzero_u8() {
599 test_all::<NonZeroU8>(256 - 1);
600 }
601
602 #[test]
603 fn test_nonzero_u16() {
604 test_all::<NonZeroU16>(256 * 256 - 1);
605 }
606
607 #[test]
608 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
609 fn test_nonzero_u32() {
610 test_all::<NonZeroU32>(usize::try_from(256u64 * 256 * 256 * 256 - 1).unwrap());
611 }
612
613 #[test]
614 #[cfg(target_pointer_width = "64")]
615 fn test_nonzero_u64() {
616 test_some::<NonZeroU64>(usize::try_from(2u128.pow(64) - 1).unwrap());
617 }
618
619 #[test]
620 fn test_nonzero_usize() {
621 test_some::<NonZeroUsize>(usize::try_from(2u128.pow(isize::BITS) - 1).unwrap());
622 }
623
624 #[test]
625 fn test_nonzero_i8() {
626 test_all::<NonZeroI8>(256 - 1);
627 }
628
629 #[test]
630 fn test_nonzero_i16() {
631 test_all::<NonZeroI16>(256 * 256 - 1);
632 }
633
634 #[test]
635 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
636 fn test_nonzero_i32() {
637 test_all::<NonZeroI32>(usize::try_from(256u64 * 256 * 256 * 256 - 1).unwrap());
638 }
639
640 #[test]
641 #[cfg(target_pointer_width = "64")]
642 fn test_nonzero_i64() {
643 test_some::<NonZeroI64>(usize::try_from(2u128.pow(64) - 1).unwrap());
644 }
645
646 #[test]
647 fn test_nonzero_isize() {
648 test_some::<NonZeroIsize>(usize::try_from(2u128.pow(isize::BITS) - 1).unwrap());
649 }
650
651 #[test]
652 fn test_char() {
653 test_all::<char>(0x11_0000 - CHAR_GAP_SIZE);
654 }
655
656 #[test]
657 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
658 #[cfg(target_pointer_width = "64")]
659 fn test_f32() {
660 test_all::<f32>(256usize.pow(4));
661 }
662
663 #[test]
664 fn test_u8_arr_0() {
665 test_all::<[u8; 0]>(1);
666 }
667
668 #[test]
669 fn test_u8_arr_1() {
670 test_all::<[u8; 1]>(256);
671 }
672
673 #[test]
674 fn test_u8_arr_2() {
675 test_all::<[u8; 2]>(256 * 256);
676 }
677
678 #[test]
679 fn test_unit_arr() {
680 test_all::<[(); 100]>(1);
681 }
682
683 #[test]
684 fn test_tuple_u8_bool() {
685 test_all::<(u8, bool)>(512);
686 }
687
688 #[test]
689 fn test_tuple_bool_u8() {
690 test_all::<(bool, u8)>(512);
691 }
692
693 #[test]
694 fn test_cow_arr() {
695 test_all::<Cow<[bool; 2]>>(4);
696 }
697
698 #[test]
699 fn test_tuple_and_arr_same_encoding() {
700 let i1 = [1u8, 2u8].to_usize();
701 let i2 = (1u8, 2u8).to_usize();
702 assert_eq!(i1, i2);
703 }
704
705 #[test]
706 #[cfg_attr(debug_assertions, ignore = "too slow in debug build")]
707 #[cfg(target_pointer_width = "64")]
708 fn test_ipv4_address() {
709 test_all::<std::net::Ipv4Addr>(256usize.pow(4));
710 }
711
712 #[test]
713 fn test_std_cmp_ordering() {
714 test_all::<std::cmp::Ordering>(3);
715 }
716
717 #[test]
718 fn test_derive_unit_struct() {
719 #[derive(Finite, Debug, PartialEq)]
720 struct UnitStruct;
721 test_all::<UnitStruct>(1);
722 }
723
724 #[test]
725 fn test_derive_empty_tuple_struct() {
726 #[derive(Finite, Debug, PartialEq)]
727 struct EmptyTupleStruct();
728 test_all::<EmptyTupleStruct>(1);
729 }
730
731 #[test]
732 fn test_derive_tuple_struct() {
733 #[allow(dead_code)]
734 #[derive(Finite, Debug, PartialEq)]
735 struct TupleStruct(u8, bool);
736 test_all::<TupleStruct>(256 * 2);
737 }
738
739 #[test]
740 fn test_derive_empty_named_struct() {
741 #[derive(Finite, Debug, PartialEq)]
742 struct EmptyNamedStruct {}
743 test_all::<EmptyNamedStruct>(1);
744 }
745
746 #[test]
747 fn test_derive_named_struct() {
748 #[derive(Finite, Debug, PartialEq)]
749 struct Struct {
750 _a: bool,
751 _b: u8,
752 _c: Option<bool>,
753 }
754 test_all::<Struct>(2 * 256 * 3);
755 }
756
757 #[test]
758 fn test_derive_empty_enum() {
759 #[derive(Finite, Debug, PartialEq)]
760 enum EmptyEnum {}
761 test_all::<EmptyEnum>(0);
762 }
763
764 #[test]
765 fn test_derive_simple_enum() {
766 #[derive(Finite, Debug, PartialEq)]
767 enum SimpleEnum {
768 _A,
769 _B,
770 _C,
771 }
772 test_all::<SimpleEnum>(3);
773 }
774
775 #[test]
776 fn test_tuple_enum() {
777 #[derive(Finite, Debug, PartialEq)]
778 enum TupleEnum {
779 _A(u8, bool),
780 _B(()),
781 _C(),
782 }
783 test_all::<TupleEnum>(256 * 2 + 1 + 1);
784 }
785
786 #[test]
787 fn test_derive_struct_enum() {
788 #[derive(Finite, Debug, PartialEq)]
789 enum StructEnum {
790 _A { _a: u8, _b: bool },
791 _B { _c: () },
792 _C {},
793 }
794 test_all::<StructEnum>(256 * 2 + 1 + 1);
795 }
796
797 #[test]
798 fn test_derive_mixed_enum() {
799 #[derive(Finite, Debug, PartialEq)]
800 enum MixedEnum {
801 _A,
802 _B(u8),
803 _C { _a: Option<bool>, _b: u8 },
804 }
805 test_all::<MixedEnum>(1 + 256 + 3 * 256);
806 }
807
808 #[test]
809 fn test_derive_struct_with_non_clone_field() {
810 #[derive(Finite, Debug, PartialEq)]
811 struct NonCopy(u8);
812
813 #[derive(Finite, Debug, PartialEq)]
814 struct Outer {
815 inner: NonCopy,
816 }
817
818 test_all::<Outer>(256);
819 }
820
821 #[test]
822 fn test_derive_enum_with_non_clone_field() {
823 #[derive(Finite, Debug, PartialEq)]
824 struct NonCopy(u8);
825
826 #[derive(Finite, Debug, PartialEq)]
827 enum Outer {
828 A(NonCopy),
829 B { inner: NonCopy },
830 }
831
832 test_all::<Outer>(2 * 256);
833 }
834
835 #[test]
836 fn test_derive_struct_with_names_from_implementation() {
837 #[allow(clippy::struct_excessive_bools)]
838 #[derive(Finite, Debug, PartialEq)]
839 struct Struct {
840 v: bool,
841 i: bool,
842 res: bool,
843 r#type: bool,
844 }
845
846 test_all::<Struct>(2usize.pow(4));
847 }
848
849 #[test]
850 fn test_derive_enum_with_names_from_implementation() {
851 #[derive(Finite, Debug, PartialEq)]
852 enum Enum {
853 Variant {
854 v: bool,
855 i: bool,
856 res: bool,
857 r#type: bool,
858 },
859 }
860
861 test_all::<Enum>(2usize.pow(4));
862 }
863
864 #[test]
865 fn test_derive_generic() {
866 #[derive(Finite, Debug, PartialEq)]
867 struct Generic<T> {
868 _a: Option<T>,
869 }
870 test_all::<Generic<u8>>(257);
871 }
872
873 #[test]
874 fn test_derive_generic_lifetime() {
875 #[derive(Finite, Debug, PartialEq)]
876 struct Lifetime<'a> {
877 _a: PhantomData<&'a ()>,
878 }
879 test_all::<Lifetime>(1);
880 }
881}