mijit/util/
array_map.rs

1use std::fmt::{self, Debug, Formatter};
2use std::hash::{Hash};
3use std::marker::{PhantomData};
4
5/// Internally represented as a small integer that is usable as an array index.
6pub trait AsUsize: Debug + Copy + Hash + Eq {
7    fn as_usize(self) -> usize;
8}
9
10/// A map that is implemented as an array.
11/// This is preferable to a `HashMap` when possible.
12#[derive(Clone)]
13pub struct ArrayMap<K: AsUsize, V>(
14    Box<[V]>,
15    PhantomData<K>,
16);
17
18impl<K: AsUsize, V> ArrayMap<K, V> {
19    pub fn new_with(length: usize, f: impl Fn() -> V) -> Self {
20        (0..length).map(|_| f()).collect()
21    }
22
23    pub fn new(length: usize) -> Self where V: Default {
24        Self::new_with(length, Default::default)
25    }
26
27    pub fn is_empty(&self) -> bool { self.0.len() == 0 }
28
29    pub fn len(&self) -> usize { self.0.len() }
30
31    pub fn iter(&self) -> std::slice::Iter<V> { self.0.iter() }
32
33    pub fn iter_mut(&mut self) -> std::slice::IterMut<V> { self.0.iter_mut() }
34}
35
36impl<K: AsUsize, V: Debug> Debug for ArrayMap<K, V> {
37    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
38        self.0.fmt(f)
39    }
40}
41
42impl<'a, K: AsUsize, V> IntoIterator for &'a ArrayMap<K, V> {
43    type Item = &'a V;
44    type IntoIter = std::slice::Iter<'a, V>;
45
46    fn into_iter(self) -> Self::IntoIter { self.iter() }
47}
48
49impl<'a, K: AsUsize, V> IntoIterator for &'a mut ArrayMap<K, V> {
50    type Item = &'a mut V;
51    type IntoIter = std::slice::IterMut<'a, V>;
52
53    fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
54}
55
56impl<K: AsUsize, V> FromIterator<V> for ArrayMap<K, V> {
57    fn from_iter<T: IntoIterator<Item=V>>(iter: T) -> Self {
58        ArrayMap(iter.into_iter().collect(), PhantomData)
59    }
60}
61
62impl<K: AsUsize, V> AsRef<[V]> for ArrayMap<K, V> {
63    fn as_ref(&self) -> &[V] { self.0.as_ref() }
64}
65
66impl<K: AsUsize, V> AsMut<[V]> for ArrayMap<K, V> {
67    fn as_mut(&mut self) -> &mut [V] { self.0.as_mut() }
68}
69
70impl<K: AsUsize, V> std::ops::Index<K> for ArrayMap<K, V> {
71    type Output = V;
72
73    fn index(&self, index: K) -> &V {
74        &self.0[index.as_usize()]
75    }
76}
77
78impl<K: AsUsize, V> std::ops::IndexMut<K> for ArrayMap<K, V> {
79    fn index_mut(&mut self, index: K) -> &mut V {
80        &mut self.0[index.as_usize()]
81    }
82}
83
84//-----------------------------------------------------------------------------
85
86/// Declares a new type that wraps an array index but such that `Option<Self>`
87/// is the same size as `Self`. This is similar to `std::num::NonZeroXXX` except
88/// that the excluded value is the maximum value, not zero.
89///
90/// Usage:
91/// ```text
92/// array_index! {
93///     /// Documentation.
94///     #[attributes]
95///     pub struct MyStruct(NonZeroU16) {
96///         debug_name: "MyStruct",
97///         UInt: u16,
98///     }
99/// }
100/// ```
101macro_rules! array_index {
102    (
103        $(#[$struct_attributes: meta])*
104        pub struct $Name: ident(
105            $(#[$field_attributes: meta])*
106            $NonZeroUInt: ty
107        ) {
108            debug_name: $debug_name: expr,
109            UInt: $UInt: ty,
110        }
111    ) => {
112        $(#[$struct_attributes])*
113        #[repr(transparent)]
114        pub struct $Name(
115            $(#[$field_attributes])*
116            $NonZeroUInt
117        );
118
119        impl $Name {
120            /// Safety
121            ///
122            /// `index` must not be the maximum value of its type.
123            #[allow(clippy::missing_safety_doc)] // Work around bug in clippy.
124            #[allow(dead_code)]
125            pub const unsafe fn new_unchecked(index: $UInt) -> Self {
126                Self(<$NonZeroUInt>::new_unchecked(index + 1))
127            }
128
129            #[allow(dead_code)]
130            pub const fn new(index: $UInt) -> Option<Self> {
131                #[allow(clippy::manual_map)] // Work around bug in clippy.
132                match <$NonZeroUInt>::new(index.wrapping_add(1)) {
133                    Some(index) => Some(Self(index)),
134                    None => None,
135                }
136            }
137        }
138
139        impl std::fmt::Debug for $Name {
140            fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
141                use crate::util::AsUsize;
142                write!(f, "{}({:?})", $debug_name, self.as_usize())
143            }
144        }
145
146        impl crate::util::AsUsize for $Name {
147            fn as_usize(self) -> usize {
148                self.0.get() as usize - 1
149            }
150        }
151    }
152}
153
154//-----------------------------------------------------------------------------
155
156#[cfg(test)]
157pub mod tests {
158    use super::*;
159
160    use std::num::NonZeroU8;
161
162    #[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
163    #[repr(transparent)]
164    struct Foo(NonZeroU8);
165
166    impl Foo {
167        fn new(index: u8) -> Self {
168            let index = NonZeroU8::new(index + 1).expect("Not a valid array index");
169            Self(index)
170        }
171    }
172
173    impl std::fmt::Debug for Foo {
174        fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
175            write!(f, "Foo({:?})", self.as_usize())
176        }
177    }
178
179    impl AsUsize for Foo {
180        fn as_usize(self) -> usize {
181            self.0.get() as usize - 1
182        }
183    }
184
185    #[test]
186    fn array_map() {
187        assert_eq!(std::mem::size_of::<Foo>(), 1);
188        assert_eq!(std::mem::size_of::<Option<Foo>>(), 1);
189        let i = Foo::new(1);
190        assert_eq!(i.0.get(), 2);
191        assert_eq!(i.as_usize(), 1);
192        assert_eq!(format!("{:#?}", i), "Foo(1)");
193        let mut a = ArrayMap::new(2);
194        assert_eq!(a.as_ref(), &[false, false]);
195        assert_eq!(a.as_mut(), &[false, false]);
196        a[i] = true;
197        assert_eq!(a.as_ref(), &[false, true]);
198        for (i, r) in a.iter().enumerate() {
199            assert_eq!(*r, i>0);
200        }
201        for r in &mut a {
202            *r = !*r;
203        }
204        assert_eq!(a.as_ref(), &[true, false]);
205        a.as_mut()[1] = true;
206        assert_eq!(a.as_ref(), &[true, true]);
207    }
208}