use crate::errors::CodecError;
use crate::util;
pub trait RpcCodec: Sized {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError>;
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError>;
}
impl RpcCodec for () {
fn from_slice(_buf: &[u8]) -> Result<Self, CodecError> {
Ok(())
}
fn fill_buf(&self, _buf: &mut Vec<u8>) -> Result<(), CodecError> {
Ok(())
}
}
impl RpcCodec for Vec<u8> {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
Ok(buf.to_vec())
}
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
buf.extend_from_slice(&self);
Ok(())
}
}
macro_rules! decl_tuple_codec {
( $($vident:ident: $tident:ident),* ) => {
impl<$($tident: RpcCodec),*> RpcCodec for ($($tident),*) {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
let mut cur = util::Cursor::new(buf);
$(let $vident = cur.take_len_tagged_inst::<$tident>()?;)*
if !cur.is_at_end() {
return Err(CodecError::LeftoverBytes(
cur.remaining_bytes(),
cur.inner().len(),
));
}
Ok(($($vident),*))
}
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
let ($($vident),*) = self;
$(util::write_len_tagged_inst($vident, buf)?;)*
Ok(())
}
}
}
}
decl_tuple_codec!(t1: T1, t2: T2);
decl_tuple_codec!(t1: T1, t2: T2, t3: T3);
decl_tuple_codec!(t1: T1, t2: T2, t3: T3, t4: T4);
impl RpcCodec for String {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
Ok(std::str::from_utf8(buf)
.map_err(|_| CodecError::NonUtf8String)?
.to_owned())
}
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
buf.extend_from_slice(self.as_bytes());
Ok(())
}
}
macro_rules! decl_int_codec {
( $ity:ty ) => {
impl RpcCodec for $ity {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
const NBYTES: usize = (<$ity>::BITS / 8) as usize;
let mut cur = util::Cursor::new(buf);
let ibuf = cur.take_arr::<NBYTES>()?;
Ok(<$ity>::from_be_bytes(ibuf))
}
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
buf.extend_from_slice(&self.to_be_bytes());
Ok(())
}
}
};
}
decl_int_codec!(i8);
decl_int_codec!(i16);
decl_int_codec!(i32);
decl_int_codec!(i64);
decl_int_codec!(u16);
decl_int_codec!(u32);
decl_int_codec!(u64);
macro_rules! decl_intvec_codec {
( $ity:ty ) => {
impl RpcCodec for Vec<$ity> {
fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
const NBYTES: usize = (<$ity>::BITS / 8) as usize;
let leftover = buf.len() % NBYTES;
if leftover > 0 {
return Err(CodecError::LeftoverBytes(leftover, buf.len()));
}
let mut cur = util::Cursor::new(buf);
let cnt = buf.len() / NBYTES;
let mut list = Vec::new();
for _ in 0..cnt {
let ibuf = cur.take_arr::<NBYTES>()?;
list.push(<$ity>::from_be_bytes(ibuf));
}
Ok(list)
}
fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
for i in self {
buf.extend_from_slice(&i.to_be_bytes());
}
Ok(())
}
}
};
}
decl_intvec_codec!(i8);
decl_intvec_codec!(i16);
decl_intvec_codec!(i32);
decl_intvec_codec!(i64);
decl_intvec_codec!(u16);
decl_intvec_codec!(u32);
decl_intvec_codec!(u64);
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_tuple_3() {
type Tup = (String, String, Vec<u8>);
let val: Tup = ("foo".to_string(), "bar".to_string(), vec![2, 5, 7, 10, 12]);
let buf = encode_to_vec(&val).expect("test: encode");
eprintln!("val {val:?}, buf {buf:?}");
let dec = <Tup as RpcCodec>::from_slice(&buf).expect("test: decode");
assert_eq!(dec, val);
}
#[test]
fn test_int_arr() {
let v: Vec<i16> = vec![5, 10, 15, -1, -2];
let buf = encode_to_vec(&v).expect("test: encode");
assert_eq!(buf.as_slice(), &[0, 5, 0, 10, 0, 15, 255, 255, 255, 254]);
let res = <Vec<i16> as RpcCodec>::from_slice(&buf).expect("test: decode");
assert_eq!(res, v);
}
#[test]
fn test_int_arr_fail() {
let raw_buf = &[0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3];
let res = <Vec<u64> as RpcCodec>::from_slice(raw_buf);
eprintln!("result {res:?}");
if !matches!(res, Err(CodecError::LeftoverBytes(_, _))) {
panic!("not expected result");
}
}
#[test]
fn test_string_invalid() {
let buf = vec![0xff];
let res = <String as RpcCodec>::from_slice(&buf);
eprintln!("result {res:?}");
if !matches!(res, Err(CodecError::NonUtf8String)) {
panic!("not expected result");
}
}
}