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);
}
}