Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BorshSize trait #23

Closed
Longarithm opened this issue Mar 18, 2021 · 3 comments · Fixed by #236
Closed

Add BorshSize trait #23

Longarithm opened this issue Mar 18, 2021 · 3 comments · Fixed by #236
Assignees

Comments

@Longarithm
Copy link
Member

Discussion started here: near/nearcore#4107 (comment)

The point is to wrap object' serialization and length computation in one method.

CC: @nearmax

@matklad
Copy link
Contributor

matklad commented Apr 9, 2021

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.

@matklad
Copy link
Contributor

matklad commented Apr 9, 2021

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.

@MaksymZavershynskyi
Copy link
Contributor

What a trick :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants