pub struct ZeroVec<'a, T>where
T: AsULE,{ /* private fields */ }Expand description
A zero-copy, byte-aligned vector for fixed-width types.
ZeroVec<T> is designed as a drop-in replacement for Vec<T> in situations where it is
desirable to borrow data from an unaligned byte slice, such as zero-copy deserialization.
T must implement AsULE, which is auto-implemented for a number of built-in types,
including all fixed-width multibyte integers. For variable-width types like str,
see VarZeroVec. zerovec::make_ule may
be used to automatically implement AsULE for a type and generate the underlying ULE type.
Typically, the zero-copy equivalent of a Vec<T> will simply be ZeroVec<'a, T>.
Most of the methods on ZeroVec<'a, T> come from its Deref implementation to ZeroSlice<T>.
For creating zero-copy vectors of fixed-size types, see VarZeroVec.
ZeroVec<T> behaves much like Cow, where it can be constructed from
owned data (and then mutated!) but can also borrow from some buffer.
§Example
use zerovec::ZeroVec;
// The little-endian bytes correspond to the numbers on the following line.
let nums: &[u16] = &[211, 281, 421, 461];
#[derive(serde::Serialize, serde::Deserialize)]
struct Data<'a> {
#[serde(borrow)]
nums: ZeroVec<'a, u16>,
}
// The owned version will allocate
let data = Data {
nums: ZeroVec::alloc_from_slice(nums),
};
let bincode_bytes =
bincode::serialize(&data).expect("Serialization should be successful");
// Will deserialize without allocations
let deserialized: Data = bincode::deserialize(&bincode_bytes)
.expect("Deserialization should be successful");
// This deserializes without allocation!
assert!(!deserialized.nums.is_owned());
assert_eq!(deserialized.nums.get(2), Some(421));
assert_eq!(deserialized.nums, nums);§How it Works
ZeroVec<T> represents a slice of T as a slice of T::ULE. The difference between T and
T::ULE is that T::ULE must be encoded in little-endian with 1-byte alignment. When accessing
items from ZeroVec<T>, we fetch the T::ULE, convert it on the fly to T, and return T by
value.
Benchmarks can be found in the project repository, with some results found in the crate-level documentation.
See the design doc for more details.
Implementations§
Source§impl<'a, T: AsULE> ZeroVec<'a, T>
impl<'a, T: AsULE> ZeroVec<'a, T>
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Creates a new, borrowed, empty ZeroVec<T>.
§Examples
use zerovec::ZeroVec;
let zv: ZeroVec<u16> = ZeroVec::new();
assert!(zv.is_empty());Sourcepub const fn const_len(&self) -> usize
pub const fn const_len(&self) -> usize
Same as ZeroSlice::len, which is available through Deref and not const.
Sourcepub const fn new_borrowed(slice: &'a [T::ULE]) -> Self
pub const fn new_borrowed(slice: &'a [T::ULE]) -> Self
Creates a new borrowed ZeroVec using an existing
backing buffer
✨ Enabled with the alloc Cargo feature.
Sourcepub fn parse_bytes(bytes: &'a [u8]) -> Result<Self, UleError>
pub fn parse_bytes(bytes: &'a [u8]) -> Result<Self, UleError>
Parses a &[u8] buffer into a ZeroVec<T>.
This function is infallible for built-in integer types, but fallible for other types,
such as char. For more information, see ULE::parse_bytes_to_slice.
The bytes within the byte buffer must remain constant for the life of the ZeroVec.
§Endianness
The byte buffer must be encoded in little-endian, even if running in a big-endian environment. This ensures a consistent representation of data across platforms.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert!(!zerovec.is_owned());
assert_eq!(zerovec.get(2), Some(421));Sourcepub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self
pub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self
Uses a &[u8] buffer as a ZeroVec<T> without any verification.
§Safety
bytes need to be an output from ZeroSlice::as_bytes().
Sourcepub const fn as_slice(&self) -> &ZeroSlice<T>
pub const fn as_slice(&self) -> &ZeroSlice<T>
Returns this ZeroVec as a ZeroSlice.
To get a reference with a longer lifetime from a borrowed ZeroVec,
use ZeroVec::as_maybe_borrowed.
Sourcepub fn as_maybe_borrowed(&self) -> Option<&'a ZeroSlice<T>>
pub fn as_maybe_borrowed(&self) -> Option<&'a ZeroSlice<T>>
If this is a borrowed ZeroVec, return it as a slice that covers
its lifetime parameter.
To infallibly get a ZeroSlice with a shorter lifetime, use
ZeroVec::as_slice.
Sourcepub fn owned_capacity(&self) -> Option<NonZeroUsize>
pub fn owned_capacity(&self) -> Option<NonZeroUsize>
If the ZeroVec is owned, returns the capacity of the vector.
Otherwise, if the ZeroVec is borrowed, returns None.
§Examples
use zerovec::ZeroVec;
let mut zv = ZeroVec::<u8>::new_borrowed(&[0, 1, 2, 3]);
assert!(!zv.is_owned());
assert_eq!(zv.owned_capacity(), None);
// Convert to owned without appending anything
zv.with_mut(|v| ());
assert!(zv.is_owned());
assert_eq!(zv.owned_capacity(), Some(4.try_into().unwrap()));
// Double the size by appending
zv.with_mut(|v| v.push(0));
assert!(zv.is_owned());
assert_eq!(zv.owned_capacity(), Some(8.try_into().unwrap()));Source§impl<'a, T> ZeroVec<'a, T>where
T: EqULE,
impl<'a, T> ZeroVec<'a, T>where
T: EqULE,
Sourcepub fn try_from_slice(slice: &'a [T]) -> Option<Self>
pub fn try_from_slice(slice: &'a [T]) -> Option<Self>
Attempts to create a ZeroVec<'a, T> from a &'a [T] by borrowing the argument.
If this is not possible, such as on a big-endian platform, None is returned.
§Example
use zerovec::ZeroVec;
// The little-endian bytes correspond to the numbers on the following line.
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let nums: &[u16] = &[211, 281, 421, 461];
if let Some(zerovec) = ZeroVec::try_from_slice(nums) {
assert!(!zerovec.is_owned());
assert_eq!(bytes, zerovec.as_bytes());
}Methods from Deref<Target = ZeroSlice<T>>§
Sourcepub fn as_zerovec(&self) -> ZeroVec<'_, T>
pub fn as_zerovec(&self) -> ZeroVec<'_, T>
Sourcepub fn as_bytes(&self) -> &[u8] ⓘ
pub fn as_bytes(&self) -> &[u8] ⓘ
Returns this slice as its underlying &[u8] byte buffer representation.
Useful for serialization.
§Example
use zerovec::ZeroVec;
// The little-endian bytes correspond to the numbers on the following line.
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let nums: &[u16] = &[211, 281, 421, 32973];
let zerovec = ZeroVec::alloc_from_slice(nums);
assert_eq!(bytes, zerovec.as_bytes());Sourcepub fn as_ule_slice(&self) -> &[T::ULE]
pub fn as_ule_slice(&self) -> &[T::ULE]
Dereferences this slice as &[T::ULE].
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of elements in this slice.
§Example
use zerovec::ule::AsULE;
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(4, zerovec.len());
assert_eq!(
bytes.len(),
zerovec.len() * std::mem::size_of::<<u16 as AsULE>::ULE>()
);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns whether this slice is empty.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert!(!zerovec.is_empty());
let emptyvec: ZeroVec<u16> = ZeroVec::parse_bytes(&[]).expect("infallible");
assert!(emptyvec.is_empty());Sourcepub fn get(&self, index: usize) -> Option<T>
pub fn get(&self, index: usize) -> Option<T>
Gets the element at the specified index. Returns None if out of range.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(zerovec.get(2), Some(421));
assert_eq!(zerovec.get(4), None);Sourcepub fn get_as_array<const N: usize>(&self) -> Option<[T; N]>
pub fn get_as_array<const N: usize>(&self) -> Option<[T; N]>
Gets the entire slice as an array of length N. Returns None if the slice
does not have exactly N elements.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
let array: [u16; 4] =
zerovec.get_as_array().expect("should be 4 items in array");
assert_eq!(array[2], 421);Sourcepub fn get_subslice(&self, range: Range<usize>) -> Option<&ZeroSlice<T>>
pub fn get_subslice(&self, range: Range<usize>) -> Option<&ZeroSlice<T>>
Gets a subslice of elements within a certain range. Returns None if the range
is out of bounds of this ZeroSlice.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(
zerovec.get_subslice(1..3),
Some(&*ZeroVec::from_slice_or_alloc(&[0x0119, 0x01A5]))
);
assert_eq!(zerovec.get_subslice(3..5), None);Sourcepub fn get_ule_ref(&self, index: usize) -> Option<&T::ULE>
pub fn get_ule_ref(&self, index: usize) -> Option<&T::ULE>
Get a borrowed reference to the underlying ULE type at a specified index.
Prefer Self::get() over this method where possible since working
directly with ULE types is less ergonomic
Sourcepub fn cast<P>(&self) -> &ZeroSlice<P>
pub fn cast<P>(&self) -> &ZeroSlice<P>
Casts a ZeroSlice<T> to a compatible ZeroSlice<P>.
T and P are compatible if they have the same ULE representation.
If the ULEs of T and P are different, use Self::try_as_converted().
§Examples
use zerovec::ZeroSlice;
const BYTES: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
const ZS_U16: &ZeroSlice<u16> = {
match ZeroSlice::<u16>::try_from_bytes(BYTES) {
Ok(s) => s,
Err(_) => unreachable!(),
}
};
let zs_i16: &ZeroSlice<i16> = ZS_U16.cast();
assert_eq!(ZS_U16.get(3), Some(32973));
assert_eq!(zs_i16.get(3), Some(-32563));Sourcepub fn try_as_converted<P: AsULE>(&self) -> Result<&ZeroSlice<P>, UleError>
pub fn try_as_converted<P: AsULE>(&self) -> Result<&ZeroSlice<P>, UleError>
Converts a &ZeroSlice<T> into a &ZeroSlice<P>.
The resulting slice will have the same length as the original slice
if and only if T::ULE and P::ULE are the same size.
If T and P have the exact same ULE, use Self::cast().
§Examples
use zerovec::ZeroSlice;
const BYTES: &[u8] = &[0x7F, 0xF3, 0x01, 0x00, 0x49, 0xF6, 0x01, 0x00];
const ZS_U32: &ZeroSlice<u32> = {
match ZeroSlice::<u32>::try_from_bytes(BYTES) {
Ok(s) => s,
Err(_) => unreachable!(),
}
};
let zs_u8_4: &ZeroSlice<[u8; 4]> =
ZS_U32.try_as_converted().expect("valid code points");
assert_eq!(ZS_U32.get(0), Some(127871));
assert_eq!(zs_u8_4.get(0), Some([0x7F, 0xF3, 0x01, 0x00]));Sourcepub fn first(&self) -> Option<T>
pub fn first(&self) -> Option<T>
Gets the first element. Returns None if empty.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(zerovec.first(), Some(211));Sourcepub fn last(&self) -> Option<T>
pub fn last(&self) -> Option<T>
Gets the last element. Returns None if empty.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(zerovec.last(), Some(32973));Sourcepub fn iter<'a>(&'a self) -> ZeroSliceIter<'a, T> ⓘ
pub fn iter<'a>(&'a self) -> ZeroSliceIter<'a, T> ⓘ
Gets an iterator over the elements.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
let mut it = zerovec.iter();
assert_eq!(it.next(), Some(211));
assert_eq!(it.next(), Some(281));
assert_eq!(it.next(), Some(421));
assert_eq!(it.next(), Some(32973));
assert_eq!(it.next(), None);Sourcepub fn split_first(&self) -> Option<(T, &ZeroSlice<T>)>
pub fn split_first(&self) -> Option<(T, &ZeroSlice<T>)>
Returns a tuple with the first element and a subslice of the remaining elements.
§Example
use zerovec::ule::AsULE;
use zerovec::ZeroSlice;
const DATA: &ZeroSlice<u16> =
ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([
211, 281, 421, 32973,
]));
const EXPECTED_VALUE: (u16, &ZeroSlice<u16>) = (
211,
ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([
281, 421, 32973,
])),
);
assert_eq!(EXPECTED_VALUE, DATA.split_first().unwrap());Sourcepub fn binary_search(&self, x: &T) -> Result<usize, usize>
pub fn binary_search(&self, x: &T) -> Result<usize, usize>
Binary searches a sorted ZeroVec<T> for the given element. For more information, see
the primitive function binary_search.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(zerovec.binary_search(&281), Ok(1));
assert_eq!(zerovec.binary_search(&282), Err(2));Sourcepub fn binary_search_by(
&self,
predicate: impl FnMut(T) -> Ordering,
) -> Result<usize, usize>
pub fn binary_search_by( &self, predicate: impl FnMut(T) -> Ordering, ) -> Result<usize, usize>
Binary searches a sorted ZeroVec<T> based on a given predicate. For more information, see
the primitive function binary_search_by.
§Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
let zerovec: ZeroVec<u16> =
ZeroVec::parse_bytes(bytes).expect("infallible");
assert_eq!(zerovec.binary_search_by(|x| x.cmp(&281)), Ok(1));
assert_eq!(zerovec.binary_search_by(|x| x.cmp(&282)), Err(2));Trait Implementations§
Source§impl<T> EncodeAsVarULE<ZeroSlice<T>> for ZeroVec<'_, T>where
T: AsULE + 'static,
impl<T> EncodeAsVarULE<ZeroSlice<T>> for ZeroVec<'_, T>where
T: AsULE + 'static,
Source§fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R
fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R
cb with a piecewise list of byte slices that when concatenated
produce the memory pattern of the corresponding instance of T. Read moreSource§fn encode_var_ule_len(&self) -> usize
fn encode_var_ule_len(&self) -> usize
VarULE typeSource§fn encode_var_ule_write(&self, dst: &mut [u8])
fn encode_var_ule_write(&self, dst: &mut [u8])
VarULE type to the dst buffer. dst should
be the size of Self::encode_var_ule_len()Source§impl<'a, T: AsULE + Ord> Ord for ZeroVec<'a, T>
impl<'a, T: AsULE + Ord> Ord for ZeroVec<'a, T>
Source§impl<'a, T: AsULE + PartialOrd> PartialOrd for ZeroVec<'a, T>
impl<'a, T: AsULE + PartialOrd> PartialOrd for ZeroVec<'a, T>
Source§impl<'a, T: 'static + AsULE> Yokeable<'a> for ZeroVec<'static, T>
This impl requires enabling the optional yoke Cargo feature of the zerovec crate
impl<'a, T: 'static + AsULE> Yokeable<'a> for ZeroVec<'static, T>
This impl requires enabling the optional yoke Cargo feature of the zerovec crate
Source§type Output = ZeroVec<'a, T>
type Output = ZeroVec<'a, T>
Self with the 'static replaced with 'a, i.e. Self<'a>