index_permute/lib.rs
1//! - A tool to reorder an array by an index array inplace when the elements are not [`Clone`] or [`Copy`].
2//! - lock-free parallel implementation.
3//! ## Example
4//! ```
5//! use index_permute::PermuteIndex;
6//! let index = PermuteIndex::try_new(&[2, 0, 1
7//! ]).unwrap();
8//! let mut data = vec![10, 20, 30];
9//! index_permute::order_by_index_inplace(&mut data, index);
10//! assert_eq!(data, vec![30, 10, 20]);
11//! ```
12#![deny(missing_docs)]
13#![deny(warnings)]
14
15use std::{mem::forget, ptr};
16use thiserror::Error;
17
18/// A struct to hold a permutation index.
19/// The index must be unique and in the range of `0..len`, where `len` is the length of the data to be permuted.
20/// This struct is used to ensure that the index is valid before performing any operations on the data.
21/// It can be created using [`PermuteIndex::try_new`], which checks the validity of the index.
22/// If the index is invalid, it returns a [`PermuteError::InvalidIndex`] error.
23/// The index can be used to permute data using the [`try_order_by_index_inplace`] function.
24/// The index length must match the data length, otherwise it returns a [`PermuteError::LengthMismatch`] error.
25/// The [`order_by_index_inplace`] function is a convenience function that panics if the index is invalid or the lengths do not match.
26/// # Example
27/// ```
28/// use index_permute::PermuteIndex;
29/// let index = PermuteIndex::try_new(&[2, 0, 1
30/// ]).unwrap();
31/// let mut data = vec![10, 20, 30];
32/// index_permute::order_by_index_inplace(&mut data, index);
33/// assert_eq!(data, vec![30, 10, 20]);
34/// ```
35pub struct PermuteIndex<T> {
36 data: T,
37}
38
39/// An error type for [`PermuteIndex`] and [`try_order_by_index_inplace`].
40#[derive(Debug, Error)]
41pub enum PermuteError {
42 /// An error indicating that the index is invalid.
43 #[error("Invalid index: indices must be unique and in 0..len")]
44 InvalidIndex,
45 /// An error indicating that the index length does not match the data length.
46 #[error("Index length must match data length")]
47 LengthMismatch,
48}
49impl<T> PermuteIndex<T>
50where
51 T: AsRef<[usize]>,
52{
53 fn check_index(index: &T) -> bool {
54 // make sure all indices are unique and from 0 to len-1
55 let indices = index.as_ref();
56 let len = indices.len();
57 let mut seen = vec![false; len];
58 for &i in indices {
59 if i >= len || seen[i] {
60 return false; // index out of bounds or duplicate
61 }
62 seen[i] = true;
63 }
64 true
65 }
66 /// Creates a new [`PermuteIndex`] if the index is valid.
67 /// Returns [`PermuteError::InvalidIndex`] if the index is not valid.
68 /// The index must be unique and in the range of `0..len`, where `len` is the length of the data to be permuted.
69 /// The index can be used to permute data using the [`try_order_by_index_inplace`] function.
70 /// The index length must match the data length, otherwise it returns [`PermuteError::LengthMismatch`] error.
71 /// The [`order_by_index_inplace`] function is a convenience function that panics if the index is invalid or the lengths do not match.
72 /// # Example
73 /// ```
74 /// use index_permute::PermuteIndex;
75 /// let index = PermuteIndex::try_new(&[2, 0, 1]).unwrap();
76 /// let mut data = vec![10, 20, 30];
77 /// index_permute::order_by_index_inplace(&mut data, index);
78 /// assert_eq!(data, vec![30, 10, 20]);
79 /// ```
80 pub fn try_new(index: T) -> Result<Self, PermuteError> {
81 if Self::check_index(&index) {
82 Ok(PermuteIndex { data: index })
83 } else {
84 Err(PermuteError::InvalidIndex)
85 }
86 }
87}
88
89/// Reorders the data in place according to the given index.
90/// First create a [`PermuteIndex`], then, it reorders the data in place
91/// # Example
92/// ```
93/// use index_permute::PermuteIndex;
94/// let index = PermuteIndex::try_new(&[2, 0, 1]).unwrap();
95/// let mut data = vec![10, 20, 30];
96/// index_permute::try_order_by_index_inplace(&mut data, index).unwrap();
97/// assert_eq!(data, vec![30, 10, 20]);
98/// ```
99pub fn try_order_by_index_inplace<T, I>(
100 data: &mut [T],
101 index: PermuteIndex<I>,
102) -> Result<(), PermuteError>
103where
104 I: AsRef<[usize]>,
105{
106 let indices = index.data.as_ref();
107 let len = data.len();
108 if indices.len() != len {
109 return Err(PermuteError::LengthMismatch);
110 }
111
112 // SAFETY: indices are unique and a valid permutation of 0..len,
113 // so we can move elements without overlap.
114
115 // Create a Vec<T> with uninitialized memory
116 let mut temp: Vec<T> = Vec::with_capacity(len);
117 unsafe {
118 temp.set_len(len);
119
120 for (i, &idx) in indices.iter().enumerate() {
121 // Move from data[idx] to temp[i]
122 ptr::write(
123 temp.get_unchecked_mut(i),
124 ptr::read(data.get_unchecked(idx)),
125 );
126 }
127
128 // Move back from temp to data
129 for i in 0..len {
130 ptr::write(data.get_unchecked_mut(i), ptr::read(temp.get_unchecked(i)));
131 }
132 forget(temp); // Prevent deallocation of temp
133 }
134 Ok(())
135}
136
137/// A convenience function that panics if the index is invalid or the lengths do not match.
138/// It is recommended to use [`try_order_by_index_inplace`] for error handling.
139/// # Example
140/// ```
141/// use index_permute::PermuteIndex;
142/// let index = PermuteIndex::try_new(&[2, 0, 1]).unwrap();
143/// let mut data = vec![10, 20, 30];
144/// index_permute::order_by_index_inplace(&mut data, index);
145/// assert_eq!(data, vec![30, 10, 20]);
146/// ```
147pub fn order_by_index_inplace<T, I>(data: &mut [T], index: PermuteIndex<I>)
148where
149 I: AsRef<[usize]>,
150{
151 if let Err(e) = try_order_by_index_inplace(data, index) {
152 panic!("Failed to order by index: {}", e);
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_permute_index() {
162 let _ = PermuteIndex::try_new(&[0usize, 2, 1]);
163 let _ = PermuteIndex::try_new(vec![0, 1, 2]);
164 let _ = PermuteIndex::try_new(&vec![0, 1, 2]);
165 let _ = PermuteIndex::try_new(&[0, 1, 2][..]);
166 }
167
168 #[test]
169 fn test_permute_order() {
170 let mut data = vec![10, 20, 30];
171 let index = PermuteIndex::try_new(&[2, 0, 1]).unwrap();
172 assert!(try_order_by_index_inplace(&mut data, index).is_ok());
173 assert_eq!(data, vec![30, 10, 20]);
174 }
175
176 #[test]
177 fn test_drop() {
178 struct DropTest {
179 value: usize,
180 }
181 impl Drop for DropTest {
182 fn drop(&mut self) {
183 println!("Dropping {}", self.value);
184 }
185 }
186 let mut data = vec![
187 DropTest { value: 1 },
188 DropTest { value: 2 },
189 DropTest { value: 3 },
190 ];
191 let index = PermuteIndex::try_new(&[2, 0, 1]).unwrap();
192
193 // now, there should be no drop
194 assert!(try_order_by_index_inplace(&mut data, index).is_ok());
195 println!("no drop should happen here");
196
197 assert_eq!(data[0].value, 3);
198 assert_eq!(data[1].value, 1);
199 assert_eq!(data[2].value, 2);
200 }
201}