Skip to content

Commit ae6cfd3

Browse files
honzaspbluss
authored andcommittedMar 7, 2024·
Improve optional support for borsh serialization
- Do not allocate when deserializing ArrayString - Serialize length as u32, not as u64, to be consistent with serialization of [T] and str - Add tests
1 parent 4337b1b commit ae6cfd3

File tree

4 files changed

+93
-16
lines changed

4 files changed

+93
-16
lines changed
 

‎.github/workflows/ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
matrix:
1919
include:
2020
- rust: 1.51.0 # MSRV
21-
features: serde
21+
features: serde, borsh
2222
experimental: false
2323
- rust: stable
2424
features:
@@ -28,7 +28,7 @@ jobs:
2828
features: serde
2929
experimental: false
3030
- rust: nightly
31-
features: serde, zeroize
31+
features: serde, borsh, zeroize
3232
experimental: false
3333

3434
steps:

‎src/array_string.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -640,13 +640,22 @@ impl<const CAP: usize> borsh::BorshSerialize for ArrayString<CAP> {
640640
/// Requires crate feature `"borsh"`
641641
impl<const CAP: usize> borsh::BorshDeserialize for ArrayString<CAP> {
642642
fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
643-
let s = <String as borsh::BorshDeserialize>::deserialize_reader(reader)?;
644-
ArrayString::from(&s).map_err(|_| {
645-
borsh::io::Error::new(
643+
let len = <u32 as borsh::BorshDeserialize>::deserialize_reader(reader)? as usize;
644+
if len > CAP {
645+
return Err(borsh::io::Error::new(
646646
borsh::io::ErrorKind::InvalidData,
647-
format!("expected a string no more than {} bytes long", CAP),
648-
)
649-
})
647+
format!("Expected a string no more than {} bytes long", CAP),
648+
))
649+
}
650+
651+
let mut buf = [0u8; CAP];
652+
let buf = &mut buf[..len];
653+
reader.read_exact(buf)?;
654+
655+
let s = str::from_utf8(&buf).map_err(|err| {
656+
borsh::io::Error::new(borsh::io::ErrorKind::InvalidData, err.to_string())
657+
})?;
658+
Ok(Self::from(s).unwrap())
650659
}
651660
}
652661

‎src/arrayvec.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -1309,12 +1309,7 @@ where
13091309
T: borsh::BorshSerialize,
13101310
{
13111311
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
1312-
let vs = self.as_slice();
1313-
<usize as borsh::BorshSerialize>::serialize(&vs.len(), writer)?;
1314-
for elem in vs {
1315-
<T as borsh::BorshSerialize>::serialize(elem, writer)?;
1316-
}
1317-
Ok(())
1312+
<[T] as borsh::BorshSerialize>::serialize(self.as_slice(), writer)
13181313
}
13191314
}
13201315

@@ -1326,13 +1321,13 @@ where
13261321
{
13271322
fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
13281323
let mut values = Self::new();
1329-
let len = <usize as borsh::BorshDeserialize>::deserialize_reader(reader)?;
1324+
let len = <u32 as borsh::BorshDeserialize>::deserialize_reader(reader)?;
13301325
for _ in 0..len {
13311326
let elem = <T as borsh::BorshDeserialize>::deserialize_reader(reader)?;
13321327
if let Err(_) = values.try_push(elem) {
13331328
return Err(borsh::io::Error::new(
13341329
borsh::io::ErrorKind::InvalidData,
1335-
format!("expected an array with no more than {} items", CAP),
1330+
format!("Expected an array with no more than {} items", CAP),
13361331
));
13371332
}
13381333
}

‎tests/borsh.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#![cfg(feature = "borsh")]
2+
use std::fmt;
3+
extern crate arrayvec;
4+
extern crate borsh;
5+
6+
fn assert_ser<T: borsh::BorshSerialize>(v: &T, expected_bytes: &[u8]) {
7+
let mut actual_bytes = Vec::new();
8+
v.serialize(&mut actual_bytes).unwrap();
9+
assert_eq!(actual_bytes, expected_bytes);
10+
}
11+
12+
fn assert_roundtrip<T: borsh::BorshSerialize + borsh::BorshDeserialize + PartialEq + fmt::Debug>(v: &T) {
13+
let mut bytes = Vec::new();
14+
v.serialize(&mut bytes).unwrap();
15+
let v_de = T::try_from_slice(&bytes).unwrap();
16+
assert_eq!(*v, v_de);
17+
}
18+
19+
mod array_vec {
20+
use arrayvec::ArrayVec;
21+
use super::{assert_ser, assert_roundtrip};
22+
23+
#[test]
24+
fn test_empty() {
25+
let vec = ArrayVec::<u32, 0>::new();
26+
assert_ser(&vec, b"\0\0\0\0");
27+
assert_roundtrip(&vec);
28+
}
29+
30+
#[test]
31+
fn test_full() {
32+
let mut vec = ArrayVec::<u32, 3>::new();
33+
vec.push(0xdeadbeef);
34+
vec.push(0x123);
35+
vec.push(0x456);
36+
assert_ser(&vec, b"\x03\0\0\0\xef\xbe\xad\xde\x23\x01\0\0\x56\x04\0\0");
37+
assert_roundtrip(&vec);
38+
}
39+
40+
#[test]
41+
fn test_with_free_capacity() {
42+
let mut vec = ArrayVec::<u32, 3>::new();
43+
vec.push(0xdeadbeef);
44+
assert_ser(&vec, b"\x01\0\0\0\xef\xbe\xad\xde");
45+
assert_roundtrip(&vec);
46+
}
47+
}
48+
49+
mod array_string {
50+
use arrayvec::ArrayString;
51+
use super::{assert_ser, assert_roundtrip};
52+
53+
#[test]
54+
fn test_empty() {
55+
let string = ArrayString::<0>::new();
56+
assert_ser(&string, b"\0\0\0\0");
57+
assert_roundtrip(&string);
58+
}
59+
60+
#[test]
61+
fn test_full() {
62+
let string = ArrayString::from_byte_string(b"hello world").unwrap();
63+
assert_ser(&string, b"\x0b\0\0\0hello world");
64+
assert_roundtrip(&string);
65+
}
66+
67+
#[test]
68+
fn test_with_free_capacity() {
69+
let string = ArrayString::<16>::from("hello world").unwrap();
70+
assert_ser(&string, b"\x0b\0\0\0hello world");
71+
assert_roundtrip(&string);
72+
}
73+
}

0 commit comments

Comments
 (0)
Please sign in to comment.