wormhole/sdk/rust/core/src/arraystring.rs

113 lines
2.9 KiB
Rust

//! A module for serializing/deserializing a `BString` as a fixed-width 32 byte array.
use std::{convert::identity, fmt, iter::repeat};
use bstr::BString;
use serde::{
de::{Error as DeError, SeqAccess, Visitor},
ser::{Error as SerError, SerializeTuple},
Deserializer, Serializer,
};
pub fn serialize<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
where
T: AsRef<[u8]>,
S: Serializer,
{
let v = value.as_ref();
let l = v.len();
if l > 32 {
return Err(S::Error::custom(format_args!(
"value is too large ({l} bytes); max 32",
)));
}
let mut tup = serializer.serialize_tuple(32)?;
for e in repeat(&0u8).take(32 - l).chain(v) {
tup.serialize_element(e)?;
}
tup.end()
}
struct ArrayStringVisitor;
impl<'de> Visitor<'de> for ArrayStringVisitor {
type Value = BString;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an array of 32 bytes")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut buf = Vec::with_capacity(32);
for i in 0..32 {
let e = seq
.next_element()
.map(|e| e.ok_or_else(|| A::Error::invalid_length(i, &self)))
.and_then(identity)?; // TODO: Replace with Result::flatten once stabilized.
if e == 0 && buf.is_empty() {
// Skip all leading zeroes.
continue;
}
buf.push(e);
}
Ok(BString::from(buf))
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<BString, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(32, ArrayStringVisitor)
}
#[cfg(test)]
mod test {
use bstr::BString;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
#[repr(transparent)]
struct MyString(#[serde(with = "super")] BString);
#[test]
fn end_to_end() {
let v = "45825ca36ef7628727b83f8a409a08ad";
let zeroes = [0u8; 32];
for i in 0..=32 {
let expected = MyString(v[i..].into());
let buf = serde_wormhole::to_vec(&expected).unwrap();
assert_eq!(&zeroes[..i], &buf[..i]);
assert_eq!(v.as_bytes()[i..], buf[i..]);
let actual = serde_wormhole::from_slice(&buf).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn value_too_large() {
let v = MyString("61ddb8ede2a3f0cec9b550ef150c08096280d0480493365f".into());
let _ = serde_wormhole::to_vec(&v)
.expect_err("successfully serialized string longer than 32 bytes");
}
#[test]
fn buffer_too_small() {
let b = [0u8; 16];
let _ = serde_wormhole::from_slice::<MyString>(&b)
.expect_err("successfully deserialized string from a buffer that's too small");
}
}