mirror of
https://github.com/trussed-dev/heapless-bytes.git
synced 2026-06-20 04:16:33 -07:00
Remove Deref impl,
Add `From` implementations
This commit is contained in:
committed by
sosthene-nitrokey
parent
7840edb274
commit
33277be39c
+180
-119
@@ -10,7 +10,6 @@ use core::{
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Deref, DerefMut},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use heapless::Vec;
|
||||
@@ -20,7 +19,7 @@ use serde::{
|
||||
ser::{Serialize, Serializer},
|
||||
};
|
||||
|
||||
#[derive(Clone, Default, Eq)]
|
||||
#[derive(Clone, Default, Eq, Ord)]
|
||||
pub struct Bytes<const N: usize> {
|
||||
bytes: Vec<u8, N>,
|
||||
}
|
||||
@@ -36,84 +35,150 @@ impl<const N: usize> From<Vec<u8, N>> for Bytes<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<Bytes<N>> for Vec<u8, N> {
|
||||
fn from(value: Bytes<N>) -> Self {
|
||||
value.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<&[u8]> for Bytes<N> {
|
||||
type Error = ();
|
||||
fn try_from(value: &[u8]) -> Result<Self, ()> {
|
||||
Ok(Self {
|
||||
bytes: Vec::from_slice(value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Bytes<N> {
|
||||
/// Construct a new, empty `Bytes<N>`.
|
||||
pub fn new() -> Self {
|
||||
Bytes::from(Vec::new())
|
||||
}
|
||||
|
||||
/// Unwraps the Vec<u8, N>, same as `into_vec`.
|
||||
pub fn into_inner(self) -> Vec<u8, N> {
|
||||
self.bytes
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.bytes.as_ptr()
|
||||
}
|
||||
|
||||
/// Unwraps the Vec<u8, N>, same as `into_inner`.
|
||||
pub fn into_vec(self) -> Vec<u8, N> {
|
||||
self.bytes
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.bytes.as_mut_ptr()
|
||||
}
|
||||
|
||||
/// Returns an immutable slice view.
|
||||
// Add as inherent method as it's annoying to import AsSlice.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
self.bytes.as_ref()
|
||||
self.bytes.as_slice()
|
||||
}
|
||||
|
||||
/// Returns a mutable slice view.
|
||||
// Add as inherent method as it's annoying to import AsSlice.
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
self.bytes.as_mut()
|
||||
self.bytes.as_mut_slice()
|
||||
}
|
||||
|
||||
/// Low-noise conversion between lengths.
|
||||
///
|
||||
/// We can't implement TryInto since it would clash with blanket implementations.
|
||||
pub fn try_convert_into<const M: usize>(&self) -> Result<Bytes<M>, ()> {
|
||||
Bytes::<M>::from_slice(self)
|
||||
pub const fn capacity(&self) -> usize {
|
||||
self.bytes.capacity()
|
||||
}
|
||||
|
||||
// pub fn try_from_slice(slice: &[u8]) -> core::result::Result<Self, ()> {
|
||||
// let mut bytes = Vec::<u8, N>::new();
|
||||
// bytes.extend_from_slice(slice)?;
|
||||
// Ok(Self::from(bytes))
|
||||
// }
|
||||
|
||||
pub fn from_slice(slice: &[u8]) -> core::result::Result<Self, ()> {
|
||||
let mut bytes = Vec::<u8, N>::new();
|
||||
bytes.extend_from_slice(slice)?;
|
||||
Ok(Self::from(bytes))
|
||||
pub fn clear(&mut self) {
|
||||
self.bytes.clear()
|
||||
}
|
||||
|
||||
/// Some APIs offer an interface of the form `f(&mut [u8]) -> Result<usize, E>`,
|
||||
/// with the contract that the Ok-value signals how many bytes were written.
|
||||
///
|
||||
/// This constructor allows wrapping such interfaces in a more ergonomic way,
|
||||
/// returning a Bytes willed using `f`.
|
||||
///
|
||||
/// It seems it's not possible to do this as an actual `TryFrom` implementation.
|
||||
pub fn try_from<E>(
|
||||
f: impl FnOnce(&mut [u8]) -> core::result::Result<usize, E>,
|
||||
) -> core::result::Result<Self, E> {
|
||||
let mut data = Self::new();
|
||||
data.resize_to_capacity();
|
||||
let result = f(&mut data);
|
||||
|
||||
result.map(|count| {
|
||||
data.resize_default(count).unwrap();
|
||||
data
|
||||
})
|
||||
#[deprecated(note = "Panics when out of capacity")]
|
||||
pub fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
|
||||
self.bytes.extend(iter)
|
||||
}
|
||||
|
||||
// pub fn try_from<'a, E>(
|
||||
// f: impl FnOnce(&'a mut [u8]) -> core::result::Result<&'a mut [u8], E>
|
||||
// )
|
||||
// -> core::result::Result<Self, E>
|
||||
// {
|
||||
pub fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), ()> {
|
||||
self.bytes.extend_from_slice(other)
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<u8> {
|
||||
self.bytes.pop()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, byte: u8) -> Result<(), ()> {
|
||||
self.bytes.push(byte).map_err(drop)
|
||||
}
|
||||
|
||||
pub unsafe fn pop_unchecked(&mut self) -> u8 {
|
||||
unsafe { self.bytes.pop_unchecked() }
|
||||
}
|
||||
|
||||
pub unsafe fn push_unchecked(&mut self, byte: u8) {
|
||||
unsafe {
|
||||
self.bytes.push_unchecked(byte);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
self.bytes.truncate(len)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_len: usize, value: u8) -> Result<(), ()> {
|
||||
self.bytes.resize(new_len, value)
|
||||
}
|
||||
|
||||
pub fn resize_zero(&mut self, new_len: usize) -> Result<(), ()> {
|
||||
self.bytes.resize_default(new_len)
|
||||
}
|
||||
|
||||
pub unsafe fn set_len(&mut self, new_len: usize) {
|
||||
self.bytes.set_len(new_len)
|
||||
}
|
||||
|
||||
pub fn swap_remove(&mut self, index: usize) -> u8 {
|
||||
self.bytes.swap_remove(index)
|
||||
}
|
||||
|
||||
pub unsafe fn swap_remove_unchecked(&mut self, index: usize) -> u8 {
|
||||
unsafe { self.bytes.swap_remove_unchecked(index) }
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.bytes.is_full()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.bytes.is_empty()
|
||||
}
|
||||
|
||||
pub fn starts_with(&self, needle: &[u8]) -> bool {
|
||||
self.bytes.starts_with(needle)
|
||||
}
|
||||
pub fn ends_with(&self, needle: &[u8]) -> bool {
|
||||
self.bytes.ends_with(needle)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, value: u8) -> Result<(), ()> {
|
||||
self.bytes.insert(index, value).map_err(drop)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> u8 {
|
||||
self.bytes.remove(index)
|
||||
}
|
||||
|
||||
pub fn retain(&mut self, f: impl FnMut(&u8) -> bool) {
|
||||
self.bytes.retain(f)
|
||||
}
|
||||
pub fn retain_mut(&mut self, f: impl FnMut(&mut u8) -> bool) {
|
||||
self.bytes.retain_mut(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Bytes<N> {
|
||||
// /// Some APIs offer an interface of the form `f(&mut [u8]) -> Result<usize, E>`,
|
||||
// /// with the contract that the Ok-value signals how many bytes were written.
|
||||
// ///
|
||||
// /// This constructor allows wrapping such interfaces in a more ergonomic way,
|
||||
// /// returning a Bytes willed using `f`.
|
||||
// ///
|
||||
// /// It seems it's not possible to do this as an actual `TryFrom` implementation.
|
||||
// pub fn from_constructor<E>(
|
||||
// f: impl FnOnce(&mut [u8]) -> core::result::Result<usize, E>,
|
||||
// ) -> core::result::Result<Self, E> {
|
||||
// let mut data = Self::new();
|
||||
// data.resize_to_capacity();
|
||||
// let result = f(&mut data);
|
||||
|
||||
// result.map(|count| {
|
||||
// data.resize_default(count).unwrap();
|
||||
// data.resize_default(count)
|
||||
// .expect("Contructor returned size larger than capacity");
|
||||
// data
|
||||
// })
|
||||
// }
|
||||
@@ -136,32 +201,32 @@ impl<const N: usize> Bytes<N> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, item: u8) -> Result<(), u8> {
|
||||
self.insert_slice_at(&[item], index).map_err(|_| item)
|
||||
}
|
||||
// pub fn insert(&mut self, index: usize, item: u8) -> Result<(), u8> {
|
||||
// self.insert_slice_at(&[item], index).map_err(|_| item)
|
||||
// }
|
||||
|
||||
pub fn remove(&mut self, index: usize) -> Result<u8, ()> {
|
||||
if index < self.len() {
|
||||
unsafe { Ok(self.remove_unchecked(index)) }
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
// pub fn remove(&mut self, index: usize) -> Result<u8, ()> {
|
||||
// if index < self.len() {
|
||||
// unsafe { Ok(self.remove_unchecked(index)) }
|
||||
// } else {
|
||||
// Err(())
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) unsafe fn remove_unchecked(&mut self, index: usize) -> u8 {
|
||||
// the place we are taking from.
|
||||
let p = self.bytes.as_mut_ptr().add(index);
|
||||
// pub(crate) unsafe fn remove_unchecked(&mut self, index: usize) -> u8 {
|
||||
// // the place we are taking from.
|
||||
// let p = self.bytes.as_mut_ptr().add(index);
|
||||
|
||||
// copy it out, unsafely having a copy of the value on
|
||||
// the stack and in the vector at the same time.
|
||||
let ret = ptr::read(p);
|
||||
// // copy it out, unsafely having a copy of the value on
|
||||
// // the stack and in the vector at the same time.
|
||||
// let ret = ptr::read(p);
|
||||
|
||||
// shift everything down to fill in that spot.
|
||||
ptr::copy(p.offset(1), p, self.len() - index - 1);
|
||||
// // shift everything down to fill in that spot.
|
||||
// ptr::copy(p.offset(1), p, self.len() - index - 1);
|
||||
|
||||
self.resize_default(self.len() - 1).unwrap();
|
||||
ret
|
||||
}
|
||||
// self.resize_default(self.len() - 1).unwrap();
|
||||
// ret
|
||||
// }
|
||||
|
||||
pub fn resize_default(&mut self, new_len: usize) -> core::result::Result<(), ()> {
|
||||
self.bytes.resize_default(new_len)
|
||||
@@ -171,6 +236,13 @@ impl<const N: usize> Bytes<N> {
|
||||
self.bytes.resize_default(self.bytes.capacity()).ok();
|
||||
}
|
||||
|
||||
/// Low-noise conversion between lengths.
|
||||
///
|
||||
/// For an infaillible version when `M` is known to be larger than `N`, see [`increase_capacity`](Self::increase_capacity)
|
||||
pub fn resize_capacity<const M: usize>(&self) -> Result<Bytes<M>, ()> {
|
||||
Bytes::try_from(&**self)
|
||||
}
|
||||
|
||||
/// Copy the contents of this `Bytes` instance into a new instance with a higher capacity.
|
||||
///
|
||||
/// ```
|
||||
@@ -194,33 +266,6 @@ impl<const N: usize> Bytes<N> {
|
||||
bytes.extend_from_slice(self.as_slice()).unwrap();
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
/// Fallible conversion into differently sized byte buffer.
|
||||
pub fn to_bytes<const M: usize>(&self) -> Result<Bytes<M>, ()> {
|
||||
Bytes::<M>::from_slice(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cbor")]
|
||||
pub fn from_serialized<T>(t: &T) -> Self
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut vec = Vec::<u8, N>::new();
|
||||
vec.resize_default(N).unwrap();
|
||||
let buffer = vec.deref_mut();
|
||||
|
||||
let writer = serde_cbor::ser::SliceWrite::new(buffer);
|
||||
let mut ser = serde_cbor::Serializer::new(writer)
|
||||
.packed_format()
|
||||
// .pack_starting_with(1)
|
||||
// .pack_to_depth(1)
|
||||
;
|
||||
t.serialize(&mut ser).unwrap();
|
||||
let writer = ser.into_inner();
|
||||
let size = writer.bytes_written();
|
||||
vec.resize_default(size).unwrap();
|
||||
Self::from(vec)
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a `Bytes<N>` instance from an array with `N` elements.
|
||||
@@ -324,7 +369,7 @@ impl<const N: usize> AsMut<[u8]> for Bytes<N> {
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for Bytes<N> {
|
||||
type Target = Vec<u8, N>;
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bytes
|
||||
@@ -337,18 +382,6 @@ impl<const N: usize> DerefMut for Bytes<N> {
|
||||
}
|
||||
}
|
||||
|
||||
// impl Borrow<Bytes> for Bytes<N> {
|
||||
// fn borrow(&self) -> &Bytes {
|
||||
// Bytes::new(&self.bytes)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl BorrowMut<Bytes> for Bytes<N> {
|
||||
// fn borrow_mut(&mut self) -> &mut Bytes {
|
||||
// unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<Rhs, const N: usize> PartialEq<Rhs> for Bytes<N>
|
||||
where
|
||||
Rhs: ?Sized + AsRef<[u8]>,
|
||||
@@ -409,7 +442,18 @@ impl<const N: usize> Serialize for Bytes<N> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can we delegate to Vec<u8, N> deserialization instead of reimplementing?
|
||||
impl<const N: usize> core::fmt::Write for Bytes<N> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.bytes.write_str(s)
|
||||
}
|
||||
fn write_char(&mut self, s: char) -> fmt::Result {
|
||||
self.bytes.write_char(s)
|
||||
}
|
||||
fn write_fmt(&mut self, s: core::fmt::Arguments<'_>) -> fmt::Result {
|
||||
self.bytes.write_fmt(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> Deserialize<'de> for Bytes<N> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@@ -445,6 +489,20 @@ impl<'de, const N: usize> Deserialize<'de> for Bytes<N> {
|
||||
}
|
||||
Ok(Bytes::<N>::from(buf))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
let mut this = Bytes::new();
|
||||
while let Some(byte) = seq.next_element()? {
|
||||
this.push(byte)
|
||||
.map_err(|()| A::Error::invalid_length(this.len(), &self))?;
|
||||
}
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(ValueVisitor)
|
||||
@@ -466,9 +524,9 @@ mod tests_serde {
|
||||
bytes.push(1).unwrap();
|
||||
assert_tokens(&bytes, &[Token::Bytes(&[1])]);
|
||||
assert!(bytes.extend_from_slice(&[2; 16]).is_err());
|
||||
assert_eq!(&**bytes, &[1]);
|
||||
assert_eq!(&*bytes, &[1]);
|
||||
assert!(bytes.extend_from_slice(&[2; 15]).is_ok());
|
||||
assert_eq!(&**bytes, &[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
|
||||
assert_eq!(&*bytes, &[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]);
|
||||
assert_tokens(
|
||||
&bytes,
|
||||
&[Token::Bytes(&[
|
||||
@@ -481,7 +539,10 @@ mod tests_serde {
|
||||
fn display() {
|
||||
assert_eq!(
|
||||
r"b'\x00abcde\n'",
|
||||
format!("{:?}", Bytes::<10>::from_slice(b"\0abcde\n").unwrap())
|
||||
format!(
|
||||
"{:?}",
|
||||
Bytes::<10>::try_from(b"\0abcde\n".as_slice()).unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user