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