use std::fmt;
pub struct CallBuf {
buf: Vec<u8>,
}
impl CallBuf {
pub const PREFIX_LEN: usize = 4 + 40;
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::from_spare(Vec::new())
}
#[doc(hidden)]
pub fn from_spare(mut spare: Vec<u8>) -> Self {
spare.clear();
spare.resize(Self::PREFIX_LEN, 0);
Self { buf: spare }
}
#[doc(hidden)]
pub fn into_vec(self) -> Vec<u8> {
self.buf
}
pub fn append_with<R>(&mut self, f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
let result = f(&mut self.buf);
if self.buf.len() < Self::PREFIX_LEN {
self.buf.resize(Self::PREFIX_LEN, 0);
}
result
}
pub fn payload(&self) -> &[u8] {
&self.buf[Self::PREFIX_LEN..]
}
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
}
impl fmt::Debug for CallBuf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CallBuf")
.field("payload_len", &self.payload().len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn call_with(payload: &[u8]) -> CallBuf {
let mut call = CallBuf::new();
call.extend_from_slice(payload);
call
}
#[test]
fn new_has_empty_payload_and_full_prefix() {
let buf = CallBuf::new();
assert!(buf.payload().is_empty());
assert_eq!(buf.into_vec().len(), CallBuf::PREFIX_LEN);
}
#[test]
fn payload_sits_after_prefix() {
let mut buf = call_with(b"abc");
buf.extend_from_slice(b"def");
assert_eq!(buf.payload(), b"abcdef");
let vec = buf.into_vec();
assert_eq!(vec.len(), CallBuf::PREFIX_LEN + 6);
assert_eq!(&vec[CallBuf::PREFIX_LEN..], b"abcdef");
}
#[test]
fn append_with_appends_after_prefix_and_returns_value() {
let mut buf = call_with(b"pre");
let len = buf.append_with(|vec| {
vec.extend_from_slice(b"fix");
vec.len()
});
assert_eq!(len, CallBuf::PREFIX_LEN + 6);
assert_eq!(buf.payload(), b"prefix");
}
#[test]
fn append_with_keeps_prefix_reserved() {
let mut buf = call_with(b"abc");
buf.append_with(Vec::clear);
assert!(buf.payload().is_empty());
assert_eq!(buf.into_vec().len(), CallBuf::PREFIX_LEN);
}
#[test]
fn from_spare_reuses_allocation_and_reserves_prefix() {
let spare = Vec::with_capacity(1024);
let ptr = spare.as_ptr();
let mut buf = CallBuf::from_spare(spare);
assert!(buf.payload().is_empty());
buf.extend_from_slice(&[1; 512]);
let vec = buf.into_vec();
assert_eq!(vec.as_ptr(), ptr);
assert_eq!(vec.len(), CallBuf::PREFIX_LEN + 512);
let dirty = vec![7; 100];
assert!(CallBuf::from_spare(dirty).payload().is_empty());
}
}