#[non_exhaustive]
pub enum ZeroVec<'a, T> where
T: AsULE + ?Sized, {
Owned(Vec<T::ULE>),
Borrowed(&'a [T::ULE]),
}
Expand description
A zero-copy 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
.
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>
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. We found that for common operations on small
and large vectors, ZeroVec<T>
performs from slightly faster to 15% slower than Vec<T>
.
However, the main performance improvement on ZeroVec<T>
is when deserializing from a byte
array; ZeroVec<T>
deserializes 80% faster than Vec<T>
in Serde Bincode, and it does not
require any heap allocations.
Safety
ZeroVec<T>
contains no unsafe code. However, the conversion from &[u8]
to &[T::ULE]
may
be unsafe. For more information, see the ule
module.
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];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).unwrap();
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
assert_eq!(zerovec.get(2), Some(421));
assert_eq!(zerovec, nums);
Variants (Non-exhaustive)
This enum is marked as non-exhaustive
Owned(Vec<T::ULE>)
An owned ZeroVec<T>
. This will typically be constructed by ZeroVec::alloc_from_slice()
or by calling ZeroVec::to_mut()
/ZeroVec::for_each_mut()
/etc on ZeroVec::Borrowed
.
Borrowed(&'a [T::ULE])
A borrowed ZeroVec<T>
. This will typically be constructed by ZeroVec::parse_byte_slice()
,
ZeroVec::from_slice()
, or deserializers capable of doing zero-copy deserialization.
If you already have a slice of [T::ULE]
s, you can directly construct one of these.
Example
use zerovec::ZeroVec;
use zerovec::ule::*;
// 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: &[RawBytesULE<2>] = &[211_u16.as_unaligned(), 281_u16.as_unaligned(),
421_u16.as_unaligned(), 461_u16.as_unaligned()];
let zerovec = ZeroVec::<u16>::Borrowed(nums);
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
assert_eq!(bytes, zerovec.as_bytes());
Implementations
Creates a new, borrowed, empty ZeroVec<T>
.
Examples
use zerovec::ZeroVec;
let zv: ZeroVec<u16> = ZeroVec::new();
assert!(zv.is_empty());
Creates a new, owned, empty ZeroVec<T>
, with a certain capacity pre-allocated.
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_byte_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_byte_slice(bytes).expect("infallible");
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
assert_eq!(zerovec.get(2), Some(421));
Converts a ZeroVec<T>
into a ZeroVec<u8>
, retaining the current ownership model.
Note that the length of the ZeroVec may change.
Examples
Convert a borrowed ZeroVec
:
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
let zv_bytes = zerovec.into_bytes();
assert!(matches!(zv_bytes, ZeroVec::Borrowed(_)));
assert_eq!(zv_bytes.get(0), Some(0xD3));
Convert an owned ZeroVec
:
use zerovec::ZeroVec;
let nums: &[u16] = &[211, 281, 421, 461];
let zerovec = ZeroVec::alloc_from_slice(nums);
let zv_bytes = zerovec.into_bytes();
assert!(matches!(zv_bytes, ZeroVec::Owned(_)));
assert_eq!(zv_bytes.get(0), Some(0xD3));
Converts a ZeroVec<T>
into a ZeroVec<P>
, retaining the current ownership model.
Panics
Panics if T::ULE
and P::ULE
are not the same size.
Examples
Convert a borrowed ZeroVec
:
use zerovec::ZeroVec;
let bytes: &[u8] = &[0x7F, 0xF3, 0x01, 0x00, 0x49, 0xF6, 0x01, 0x00];
let zv_char: ZeroVec<char> = ZeroVec::parse_byte_slice(bytes)
.expect("valid code points");
let zv_u32: ZeroVec<u32> = zv_char.try_into_converted()
.expect("infallible conversion");
assert!(matches!(zv_u32, ZeroVec::Borrowed(_)));
assert_eq!(zv_u32.get(0), Some(u32::from('🍿')));
Convert an owned ZeroVec
:
use zerovec::ZeroVec;
let chars: &[char] = &['🍿', '🙉'];
let zv_char = ZeroVec::alloc_from_slice(chars);
let zv_u32: ZeroVec<u32> = zv_char.try_into_converted()
.expect("length is divisible");
assert!(matches!(zv_u32, ZeroVec::Owned(_)));
assert_eq!(zv_u32.get(0), Some(u32::from('🍿')));
If the types are not the same size, we refuse to convert:
use zerovec::ZeroVec;
let bytes: &[u8] = &[0x7F, 0xF3, 0x01, 0x00, 0x49, 0xF6, 0x01, 0x00];
let zv_char: ZeroVec<char> = ZeroVec::parse_byte_slice(bytes)
.expect("valid code points");
// Panics! mem::size_of::<char::ULE> != mem::size_of::<u16::ULE>
zv_char.try_into_converted::<u16>();
Instead, convert to bytes and then parse:
use zerovec::ZeroVec;
let bytes: &[u8] = &[0x7F, 0xF3, 0x01, 0x00, 0x49, 0xF6, 0x01, 0x00];
let zv_char: ZeroVec<char> = ZeroVec::parse_byte_slice(bytes)
.expect("valid code points");
let zv_u16: ZeroVec<u16> = zv_char.into_bytes().try_into_parsed()
.expect("infallible");
assert!(matches!(zv_u16, ZeroVec::Borrowed(_)));
assert_eq!(zv_u16.get(0), Some(0xF37F));
Converts a ZeroVec<u8>
into a ZeroVec<T>
, retaining the current ownership model.
Note that the length of the ZeroVec may change.
Examples
Convert a borrowed ZeroVec
:
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zv_bytes = ZeroVec::Borrowed(bytes);
let zerovec: ZeroVec<u16> = zv_bytes.try_into_parsed().expect("infallible");
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
assert_eq!(zerovec.get(0), Some(211));
Convert an owned ZeroVec
:
use zerovec::ZeroVec;
let bytes: Vec<u8> = vec![0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zv_bytes = ZeroVec::Owned(bytes);
let zerovec: ZeroVec<u16> = zv_bytes.try_into_parsed().expect("infallible");
assert!(matches!(zerovec, ZeroVec::Owned(_)));
assert_eq!(zerovec.get(0), Some(211));
Creates a ZeroVec<T>
from a &[T]
by allocating memory.
This function results in an Owned
instance of ZeroVec<T>
.
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];
let zerovec = ZeroVec::alloc_from_slice(nums);
assert!(matches!(zerovec, ZeroVec::Owned(_)));
assert_eq!(bytes, zerovec.as_bytes());
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!(matches!(zerovec, ZeroVec::Borrowed(_)));
assert_eq!(bytes, zerovec.as_bytes());
}
Creates a ZeroVec<'a, T>
from a &'a [T]
, either by borrowing the argument or by
allocating a new vector.
This is a cheap operation on little-endian platforms, falling back to a more expensive operation on big-endian platforms.
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];
let zerovec = ZeroVec::from_slice(nums);
// Note: zerovec could be either borrowed or owned.
assert_eq!(bytes, zerovec.as_bytes());
Mutates each element according to a given function, meant to be
a more convenient version of calling .iter_mut()
on
ZeroVec::to_mut()
which serves fewer use cases.
This will convert the ZeroVec into an owned ZeroVec if not already the case.
Example
use zerovec::ZeroVec;
use zerovec::ule::AsULE;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let mut zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
zerovec.for_each_mut(|item| *item += 1);
assert_eq!(zerovec.to_vec(), &[212, 282, 422, 462]);
assert!(matches!(zerovec, ZeroVec::Owned(_)));
Same as ZeroVec::for_each_mut()
, but bubbles up errors.
Example
use zerovec::ZeroVec;
use zerovec::ule::AsULE;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let mut zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
zerovec.try_for_each_mut(|item| {
*item = item.checked_add(1).ok_or(())?;
Ok(())
})?;
assert_eq!(zerovec.to_vec(), &[212, 282, 422, 462]);
assert!(matches!(zerovec, ZeroVec::Owned(_)));
Converts a borrowed ZeroVec to an owned ZeroVec. No-op if already owned.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
let owned = zerovec.into_owned();
assert!(matches!(owned, ZeroVec::Owned(_)));
Allows the ZeroVec to be mutated by converting it to an owned variant, and producing a mutable vector of ULEs.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let mut zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert!(matches!(zerovec, ZeroVec::Borrowed(_)));
zerovec.to_mut().push(12_u16.as_unaligned());
assert!(matches!(zerovec, ZeroVec::Owned(_)));
Methods from Deref<Target = ZeroSlice<T>>
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, 0x01];
let nums: &[u16] = &[211, 281, 421, 461];
let zerovec = ZeroVec::alloc_from_slice(nums);
assert_eq!(bytes, zerovec.as_bytes());
Dereferences this slice as &[T::ULE]
.
Returns the number of elements in this slice.
Example
use zerovec::ZeroVec;
use zerovec::ule::AsULE;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(4, zerovec.len());
assert_eq!(
bytes.len(),
zerovec.len() * std::mem::size_of::<<u16 as AsULE>::ULE>()
);
Returns whether this slice is empty.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert!(!zerovec.is_empty());
let emptyvec: ZeroVec<u16> = ZeroVec::parse_byte_slice(&[]).expect("infallible");
assert!(emptyvec.is_empty());
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, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(zerovec.get(2), Some(421));
assert_eq!(zerovec.get(4), None);
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, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(
zerovec.get_subslice(1..3),
Some(&*ZeroVec::from_slice(&[0x0119, 0x01A5]))
);
assert_eq!(zerovec.get_subslice(3..5), None);
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
Gets the first element. Returns None if empty.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(zerovec.first(), Some(211));
Gets the last element. Returns None if empty.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(zerovec.last(), Some(461));
Gets an iterator over the elements.
Example
use zerovec::ZeroVec;
let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(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(461));
assert_eq!(it.next(), None);
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, 0x01];
let zerovec: ZeroVec<u16> = ZeroVec::parse_byte_slice(bytes).expect("infallible");
assert_eq!(zerovec.binary_search(&281), Ok(1));
assert_eq!(zerovec.binary_search(&282), Err(2));
Trait Implementations
impl<'de, 'a, T> Deserialize<'de> for ZeroVec<'a, T> where
T: 'de + Deserialize<'de> + AsULE,
'de: 'a,
impl<'de, 'a, T> Deserialize<'de> for ZeroVec<'a, T> where
T: 'de + Deserialize<'de> + AsULE,
'de: 'a,
This impl can be made available by enabling the optional serde
feature of the zerovec
crate
Deserialize this value from the given Serde deserializer. Read more
Calls cb
with a piecewise list of byte slices that when concatenated
produce the memory pattern of the corresponding instance of T
. Read more
Return the length, in bytes, of the corresponding VarULE
type
Write the corresponding VarULE
type to the dst
buffer. dst
should
be the size of Self::encode_var_ule_len()
Read more
Creates a ZeroVec::Owned
from an iterator of values.
type OwnedType = T
type OwnedType = T
The type returned by Self::remove()
and Self::replace()
Insert an element at index
Remove the element at index
(panicking if nonexistant)
Replace the element at index
with another one, returning the old element
Create a new, empty vector, with given capacity
Reserve space for addl
additional elements
Convert an owned value to a borrowed T
This method returns an ordering between self
and other
values if one exists. Read more
This method tests less than (for self
and other
) and is used by the <
operator. Read more
This method tests less than or equal to (for self
and other
) and is used by the <=
operator. Read more
This method tests greater than (for self
and other
) and is used by the >
operator. Read more
This impl can be made available by enabling the optional serde
feature of the zerovec
crate
This impl can be made available by enabling the optional yoke
feature of the zerovec
crate
This method must cast self
between &'a Self<'static>
and &'a Self<'a>
. Read more
This method must cast self
between Self<'static>
and Self<'a>
. Read more
This method can be used to cast away Self<'a>
’s lifetime. Read more
fn transform_mut<F>(&'a mut self, f: F) where
F: 'static + for<'b> FnOnce(&'b mut Self::Output),
fn transform_mut<F>(&'a mut self, f: F) where
F: 'static + for<'b> FnOnce(&'b mut Self::Output),
This method must cast self
between &'a mut Self<'static>
and &'a mut Self<'a>
,
and pass it to f
. Read more
Clone the cart C
into a struct that may retain references into C
.
type BorrowedVariant = &'a ZeroSlice<T>
type BorrowedVariant = &'a ZeroSlice<T>
A fully borrowed version of this
Search for a key in a sorted vector, returns Ok(index)
if found,
returns Err(insert_index)
if not found, where insert_index
is the
index where it should be inserted to maintain sort order. Read more
Search for a key within a certain range in a sorted vector. Returns None
if the
range is out of bounds, and Ok
or Err
in the same way as zvl_binary_search
.
Indices are returned relative to the start of the range. Read more
Check if this vector is in ascending order according to T
s Ord
impl
Construct a borrowed variant by borrowing from &self
. Read more
Extract the inner borrowed variant if possible. Returns None
if the data is owned. Read more
Construct from the borrowed version of the type Read more
Compare this type with a Self::GetType
. This must produce the same result as
if g
were converted to Self
Read more
Compare two values of Self::GetType
. This must produce the same result as
if both a
and b
were converted to Self
Read more
Obtain a version of T suitable for serialization Read more
Check if this vector is empty
Auto Trait Implementations
impl<'a, T> RefUnwindSafe for ZeroVec<'a, T> where
<T as AsULE>::ULE: RefUnwindSafe,
impl<'a, T> UnwindSafe for ZeroVec<'a, T> where
<T as AsULE>::ULE: UnwindSafe + RefUnwindSafe,
Blanket Implementations
Mutably borrows from an owned value. Read more