periodic_array/
lib.rs

1use std::ops::{Deref, DerefMut, Index, IndexMut};
2
3/// A macro for creating a `PeriodicArray` from a list of elements.
4///
5/// # Examples
6///
7/// ```
8/// use periodic_array::p_arr;
9///
10/// let pa = p_arr![1, 2, 3];
11/// ```
12#[macro_export]
13macro_rules! p_arr {
14    ($($x:expr),* $(,)?) => {{
15        $crate::PeriodicArray::new([$($x),*])
16    }};
17}
18
19/// A struct representing a fixed-size array that provides periodic access to its elements.
20///
21/// Elements in the array are accessed such that indexing beyond the array's bounds
22/// will wrap around to the beginning, effectively treating the array as infinite/periodic.
23/// Internally, bounds checks are skipped via the use of `get_unchecked` and `get_unchecked_mut`.
24///
25/// Copy is optionally derived when the `"copy"` feature is enabled. This separation is done for
26/// those of us that want full control on when copies are performed.
27///
28/// # Type Parameters
29///
30/// * `T` - The type of elements held in the array.
31/// * `N` - The compile-time fixed size of the array.
32///
33/// # Examples
34///
35/// ```
36/// use periodic_array::p_arr;
37///
38/// let pa = p_arr![1, 2, 3];
39/// assert_eq!(pa[1], 2);
40/// assert_eq!(pa[4], 2); // Access beyond the length wraps around
41/// ```
42#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
43#[cfg_attr(feature = "copy", derive(Copy))]
44#[repr(C)]
45pub struct PeriodicArray<T: Clone + Copy, const N: usize> {
46    /// The inner array.
47    ///
48    /// Note: This is public so that the `p_arr!` macro can work by explicitly
49    /// declaring the array
50    pub(crate) inner: [T; N],
51}
52
53impl<T: Clone + Copy, const N: usize> PeriodicArray<T, N> {
54    #[inline(always)]
55    pub fn new(inner: [T; N]) -> Self {
56        PeriodicArray { inner }
57    }
58}
59
60impl<T: Clone + Copy, const N: usize> Index<usize> for PeriodicArray<T, N> {
61    type Output = T;
62    #[inline(always)]
63    fn index(&self, index: usize) -> &Self::Output {
64        unsafe { self.inner.get_unchecked(index % N) }
65    }
66}
67
68impl<T: Clone + Copy, const N: usize> IndexMut<usize> for PeriodicArray<T, N> {
69    #[inline(always)]
70    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
71        unsafe { self.inner.get_unchecked_mut(index % N) }
72    }
73}
74
75impl<T: Clone + Copy, const N: usize> Deref for PeriodicArray<T, N> {
76    type Target = [T; N];
77    #[inline(always)]
78    fn deref(&self) -> &Self::Target {
79        &self.inner
80    }
81}
82
83impl<T: Clone + Copy, const N: usize> DerefMut for PeriodicArray<T, N> {
84    #[inline(always)]
85    fn deref_mut(&mut self) -> &mut Self::Target {
86        &mut self.inner
87    }
88}
89
90impl<T: Clone + Copy, const N: usize> From<[T; N]> for PeriodicArray<T, N> {
91    #[inline(always)]
92    fn from(inner: [T; N]) -> Self {
93        PeriodicArray { inner }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::{p_arr, PeriodicArray};
100
101    #[test]
102    pub fn declare_with_macro() {
103        let pa_macro = p_arr![1, 2, 3];
104
105        let pa = PeriodicArray { inner: [1, 2, 3] };
106
107        assert_eq!(pa, pa_macro);
108    }
109
110    #[test]
111    pub fn index_into() {
112        let pa = p_arr![1, 2, 3];
113
114        // in domain
115        assert_eq!(pa[0], 1);
116        assert_eq!(pa[1], 2);
117        assert_eq!(pa[2], 3);
118
119        // periodic
120        assert_eq!(pa[3], 1);
121        assert_eq!(pa[4], 2);
122        assert_eq!(pa[5], 3);
123    }
124
125    #[test]
126    pub fn use_array_methods() {
127        let mut pa = p_arr![1, 2, 3];
128
129        // .map() method of array
130        let _x2 = pa.map(|x| x * x);
131
132        // .iter() and .iter_mut() method of array
133        for x in pa.iter() {
134            let _ = x * x;
135        }
136        for p in pa.iter_mut() {
137            *p = *p * *p;
138        }
139    }
140}