chunked_vec 0.5.0

A chunk-oriented vector collection with fixed-size chunk storage
Documentation
use crate::ChunkedVec;

impl<T, const N: usize> ChunkedVec<T, N> {
    /// Returns the fixed chunk size used by this vector.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let vec: ChunkedVec<i32, 128> = ChunkedVecSized::new();
    /// assert_eq!(vec.chunk_size(), 128);
    /// ```
    #[inline]
    #[must_use]
    pub const fn chunk_size(&self) -> usize {
        N
    }

    /// Returns the number of logical chunks currently containing elements.
    ///
    /// This count is derived from the vector length, so retained but unused
    /// chunks after [`truncate`](ChunkedVec::truncate) or
    /// [`clear`](ChunkedVec::clear) are not included.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// vec.extend(0..9);
    /// assert_eq!(vec.chunk_count(), 3);
    ///
    /// vec.truncate(4);
    /// assert_eq!(vec.chunk_count(), 1);
    /// ```
    #[inline]
    #[must_use]
    pub fn chunk_count(&self) -> usize {
        Self::chunk_count_for_len(self.len)
    }

    /// Returns the elements stored in the chunk at `chunk_index`.
    ///
    /// The last chunk may contain fewer than `N` elements.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// vec.extend(0..6);
    ///
    /// assert_eq!(vec.chunk(0), Some(&[0, 1, 2, 3][..]));
    /// assert_eq!(vec.chunk(1), Some(&[4, 5][..]));
    /// assert_eq!(vec.chunk(2), None);
    /// ```
    #[must_use]
    pub fn chunk(&self, chunk_index: usize) -> Option<&[T]> {
        let len = self.chunk_len(chunk_index)?;
        let ptr = self.data[chunk_index].as_ptr().cast::<T>();

        unsafe { Some(std::slice::from_raw_parts(ptr, len)) }
    }

    /// Returns a mutable slice over the elements stored in the chunk at
    /// `chunk_index`.
    ///
    /// The last chunk may contain fewer than `N` elements.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// vec.extend(0..6);
    ///
    /// if let Some(chunk) = vec.chunk_mut(1) {
    ///     chunk[0] = 40;
    ///     chunk[1] = 50;
    /// }
    ///
    /// assert_eq!(vec[4], 40);
    /// assert_eq!(vec[5], 50);
    /// ```
    pub fn chunk_mut(&mut self, chunk_index: usize) -> Option<&mut [T]> {
        let len = self.chunk_len(chunk_index)?;
        let ptr = self.data[chunk_index].as_mut_ptr().cast::<T>();

        unsafe { Some(std::slice::from_raw_parts_mut(ptr, len)) }
    }

    /// Returns the first logical chunk in the vector.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// assert_eq!(vec.first_chunk(), None);
    ///
    /// vec.extend(0..6);
    /// assert_eq!(vec.first_chunk(), Some(&[0, 1, 2, 3][..]));
    /// ```
    #[must_use]
    pub fn first_chunk(&self) -> Option<&[T]> {
        self.chunk(0)
    }

    /// Returns the first logical chunk in the vector as a mutable slice.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// vec.extend(0..6);
    ///
    /// if let Some(chunk) = vec.first_chunk_mut() {
    ///     chunk[0] = 10;
    ///     chunk[1] = 11;
    /// }
    ///
    /// assert_eq!(vec.first_chunk(), Some(&[10, 11, 2, 3][..]));
    /// ```
    pub fn first_chunk_mut(&mut self) -> Option<&mut [T]> {
        self.chunk_mut(0)
    }

    /// Returns the last logical chunk in the vector.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// assert_eq!(vec.last_chunk(), None);
    ///
    /// vec.extend(0..6);
    /// assert_eq!(vec.last_chunk(), Some(&[4, 5][..]));
    /// ```
    #[must_use]
    pub fn last_chunk(&self) -> Option<&[T]> {
        self.chunk(self.chunk_count().checked_sub(1)?)
    }

    /// Returns the last logical chunk in the vector as a mutable slice.
    ///
    /// # Examples
    /// ```
    /// use chunked_vec::{ChunkedVec, ChunkedVecSized};
    ///
    /// let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
    /// vec.extend(0..6);
    ///
    /// if let Some(chunk) = vec.last_chunk_mut() {
    ///     chunk[0] = 10;
    ///     chunk[1] = 11;
    /// }
    ///
    /// assert_eq!(vec.last_chunk(), Some(&[10, 11][..]));
    /// ```
    pub fn last_chunk_mut(&mut self) -> Option<&mut [T]> {
        let last_chunk_index = self.chunk_count().checked_sub(1)?;
        self.chunk_mut(last_chunk_index)
    }

    #[inline]
    #[must_use]
    pub(crate) fn chunk_len(&self, chunk_index: usize) -> Option<usize> {
        let chunk_count = self.chunk_count();
        if chunk_index >= chunk_count {
            return None;
        }

        let start = chunk_index * N;
        Some((self.len - start).min(N))
    }
}

#[cfg(test)]
mod tests {
    use crate::{ChunkedVec, ChunkedVecSized};

    #[test]
    fn test_chunk_count_tracks_logical_chunks() {
        let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
        assert_eq!(vec.chunk_size(), 4);
        assert_eq!(vec.chunk_count(), 0);

        vec.extend(0..9);
        assert_eq!(vec.chunk_count(), 3);

        vec.truncate(4);
        assert_eq!(vec.chunk_count(), 1);
        assert_eq!(vec.allocated_capacity(), 12);
    }

    #[test]
    fn test_chunk_access() {
        let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
        vec.extend(0..10);

        assert_eq!(vec.chunk(0), Some(&[0, 1, 2, 3][..]));
        assert_eq!(vec.chunk(1), Some(&[4, 5, 6, 7][..]));
        assert_eq!(vec.chunk(2), Some(&[8, 9][..]));
        assert_eq!(vec.chunk(3), None);
    }

    #[test]
    fn test_chunk_mut_access() {
        let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
        vec.extend(0..6);

        let chunk = vec.chunk_mut(1).unwrap();
        chunk[0] = 40;
        chunk[1] = 50;

        assert_eq!(vec.chunk(1), Some(&[40, 50][..]));
        assert_eq!(vec.chunk_mut(2), None);
    }

    #[test]
    fn test_last_chunk_access() {
        let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
        assert_eq!(vec.last_chunk(), None);
        assert_eq!(vec.last_chunk_mut(), None);

        vec.extend(0..6);
        assert_eq!(vec.last_chunk(), Some(&[4, 5][..]));

        let last_chunk = vec.last_chunk_mut().unwrap();
        last_chunk[0] = 10;
        last_chunk[1] = 11;

        assert_eq!(vec.last_chunk(), Some(&[10, 11][..]));
    }

    #[test]
    fn test_first_chunk_access() {
        let mut vec: ChunkedVec<i32, 4> = ChunkedVecSized::new();
        assert_eq!(vec.first_chunk(), None);
        assert_eq!(vec.first_chunk_mut(), None);

        vec.extend(0..6);
        assert_eq!(vec.first_chunk(), Some(&[0, 1, 2, 3][..]));

        let first_chunk = vec.first_chunk_mut().unwrap();
        first_chunk[0] = 10;
        first_chunk[1] = 11;

        assert_eq!(vec.first_chunk(), Some(&[10, 11, 2, 3][..]));
    }
}