diff --git a/src/lib.rs b/src/lib.rs index 016bcc8..20a2a0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ use std::io::Write; // for bytes.write_all; push_all is unstable and extend is s use std::io::Result as IOResult; use std::marker::PhantomData; use std::num::*; +use std::sync::Arc; pub mod abomonated; @@ -534,6 +535,36 @@ impl Abomonation for Box { } } +impl Abomonation for Arc { + #[inline] + unsafe fn entomb(&self, write: &mut W) -> std::io::Result<()> { + /* Since the value T has not yet been seen by abomonate (it is behind a pointer) + * we need to fully encode it. + */ + encode(self.as_ref(), write) + } + #[inline] + unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { + use std::ptr; + /* The idea here is to construct a new Arc from the entombed bytes. + * The state of this ArcVal upon entry of this function contains only an invalid + * pointer to an ArcInner that we need to dispose of without trying to run + * its destructor (which would panic). + */ + let (value, bytes) = decode::(bytes)?; + // value is just a reference to the first part of old bytes, so move it into a new Arc + let arc = Arc::new(ptr::read(value)); + // now swap the fresh arc into its place ... + let garbage = std::mem::replace(self, arc); + // ... and forget about the old one + mem::forget(garbage); + Some(bytes) + } + fn extent(&self) -> usize { + std::mem::size_of::() + self.as_ref().extent() + } +} + // This method currently enables undefined behavior, by exposing padding bytes. #[inline] unsafe fn typed_to_bytes(slice: &[T]) -> &[u8] { std::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::()) diff --git a/tests/tests.rs b/tests/tests.rs index 340e776..0329e1d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -128,3 +128,27 @@ fn test_net_types() { let (t, r) = unsafe { decode::(&mut bytes) }.unwrap(); assert!(*t == socket_addr4); let (t, _r) = unsafe { decode::(r) }.unwrap(); assert!(*t == socket_addr6); } + +#[test] +fn test_ref_types() { + use std::sync::Arc; + let value = Arc::new("hello".to_owned()); + + let mut bytes = Vec::new(); + unsafe { abomonation::encode(&value, &mut bytes).unwrap() }; + // abomonated bytes end with "hello" + assert_eq!(&bytes[bytes.len() - 5..], b"hello"); + + let mut reference = Vec::new(); + unsafe { abomonation::encode(&"hello".to_owned(), &mut reference).unwrap() }; + // abomonating an Arc is like abomonating a pointer and then the contained value + assert_eq!(&bytes[std::mem::size_of::() + 2..], &reference[2..]); + + // modify the bytes to see that deserialization uses them and not the pointer + let pos = bytes.len() - 4; + bytes[pos] = b'a'; + + let (value2, bytes) = unsafe { abomonation::decode::>(&mut bytes).unwrap() }; + assert_eq!(value2.as_ref(), "hallo"); + assert!(bytes.is_empty()); +} \ No newline at end of file