epee: make {read,write}_varint public, create write_{bytes,container} (#185)

* make `{read,write}_varint` public, create `write_{container,bytes}`

* add doc tests to varint functions

* `write_container` -> `write_iterator`

* add `write_{iterator,bytes}` doc test

* fix `write_iterator()` doc
This commit is contained in:
hinto-janai 2024-06-21 20:25:21 -04:00 committed by GitHub
parent f6c4e4e9a8
commit ff1172f2ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 130 additions and 85 deletions

View file

@ -77,7 +77,7 @@ pub use error::*;
use io::*;
pub use marker::{InnerMarker, Marker};
pub use value::EpeeValue;
use varint::*;
pub use varint::{read_varint, write_varint};
/// Header that needs to be at the beginning of every binary blob that follows
/// this binary serialization format.
@ -212,6 +212,87 @@ fn write_epee_value<T: EpeeValue, B: BufMut>(val: T, w: &mut B) -> Result<()> {
val.write(w)
}
/// Write a byte array to `w` with [`write_varint`].
///
/// This function:
/// - Writes the length of `t`'s bytes into `w` using [`write_varint`]
/// - Writes `t`'s bytes into `w`
///
/// It is used as the internal [`EpeeValue::write`]
/// implementation of byte-like containers such as:
/// - [`EpeeValue::<Vec<u8>>::write`]
/// - [`EpeeValue::<String>::write`]
///
/// # Errors
/// This will error if:
/// - [`write_varint`] fails
/// - `w` does not have enough capacity
///
/// # Example
/// ```rust
/// let t: [u8; 8] = [3, 0, 0, 0, 1, 0, 0, 0];
/// let mut w = vec![];
///
/// epee_encoding::write_bytes(t, &mut w).unwrap();
///
/// assert_eq!(w.len(), 9); // length of bytes + bytes
/// assert_eq!(w[1..], t);
/// ```
pub fn write_bytes<T: AsRef<[u8]>, B: BufMut>(t: T, w: &mut B) -> Result<()> {
let bytes = t.as_ref();
let len = bytes.len();
write_varint(len.try_into()?, w)?;
if w.remaining_mut() < len {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put_slice(bytes);
Ok(())
}
/// Write an [`Iterator`] of [`EpeeValue`]s to `w` with [`write_varint`].
///
/// This function:
/// - Writes the length of the `iterator`, into `w` using [`write_varint`]
/// - [`EpeeValue::write`]s each `T` of the iterator into `w`
///
/// It is used as the internal [`EpeeValue::write`]
/// implementation of containers such as [`EpeeValue::<Vec<T>>::write`].
///
/// # Errors
/// This will error if:
/// - [`write_varint`] fails
/// - [`EpeeValue::<T>::write`] fails
///
/// # Example
/// ```rust
/// let t: u64 = 3;
/// let vec: Vec<u64> = vec![t, t];
/// let mut w = vec![];
///
/// let iter: std::vec::IntoIter<u64> = vec.into_iter();
/// epee_encoding::write_iterator(iter, &mut w).unwrap();
///
/// assert_eq!(w.len(), 17);
/// assert_eq!(w[1..9], [3, 0, 0, 0, 0, 0, 0, 0]);
/// assert_eq!(w[9..], [3, 0, 0, 0, 0, 0, 0, 0]);
/// ```
pub fn write_iterator<T, I, B>(iterator: I, w: &mut B) -> Result<()>
where
T: EpeeValue,
I: Iterator<Item = T> + ExactSizeIterator,
B: BufMut,
{
write_varint(iterator.len().try_into()?, w)?;
for item in iterator.into_iter() {
item.write(w)?;
}
Ok(())
}
/// A helper object builder that just skips every field.
#[derive(Default)]
struct SkipObjectBuilder;

View file

@ -9,7 +9,10 @@ use bytes::{Buf, BufMut, Bytes, BytesMut};
use fixed_bytes::{ByteArray, ByteArrayVec};
use crate::{
io::*, varint::*, EpeeObject, Error, InnerMarker, Marker, Result, MAX_STRING_LEN_POSSIBLE,
io::{checked_read_primitive, checked_write_primitive},
varint::{read_varint, write_varint},
write_bytes, write_iterator, EpeeObject, Error, InnerMarker, Marker, Result,
MAX_STRING_LEN_POSSIBLE,
};
/// A trait for epee values.
@ -83,11 +86,7 @@ impl<T: EpeeObject> EpeeValue for Vec<T> {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
for item in self.into_iter() {
item.write(w)?;
}
Ok(())
write_iterator(self.into_iter(), w)
}
}
@ -105,11 +104,7 @@ impl<T: EpeeObject + Debug, const N: usize> EpeeValue for [T; N] {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
for item in self.into_iter() {
item.write(w)?;
}
Ok(())
write_iterator(self.into_iter(), w)
}
}
@ -191,14 +186,7 @@ impl EpeeValue for Vec<u8> {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
if w.remaining_mut() < self.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put_slice(&self);
Ok(())
write_bytes(self, w)
}
}
@ -231,14 +219,7 @@ impl EpeeValue for Bytes {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
if w.remaining_mut() < self.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put(self);
Ok(())
write_bytes(self, w)
}
}
@ -274,14 +255,7 @@ impl EpeeValue for BytesMut {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
if w.remaining_mut() < self.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put(self);
Ok(())
write_bytes(self, w)
}
}
@ -316,15 +290,7 @@ impl<const N: usize> EpeeValue for ByteArrayVec<N> {
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
let bytes = self.take_bytes();
write_varint(bytes.len().try_into()?, w)?;
if w.remaining_mut() < bytes.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put(bytes);
Ok(())
write_bytes(bytes, w)
}
}
@ -351,15 +317,7 @@ impl<const N: usize> EpeeValue for ByteArray<N> {
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
let bytes = self.take_bytes();
write_varint(N.try_into().unwrap(), w)?;
if w.remaining_mut() < N {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put(bytes);
Ok(())
write_bytes(bytes, w)
}
}
@ -380,14 +338,7 @@ impl EpeeValue for String {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
if w.remaining_mut() < self.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put_slice(self.as_bytes());
Ok(())
write_bytes(self, w)
}
}
@ -405,14 +356,7 @@ impl<const N: usize> EpeeValue for [u8; N] {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
if w.remaining_mut() < self.len() {
return Err(Error::IO("Not enough capacity to write bytes"));
}
w.put_slice(&self);
Ok(())
write_bytes(self, w)
}
}
@ -446,11 +390,7 @@ impl<const N: usize> EpeeValue for Vec<[u8; N]> {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
for item in self.into_iter() {
item.write(w)?;
}
Ok(())
write_iterator(self.into_iter(), w)
}
}
@ -486,11 +426,7 @@ macro_rules! epee_seq {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
for item in self.into_iter() {
item.write(w)?;
}
Ok(())
write_iterator(self.into_iter(), w)
}
}
@ -508,11 +444,7 @@ macro_rules! epee_seq {
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_varint(self.len().try_into()?, w)?;
for item in self.into_iter() {
item.write(w)?;
}
Ok(())
write_iterator(self.into_iter(), w)
}
}
};

