concurrent_slice/
slice.rs

1use crate::{chunk::Chunk, chunks::Chunks, common::*, iter::Iter, windows::Windows};
2
3/// The trait adds methods for concurrent processing on any type that can be borrowed as a slice.
4pub trait ConcurrentSlice<T> {
5    /// Splits the slice-like data into two sub-slices, divided at specified index.
6    ///
7    /// # Panics
8    /// The method panics if the index is out of bound.
9    fn concurrent_split_at(self, index: usize) -> (Chunk<Self, T>, Chunk<Self, T>)
10    where
11        Self: 'static + AsMut<[T]> + Sized + Send,
12        T: 'static + Send,
13    {
14        unsafe {
15            let data = Arc::new(self);
16            let ptr = Arc::as_ptr(&data) as *mut Self;
17            let slice: &mut [T] = ptr.as_mut().unwrap().as_mut();
18            let lslice = NonNull::new_unchecked(&mut slice[0..index] as *mut [T]);
19            let rslice = NonNull::new_unchecked(&mut slice[index..] as *mut [T]);
20
21            (
22                Chunk {
23                    data: data.clone(),
24                    slice: lslice,
25                },
26                Chunk {
27                    data,
28                    slice: rslice,
29                },
30            )
31        }
32    }
33
34    /// Returns an iterator of roughly fixed-sized chunks of the slice.
35    ///
36    /// Each chunk has `chunk_size` elements, expect the last chunk maybe shorter
37    /// if there aren't enough elements.
38    ///
39    /// The yielded chunks maintain a global reference count. Each chunk refers to
40    /// a mutable and exclusive sub-slice, enabling concurrent processing on input data.
41    ///
42    /// # Panics
43    /// The method panics if `chunk_size` is zero and slice length is not zero.
44    fn concurrent_chunks(mut self, chunk_size: usize) -> Chunks<Self, T>
45    where
46        Self: 'static + AsMut<[T]> + Sized + Send,
47        T: 'static + Send,
48    {
49        let len = self.as_mut().len();
50        assert!(
51            len == 0 || chunk_size > 0,
52            "chunk_size must be positive for non-empty slice"
53        );
54
55        Chunks {
56            index: 0,
57            chunk_size,
58            end: len,
59            data: Arc::new(self),
60            _phantom: PhantomData,
61        }
62    }
63
64    /// Returns an iterator with roughly `division` length of roughly fixed-sized chunks of the slice.
65    ///
66    /// The chunk size is determined by `division`. The last chunk maybe shorter if
67    /// there aren't enough elements. If `division` is `None`, it defaults to
68    /// the number of system processors.
69    ///
70    /// # Panics
71    /// The method panics if `division` is zero and slice length is not zero.
72    fn concurrent_chunks_by_division(
73        mut self,
74        division: impl Into<Option<usize>>,
75    ) -> Chunks<Self, T>
76    where
77        Self: 'static + AsMut<[T]> + Sized + Send,
78        T: 'static + Send,
79    {
80        let len = self.as_mut().len();
81        let division = division.into().unwrap_or_else(num_cpus::get);
82
83        let chunk_size = if len == 0 {
84            0
85        } else {
86            assert!(
87                division > 0,
88                "division must be positive for non-empty slice, but get zero"
89            );
90            (len + division - 1) / division
91        };
92
93        Chunks {
94            index: 0,
95            chunk_size,
96            end: len,
97            data: Arc::new(self),
98            _phantom: PhantomData,
99        }
100    }
101
102    /// Returns an iterator of owned references to each element of the slice.
103    fn owning_iter(self) -> Iter<Self, T>
104    where
105        Self: 'static + Send + Deref + CloneStableAddress,
106        Self::Target: AsRef<[T]>,
107    {
108        let owner = OwningRef::new(self).map(|me| me.as_ref());
109        Iter { owner, index: 0 }
110    }
111
112    /// Returns an iterator of owned windows of length `size`.
113    /// The windows are contiguous and overlap. If the slice is shorter than size,
114    /// the iterator returns no values.
115    fn owning_windows(self, size: usize) -> Windows<Self, T>
116    where
117        Self: 'static + Send + Deref + CloneStableAddress,
118        Self::Target: AsRef<[T]>,
119    {
120        assert!(size > 0, "size must be positive");
121        let owner = OwningRef::new(self).map(|me| me.as_ref());
122
123        Windows {
124            owner,
125            size,
126            index: 0,
127        }
128    }
129}
130
131impl<S, T> ConcurrentSlice<T> for S {}