diff --git a/benches/rlp.rs b/benches/rlp.rs new file mode 100644 index 000000000..73af76bbe --- /dev/null +++ b/benches/rlp.rs @@ -0,0 +1,95 @@ +//! benchmarking for rlp +//! should be started with: +//! ```bash +//! multirust run nightly cargo bench +//! ``` + +#![feature(test)] + +extern crate test; +extern crate ethcore_util; + +use test::Bencher; +use std::str::FromStr; +use ethcore_util::rlp::{RlpStream, Rlp, Decodable}; +use ethcore_util::uint::U256; + +#[bench] +fn bench_stream_u64_value(b: &mut Bencher) { + b.iter( || { + //1029 + let mut stream = RlpStream::new(); + stream.append(&0x1023456789abcdefu64); + let _ = stream.out().unwrap(); + }); +} + +#[bench] +fn bench_decode_u64_value(b: &mut Bencher) { + b.iter( || { + // 1029 + let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; + let rlp = Rlp::new(&data); + let _ = u64::decode(&rlp).unwrap(); + }); +} + +#[bench] +fn bench_stream_u256_value(b: &mut Bencher) { + b.iter( || { + //u256 + let mut stream = RlpStream::new(); + stream.append(&U256::from_str("8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0").unwrap()); + let _ = stream.out().unwrap(); + }); +} + +#[bench] +fn bench_decode_u256_value(b: &mut Bencher) { + b.iter( || { + // u256 + let data = vec![0xa0, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x09, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0]; + let rlp = Rlp::new(&data); + let _ = U256::decode(&rlp).unwrap(); + }); +} + +#[bench] +fn bench_stream_nested_empty_lists(b: &mut Bencher) { + b.iter( || { + // [ [], [[]], [ [], [[]] ] ] + let mut stream = RlpStream::new_list(3); + stream.append_list(0); + stream.append_list(1).append_list(0); + stream.append_list(2).append_list(0).append_list(1).append_list(0); + let _ = stream.out().unwrap(); + }); +} + +#[bench] +fn bench_decode_nested_empty_lists(b: &mut Bencher) { + b.iter( || { + // [ [], [[]], [ [], [[]] ] ] + let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; + let rlp = Rlp::new(&data); + let _v0: Vec = Decodable::decode(&rlp.at(0).unwrap()).unwrap(); + let _v1: Vec> = Decodable::decode(&rlp.at(1).unwrap()).unwrap(); + let nested_rlp = rlp.at(2).unwrap(); + let _v2a: Vec = Decodable::decode(&nested_rlp.at(0).unwrap()).unwrap(); + let _v2b: Vec> = Decodable::decode(&nested_rlp.at(1).unwrap()).unwrap(); + }); +} + +#[bench] +fn bench_stream_1000_empty_lists(b: &mut Bencher) { + b.iter( || { + let mut stream = RlpStream::new_list(1000); + for _ in 0..1000 { + stream.append_list(0); + } + let _ = stream.out().unwrap(); + }); +} diff --git a/src/bytes.rs b/src/bytes.rs index 06735995f..1b331cf15 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -8,6 +8,7 @@ use std::fmt; use std::error::Error as StdError; +use uint::{U128, U256}; /// TODO: optimise some conversations pub trait ToBytes { @@ -59,6 +60,7 @@ impl ToBytes for u64 { fn to_bytes(&self) -> Vec { let mut res= vec![]; let count = self.to_bytes_len(); + res.reserve(count); for i in 0..count { let j = count - 1 - i; res.push((*self >> (j * 8)) as u8); @@ -82,6 +84,27 @@ impl_map_to_bytes!(usize, u64); impl_map_to_bytes!(u16, u64); impl_map_to_bytes!(u32, u64); +macro_rules! impl_uint_to_bytes { + ($name: ident) => { + impl ToBytes for $name { + fn to_bytes(&self) -> Vec { + let mut res= vec![]; + let count = self.to_bytes_len(); + res.reserve(count); + for i in 0..count { + let j = count - 1 - i; + res.push(self.byte(j)); + } + res + } + fn to_bytes_len(&self) -> usize { (self.bits() + 7) / 8 } + } + } +} + +impl_uint_to_bytes!(U256); +impl_uint_to_bytes!(U128); + #[derive(Debug, PartialEq, Eq)] pub enum FromBytesError { UnexpectedEnd @@ -101,6 +124,7 @@ pub type FromBytesResult = Result; /// implements "Sized", so the compiler can deducate the size /// of the return type +/// TODO: check size of bytes before conversation and return appropriate error pub trait FromBytes: Sized { fn from_bytes(bytes: &[u8]) -> FromBytesResult; } @@ -149,3 +173,16 @@ macro_rules! impl_map_from_bytes { impl_map_from_bytes!(usize, u64); impl_map_from_bytes!(u16, u64); impl_map_from_bytes!(u32, u64); + +macro_rules! impl_uint_from_bytes { + ($name: ident) => { + impl FromBytes for $name { + fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> { + Ok($name::from(bytes)) + } + } + } +} + +impl_uint_from_bytes!(U256); +impl_uint_from_bytes!(U128); diff --git a/src/lib.rs b/src/lib.rs index 91c4f8db6..7d9212634 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,10 @@ pub use std::str::FromStr; pub mod error; pub mod hash; +pub mod uint; pub mod bytes; pub mod rlp; +pub mod vector; #[test] fn it_works() { diff --git a/src/rlp.rs b/src/rlp.rs index a0fe455b5..f70062457 100644 --- a/src/rlp.rs +++ b/src/rlp.rs @@ -1,9 +1,54 @@ //! Rlp serialization module +//! +//! Types implementing `Endocable` and `Decodable` traits +//! can be easily coverted to and from rlp +//! +//! # Examples: +//! +//! ```rust +//! extern crate ethcore_util; +//! use ethcore_util::rlp::{RlpStream}; +//! +//! fn encode_value() { +//! // 1029 +//! let mut stream = RlpStream::new(); +//! stream.append(&1029u32); +//! let out = stream.out().unwrap(); +//! assert_eq!(out, vec![0x82, 0x04, 0x05]); +//! } +//! +//! fn encode_list() { +//! // [ "cat", "dog" ] +//! let mut stream = RlpStream::new_list(2); +//! stream.append(&"cat").append(&"dog"); +//! let out = stream.out().unwrap(); +//! assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); +//! } +//! +//! fn encode_list2() { +//! // [ [], [[]], [ [], [[]] ] ] +//! let mut stream = RlpStream::new_list(3); +//! stream.append_list(0); +//! stream.append_list(1).append_list(0); +//! stream.append_list(2).append_list(0).append_list(1).append_list(0); +//! let out = stream.out().unwrap(); +//! assert_eq!(out, vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]); +//! } +//! +//! fn main() { +//! encode_value(); +//! encode_list(); +//! encode_list2(); +//! } +//! ``` +//! use std::fmt; use std::cell::Cell; +use std::collections::LinkedList; use std::error::Error as StdError; -use bytes::{FromBytes, FromBytesError}; +use bytes::{ToBytes, FromBytes, FromBytesError}; +use vector::InsertSlice; /// rlp container #[derive(Debug)] @@ -41,7 +86,8 @@ impl ItemInfo { pub enum DecoderError { FromBytesError(FromBytesError), RlpIsTooShort, - RlpExpectedToBeArray, + RlpExpectedToBeList, + RlpExpectedToBeValue, BadRlp, } impl StdError for DecoderError { @@ -71,16 +117,16 @@ impl <'a>Rlp<'a> { /// /// paren container caches searched position pub fn at(&self, index: usize) -> Result, DecoderError> { - if !self.is_array() { - return Err(DecoderError::RlpExpectedToBeArray); + if !self.is_list() { + return Err(DecoderError::RlpExpectedToBeList); } // move to cached position if it's index is less or equal to - // current search index, otherwise move to beginning of array + // current search index, otherwise move to beginning of list let c = self.cache.get(); let (mut bytes, to_skip) = match c.index <= index { true => (try!(Rlp::consume(self.bytes, c.offset)), index - c.index), - false => (try!(self.consume_array_prefix()), index) + false => (try!(self.consume_list_prefix()), index) }; // skip up to x items @@ -94,8 +140,8 @@ impl <'a>Rlp<'a> { Ok(Rlp::new(&bytes[0..found.prefix_len + found.value_len])) } - /// returns true if rlp is an array - pub fn is_array(&self) -> bool { + /// returns true if rlp is a list + pub fn is_list(&self) -> bool { self.bytes.len() > 0 && self.bytes[0] >= 0xc0 } @@ -110,7 +156,7 @@ impl <'a>Rlp<'a> { } /// consumes first found prefix - fn consume_array_prefix(&self) -> Result<&'a [u8], DecoderError> { + fn consume_list_prefix(&self) -> Result<&'a [u8], DecoderError> { let item = try!(Rlp::item_info(self.bytes)); let bytes = try!(Rlp::consume(self.bytes, item.prefix_len)); Ok(bytes) @@ -189,29 +235,315 @@ impl <'a> Iterator for RlpIterator<'a> { } } +/// shortcut function to decode a Rlp `&[u8]` into an object +pub fn decode(bytes: &[u8]) -> Result where T: Decodable { + let rlp = Rlp::new(bytes); + T::decode(&rlp) +} + +pub trait Decodable: Sized { + fn decode(rlp: &Rlp) -> Result; +} + +impl Decodable for T where T: FromBytes { + fn decode(rlp: &Rlp) -> Result { + match rlp.is_value() { + true => BasicDecoder::read_value(rlp.bytes), + false => Err(DecoderError::RlpExpectedToBeValue) + } + } +} + +impl Decodable for Vec where T: Decodable { + fn decode(rlp: &Rlp) -> Result { + match rlp.is_list() { + true => rlp.iter().map(|rlp| T::decode(&rlp)).collect(), + false => Err(DecoderError::RlpExpectedToBeList) + } + } +} + +pub trait Decoder { + fn read_value(bytes: &[u8]) -> Result where T: FromBytes; +} + +struct BasicDecoder; + +impl Decoder for BasicDecoder { + fn read_value(bytes: &[u8]) -> Result where T: FromBytes { + match bytes.first().map(|&x| x) { + // rlp is too short + None => Err(DecoderError::RlpIsTooShort), + // single byt value + Some(l @ 0...0x7f) => Ok(try!(T::from_bytes(&[l]))), + // 0-55 bytes + Some(l @ 0x80...0xb7) => Ok(try!(T::from_bytes(&bytes[1..(1 + l as usize - 0x80)]))), + // longer than 55 bytes + Some(l @ 0xb8...0xbf) => { + let len_of_len = l as usize - 0xb7; + let begin_of_value = 1 as usize + len_of_len; + let len = try!(usize::from_bytes(&bytes[1..begin_of_value])); + Ok(try!(T::from_bytes(&bytes[begin_of_value..begin_of_value + len]))) + }, + _ => Err(DecoderError::BadRlp) + } + } +} + +#[derive(Debug)] +struct ListInfo { + position: usize, + current: usize, + max: usize +} + +impl ListInfo { + fn new(position: usize, max: usize) -> ListInfo { + ListInfo { + position: position, + current: 0, + max: max + } + } +} + +/// container that should be used to encode rlp +pub struct RlpStream { + unfinished_lists: LinkedList, + encoder: BasicEncoder +} + +impl RlpStream { + /// create new container for values appended one after another, + /// but not being part of the same list + pub fn new() -> RlpStream { + RlpStream { + unfinished_lists: LinkedList::new(), + encoder: BasicEncoder::new() + } + } + + /// create new container for list of size `max_len` + pub fn new_list(len: usize) -> RlpStream { + let mut stream = RlpStream::new(); + stream.append_list(len); + stream + } + + /// apends value to the end of stream, chainable + pub fn append<'a, E>(&'a mut self, object: &E) -> &'a mut RlpStream where E: Encodable { + // encode given value and add it at the end of the stream + object.encode(&mut self.encoder); + + // if list is finished, prepend the length + self.try_to_finish(); + + // return chainable self + self + } + + /// declare appending the list of given size + pub fn append_list<'a>(&'a mut self, len: usize) -> &'a mut RlpStream { + // push new list + let position = self.encoder.bytes.len(); + match len { + 0 => { + // we may finish, if the appended list len is equal 0 + self.encoder.bytes.push(0xc0u8); + self.try_to_finish(); + }, + _ => self.unfinished_lists.push_back(ListInfo::new(position, len)) + } + + // return chainable self + self + } + + /// return true if stream is ready + pub fn is_finished(&self) -> bool { + self.unfinished_lists.back().is_none() + } + + /// streams out encoded bytes + pub fn out(self) -> Result, EncoderError> { + match self.is_finished() { + true => Ok(self.encoder.out()), + false => Err(EncoderError::StreamIsUnfinished) + } + } + + /// try to finish lists + fn try_to_finish(&mut self) -> () { + let should_finish = match self.unfinished_lists.back_mut() { + None => false, + Some(ref mut x) => { + x.current += 1; + x.current == x.max + } + }; + + if should_finish { + let x = self.unfinished_lists.pop_back().unwrap(); + let len = self.encoder.bytes.len() - x.position; + self.encoder.insert_list_len_at_pos(len, x.position); + self.try_to_finish(); + } + } +} + +/// shortcut function to encode a `T: Encodable` into a Rlp `Vec` +pub fn encode(object: &E) -> Vec where E: Encodable { + let mut encoder = BasicEncoder::new(); + object.encode(&mut encoder); + encoder.out() +} + +#[derive(Debug)] +pub enum EncoderError { + StreamIsUnfinished +} + +impl StdError for EncoderError { + fn description(&self) -> &str { "encoder error" } +} + +impl fmt::Display for EncoderError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self, f) + } +} + +pub trait Encodable { + fn encode(&self, encoder: &mut E) -> () where E: Encoder; +} + +pub trait Encoder { + fn emit_value(&mut self, bytes: &[u8]) -> (); + fn emit_list(&mut self, f: F) -> () where F: FnOnce(&mut Self) -> (); +} + +impl Encodable for T where T: ToBytes { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_value(&self.to_bytes()) + } +} + +impl <'a, T> Encodable for &'a [T] where T: Encodable + 'a { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_list(|e| { + // insert all list elements + for el in self.iter() { + el.encode(e); + } + }) + } +} + +impl Encodable for Vec where T: Encodable { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + let r: &[T] = self.as_ref(); + r.encode(encoder) + } +} + +struct BasicEncoder { + bytes: Vec +} + +impl BasicEncoder { + fn new() -> BasicEncoder { + BasicEncoder { bytes: vec![] } + } + + /// inserts list prefix at given position + /// TODO: optimise it further? + fn insert_list_len_at_pos(&mut self, len: usize, pos: usize) -> () { + let mut res = vec![]; + match len { + 0...55 => res.push(0xc0u8 + len as u8), + _ => { + res.push(0x7fu8 + len.to_bytes_len() as u8); + res.extend(len.to_bytes()); + } + }; + + self.bytes.insert_slice(pos, &res); + } + + /// get encoded value + fn out(self) -> Vec { + self.bytes + } +} + +impl Encoder for BasicEncoder { + fn emit_value(&mut self, bytes: &[u8]) -> () { + match bytes.len() { + // just 0 + 0 => self.bytes.push(0x80u8), + // byte is its own encoding + 1 if bytes[0] < 0x80 => self.bytes.extend(bytes), + // (prefix + length), followed by the string + len @ 1 ... 55 => { + self.bytes.push(0x80u8 + len as u8); + self.bytes.extend(bytes); + } + // (prefix + length of length), followed by the length, followd by the string + len => { + self.bytes.push(0xb7 + len.to_bytes_len() as u8); + self.bytes.extend(len.to_bytes()); + self.bytes.extend(bytes); + } + } + } + + fn emit_list(&mut self, f: F) -> () where F: FnOnce(&mut Self) -> () { + // get len before inserting a list + let before_len = self.bytes.len(); + + // insert all list elements + f(self); + + // get len after inserting a list + let after_len = self.bytes.len(); + + // diff is list len + let list_len = after_len - before_len; + self.insert_list_len_at_pos(list_len, before_len); + } +} + #[cfg(test)] mod tests { + use std::{fmt, cmp}; + use std::str::FromStr; use rlp; - use rlp::Rlp; + use rlp::{Rlp, RlpStream, Decodable}; + use uint::U256; #[test] fn rlp_at() { let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; { let rlp = Rlp::new(&data); - assert!(rlp.is_array()); + assert!(rlp.is_list()); + let animals = as rlp::Decodable>::decode(&rlp).unwrap(); + assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]); let cat = rlp.at(0).unwrap(); assert!(cat.is_value()); assert_eq!(cat.bytes, &[0x83, b'c', b'a', b't']); + assert_eq!(String::decode(&cat).unwrap(), "cat".to_string()); let dog = rlp.at(1).unwrap(); assert!(dog.is_value()); assert_eq!(dog.bytes, &[0x83, b'd', b'o', b'g']); + assert_eq!(String::decode(&dog).unwrap(), "dog".to_string()); let cat_again = rlp.at(0).unwrap(); assert!(cat_again.is_value()); assert_eq!(cat_again.bytes, &[0x83, b'c', b'a', b't']); + assert_eq!(String::decode(&cat_again).unwrap(), "cat".to_string()); } } @@ -220,7 +552,7 @@ mod tests { let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o']; { let rlp = Rlp::new(&data); - assert!(rlp.is_array()); + assert!(rlp.is_list()); let cat_err = rlp.at(0).unwrap_err(); assert_eq!(cat_err, rlp::DecoderError::RlpIsTooShort); @@ -253,5 +585,269 @@ mod tests { assert_eq!(cat_again.bytes, &[0x83, b'c', b'a', b't']); } } + + struct ETestPair(T, Vec) where T: rlp::Encodable; + + fn run_encode_tests(tests: Vec>) where T: rlp::Encodable { + for t in &tests { + let res = rlp::encode(&t.0); + assert_eq!(res, &t.1[..]); + } + } + + #[test] + fn encode_u8() { + let tests = vec![ + ETestPair(0u8, vec![0x80u8]), + ETestPair(15, vec![15]), + ETestPair(55, vec![55]), + ETestPair(56, vec![56]), + ETestPair(0x7f, vec![0x7f]), + ETestPair(0x80, vec![0x81, 0x80]), + ETestPair(0xff, vec![0x81, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_u16() { + let tests = vec![ + ETestPair(0u16, vec![0x80u8]), + ETestPair(0x100, vec![0x82, 0x01, 0x00]), + ETestPair(0xffff, vec![0x82, 0xff, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_u32() { + let tests = vec![ + ETestPair(0u32, vec![0x80u8]), + ETestPair(0x10000, vec![0x83, 0x01, 0x00, 0x00]), + ETestPair(0xffffff, vec![0x83, 0xff, 0xff, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_u64() { + let tests = vec![ + ETestPair(0u64, vec![0x80u8]), + ETestPair(0x1000000, vec![0x84, 0x01, 0x00, 0x00, 0x00]), + ETestPair(0xFFFFFFFF, vec![0x84, 0xff, 0xff, 0xff, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_u256() { + let tests = vec![ + ETestPair(U256::from(0u64), vec![0x80u8]), + ETestPair(U256::from(0x1000000u64), vec![0x84, 0x01, 0x00, 0x00, 0x00]), + ETestPair(U256::from(0xffffffffu64), vec![0x84, 0xff, 0xff, 0xff, 0xff]), + ETestPair(U256::from_str("8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0").unwrap(), + vec![0xa0, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x09, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0]) + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_str() { + let tests = vec![ + ETestPair("cat", vec![0x83, b'c', b'a', b't']), + ETestPair("dog", vec![0x83, b'd', b'o', b'g']), + ETestPair("Marek", vec![0x85, b'M', b'a', b'r', b'e', b'k']), + ETestPair("", vec![0x80]), + ETestPair("Lorem ipsum dolor sit amet, consectetur adipisicing elit", + vec![0xb8, 0x38, b'L', b'o', b'r', b'e', b'm', b' ', b'i', + b'p', b's', b'u', b'm', b' ', b'd', b'o', b'l', b'o', b'r', + b' ', b's', b'i', b't', b' ', b'a', b'm', b'e', b't', b',', + b' ', b'c', b'o', b'n', b's', b'e', b'c', b't', b'e', b't', + b'u', b'r', b' ', b'a', b'd', b'i', b'p', b'i', b's', b'i', + b'c', b'i', b'n', b'g', b' ', b'e', b'l', b'i', b't']) + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_vector_u8() { + let tests = vec![ + ETestPair(vec![], vec![0xc0]), + ETestPair(vec![15u8], vec![0xc1, 0x0f]), + ETestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_vector_u64() { + let tests = vec![ + ETestPair(vec![], vec![0xc0]), + ETestPair(vec![15u64], vec![0xc1, 0x0f]), + ETestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + ETestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]), + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_vector_str() { + let tests = vec![ + ETestPair(vec!["cat", "dog"], vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']) + ]; + run_encode_tests(tests); + } + + #[test] + fn encode_vector_of_vectors_str() { + let tests = vec![ + ETestPair(vec![vec!["cat"]], vec![0xc5, 0xc4, 0x83, b'c', b'a', b't']) + ]; + run_encode_tests(tests); + } + + #[test] + fn rlp_stream() { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let out = stream.out().unwrap(); + assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + } + + #[test] + fn rlp_stream_list() { + let mut stream = RlpStream::new_list(3); + stream.append_list(0); + stream.append_list(1).append_list(0); + stream.append_list(2).append_list(0).append_list(1).append_list(0); + let out = stream.out().unwrap(); + assert_eq!(out, vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]); + } + + struct DTestPair(T, Vec) where T: rlp::Decodable + fmt::Debug + cmp::Eq; + + fn run_decode_tests(tests: Vec>) where T: rlp::Decodable + fmt::Debug + cmp::Eq { + for t in &tests { + let res: T = rlp::decode(&t.1).unwrap(); + assert_eq!(res, t.0); + } + } + + #[test] + fn decode_u8() { + let tests = vec![ + DTestPair(0u8, vec![0u8]), + DTestPair(15, vec![15]), + DTestPair(55, vec![55]), + DTestPair(56, vec![56]), + DTestPair(0x7f, vec![0x7f]), + DTestPair(0x80, vec![0x81, 0x80]), + DTestPair(0xff, vec![0x81, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u16() { + let tests = vec![ + DTestPair(0u16, vec![0u8]), + DTestPair(0x100, vec![0x82, 0x01, 0x00]), + DTestPair(0xffff, vec![0x82, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u32() { + let tests = vec![ + DTestPair(0u32, vec![0u8]), + DTestPair(0x10000, vec![0x83, 0x01, 0x00, 0x00]), + DTestPair(0xffffff, vec![0x83, 0xff, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u64() { + let tests = vec![ + DTestPair(0u64, vec![0u8]), + DTestPair(0x1000000, vec![0x84, 0x01, 0x00, 0x00, 0x00]), + DTestPair(0xFFFFFFFF, vec![0x84, 0xff, 0xff, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u256() { + let tests = vec![ + DTestPair(U256::from(0u64), vec![0x80u8]), + DTestPair(U256::from(0x1000000u64), vec![0x84, 0x01, 0x00, 0x00, 0x00]), + DTestPair(U256::from(0xffffffffu64), vec![0x84, 0xff, 0xff, 0xff, 0xff]), + DTestPair(U256::from_str("8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0").unwrap(), + vec![0xa0, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x09, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0]) + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_str() { + let tests = vec![ + DTestPair("cat".to_string(), vec![0x83, b'c', b'a', b't']), + DTestPair("dog".to_string(), vec![0x83, b'd', b'o', b'g']), + DTestPair("Marek".to_string(), vec![0x85, b'M', b'a', b'r', b'e', b'k']), + DTestPair("".to_string(), vec![0x80]), + DTestPair("Lorem ipsum dolor sit amet, consectetur adipisicing elit".to_string(), + vec![0xb8, 0x38, b'L', b'o', b'r', b'e', b'm', b' ', b'i', + b'p', b's', b'u', b'm', b' ', b'd', b'o', b'l', b'o', b'r', + b' ', b's', b'i', b't', b' ', b'a', b'm', b'e', b't', b',', + b' ', b'c', b'o', b'n', b's', b'e', b'c', b't', b'e', b't', + b'u', b'r', b' ', b'a', b'd', b'i', b'p', b'i', b's', b'i', + b'c', b'i', b'n', b'g', b' ', b'e', b'l', b'i', b't']) + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_u8() { + let tests = vec![ + DTestPair(vec![] as Vec, vec![0xc0]), + DTestPair(vec![15u8], vec![0xc1, 0x0f]), + DTestPair(vec![1u8, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_u64() { + let tests = vec![ + DTestPair(vec![], vec![0xc0]), + DTestPair(vec![15u64], vec![0xc1, 0x0f]), + DTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + DTestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_str() { + let tests = vec![ + DTestPair(vec!["cat".to_string(), "dog".to_string()], vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']) + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_of_vectors_str() { + let tests = vec![ + DTestPair(vec![vec!["cat".to_string()]], vec![0xc5, 0xc4, 0x83, b'c', b'a', b't']) + ]; + run_decode_tests(tests); + } } diff --git a/src/uint.rs b/src/uint.rs new file mode 100644 index 000000000..bc333631b --- /dev/null +++ b/src/uint.rs @@ -0,0 +1,610 @@ +// taken from Rust Bitcoin Library (https://github.com/apoelstra/rust-bitcoin) +// original author: Andrew Poelstra + +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! Big unsigned integer types +///! +///! Implementation of a various large-but-fixed sized unsigned integer types. +///! The functions here are designed to be fast. +///! + +use std::fmt; +use std::cmp::{Ord, PartialOrd, Ordering}; +use std::ops::*; +use std::str::FromStr; +use rustc_serialize::hex::{FromHex, FromHexError}; + +macro_rules! impl_map_from { + ($thing:ident, $from:ty, $to:ty) => { + impl From<$from> for $thing { + fn from(value: $from) -> $thing { + From::from(value as $to) + } + } + } +} + +macro_rules! impl_array_newtype { + ($thing:ident, $ty:ty, $len:expr) => { + impl $thing { + #[inline] + /// Converts the object to a raw pointer + pub fn as_ptr(&self) -> *const $ty { + let &$thing(ref dat) = self; + dat.as_ptr() + } + + #[inline] + /// Converts the object to a mutable raw pointer + pub fn as_mut_ptr(&mut self) -> *mut $ty { + let &mut $thing(ref mut dat) = self; + dat.as_mut_ptr() + } + + #[inline] + /// Returns the length of the object as an array + pub fn len(&self) -> usize { $len } + + #[inline] + /// Returns whether the object, as an array, is empty. Always false. + pub fn is_empty(&self) -> bool { false } + } + + impl<'a> From<&'a [$ty]> for $thing { + fn from(data: &'a [$ty]) -> $thing { + assert_eq!(data.len(), $len); + unsafe { + use std::intrinsics::copy_nonoverlapping; + use std::mem; + let mut ret: $thing = mem::uninitialized(); + copy_nonoverlapping(data.as_ptr(), + ret.as_mut_ptr(), + mem::size_of::<$thing>()); + ret + } + } + } + + impl Index for $thing { + type Output = $ty; + + #[inline] + fn index(&self, index: usize) -> &$ty { + let &$thing(ref dat) = self; + &dat[index] + } + } + + impl_index_newtype!($thing, $ty); + + impl PartialEq for $thing { + #[inline] + fn eq(&self, other: &$thing) -> bool { + &self[..] == &other[..] + } + } + + impl Eq for $thing {} + + impl Clone for $thing { + #[inline] + fn clone(&self) -> $thing { + $thing::from(&self[..]) + } + } + + impl Copy for $thing {} + } +} + +macro_rules! impl_index_newtype { + ($thing:ident, $ty:ty) => { + impl Index> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: Range) -> &[$ty] { + &self.0[index] + } + } + + impl Index> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: RangeTo) -> &[$ty] { + &self.0[index] + } + } + + impl Index> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: RangeFrom) -> &[$ty] { + &self.0[index] + } + } + + impl Index for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, _: RangeFull) -> &[$ty] { + &self.0[..] + } + } + } +} + +macro_rules! construct_uint { + ($name:ident, $n_words:expr) => ( + /// Little-endian large integer type + pub struct $name(pub [u64; $n_words]); + impl_array_newtype!($name, u64, $n_words); + + impl $name { + /// Conversion to u32 + #[inline] + fn low_u32(&self) -> u32 { + let &$name(ref arr) = self; + arr[0] as u32 + } + + /// Return the least number of bits needed to represent the number + #[inline] + pub fn bits(&self) -> usize { + let &$name(ref arr) = self; + for i in 1..$n_words { + if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; } + } + 0x40 - arr[0].leading_zeros() as usize + } + + #[inline] + pub fn bit(&self, index: usize) -> bool { + let &$name(ref arr) = self; + arr[index / 64] & (1 << (index % 64)) != 0 + } + + #[inline] + pub fn byte(&self, index: usize) -> u8 { + let &$name(ref arr) = self; + (arr[index / 8] >> ((index % 8)) * 8) as u8 + } + + /// Multiplication by u32 + fn mul_u32(self, other: u32) -> $name { + let $name(ref arr) = self; + let mut carry = [0u64; $n_words]; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + let upper = other as u64 * (arr[i] >> 32); + let lower = other as u64 * (arr[i] & 0xFFFFFFFF); + if i < 3 { + carry[i + 1] += upper >> 32; + } + ret[i] = lower + (upper << 32); + } + $name(ret) + $name(carry) + } + } + + impl From for $name { + fn from(value: u64) -> $name { + let mut ret = [0; $n_words]; + ret[0] = value; + $name(ret) + } + } + + impl_map_from!($name, u8, u64); + impl_map_from!($name, u16, u64); + impl_map_from!($name, u32, u64); + + impl<'a> From<&'a [u8]> for $name { + fn from(bytes: &[u8]) -> $name { + assert!($n_words * 8 >= bytes.len()); + + let mut ret = [0; $n_words]; + for i in 0..bytes.len() { + let rev = bytes.len() - 1 - i; + let pos = rev / 8; + ret[pos] += (bytes[i] as u64) << (rev % 8) * 8; + } + $name(ret) + } + } + + impl FromStr for $name { + type Err = FromHexError; + + fn from_str(value: &str) -> Result<$name, Self::Err> { + let bytes: &[u8] = &try!(value.from_hex()); + Ok(From::from(bytes)) + } + } + + impl Add<$name> for $name { + type Output = $name; + + fn add(self, other: $name) -> $name { + let $name(ref me) = self; + let $name(ref you) = other; + let mut ret = [0u64; $n_words]; + let mut carry = [0u64; $n_words]; + let mut b_carry = false; + for i in 0..$n_words { + ret[i] = me[i].wrapping_add(you[i]); + if i < $n_words - 1 && ret[i] < me[i] { + carry[i + 1] = 1; + b_carry = true; + } + } + if b_carry { $name(ret) + $name(carry) } else { $name(ret) } + } + } + + impl Sub<$name> for $name { + type Output = $name; + + #[inline] + fn sub(self, other: $name) -> $name { + self + !other + From::from(1u64) + } + } + + impl Mul<$name> for $name { + type Output = $name; + + fn mul(self, other: $name) -> $name { + let mut me = self; + // TODO: be more efficient about this + for i in 0..(2 * $n_words) { + me = (me + me.mul_u32((other >> (32 * i)).low_u32())) << (32 * i); + } + me + } + } + + impl Div<$name> for $name { + type Output = $name; + + fn div(self, other: $name) -> $name { + let mut sub_copy = self; + let mut shift_copy = other; + let mut ret = [0u64; $n_words]; + + let my_bits = self.bits(); + let your_bits = other.bits(); + + // Check for division by 0 + assert!(your_bits != 0); + + // Early return in case we are dividing by a larger number than us + if my_bits < your_bits { + return $name(ret); + } + + // Bitwise long division + let mut shift = my_bits - your_bits; + shift_copy = shift_copy << shift; + loop { + if sub_copy >= shift_copy { + ret[shift / 64] |= 1 << (shift % 64); + sub_copy = sub_copy - shift_copy; + } + shift_copy = shift_copy >> 1; + if shift == 0 { break; } + shift -= 1; + } + + $name(ret) + } + } + + impl BitAnd<$name> for $name { + type Output = $name; + + #[inline] + fn bitand(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] & arr2[i]; + } + $name(ret) + } + } + + impl BitXor<$name> for $name { + type Output = $name; + + #[inline] + fn bitxor(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] ^ arr2[i]; + } + $name(ret) + } + } + + impl BitOr<$name> for $name { + type Output = $name; + + #[inline] + fn bitor(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] | arr2[i]; + } + $name(ret) + } + } + + impl Not for $name { + type Output = $name; + + #[inline] + fn not(self) -> $name { + let $name(ref arr) = self; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = !arr[i]; + } + $name(ret) + } + } + + impl Shl for $name { + type Output = $name; + + fn shl(self, shift: usize) -> $name { + let $name(ref original) = self; + let mut ret = [0u64; $n_words]; + let word_shift = shift / 64; + let bit_shift = shift % 64; + for i in 0..$n_words { + // Shift + if bit_shift < 64 && i + word_shift < $n_words { + ret[i + word_shift] += original[i] << bit_shift; + } + // Carry + if bit_shift > 0 && i + word_shift + 1 < $n_words { + ret[i + word_shift + 1] += original[i] >> (64 - bit_shift); + } + } + $name(ret) + } + } + + impl Shr for $name { + type Output = $name; + + fn shr(self, shift: usize) -> $name { + let $name(ref original) = self; + let mut ret = [0u64; $n_words]; + let word_shift = shift / 64; + let bit_shift = shift % 64; + for i in word_shift..$n_words { + // Shift + ret[i - word_shift] += original[i] >> bit_shift; + // Carry + if bit_shift > 0 && i < $n_words - 1 { + ret[i - word_shift] += original[i + 1] << (64 - bit_shift); + } + } + $name(ret) + } + } + + impl Ord for $name { + fn cmp(&self, other: &$name) -> Ordering { + let &$name(ref me) = self; + let &$name(ref you) = other; + for i in 0..$n_words { + if me[$n_words - 1 - i] < you[$n_words - 1 - i] { return Ordering::Less; } + if me[$n_words - 1 - i] > you[$n_words - 1 - i] { return Ordering::Greater; } + } + Ordering::Equal + } + } + + impl PartialOrd for $name { + fn partial_cmp(&self, other: &$name) -> Option { + Some(self.cmp(other)) + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let &$name(ref data) = self; + try!(write!(f, "0x")); + for ch in data.iter().rev() { + try!(write!(f, "{:02x}", ch)); + } + Ok(()) + } + } + ); +} + +construct_uint!(U256, 4); +construct_uint!(U128, 2); + +impl From for U256 { + fn from(value: U128) -> U256 { + let U128(ref arr) = value; + let mut ret = [0; 4]; + ret[0] = arr[0]; + ret[1] = arr[1]; + U256(ret) + } +} + +#[cfg(test)] +mod tests { + use uint::U256; + use std::str::FromStr; + + #[test] + pub fn uint256_from() { + let e = U256([10, 0, 0, 0]); + + // test unsigned initialization + let ua = U256::from(10u8); + let ub = U256::from(10u16); + let uc = U256::from(10u32); + let ud = U256::from(10u64); + assert_eq!(e, ua); + assert_eq!(e, ub); + assert_eq!(e, uc); + assert_eq!(e, ud); + + // test initialization from bytes + let va = U256::from(&[10u8][..]); + assert_eq!(e, va); + + // more tests for initialization from bytes + assert_eq!(U256([0x1010, 0, 0, 0]), U256::from(&[0x10u8, 0x10][..])); + assert_eq!(U256([0x12f0, 0, 0, 0]), U256::from(&[0x12u8, 0xf0][..])); + assert_eq!(U256([0x12f0, 0, 0, 0]), U256::from(&[0, 0x12u8, 0xf0][..])); + assert_eq!(U256([0x12f0, 0 , 0, 0]), U256::from(&[0, 0, 0, 0, 0, 0, 0, 0x12u8, 0xf0][..])); + assert_eq!(U256([0x12f0, 1 , 0, 0]), U256::from(&[1, 0, 0, 0, 0, 0, 0, 0x12u8, 0xf0][..])); + assert_eq!(U256([0x12f0, 1 , 0x0910203040506077, 0x8090a0b0c0d0e0f0]), U256::from(&[ + 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0x09, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x77, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0x12u8, 0xf0][..])); + assert_eq!(U256([0x00192437100019fa, 0x243710, 0, 0]), U256::from(&[ + 0x24u8, 0x37, 0x10, + 0, 0x19, 0x24, 0x37, 0x10, 0, 0x19, 0xfa][..])); + + // test initializtion from string + let sa = U256::from_str("0a").unwrap(); + assert_eq!(e, sa); + assert_eq!(U256([0x1010, 0, 0, 0]), U256::from_str("1010").unwrap()); + assert_eq!(U256([0x12f0, 0, 0, 0]), U256::from_str("12f0").unwrap()); + assert_eq!(U256([0x12f0, 0, 0, 0]), U256::from_str("12f0").unwrap()); + assert_eq!(U256([0x12f0, 0 , 0, 0]), U256::from_str("0000000012f0").unwrap()); + assert_eq!(U256([0x12f0, 1 , 0, 0]), U256::from_str("0100000000000012f0").unwrap()); + assert_eq!(U256([0x12f0, 1 , 0x0910203040506077, 0x8090a0b0c0d0e0f0]), U256::from_str("8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0").unwrap()); + } + + #[test] + pub fn uint256_bits_test() { + assert_eq!(U256::from(0u64).bits(), 0); + assert_eq!(U256::from(255u64).bits(), 8); + assert_eq!(U256::from(256u64).bits(), 9); + assert_eq!(U256::from(300u64).bits(), 9); + assert_eq!(U256::from(60000u64).bits(), 16); + assert_eq!(U256::from(70000u64).bits(), 17); + + //// Try to read the following lines out loud quickly + let mut shl = U256::from(70000u64); + shl = shl << 100; + assert_eq!(shl.bits(), 117); + shl = shl << 100; + assert_eq!(shl.bits(), 217); + shl = shl << 100; + assert_eq!(shl.bits(), 0); + + //// Bit set check + //// 01010 + assert!(!U256::from(10u8).bit(0)); + assert!(U256::from(10u8).bit(1)); + assert!(!U256::from(10u8).bit(2)); + assert!(U256::from(10u8).bit(3)); + assert!(!U256::from(10u8).bit(4)); + + //// byte check + assert_eq!(U256::from(10u8).byte(0), 10); + assert_eq!(U256::from(0xffu64).byte(0), 0xff); + assert_eq!(U256::from(0xffu64).byte(1), 0); + assert_eq!(U256::from(0x01ffu64).byte(0), 0xff); + assert_eq!(U256::from(0x01ffu64).byte(1), 0x1); + assert_eq!(U256([0u64, 0xfc, 0, 0]).byte(8), 0xfc); + assert_eq!(U256([0u64, 0, 0, u64::max_value()]).byte(31), 0xff); + assert_eq!(U256([0u64, 0, 0, (u64::max_value() >> 8) + 1]).byte(31), 0x01); + } + + #[test] + pub fn uint256_comp_test() { + let small = U256([10u64, 0, 0, 0]); + let big = U256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); + let bigger = U256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); + let biggest = U256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]); + + assert!(small < big); + assert!(big < bigger); + assert!(bigger < biggest); + assert!(bigger <= biggest); + assert!(biggest <= biggest); + assert!(bigger >= big); + assert!(bigger >= small); + assert!(small <= small); + } + + #[test] + pub fn uint256_arithmetic_test() { + let init = U256::from(0xDEADBEEFDEADBEEFu64); + let copy = init; + + let add = init + copy; + assert_eq!(add, U256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0])); + // Bitshifts + let shl = add << 88; + assert_eq!(shl, U256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0])); + let shr = shl >> 40; + assert_eq!(shr, U256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0])); + // Increment + let incr = shr + U256::from(1u64); + assert_eq!(incr, U256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0])); + // Subtraction + let sub = incr - init; + assert_eq!(sub, U256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); + // Multiplication + let mult = sub.mul_u32(300); + assert_eq!(mult, U256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0])); + // Division + assert_eq!(U256::from(105u8) / U256::from(5u8), U256::from(21u8)); + let div = mult / U256::from(300u16); + assert_eq!(div, U256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); + //// TODO: bit inversion + } + + #[test] + pub fn uint256_extreme_bitshift_test() { + //// Shifting a u64 by 64 bits gives an undefined value, so make sure that + //// we're doing the Right Thing here + let init = U256::from(0xDEADBEEFDEADBEEFu64); + + assert_eq!(init << 64, U256([0, 0xDEADBEEFDEADBEEF, 0, 0])); + let add = (init << 64) + init; + assert_eq!(add, U256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); + assert_eq!(add >> 0, U256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); + assert_eq!(add << 0, U256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); + assert_eq!(add >> 64, U256([0xDEADBEEFDEADBEEF, 0, 0, 0])); + assert_eq!(add << 64, U256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0])); + } +} + diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 000000000..ce891beba --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,34 @@ + +use std::ptr; + +pub trait InsertSlice { + fn insert_slice(&mut self, index: usize, elements: &[T]); +} + +/// based on `insert` function implementation from standard library +impl InsertSlice for Vec { + fn insert_slice(&mut self, index: usize, elements: &[T]) { + let e_len = elements.len(); + if e_len == 0 { + return; + } + + let len = self.len(); + assert!(index <= len); + + // space for the new element + self.reserve(e_len); + + unsafe { + { + let p = self.as_mut_ptr().offset(index as isize); + let ep = elements.as_ptr().offset(0); + // shift everything by e_len, to make space + ptr::copy(p, p.offset(e_len as isize), len - index); + // write new element + ptr::copy(ep, p, e_len); + } + self.set_len(len + e_len); + } + } +}