blob: 8199f211b200d52542d48dcc2f917ad1f3c7b4cd [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use core::ops::{Range, RangeBounds};
use zerocopy::{ByteSlice, ByteSliceMut};
use crate::{canonicalize_range, take_back, take_back_mut, take_front, take_front_mut};
/// A wrapper for a sequence of byte slices.
///
/// `FragmentedByteSlice` shares its underlying memory with the slice it was
/// constructed from and, as a result, operations on a `FragmentedByteSlice` may
/// mutate the backing slice.
#[derive(Debug)]
pub struct FragmentedByteSlice<'a, B: 'a + ByteSlice>(&'a mut [B]);
/// A single byte slice fragment in a [`FragmentedByteSlice`].
pub trait Fragment: ByteSlice {
/// Takes `n` bytes out of the front of this fragment.
///
/// # Panics
///
/// Panics if `n` is larger than the length of this `ByteSlice`.
fn take_front(&mut self, n: usize) -> Self;
/// Takes `n` bytes out of the back of this fragment.
///
/// # Panics
///
/// Panics if `n` is larger than the length of this `ByteSlice`.
fn take_back(&mut self, n: usize) -> Self;
/// Constructs a new empty `Fragment`.
fn empty() -> Self;
}
/// A type that can produce a `FragmentedByteSlice` view of itself.
pub trait AsFragmentedByteSlice<B: Fragment> {
/// Generates a `FragmentedByteSlice` view of `self`.
fn as_fragmented_byte_slice(&mut self) -> FragmentedByteSlice<'_, B>;
}
impl<O, B> AsFragmentedByteSlice<B> for O
where
B: Fragment,
O: AsMut<[B]>,
{
fn as_fragmented_byte_slice(&mut self) -> FragmentedByteSlice<'_, B> {
FragmentedByteSlice::new(self.as_mut())
}
}
impl<'a> Fragment for &'a [u8] {
fn take_front(&mut self, n: usize) -> Self {
take_front(self, n)
}
fn take_back(&mut self, n: usize) -> Self {
take_back(self, n)
}
fn empty() -> Self {
&[]
}
}
impl<'a> Fragment for &'a mut [u8] {
fn take_front(&mut self, n: usize) -> Self {
take_front_mut(self, n)
}
fn take_back(&mut self, n: usize) -> Self {
take_back_mut(self, n)
}
fn empty() -> Self {
&mut []
}
}
impl<'a, B: 'a + Fragment> FragmentedByteSlice<'a, B> {
/// Constructs a new `FragmentedByteSlice` from `bytes`.
///
/// It is important to note that `FragmentedByteSlice` takes a mutable
/// reference to a backing slice. Operations on the `FragmentedByteSlice`
/// may mutate `bytes` as an optimization to avoid extra allocations.
///
/// Users are encouraged to treat slices used to construct
/// `FragmentedByteSlice`s as if they are not owned anymore and only serve
/// as (usually temporary) backing for a `FragmentedByteSlice`.
pub fn new(bytes: &'a mut [B]) -> Self {
Self(bytes)
}
/// Constructs a new empty `FragmentedByteSlice`.
pub fn new_empty() -> Self {
Self(&mut [])
}
/// Gets the total length, in bytes, of this `FragmentedByteSlice`.
pub fn len(&self) -> usize {
// TODO(brunodalbo) explore if caching the total length in a
// FragmentedByteSlice could be a worthy performance optimization.
self.0.iter().map(|x| x.len()).sum()
}
/// Returns `true` if the `FragmentedByteSlice` is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Slices this `FragmentedByteSlice`, reducing it to only the bytes within
/// `range`.
///
/// `slice` will mutate the backing slice by dropping or shrinking fragments
/// as necessary so the overall composition now matches the requested
/// `range`. The returned new `FragmentedByteSlice` uses the same (albeit
/// likely modified) backing mutable slice reference as `self`.
///
/// # Panics
///
/// Panics if the provided `range` is not within the bounds of this
/// `FragmentedByteSlice`, or if the range is nonsensical (the end precedes
/// the start).
pub fn slice<R>(self, range: R) -> Self
where
R: RangeBounds<usize>,
{
let len = self.len();
let range = canonicalize_range(len, &range);
let mut bytes = self.0;
// c is the amount of bytes we need to discard from the beginning of the
// fragments.
let mut c = range.start;
while c != 0 {
if bytes[0].len() > c {
// if the first fragment contains more than c bytes, just take
// c bytes out of its front and we're done.
bytes[0].take_front(c);
break;
} else {
// otherwise, just account for the first fragment's entire
// length and drop it.
c -= bytes[0].len();
bytes = bytes.split_first_mut().unwrap().1;
}
}
// c is the amount of bytes we need to discard from the end of the
// fragments.
let mut c = len - range.end;
while c != 0 {
let last = bytes.last_mut().unwrap();
if last.len() > c {
// if the last fragment contains more than c bytes, just take
// c bytes out of its back and we're done.
last.take_back(c);
break;
} else {
// otherwise, just account for the last fragment's entire length
// and drop it.
c -= last.len();
bytes = bytes.split_last_mut().unwrap().1;
}
}
Self(bytes)
}
/// Checks if the contents of this `FragmentedByteSlice` are equal to the
/// contents of `other`.
pub fn eq_slice(&self, mut other: &[u8]) -> bool {
for x in self.0.iter() {
let x = x.as_ref();
if other.len() < x.len() || !x.eq(&other[..x.len()]) {
return false;
}
other = &other[x.len()..];
}
other.len() == 0
}
/// Gets an iterator over all the bytes in this `FragmentedByteSlice`.
pub fn iter(&self) -> impl '_ + Iterator<Item = u8> {
self.0.iter().map(|x| x.iter()).flatten().map(|x| *x)
}
/// Gets an iterator over the fragments of this `FragmentedByteSlice`.
pub fn iter_fragments(&'a self) -> impl 'a + Iterator<Item = &'a [u8]> + Clone {
self.0.iter().map(|x| x.as_ref())
}
/// Copies all the bytes in `self` into the contiguous slice `dst`.
///
/// # Panics
///
/// Panics if `dst.len() != self.len()`.
pub fn copy_into_slice(&self, mut dst: &mut [u8]) {
for p in self.0.iter() {
let (tgt, nxt) = dst.split_at_mut(p.len());
tgt.copy_from_slice(p.as_ref());
dst = nxt;
}
assert_eq!(dst.len(), 0);
}
/// Copies all the bytes in `self` to a `Vec`.
pub fn to_flattened_vec(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.len());
for x in self.0.iter() {
out.extend_from_slice(x);
}
out
}
/// Creates an index tuple from a linear index `idx`.
///
/// `get_index` creates a tuple index `(slice, byte)` where `slice` is the
/// index in the backing slice of slices and `byte` is the byte index in the
/// slice at `self.0[slice]` where `(slice, byte)` represents the `idx`th
/// byte in this `FragmentedByteSlice`.
///
/// # Panics
///
/// Panics if `idx` is out of bounds.
fn get_index(&self, mut idx: usize) -> (usize, usize) {
let mut a = 0;
while self.0[a].len() <= idx || self.0[a].len() == 0 {
idx -= self.0[a].len();
a += 1;
}
(a, idx)
}
/// Increments the index tuple `idx`.
///
/// Increments the index tuple `idx` (see
/// [`FragmentedByteSlice::get_index`]) so it references the next byte.
/// `increment_index` will stop incrementing and just return a slice index
/// equal to the length of the backing slice if `idx` can't be incremented
/// anymore.
fn increment_index(&self, idx: &mut (usize, usize)) {
if self.0[idx.0].len() > (idx.1 + 1) {
idx.1 += 1;
} else {
idx.0 += 1;
// skip any empty slices:
while idx.0 < self.0.len() && self.0[idx.0].len() == 0 {
idx.0 += 1;
}
idx.1 = 0;
}
}
/// Decrements the index tuple `idx`.
///
/// Decrements the index tuple `idx` (see
/// [`FragmentedByteSlice::get_index`]) so it references the previous byte.
/// `decrement_index` will wrap around to an invalid out of bounds index
/// (slice index is equal to the length of the backing slice) if `idx` is
/// pointing to the `0`th byte.
fn decrement_index(&self, idx: &mut (usize, usize)) {
if idx.1 == 0 {
if idx.0 == 0 {
idx.0 = self.0.len();
idx.1 = 0;
return;
}
idx.0 -= 1;
// skip any empty slices:
while idx.0 != 0 && self.0[idx.0].len() == 0 {
idx.0 -= 1;
}
if self.0[idx.0].len() != 0 {
idx.1 = self.0[idx.0].len() - 1;
} else {
idx.0 = self.0.len();
idx.1 = 0;
}
} else {
idx.1 -= 1;
}
}
/// Tries to convert this `FragmentedByteSlice` into a contiguous one.
///
/// Returns `Ok` if `self` is a single contiguous byte slice (or is empty),
/// and `Err` otherwise.
///
/// On success, the backing slice is mutated to contain a single, empty byte
/// slice.
pub fn try_into_contiguous(self) -> Result<B, Self> {
if self.0.len() == 0 {
Ok(B::empty())
} else if self.0.len() == 1 {
Ok(std::mem::replace(&mut self.0[0], B::empty()))
} else {
Err(self)
}
}
/// Attempt to get a contiguous reference to this `FragmentedByteSlice`.
///
/// Returns `Some` if this `FragmentedByteSlice` is a single contiguous part
/// (or is empty). Returns `None` otherwise.
pub fn try_get_contiguous(&self) -> Option<&[u8]> {
if self.0.len() == 0 {
Some(&mut [])
} else if self.0.len() == 1 {
Some(self.0[0].as_ref())
} else {
None
}
}
/// Tries to splits this `FragmentedByteSlice` into a contiguous prefix, a
/// (possibly fragmented) body, and a contiguous suffix.
///
/// Returns `None` if it isn't possible to form a contiguous prefix and
/// suffix with the provided `range`.
///
/// # Panics
///
/// Panics if the range is out of bounds, or if the range is nonsensical
/// (the end precedes the start).
pub fn try_split_contiguous<R>(self, range: R) -> Option<(B, Self, B)>
where
R: RangeBounds<usize>,
{
let len = self.len();
let range = canonicalize_range(len, &range);
if len == 0 && range.start == 0 && range.end == 0 {
// If own length is zero and the requested body range is an empty
// body start at zero, avoid returning None in the call to
// last_mut() below.
return Some((B::empty(), FragmentedByteSlice(&mut []), B::empty()));
}
// take foot first, because if we have a single fragment, taking head
// first will mess with the index calculations.
let foot = self.0.last_mut()?;
let take = len - range.end;
if foot.len() < take {
return None;
}
let foot = foot.take_back(take);
let head = self.0.first_mut()?;
if head.len() < range.start {
return None;
}
let head = head.take_front(range.start);
Some((head, self, foot))
}
}
impl<'a, B: 'a + ByteSliceMut + Fragment> FragmentedByteSlice<'a, B> {
/// Gets an iterator of mutable references to all the bytes in this
/// `FragmentedByteSlice`.
pub fn iter_mut(&mut self) -> impl '_ + Iterator<Item = &'_ mut u8> {
self.0.iter_mut().map(|x| x.iter_mut()).flatten()
}
/// Copies all the bytes in `src` to `self`.
///
/// # Panics
///
/// Panics if `self.len() != src.len()`.
pub fn copy_from_slice(&mut self, mut src: &[u8]) {
for p in self.0.iter_mut() {
let (cur, nxt) = src.split_at(p.len());
p.as_mut().copy_from_slice(cur);
src = nxt;
}
assert_eq!(src.len(), 0);
}
/// Copies all the bytes from another `FragmentedByteSlice` `other` into
/// `self`.
///
/// # Panics
///
/// Panics if `self.len() != other.len()`.
pub fn copy_from<BB>(&mut self, other: &FragmentedByteSlice<'_, BB>)
where
BB: ByteSlice,
{
// keep an iterator over the fragments in other.
let mut oth = other.0.iter().map(|z| z.as_ref());
// op is the current fragment in other we're copying from.
let mut op = oth.next();
for part in self.0.iter_mut() {
// p is the current fragment in self we're feeding bytes into.
let mut p = part.as_mut();
// iterate until this fragment is all consumed.
while p.len() != 0 {
// skip any empty slices in other.
while op.unwrap().len() == 0 {
op = oth.next();
}
// get the current fragment in other.
let k = op.unwrap();
if k.len() <= p.len() {
// if k does not have enough bytes to fill p, copy what we
// can, change p to the region that hasn't been updated, and
// then fetch the next fragment from other.
let (dst, nxt) = p.split_at_mut(k.len());
dst.copy_from_slice(k.as_ref());
p = nxt;
op = oth.next();
} else {
// Otherwise, copy the p.len() first bytes from k, and
// modify op to keep the rest of the bytes in k.
let (src, nxt) = k.split_at(p.len());
p.copy_from_slice(src.as_ref());
op = Some(nxt);
// break from loop, p had all its bytes copied.
break;
}
}
}
// If anything is left in our iterator, panic if it isn't an empty slice
// since the lengths must match.
while let Some(v) = op {
assert_eq!(v.len(), 0);
op = oth.next();
}
}
/// Copies elements from one part of the `FragmentedByteSlice` to another
/// part of itself.
///
/// `src` is the range within `self` to copy from. `dest` is the starting
/// index of the range within `self` to copy to, which will have the same
/// length as `src`. The two ranges may overlap. The ends of the two ranges
/// must be less than or equal to `self.len()`.
///
/// # Panics
///
/// This function will panic if either range is out of bounds, or if the end
/// of `src` is before the start.
pub fn copy_within<R: RangeBounds<usize>>(&mut self, src: R, dest: usize) {
let Range { start, end } = canonicalize_range(self.len(), &src);
assert!(end >= start);
let len = end - start;
if start == dest || len == 0 {
return;
} else if start > dest {
// copy front to back
let mut start = self.get_index(start);
let mut dest = self.get_index(dest);
for _ in 0..len {
self.0[dest.0][dest.1] = self.0[start.0][start.1];
self.increment_index(&mut start);
self.increment_index(&mut dest);
}
} else {
// copy back to front
let mut start = self.get_index(end - 1);
let mut dest = self.get_index(dest + len - 1);
for _ in 0..len {
self.0[dest.0][dest.1] = self.0[start.0][start.1];
self.decrement_index(&mut start);
self.decrement_index(&mut dest);
}
}
}
/// Attempt to get a contiguous mutable reference to this
/// `FragmentedByteSlice`.
///
/// Returns `Some` if this `FragmentedByteSlice` is a single contiguous part
/// (or is empty). Returns `None` otherwise.
pub fn try_get_contiguous_mut(&mut self) -> Option<&mut [u8]> {
if self.0.len() == 0 {
Some(&mut [])
} else if self.0.len() == 1 {
Some(self.0[0].as_mut())
} else {
None
}
}
}
/// A [`FragmentedByteSlice`] with immutable byte slices.
pub type FragmentedBytes<'a> = FragmentedByteSlice<'a, &'a [u8]>;
/// A [`FragmentedByteSlice`] with mutable byte slices.
pub type FragmentedBytesMut<'a> = FragmentedByteSlice<'a, &'a mut [u8]>;
#[cfg(test)]
mod tests {
use super::*;
/// Calls `f` with all the possible three way slicings of a non-mutable
/// buffer containing `[1,2,3,4,5]` (including cases with empty slices).
fn with_fragments<F: FnMut(FragmentedBytes<'_>)>(mut f: F) {
let buff = [1_u8, 2, 3, 4, 5];
for i in 0..buff.len() {
for j in i..buff.len() {
let (a, x) = buff.split_at(i);
let (b, c) = x.split_at(j - i);
let mut frags = [a, b, c];
f(frags.as_fragmented_byte_slice());
}
}
}
/// Calls `f` with all the possible three way slicings of a non-mutable
/// buffer containing `[1,2,3,4,5]` (including cases with empty slices).
fn with_fragments_mut<F: FnMut(FragmentedBytesMut<'_>)>(mut f: F) {
let buff = [1_u8, 2, 3, 4, 5];
for i in 0..buff.len() {
for j in i..buff.len() {
let mut buff = [1_u8, 2, 3, 4, 5];
let (a, x) = buff.split_at_mut(i);
let (b, c) = x.split_at_mut(j - i);
let mut frags = [a, b, c];
f(frags.as_fragmented_byte_slice());
}
}
}
#[test]
fn test_iter() {
// check iterator over different fragment permutations.
with_fragments(|bytes| {
let mut iter = bytes.iter();
for i in 1_u8..6 {
assert_eq!(iter.next().unwrap(), i);
}
assert!(iter.next().is_none());
assert!(iter.next().is_none());
});
}
#[test]
fn test_eq() {
// check equality over different fragment permutations.
with_fragments(|bytes| {
assert!(bytes.eq_slice([1_u8, 2, 3, 4, 5].as_ref()));
assert!(!bytes.eq_slice([1_u8, 2, 3, 4].as_ref()));
assert!(!bytes.eq_slice([1_u8, 2, 3, 4, 5, 6].as_ref()));
assert!(!bytes.eq_slice(&[]));
});
// check equality for the empty slice case.
let bytes = FragmentedBytes::new_empty();
assert!(!bytes.eq_slice([1_u8, 2, 3, 4, 5].as_ref()));
assert!(bytes.eq_slice(&[]));
}
#[test]
fn test_slice() {
// test all valid ranges with all possible permutations of a three way
// slice.
for i in 0..6 {
for j in i..6 {
with_fragments(|bytes| {
let range = bytes.slice(i..j);
let x = [1_u8, 2, 3, 4, 5];
assert_eq!(&range.to_flattened_vec()[..], &x[i..j]);
});
}
}
}
#[test]
#[should_panic]
fn test_slice_out_of_range() {
// check that slicing out of range will panic
with_fragments(|bytes| {
let _ = bytes.slice(0..15);
});
}
#[test]
#[should_panic]
fn test_copy_into_slice_too_big() {
// check that copy_into_slice panics for different lengths.
with_fragments(|bytes| {
let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
bytes.copy_into_slice(&mut slice[..]);
});
}
#[test]
#[should_panic]
fn test_copy_into_slice_too_small() {
// check that copy_into_slice panics for different lengths.
with_fragments(|bytes| {
let mut slice = [1, 2];
bytes.copy_into_slice(&mut slice[..]);
});
}
#[test]
fn test_copy_into_slice() {
// try copy_into_slice with all different fragment permutations.
with_fragments(|bytes| {
let mut slice = [0; 5];
bytes.copy_into_slice(&mut slice[..]);
assert_eq!(slice, &[1, 2, 3, 4, 5][..]);
});
}
#[test]
#[should_panic]
fn test_copy_from_slice_too_big() {
// check that copy_from_slice panics for different lengths.
with_fragments_mut(|mut bytes| {
let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
bytes.copy_from_slice(&slice[..]);
});
}
#[test]
#[should_panic]
fn test_copy_from_slice_too_small() {
// check that copy_from_slice panics for different lengths.
with_fragments_mut(|mut bytes| {
let slice = [1, 2, 3];
bytes.copy_from_slice(&slice[..]);
});
}
#[test]
fn test_copy_from_slice() {
// test copy_from_slice with all fragment permutations.
with_fragments_mut(|mut bytes| {
let slice = [10, 20, 30, 40, 50];
bytes.copy_from_slice(&slice[..]);
assert_eq!(&bytes.to_flattened_vec()[..], &slice[..]);
});
}
#[test]
fn test_copy_from() {
// test copying from another FragmentedByteSlice, going over all
// fragment permutations for both src and dst.
with_fragments(|src| {
with_fragments_mut(|mut dst| {
// zer-out dst
dst.copy_from_slice(&[0; 5][..]);
dst.copy_from(&src);
assert_eq!(&dst.to_flattened_vec()[..], &[1_u8, 2, 3, 4, 5][..]);
})
});
}
#[test]
#[should_panic]
fn test_copy_from_too_long() {
// copying from another FragmentedByteSlice should panic if the lengths
// differ.
let mut a = [0; 2];
let mut b = [0; 2];
let mut frags = [a.as_mut(), b.as_mut()];
with_fragments(|src| {
frags.as_fragmented_byte_slice().copy_from(&src);
});
}
#[test]
#[should_panic]
fn test_copy_from_too_short() {
// copying from another FragmentedByteSlice should panic if the lengths
// differ.
let mut a = [0; 5];
let mut b = [0; 2];
let mut frags = [a.as_mut(), b.as_mut()];
with_fragments(|src| {
frags.as_fragmented_byte_slice().copy_from(&src);
});
}
#[test]
fn test_indexing() {
// Test the internal indexing functions over all fragment permutations.
with_fragments(|bytes| {
for i in 0..5 {
// check that get_index addresses the expected byte.
let mut idx = bytes.get_index(i);
assert_eq!(bytes.0[idx.0][idx.1], (i + 1) as u8);
// check that we can increase it correctly until the end of the
// buffer.
for j in 1..(6 - i - 1) {
bytes.increment_index(&mut idx);
assert_eq!(bytes.0[idx.0][idx.1], (i + j + 1) as u8);
}
// fetch the same index again.
let mut idx = bytes.get_index(i);
assert_eq!(bytes.0[idx.0][idx.1], (i + 1) as u8);
// check that we can decrease it correctly until the beginning
// of the buffer.
for j in 1..=i {
bytes.decrement_index(&mut idx);
assert_eq!(bytes.0[idx.0][idx.1], (i - j + 1) as u8);
}
}
});
}
#[test]
fn test_copy_within() {
with_fragments_mut(|mut bytes| {
// copy last half to beginning:
bytes.copy_within(3..5, 0);
assert_eq!(&bytes.to_flattened_vec()[..], &[4, 5, 3, 4, 5]);
});
with_fragments_mut(|mut bytes| {
// copy first half to end:
bytes.copy_within(0..2, 3);
assert_eq!(&bytes.to_flattened_vec()[..], &[1, 2, 3, 1, 2]);
});
}
#[test]
#[should_panic]
fn test_copy_within_src_out_of_bounds() {
with_fragments_mut(|mut bytes| {
// try to copy out of bounds
bytes.copy_within(3..15, 0);
});
}
#[test]
#[should_panic]
fn test_copy_within_dst_out_of_bounds() {
with_fragments_mut(|mut bytes| {
// try to copy out of bounds
bytes.copy_within(3..5, 15);
});
}
#[test]
#[should_panic]
fn test_copy_within_bad_range() {
with_fragments_mut(|mut bytes| {
// pass a bad range (end before start)
bytes.copy_within(5..3, 0);
});
}
#[test]
fn test_get_contiguous() {
// If we have fragments, get_contiguous should fail:
with_fragments_mut(|mut bytes| {
assert!(bytes.try_get_contiguous().is_none());
assert!(bytes.try_get_contiguous_mut().is_none());
assert!(bytes.try_into_contiguous().is_err());
});
// otherwise we should be able to get the contiguous bytes:
let mut single = [1_u8, 2, 3, 4, 5];
let mut single = [&mut single[..]];
let mut single = single.as_fragmented_byte_slice();
assert_eq!(single.try_get_contiguous().unwrap(), &[1, 2, 3, 4, 5][..]);
assert_eq!(single.try_get_contiguous_mut().unwrap(), &[1, 2, 3, 4, 5][..]);
assert_eq!(single.try_into_contiguous().unwrap(), &[1, 2, 3, 4, 5][..]);
}
#[test]
fn test_split_contiguous() {
let data = [1_u8, 2, 3, 4, 5, 6];
// try with a single continuous slice
let mut refs = [&data[..]];
let frag = refs.as_fragmented_byte_slice();
let (head, body, foot) = frag.try_split_contiguous(2..4).unwrap();
assert_eq!(head, &data[..2]);
assert_eq!(&body.to_flattened_vec()[..], &data[2..4]);
assert_eq!(foot, &data[4..]);
// try splitting just part of the header
let mut refs = [&data[0..3], &data[3..]];
let frag = refs.as_fragmented_byte_slice();
let (head, body, foot) = frag.try_split_contiguous(2..6).unwrap();
assert_eq!(head, &data[..2]);
assert_eq!(&body.to_flattened_vec()[..], &data[2..]);
assert!(foot.is_empty());
// try splitting just part of the footer
let mut refs = [&data[0..3], &data[3..]];
let frag = refs.as_fragmented_byte_slice();
let (head, body, foot) = frag.try_split_contiguous(..4).unwrap();
assert!(head.is_empty());
assert_eq!(&body.to_flattened_vec()[..], &data[..4]);
assert_eq!(foot, &data[4..]);
// try completely extracting both:
let mut refs = [&data[0..3], &data[3..]];
let frag = refs.as_fragmented_byte_slice();
let (head, body, foot) = frag.try_split_contiguous(3..3).unwrap();
assert_eq!(head, &data[0..3]);
assert_eq!(body.len(), 0);
assert_eq!(foot, &data[3..]);
// try getting contiguous bytes from an empty FragmentedByteSlice:
let frag = FragmentedBytes::new_empty();
let (head, body, foot) = frag.try_split_contiguous(..).unwrap();
assert!(head.is_empty());
assert!(body.is_empty());
assert!(foot.is_empty());
}
#[test]
#[should_panic]
fn test_split_contiguous_out_of_bounds() {
let data = [1_u8, 2, 3, 4, 5, 6];
let mut refs = [&data[..]];
let frag = refs.as_fragmented_byte_slice();
let _ = frag.try_split_contiguous(2..8);
}
#[test]
fn test_empty() {
// Can create empty FragmentedByteSlices with no fragments or with one
// empty fragment.
// is_empty should return true for both cases.
let empty = FragmentedByteSlice::<&'static [u8]>::new_empty();
assert!(empty.is_empty());
let empty = [0_u8; 0];
let mut empty = [&empty[..]];
let empty = empty.as_fragmented_byte_slice();
assert!(empty.is_empty());
}
}