View file

@ -7,6 +7,18 @@ const FITS_IN_ONE_BYTE: u64 = 2_u64.pow(8 - SIZE_OF_SIZE_MARKER) - 1;
const FITS_IN_TWO_BYTES: u64 = 2_u64.pow(16 - SIZE_OF_SIZE_MARKER) - 1;
const FITS_IN_FOUR_BYTES: u64 = 2_u64.pow(32 - SIZE_OF_SIZE_MARKER) - 1;
/// Read an epee variable sized number from `r`.
///
/// ```rust
/// use epee_encoding::read_varint;
///
/// assert_eq!(read_varint(&mut [252].as_slice()).unwrap(), 63);
/// assert_eq!(read_varint(&mut [1, 1].as_slice()).unwrap(), 64);
/// assert_eq!(read_varint(&mut [253, 255].as_slice()).unwrap(), 16_383);
/// assert_eq!(read_varint(&mut [2, 0, 1, 0].as_slice()).unwrap(), 16_384);
/// assert_eq!(read_varint(&mut [254, 255, 255, 255].as_slice()).unwrap(), 1_073_741_823);
/// assert_eq!(read_varint(&mut [3, 0, 0, 0, 1, 0, 0, 0].as_slice()).unwrap(), 1_073_741_824);
/// ```
pub fn read_varint<B: Buf>(r: &mut B) -> Result<u64> {
if !r.has_remaining() {
Err(Error::IO("Not enough bytes to build VarInt"))?
@ -26,6 +38,26 @@ pub fn read_varint<B: Buf>(r: &mut B) -> Result<u64> {
Ok(vi)
}
/// Write an epee variable sized number into `w`.
///
/// ```rust
/// use epee_encoding::write_varint;
///
/// let mut buf = vec![];
///
/// for (number, expected_bytes) in [
/// (63, [252].as_slice()),
/// (64, [1, 1].as_slice()),
/// (16_383, [253, 255].as_slice()),
/// (16_384, [2, 0, 1, 0].as_slice()),
/// (1_073_741_823, [254, 255, 255, 255].as_slice()),
/// (1_073_741_824, [3, 0, 0, 0, 1, 0, 0, 0].as_slice()),
/// ] {
/// buf.clear();
/// write_varint(number, &mut buf);
/// assert_eq!(buf.as_slice(), expected_bytes);
/// }
/// ```
pub fn write_varint<B: BufMut>(number: u64, w: &mut B) -> Result<()> {
let size_marker = match number {
0..=FITS_IN_ONE_BYTE => 0,