sealingslice 0.1.0

A mutable slice that can seal parts of itself off from mutability, and hand out the sealed parts as immutable references.
Documentation
#![no_std]
//! This crate impements a slice that can seal parts of itself off from mutability, and hand out
//! the sealed parts as immutable references.
//!
//! A typical application is message processing in constrained environments where some
//! normalization is done in-place (because no message copy can be allocated), and the message is
//! sent off piecewise for further processing.
//!
//! A stand-alone usage example that capitalizes text in place and passes it out word by word:
//!
//! ```
//! let mut text = b"lorem ipsum dolor sit amet".to_vec();
//! let mut s = sealingslice::SealingSlice::new(&mut text[..]);
//! let mut capitalized = vec![];
//! let mut lastword = 0;
//! loop {
//!     let n;
//!     {
//!         let tail = s.mutable();
//!         
//!         if tail.len() == 0 {
//!             break;
//!         }
//!         
//!         // 'a' to 'z'
//!         if tail[0] >= 0x61 && tail[0] <= 0x7a {
//!             // ASCII shift to capital letter
//!             tail[0] -= 0x20;
//!         }
//!         
//!         let space = tail.iter().position(|&c| c == 0x20);
//!         let space = space.unwrap_or(tail.len());
//!         n = std::cmp::min(space + 1, tail.len());
//!     }
//!     s.seal(n);
//!
//!     capitalized.push(std::str::from_utf8(&s.sealed()[lastword..]).unwrap());
//!     lastword = s.sealed().len();
//! }
//! assert_eq!(capitalized, &["Lorem ", "Ipsum ", "Dolor ", "Sit ", "Amet"]);
//! ```

/// A slice that can seal parts of itself off from mutability, and hand out the sealed parts as
/// immutable references.
///
/// The slice grows immutable from start to end in a contiguous way, is initially mutable as a
/// whole. The seal is moved forward using the [.seal(n)](SealingSlice::seal()) method. The sealed
/// and unsealed parts can be accessed with the [.sealed()](SealingSlice::sealed()) and
/// [.mutable()](SealingSlice::mutable()) methods, respectively.
pub struct SealingSlice<'s, T: 's> {
    slice: &'s mut [T],
    index: usize,
}

impl<'s, T> SealingSlice<'s, T> {
    /// Create a new `SealingSlice`. The slice is initially fully mutable.
    pub fn new(slice: &'s mut [T]) -> Self {
        SealingSlice { slice, index: 0 }
    }

    /// Seal off `n` more items in the slice.
    ///
    /// Like out-of-bounds slice access, this panics if n is less than `.mutable().len()` -- thus
    /// ensuring that `.sealed()` and `.mutable()` will not.
    pub fn seal(&mut self, n: usize) {
        self.index = self.index.saturating_add(n);
        if self.index > self.slice.len() {
            panic!("Seal operation exceeds slice length");
        }
    }

    /// Obtain the unsealed part of the slice. This is an immutable view on the original slice,
    /// starts at its beginning, and always as long as the sum of all previous `.seal(n)`
    /// arguments.
    pub fn mutable<'a>(&'a mut self) -> &'a mut [T] {
        &mut self.slice[self.index..]
    }

    /// Obtain the sealed part of the slice. That is a mutable view on the original slice, ends at
    /// its end, and always has the length of the original slice minus the sum of all sealed parts.
    pub fn sealed<'a>(&'a self) -> &'s [T] {
        unsafe { &*(&self.slice[..self.index] as *const _) }
    }
}


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn seal_nothing() {
        let mut data = [0, 1, 2].to_vec();
        let mut s = SealingSlice::new(data.as_mut());
        s.seal(0);
        assert_eq!(s.mutable(), &[0, 1, 2]);
        assert_eq!(s.sealed(), &[]);
    }

    #[test]
    fn seal_all() {
        let mut data = [0, 1, 2].to_vec();
        let mut s = SealingSlice::new(data.as_mut());
        s.seal(3);
        assert_eq!(s.mutable(), &[]);
        assert_eq!(s.sealed(), &[0, 1, 2]);
    }

    #[test]
    #[should_panic]
    fn seal_too_much() {
        let mut data = [0, 1, 2].to_vec();
        let mut s = SealingSlice::new(data.as_mut());
        s.seal(4);
    }

    #[test]
    fn seal_some() {
        let mut data = [0, 1, 2].to_vec();
        let mut s = SealingSlice::new(data.as_mut());
        s.seal(2);
        assert_eq!(s.mutable(), &[2]);
        assert_eq!(s.sealed(), &[0, 1]);

        s.mutable()[0] = 4;
        s.seal(1);
        assert_eq!(s.sealed(), &[0, 1, 4]);
    }

    #[test]
    /// We don't guarantee that the slices are identical, but it's the only way it makes sense, and
    /// if that's not the case, something is probably wrong.
    fn identical_ptrs() {
        let mut data = [0, 1, 2].to_vec();
        let dataptr = data.as_ref() as *const _;
        let datalen = data.len();
        let mut s = SealingSlice::new(data.as_mut());

        assert_eq!(s.mutable() as *const _, dataptr);
        assert_eq!(s.mutable().len(), datalen);

        s.seal(1);
        s.seal(2);

        assert_eq!(s.sealed() as *const _, dataptr);
        assert_eq!(s.sealed().len(), datalen);
    }
}