diff --git a/ssz/src/decode.rs b/ssz/src/decode.rs index 591d8c863..ae7a78c83 100644 --- a/ssz/src/decode.rs +++ b/ssz/src/decode.rs @@ -59,7 +59,7 @@ fn decode_length(bytes: &[u8], length_bytes: usize) #[cfg(test)] mod tests { use super::*; - use super::super::encode_length; + use super::super::encode::encode_length; #[test] fn test_ssz_decode_length() { diff --git a/ssz/src/encode.rs b/ssz/src/encode.rs new file mode 100644 index 000000000..6841631e1 --- /dev/null +++ b/ssz/src/encode.rs @@ -0,0 +1,156 @@ +use super::LENGTH_BYTES; + +pub trait Encodable { + fn ssz_append(&self, s: &mut SszStream); +} + +pub struct SszStream { + buffer: Vec +} + +impl SszStream { + /// Create a new, empty steam for writing ssz values. + pub fn new() -> Self { + SszStream { + buffer: Vec::new() + } + } + + /// Append some ssz encodable value to the stream. + pub fn append(&mut self, value: &E) -> &mut Self + where E: Encodable + { + value.ssz_append(self); + self + } + + pub fn extend_buffer(&mut self, vec: &Vec) { + self.buffer.extend_from_slice( + &encode_length(vec.len(), + LENGTH_BYTES)); + self.buffer.extend_from_slice(&vec); + } + + /// Append some vector (list) of encoded values to the stream. + pub fn append_vec(&mut self, vec: &Vec) + where E: Encodable + { + self.buffer.extend_from_slice(&encode_length(vec.len(), LENGTH_BYTES)); + for v in vec { + v.ssz_append(self); + } + } + + /// Append some array (list) of encoded values to the stream. + pub fn append_encoded_array(&mut self, a: &mut [u8]) { + let len = a.len(); + self.buffer.append(&mut encode_length(len, LENGTH_BYTES)); + self.buffer.extend_from_slice(&a[0..len]); + } + + /// Consume the stream and return the underlying bytes. + pub fn drain(self) -> Vec { + self.buffer + } +} + +pub fn encode_length(len: usize, length_bytes: usize) -> Vec { + assert!(length_bytes > 0); // For sanity + assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8)); + let mut header: Vec = vec![0; length_bytes]; + for i in 0..length_bytes { + let offset = (length_bytes - i - 1) * 8; + header[i] = ((len >> offset) & 0xff) as u8; + }; + header +} + + +#[cfg(test)] +mod tests { + use super::*; + use super::super::ethereum_types::{ H256, U256 }; + + #[test] + #[should_panic] + fn test_encode_length_0_bytes_panic() { + encode_length(0, 0); + } + + #[test] + fn test_encode_length_4_bytes() { + assert_eq!( + encode_length(0, 4), + vec![0; 4] + ); + assert_eq!( + encode_length(1, 4), + vec![0, 0, 0, 1] + ); + assert_eq!( + encode_length(255, 4), + vec![0, 0, 0, 255] + ); + assert_eq!( + encode_length(256, 4), + vec![0, 0, 1, 0] + ); + assert_eq!( + encode_length(4294967295, 4), // 2^(4*8) - 1 + vec![255, 255, 255, 255] + ); + } + + #[test] + #[should_panic] + fn test_encode_length_4_bytes_panic() { + encode_length(4294967296, 4); // 2^(4*8) + } + + #[test] + fn test_serialization() { + pub struct TestStruct { + pub one: u32, + pub two: H256, + pub three: u64, + pub four: U256, + } + + impl Encodable for TestStruct { + fn ssz_append(&self, s: &mut SszStream) { + s.append(&self.one); + s.append(&self.two); + s.append(&self.three); + s.append(&self.four); + } + } + + let t = TestStruct { + one: 1, + two: H256::zero(), + three: 100, + four: U256::zero(), + }; + + let mut s = SszStream::new(); + s.append(&t); + let e = s.drain(); + + let expected_len = { + 4 + 4 + + 4 + 32 + + 4 + 8 + + 4 + 32 + }; + + assert_eq!(e[0..4], [0, 0, 0, 4]); + assert_eq!(e[4..8], [0, 0, 0, 1]); + assert_eq!(e[8..12], [0, 0, 0, 32]); + assert_eq!(e[12..44], [0; 32]); + assert_eq!(e[44..48], [0, 0, 0, 8]); + assert_eq!(e[48..56], [0, 0, 0, 0, 0, 0, 0, 100]); + assert_eq!(e[56..60], [0, 0, 0, 32]); + assert_eq!(e[60..92], [0; 32]); + assert_eq!(e.len(), expected_len); + } +} diff --git a/ssz/src/impls.rs b/ssz/src/impls.rs index eae6355d6..ecbfd7398 100644 --- a/ssz/src/impls.rs +++ b/ssz/src/impls.rs @@ -40,7 +40,7 @@ impl_decodable_for_uint!(u16, 16 / 8); impl Encodable for u8 { fn ssz_append(&self, s: &mut SszStream) { - s.buffer.append(&mut vec![*self]); + s.extend_buffer(&mut vec![*self]); } } diff --git a/ssz/src/lib.rs b/ssz/src/lib.rs index 1d8f36bb9..8c3d0d9cb 100644 --- a/ssz/src/lib.rs +++ b/ssz/src/lib.rs @@ -10,168 +10,18 @@ extern crate bytes; extern crate ethereum_types; -mod impls; +mod encode; mod decode; +mod impls; pub use decode::{ - decode_ssz_list_element, Decodable, - DecodeError + DecodeError, + decode_ssz_list_element, +}; +pub use encode::{ + Encodable, + SszStream, }; pub const LENGTH_BYTES: usize = 4; - -pub trait Encodable { - fn ssz_append(&self, s: &mut SszStream); -} - -pub struct SszStream { - buffer: Vec -} - -impl SszStream { - /// Create a new, empty steam for writing ssz values. - pub fn new() -> Self { - SszStream { - buffer: Vec::new() - } - } - - /// Append some ssz encodable value to the stream. - pub fn append(&mut self, value: &E) -> &mut Self - where E: Encodable - { - value.ssz_append(self); - self - } - - pub fn extend_buffer(&mut self, vec: &Vec) { - self.buffer.extend_from_slice( - &encode_length(vec.len(), - LENGTH_BYTES)); - self.buffer.extend_from_slice(&vec); - } - - /// Append some vector (list) of encoded values to the stream. - pub fn append_vec(&mut self, vec: &Vec) - where E: Encodable - { - self.buffer.extend_from_slice(&encode_length(vec.len(), LENGTH_BYTES)); - for v in vec { - v.ssz_append(self); - } - } - - /// Append some array (list) of encoded values to the stream. - pub fn append_encoded_array(&mut self, a: &mut [u8]) { - let len = a.len(); - self.buffer.append(&mut encode_length(len, LENGTH_BYTES)); - self.buffer.extend_from_slice(&a[0..len]); - } - - /// Consume the stream and return the underlying bytes. - pub fn drain(self) -> Vec { - self.buffer - } -} - -fn encode_length(len: usize, length_bytes: usize) -> Vec { - assert!(length_bytes > 0); // For sanity - assert!((len as usize) < 2usize.pow(length_bytes as u32 * 8)); - let mut header: Vec = vec![0; length_bytes]; - for i in 0..length_bytes { - let offset = (length_bytes - i - 1) * 8; - header[i] = ((len >> offset) & 0xff) as u8; - }; - header -} - - -#[cfg(test)] -mod tests { - use super::*; - use super::ethereum_types::{ H256, U256 }; - - #[test] - #[should_panic] - fn test_encode_length_0_bytes_panic() { - encode_length(0, 0); - } - - #[test] - fn test_encode_length_4_bytes() { - assert_eq!( - encode_length(0, 4), - vec![0; 4] - ); - assert_eq!( - encode_length(1, 4), - vec![0, 0, 0, 1] - ); - assert_eq!( - encode_length(255, 4), - vec![0, 0, 0, 255] - ); - assert_eq!( - encode_length(256, 4), - vec![0, 0, 1, 0] - ); - assert_eq!( - encode_length(4294967295, 4), // 2^(4*8) - 1 - vec![255, 255, 255, 255] - ); - } - - #[test] - #[should_panic] - fn test_encode_length_4_bytes_panic() { - encode_length(4294967296, 4); // 2^(4*8) - } - - #[test] - fn test_serialization() { - pub struct TestStruct { - pub one: u32, - pub two: H256, - pub three: u64, - pub four: U256, - } - - impl Encodable for TestStruct { - fn ssz_append(&self, s: &mut SszStream) { - s.append(&self.one); - s.append(&self.two); - s.append(&self.three); - s.append(&self.four); - } - } - - let t = TestStruct { - one: 1, - two: H256::zero(), - three: 100, - four: U256::zero(), - }; - - let mut s = SszStream::new(); - s.append(&t); - let e = s.drain(); - - let expected_len = { - 4 + 4 + - 4 + 32 + - 4 + 8 + - 4 + 32 - }; - - assert_eq!(e[0..4], [0, 0, 0, 4]); - assert_eq!(e[4..8], [0, 0, 0, 1]); - assert_eq!(e[8..12], [0, 0, 0, 32]); - assert_eq!(e[12..44], [0; 32]); - assert_eq!(e[44..48], [0, 0, 0, 8]); - assert_eq!(e[48..56], [0, 0, 0, 0, 0, 0, 0, 100]); - assert_eq!(e[56..60], [0, 0, 0, 32]); - assert_eq!(e[60..92], [0; 32]); - assert_eq!(e.len(), expected_len); - } -}