1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#![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);
    }
}