borsh-rs
borsh-rs copied to clipboard
Add BorshSize trait
Discussion started here: https://github.com/near/nearcore/pull/4107#discussion_r594754389
The point is to wrap object' serialization and length computation in one method.
CC: @nearmax
80% solution:
pub fn borsh_len(value: &impl BorshSerialize) -> Result<usize> {
struct Writer {
len: usize,
}
impl Write for Writer {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.len += buf.len();
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
let mut w = Writer { len: 0 };
value.serialize(&mut w)?;
Ok(w.len)
}
This won't do exactly what we want for types that use extra heap-allocated buffers for serilization, but my gut feeling is that that's rare.
EDIT: didnt' resist the temptation to look at the asm, looks right for simple cases:
15:22:23|~/projects/borsh-rs|master⚡✚*
λ git diff --cached
diff --git a/borsh/examples/len.rs b/borsh/examples/len.rs
new file mode 100644
index 00000000..69b84a0c
--- /dev/null
+++ b/borsh/examples/len.rs
@@ -0,0 +1,17 @@
+#[derive(borsh::BorshSerialize)]
+pub struct Foo {
+ x: u8,
+ y: u32
+}
+
+#[no_mangle]
+#[inline(never)]
+pub fn size_of_foo(foo: &Foo) -> usize {
+ borsh::len(foo).unwrap_or_default()
+}
+
+fn main() {
+ let foo = Foo { x: 62, y: 92 };
+ let res = size_of_foo(&foo);
+ eprintln!("{}", res);
+}
diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs
index 51c81919..55088283 100644
--- a/borsh/src/lib.rs
+++ b/borsh/src/lib.rs
@@ -45,3 +45,23 @@ pub mod maybestd {
pub use hashbrown::hash_map::Entry;
}
}
+
+pub fn len(value: &impl BorshSerialize) -> std::io::Result<usize> {
+ struct Writer {
+ len: usize,
+ }
+ impl std::io::Write for Writer {
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.len += buf.len();
+ Ok(buf.len())
+ }
+ #[inline]
+ fn flush(&mut self) -> std::io::Result<()> {
+ Ok(())
+ }
+ }
+ let mut w = Writer { len: 0 };
+ value.serialize(&mut w)?;
+ Ok(w.len)
+}
15:22:28|~/projects/borsh-rs|master⚡✚*
λ cargo build --example len --release -q
15:22:41|~/projects/borsh-rs|master⚡✚*
λ dis ./target/release/examples/len size_of_foo
Dump of assembler code for function size_of_foo:
0x0000000000011b70 <+0>: mov eax,0x5
0x0000000000011b75 <+5>: ret
End of assembler dump.
And output for slightly more intersting
#[derive(borsh::BorshSerialize)]
pub struct Foo {
x: u8,
y: u32,
zs: Vec<u32>,
}
Dump of assembler code for function size_of_foo:
0x0000000000011be0 <+0>: mov rax,QWORD PTR [rdi+0x10]
0x0000000000011be4 <+4>: mov rcx,rax
0x0000000000011be7 <+7>: shr rcx,0x20
0x0000000000011beb <+11>: test rax,rax
0x0000000000011bee <+14>: lea rax,[rax*4+0x9]
0x0000000000011bf6 <+22>: mov edx,0x9
0x0000000000011bfb <+27>: cmovne rdx,rax
0x0000000000011bff <+31>: xor eax,eax
0x0000000000011c01 <+33>: test rcx,rcx
0x0000000000011c04 <+36>: cmove rax,rdx
0x0000000000011c08 <+40>: ret
End of assembler dump.
What a trick :D