Merge branch 'master' of github.com:gavofyork/ethcore-util into network
This commit is contained in:
commit
2681f6290f
@ -1,7 +1,7 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- 1.4.0
|
||||
- beta
|
||||
|
||||
os:
|
||||
#- linux
|
||||
|
34
src/bytes.rs
34
src/bytes.rs
@ -23,7 +23,7 @@
|
||||
//! use util::bytes::FromBytes;
|
||||
//!
|
||||
//! let a = String::from_bytes(&[b'd', b'o', b'g']);
|
||||
//! let b = u8::from_bytes(&[0xfa]);
|
||||
//! let b = u16::from_bytes(&[0xfa]);
|
||||
//! let c = u64::from_bytes(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
|
||||
//! }
|
||||
//!
|
||||
@ -104,28 +104,6 @@ impl ToBytes for String {
|
||||
fn to_bytes_len(&self) -> usize { self.len() }
|
||||
}
|
||||
|
||||
impl ToBytes for u8 {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
0 => vec![],
|
||||
_ => vec![*self]
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes_len(&self) -> usize {
|
||||
match *self {
|
||||
0 => 0,
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
fn first_byte(&self) -> Option<u8> {
|
||||
match *self {
|
||||
0 => None,
|
||||
_ => Some(*self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytes for u64 {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut res= vec![];
|
||||
@ -223,15 +201,6 @@ impl FromBytes for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytes for u8 {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<u8> {
|
||||
match bytes.len() {
|
||||
0 => Ok(0),
|
||||
_ => Ok(bytes[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytes for u64 {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<u64> {
|
||||
match bytes.len() {
|
||||
@ -293,3 +262,4 @@ impl <T>FromBytes for T where T: FixedHash {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use hash::*;
|
||||
use bytes::Bytes;
|
||||
use bytes::*;
|
||||
|
||||
pub trait HashDB {
|
||||
/// Look up a given hash into the bytes that hash to it, returning None if the
|
||||
@ -17,7 +17,7 @@ pub trait HashDB {
|
||||
/// assert_eq!(m.lookup(&hash).unwrap(), hello_bytes);
|
||||
/// }
|
||||
/// ```
|
||||
fn lookup(&self, key: &H256) -> Option<Bytes>;
|
||||
fn lookup(&self, key: &H256) -> Option<&[u8]>;
|
||||
|
||||
/// Check for the existance of a hash-key.
|
||||
///
|
||||
@ -57,6 +57,9 @@ pub trait HashDB {
|
||||
/// ```
|
||||
fn insert(&mut self, value: &[u8]) -> H256;
|
||||
|
||||
/// Like `insert()` , except you provide the key and the data is all moved.
|
||||
fn emplace(&mut self, key: H256, value: Bytes);
|
||||
|
||||
/// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may
|
||||
/// happen without the data being eventually being inserted into the DB.
|
||||
///
|
||||
|
@ -33,6 +33,9 @@ pub mod overlaydb;
|
||||
pub mod math;
|
||||
pub mod chainfilter;
|
||||
pub mod crypto;
|
||||
pub mod triehash;
|
||||
pub mod trie;
|
||||
pub mod nibbleslice;
|
||||
|
||||
pub mod network;
|
||||
|
||||
|
@ -37,6 +37,12 @@ use std::collections::HashMap;
|
||||
/// m.kill(&k);
|
||||
/// assert!(!m.exists(&k));
|
||||
///
|
||||
/// m.kill(&k);
|
||||
/// assert!(!m.exists(&k));
|
||||
///
|
||||
/// m.insert(d);
|
||||
/// assert!(!m.exists(&k));
|
||||
|
||||
/// m.insert(d);
|
||||
/// assert!(m.exists(&k));
|
||||
/// assert_eq!(m.lookup(&k).unwrap(), d);
|
||||
@ -100,12 +106,22 @@ impl MemoryDB {
|
||||
mem::swap(&mut self.data, &mut data);
|
||||
data
|
||||
}
|
||||
|
||||
pub fn denote(&self, key: &H256, value: Bytes) -> &(Bytes, i32) {
|
||||
if self.data.get(&key) == None {
|
||||
unsafe {
|
||||
let p = &self.data as *const HashMap<H256, (Bytes, i32)> as *mut HashMap<H256, (Bytes, i32)>;
|
||||
(*p).insert(key.clone(), (value, 0));
|
||||
}
|
||||
}
|
||||
self.data.get(key).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDB for MemoryDB {
|
||||
fn lookup(&self, key: &H256) -> Option<Bytes> {
|
||||
fn lookup(&self, key: &H256) -> Option<&[u8]> {
|
||||
match self.data.get(key) {
|
||||
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
|
||||
Some(&(ref d, rc)) if rc > 0 => Some(d),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@ -120,9 +136,9 @@ impl HashDB for MemoryDB {
|
||||
fn insert(&mut self, value: &[u8]) -> H256 {
|
||||
let key = value.sha3();
|
||||
if match self.data.get_mut(&key) {
|
||||
Some(&mut (ref mut old_value, ref mut rc @ 0)) => {
|
||||
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => {
|
||||
*old_value = From::from(value.bytes());
|
||||
*rc = 1;
|
||||
*rc += 1;
|
||||
false
|
||||
},
|
||||
Some(&mut (_, ref mut x)) => { *x += 1; false } ,
|
||||
@ -133,6 +149,20 @@ impl HashDB for MemoryDB {
|
||||
key
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: H256, value: Bytes) {
|
||||
match self.data.get_mut(&key) {
|
||||
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => {
|
||||
*old_value = value;
|
||||
*rc += 1;
|
||||
return;
|
||||
},
|
||||
Some(&mut (_, ref mut x)) => { *x += 1; return; } ,
|
||||
None => {},
|
||||
}
|
||||
// ... None falls through into...
|
||||
self.data.insert(key, (value, 1));
|
||||
}
|
||||
|
||||
fn kill(&mut self, key: &H256) {
|
||||
if match self.data.get_mut(key) {
|
||||
Some(&mut (_, ref mut x)) => { *x -= 1; false }
|
||||
@ -143,3 +173,20 @@ impl HashDB for MemoryDB {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memorydb_denote() {
|
||||
let mut m = MemoryDB::new();
|
||||
let hello_bytes = b"Hello world!";
|
||||
let hash = m.insert(hello_bytes);
|
||||
assert_eq!(m.lookup(&hash).unwrap(), b"Hello world!");
|
||||
|
||||
for _ in 0..1000 {
|
||||
let r = H256::random();
|
||||
let k = r.sha3();
|
||||
let &(ref v, ref rc) = m.denote(&k, r.bytes().to_vec());
|
||||
assert_eq!(v, &r.bytes());
|
||||
assert_eq!(*rc, 0);
|
||||
}
|
||||
|
||||
assert_eq!(m.lookup(&hash).unwrap(), b"Hello world!");
|
||||
}
|
195
src/nibbleslice.rs
Normal file
195
src/nibbleslice.rs
Normal file
@ -0,0 +1,195 @@
|
||||
//! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets.
|
||||
use std::cmp::*;
|
||||
use bytes::*;
|
||||
|
||||
/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets.
|
||||
///
|
||||
/// This is an immutable struct. No operations actually change it.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// extern crate ethcore_util;
|
||||
/// use ethcore_util::nibbleslice::*;
|
||||
/// fn main() {
|
||||
/// let d1 = &[0x01u8, 0x23, 0x45];
|
||||
/// let d2 = &[0x34u8, 0x50, 0x12];
|
||||
/// let d3 = &[0x00u8, 0x12];
|
||||
/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5
|
||||
/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2
|
||||
/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2
|
||||
/// assert!(n1 > n3); // 0,1,2,... > 0,1,2
|
||||
/// assert!(n1 < n2); // 0,... < 3,...
|
||||
/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2
|
||||
/// assert!(n1.starts_with(&n3));
|
||||
/// assert_eq!(n1.common_prefix(&n3), 3);
|
||||
/// assert_eq!(n2.mid(3).common_prefix(&n1), 3);
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, Ord)]
|
||||
pub struct NibbleSlice<'a> {
|
||||
data: &'a [u8],
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> NibbleSlice<'a> {
|
||||
/// Create a new nibble slice with the given byte-slice.
|
||||
pub fn new(data: &[u8]) -> NibbleSlice { NibbleSlice::new_offset(data, 0) }
|
||||
|
||||
/// Create a new nibble slice with the given byte-slice with a nibble offset.
|
||||
pub fn new_offset(data: &'a [u8], offset: usize) -> NibbleSlice { NibbleSlice{data: data, offset: offset} }
|
||||
|
||||
/// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`).
|
||||
pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) {
|
||||
(Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32)
|
||||
}
|
||||
|
||||
/// Is this an empty slice?
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
|
||||
/// Get the length (in nibbles, naturally) of this slice.
|
||||
pub fn len(&self) -> usize { self.data.len() * 2 - self.offset }
|
||||
|
||||
/// Get the nibble at position `i`.
|
||||
pub fn at(&self, i: usize) -> u8 {
|
||||
if (self.offset + i) & 1 == 1 {
|
||||
self.data[(self.offset + i) / 2] & 15u8
|
||||
}
|
||||
else {
|
||||
self.data[(self.offset + i) / 2] >> 4
|
||||
}
|
||||
}
|
||||
|
||||
/// Return object which represents a view on to this slice (further) offset by `i` nibbles.
|
||||
pub fn mid(&self, i: usize) -> Self { NibbleSlice{ data: self.data, offset: self.offset + i} }
|
||||
|
||||
/// Do we start with the same nibbles as the whole of `them`?
|
||||
pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() }
|
||||
|
||||
/// How many of the same nibbles at the beginning do we match with `them`?
|
||||
pub fn common_prefix(&self, them: &Self) -> usize {
|
||||
let s = min(self.len(), them.len());
|
||||
let mut i = 0usize;
|
||||
while i < s {
|
||||
if self.at(i) != them.at(i) { break; }
|
||||
i += 1;
|
||||
}
|
||||
i
|
||||
}
|
||||
|
||||
pub fn encoded(&self, is_leaf: bool) -> Bytes {
|
||||
let l = self.len();
|
||||
let mut r = Bytes::with_capacity(l / 2 + 1);
|
||||
let mut i = l % 2;
|
||||
r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0});
|
||||
while i < l {
|
||||
r.push(self.at(i) * 16 + self.at(i + 1));
|
||||
i += 2;
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for NibbleSlice<'a> {
|
||||
fn eq(&self, them: &Self) -> bool {
|
||||
self.len() == them.len() && self.starts_with(them)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd for NibbleSlice<'a> {
|
||||
fn partial_cmp(&self, them: &Self) -> Option<Ordering> {
|
||||
let s = min(self.len(), them.len());
|
||||
let mut i = 0usize;
|
||||
while i < s {
|
||||
match self.at(i).partial_cmp(&them.at(i)).unwrap() {
|
||||
Ordering::Less => return Some(Ordering::Less),
|
||||
Ordering::Greater => return Some(Ordering::Greater),
|
||||
_ => i += 1,
|
||||
}
|
||||
}
|
||||
self.len().partial_cmp(&them.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NibbleSlice;
|
||||
static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45];
|
||||
|
||||
#[test]
|
||||
fn basics() {
|
||||
let n = NibbleSlice::new(D);
|
||||
assert_eq!(n.len(), 6);
|
||||
assert!(!n.is_empty());
|
||||
|
||||
let n = NibbleSlice::new_offset(D, 6);
|
||||
assert!(n.is_empty());
|
||||
|
||||
let n = NibbleSlice::new_offset(D, 3);
|
||||
assert_eq!(n.len(), 3);
|
||||
for i in 0..3 {
|
||||
assert_eq!(n.at(i), i as u8 + 3);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mid() {
|
||||
let n = NibbleSlice::new(D);
|
||||
let m = n.mid(2);
|
||||
for i in 0..4 {
|
||||
assert_eq!(m.at(i), i as u8 + 2);
|
||||
}
|
||||
let m = n.mid(3);
|
||||
for i in 0..3 {
|
||||
assert_eq!(m.at(i), i as u8 + 3);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoded() {
|
||||
let n = NibbleSlice::new(D);
|
||||
assert_eq!(n.encoded(false), &[0x00, 0x01, 0x23, 0x45]);
|
||||
assert_eq!(n.encoded(true), &[0x20, 0x01, 0x23, 0x45]);
|
||||
assert_eq!(n.mid(1).encoded(false), &[0x11, 0x23, 0x45]);
|
||||
assert_eq!(n.mid(1).encoded(true), &[0x31, 0x23, 0x45]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_encoded() {
|
||||
let n = NibbleSlice::new(D);
|
||||
assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45]));
|
||||
assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45]));
|
||||
assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45]));
|
||||
assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared() {
|
||||
let n = NibbleSlice::new(D);
|
||||
|
||||
let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67];
|
||||
let m = NibbleSlice::new(other);
|
||||
|
||||
assert_eq!(n.common_prefix(&m), 4);
|
||||
assert_eq!(m.common_prefix(&n), 4);
|
||||
assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3);
|
||||
assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0);
|
||||
assert_eq!(n.common_prefix(&m.mid(4)), 6);
|
||||
assert!(!n.starts_with(&m.mid(4)));
|
||||
assert!(m.mid(4).starts_with(&n));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compare() {
|
||||
let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45];
|
||||
let n = NibbleSlice::new(D);
|
||||
let m = NibbleSlice::new(other);
|
||||
|
||||
assert!(n != m);
|
||||
assert!(n > m);
|
||||
assert!(m < n);
|
||||
|
||||
assert!(n == m.mid(4));
|
||||
assert!(n >= m.mid(4));
|
||||
assert!(n <= m.mid(4));
|
||||
}
|
||||
}
|
@ -130,24 +130,24 @@ impl OverlayDB {
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&payload.1);
|
||||
s.append(&payload.0);
|
||||
self.backing.put(&key.bytes(), &s.out().unwrap()).expect("Low-level database error. Some issue with your hard disk?");
|
||||
self.backing.put(&key.bytes(), &s.out()).expect("Low-level database error. Some issue with your hard disk?");
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDB for OverlayDB {
|
||||
fn lookup(&self, key: &H256) -> Option<Bytes> {
|
||||
fn lookup(&self, key: &H256) -> Option<&[u8]> {
|
||||
// return ok if positive; if negative, check backing - might be enough references there to make
|
||||
// it positive again.
|
||||
let k = self.overlay.raw(key);
|
||||
match k {
|
||||
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
|
||||
Some(&(ref d, rc)) if rc > 0 => Some(d),
|
||||
_ => {
|
||||
let memrc = k.map(|&(_, rc)| rc).unwrap_or(0);
|
||||
match self.payload(key) {
|
||||
Some(x) => {
|
||||
let (d, rc) = x;
|
||||
if rc as i32 + memrc > 0 {
|
||||
Some(d)
|
||||
Some(&self.overlay.denote(key, d).0)
|
||||
}
|
||||
else {
|
||||
None
|
||||
@ -186,6 +186,7 @@ impl HashDB for OverlayDB {
|
||||
}
|
||||
}
|
||||
fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) }
|
||||
fn emplace(&mut self, key: H256, value: Bytes) { self.overlay.emplace(key, value); }
|
||||
fn kill(&mut self, key: &H256) { self.overlay.kill(key); }
|
||||
}
|
||||
|
||||
@ -193,7 +194,7 @@ impl HashDB for OverlayDB {
|
||||
fn overlaydb_overlay_insert_and_kill() {
|
||||
let mut trie = OverlayDB::new_temp();
|
||||
let h = trie.insert(b"hello world");
|
||||
assert_eq!(trie.lookup(&h), Some(b"hello world".to_vec()));
|
||||
assert_eq!(trie.lookup(&h).unwrap(), b"hello world");
|
||||
trie.kill(&h);
|
||||
assert_eq!(trie.lookup(&h), None);
|
||||
}
|
||||
@ -202,11 +203,11 @@ fn overlaydb_overlay_insert_and_kill() {
|
||||
fn overlaydb_backing_insert_revert() {
|
||||
let mut trie = OverlayDB::new_temp();
|
||||
let h = trie.insert(b"hello world");
|
||||
assert_eq!(trie.lookup(&h), Some(b"hello world".to_vec()));
|
||||
assert_eq!(trie.lookup(&h).unwrap(), b"hello world");
|
||||
trie.commit().unwrap();
|
||||
assert_eq!(trie.lookup(&h), Some(b"hello world".to_vec()));
|
||||
assert_eq!(trie.lookup(&h).unwrap(), b"hello world");
|
||||
trie.revert();
|
||||
assert_eq!(trie.lookup(&h), Some(b"hello world".to_vec()));
|
||||
assert_eq!(trie.lookup(&h).unwrap(), b"hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -230,7 +231,7 @@ fn overlaydb_backing_kill_revert() {
|
||||
trie.kill(&h);
|
||||
assert_eq!(trie.lookup(&h), None);
|
||||
trie.revert();
|
||||
assert_eq!(trie.lookup(&h), Some(b"hello world".to_vec()));
|
||||
assert_eq!(trie.lookup(&h).unwrap(), b"hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -248,29 +249,29 @@ fn overlaydb_negative() {
|
||||
fn overlaydb_complex() {
|
||||
let mut trie = OverlayDB::new_temp();
|
||||
let hfoo = trie.insert(b"foo");
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
let hbar = trie.insert(b"bar");
|
||||
assert_eq!(trie.lookup(&hbar), Some(b"bar".to_vec()));
|
||||
assert_eq!(trie.lookup(&hbar).unwrap(), b"bar");
|
||||
trie.commit().unwrap();
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hbar), Some(b"bar".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
assert_eq!(trie.lookup(&hbar).unwrap(), b"bar");
|
||||
trie.insert(b"foo"); // two refs
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
trie.commit().unwrap();
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hbar), Some(b"bar".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
assert_eq!(trie.lookup(&hbar).unwrap(), b"bar");
|
||||
trie.kill(&hbar); // zero refs - delete
|
||||
assert_eq!(trie.lookup(&hbar), None);
|
||||
trie.kill(&hfoo); // one ref - keep
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
trie.commit().unwrap();
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
trie.kill(&hfoo); // zero ref - would delete, but...
|
||||
assert_eq!(trie.lookup(&hfoo), None);
|
||||
trie.insert(b"foo"); // one ref - keep after all.
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
trie.commit().unwrap();
|
||||
assert_eq!(trie.lookup(&hfoo), Some(b"foo".to_vec()));
|
||||
assert_eq!(trie.lookup(&hfoo).unwrap(), b"foo");
|
||||
trie.kill(&hfoo); // zero ref - delete
|
||||
assert_eq!(trie.lookup(&hfoo), None);
|
||||
trie.commit().unwrap(); //
|
||||
|
1128
src/rlp.rs
1128
src/rlp.rs
File diff suppressed because it is too large
Load Diff
160
src/trie.rs
Normal file
160
src/trie.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use memorydb::*;
|
||||
use hashdb::*;
|
||||
use hash::*;
|
||||
use nibbleslice::*;
|
||||
use bytes::*;
|
||||
use rlp::*;
|
||||
|
||||
pub const NULL_RLP: [u8; 1] = [0x80; 1];
|
||||
pub const SHA3_NULL_RLP: H256 = H256( [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21] );
|
||||
|
||||
/*lazy_static! {
|
||||
pub static ref NULL_RLP: Bytes = { let mut r = RlpStream::new(); r.append(&""); r.out().unwrap() };
|
||||
pub static ref SHA3_NULL_RLP: H256 = { use sha3::Hashable; NULL_RLP.sha3() };
|
||||
}*/
|
||||
|
||||
pub trait Trie {
|
||||
fn root(&self) -> &H256;
|
||||
fn is_empty(&self) -> bool { *self.root() == SHA3_NULL_RLP }
|
||||
|
||||
// TODO: consider returning &[u8]...
|
||||
fn contains(&self, key: &[u8]) -> bool;
|
||||
fn at(&self, key: &[u8]) -> Option<&[u8]>;
|
||||
fn insert(&mut self, key: &[u8], value: &[u8]);
|
||||
fn remove(&mut self, key: &[u8]);
|
||||
}
|
||||
|
||||
pub struct TrieDB {
|
||||
db: Box<HashDB>,
|
||||
root: H256,
|
||||
}
|
||||
|
||||
struct Diff {
|
||||
new: Vec<(H256, Bytes)>,
|
||||
old: Vec<H256>,
|
||||
}
|
||||
|
||||
impl Diff {
|
||||
pub fn new() -> Diff { Diff { new: vec![], old: vec![] }}
|
||||
}
|
||||
|
||||
impl TrieDB {
|
||||
pub fn new<T>(db: T) -> Self where T: HashDB + 'static { TrieDB{ db: Box::new(db), root: H256::new() } }
|
||||
|
||||
pub fn new_boxed(db_box: Box<HashDB>) -> Self { TrieDB{ db: db_box, root: H256::new() } }
|
||||
|
||||
pub fn new_memory() -> Self { TrieDB{ db: Box::new(MemoryDB::new()), root: H256::new() } }
|
||||
|
||||
pub fn init(&mut self) { self.set_root_rlp(&NULL_RLP); }
|
||||
|
||||
pub fn db(&self) -> &HashDB { self.db.as_ref() }
|
||||
|
||||
fn set_root_rlp(&mut self, root_data: &[u8]) {
|
||||
self.db.kill(&self.root);
|
||||
self.root = self.db.insert(root_data);
|
||||
println!("set_root_rlp {:?} {:?}", root_data, self.root);
|
||||
}
|
||||
|
||||
fn add(&mut self, key: &NibbleSlice, value: &[u8]) {
|
||||
// determine what the new root is, insert new nodes and remove old as necessary.
|
||||
let todo = {
|
||||
let root_rlp = self.db.lookup(&self.root).expect("Trie root not found!");
|
||||
self.merge(root_rlp, key, value)
|
||||
};
|
||||
self.apply(todo.1);
|
||||
self.set_root_rlp(&todo.0);
|
||||
}
|
||||
|
||||
fn apply(&mut self, diff: Diff) {
|
||||
for d in diff.old.iter() {
|
||||
self.db.kill(&d);
|
||||
}
|
||||
for d in diff.new.into_iter() {
|
||||
self.db.emplace(d.0, d.1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine the RLP of the node, assuming we're inserting `partial_key` into the
|
||||
/// node at `old`. This will *not* delete the old mode; it will just return the new RLP
|
||||
/// that includes the new node.
|
||||
///
|
||||
/// The database will be updated so as to make the returned RLP valid through inserting
|
||||
/// and deleting nodes as necessary.
|
||||
fn merge(&self, old: &[u8], partial_key: &NibbleSlice, value: &[u8]) -> (Bytes, Diff) {
|
||||
let o = Rlp::new(old);
|
||||
match o.prototype() {
|
||||
Prototype::List(17) => {
|
||||
// already have a branch. route and merge.
|
||||
unimplemented!();
|
||||
},
|
||||
Prototype::List(2) => {
|
||||
let their_key_rlp = o.at(0);
|
||||
let (them, _) = NibbleSlice::from_encoded(their_key_rlp.data());
|
||||
match partial_key.common_prefix(&them) {
|
||||
0 => {
|
||||
// transmute to branch here
|
||||
},
|
||||
cp if cp == them.len() => {
|
||||
// fast-forward
|
||||
},
|
||||
_ => {
|
||||
// cleve into two + branch in the middle
|
||||
},
|
||||
}
|
||||
// already have an extension. either fast_forward, cleve or transmute_to_branch.
|
||||
unimplemented!();
|
||||
},
|
||||
Prototype::Data(0) => {
|
||||
(Self::compose_extension(partial_key, value, true), Diff::new())
|
||||
},
|
||||
_ => panic!("Invalid RLP for node."),
|
||||
}
|
||||
}
|
||||
|
||||
fn compose_extension(partial_key: &NibbleSlice, value: &[u8], is_leaf: bool) -> Bytes {
|
||||
println!("compose_extension {:?} {:?} {:?} ({:?})", partial_key, value, is_leaf, partial_key.encoded(is_leaf));
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&partial_key.encoded(is_leaf));
|
||||
s.append(&value.to_vec()); // WTF?!?!
|
||||
//s.append(value); // <-- should be.
|
||||
let r = s.out();
|
||||
println!("output: -> {:?}", &r);
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl Trie for TrieDB {
|
||||
fn root(&self) -> &H256 { &self.root }
|
||||
|
||||
fn contains(&self, _key: &[u8]) -> bool {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn at(&self, _key: &[u8]) -> Option<&[u8]> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn insert(&mut self, key: &[u8], value: &[u8]) {
|
||||
(self as &mut TrieDB).add(&NibbleSlice::new(key), value);
|
||||
}
|
||||
|
||||
fn remove(&mut self, _key: &[u8]) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn playpen() {
|
||||
use overlaydb::*;
|
||||
use triehash::*;
|
||||
|
||||
(&[1, 2, 3]).starts_with(&[1, 2]);
|
||||
|
||||
let mut t = TrieDB::new(OverlayDB::new_temp());
|
||||
t.init();
|
||||
assert_eq!(*t.root(), SHA3_NULL_RLP);
|
||||
assert!(t.is_empty());
|
||||
|
||||
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
|
||||
assert_eq!(*t.root(), trie_root(vec![ (vec![1u8, 0x23], vec![1u8, 0x23]) ]));
|
||||
}
|
354
src/triehash.rs
Normal file
354
src/triehash.rs
Normal file
@ -0,0 +1,354 @@
|
||||
//! Generete trie root.
|
||||
//!
|
||||
//! This module should be used to generate trie root hash.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp;
|
||||
use hash::*;
|
||||
use sha3::*;
|
||||
use rlp;
|
||||
use rlp::RlpStream;
|
||||
use vector::SharedPrefix;
|
||||
|
||||
// todo: verify if example for ordered_trie_root is valid
|
||||
/// Generates a trie root hash for a vector of values
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate ethcore_util as util;
|
||||
/// use std::str::FromStr;
|
||||
/// use util::triehash::*;
|
||||
/// use util::hash::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let v = vec![From::from("doe"), From::from("reindeer")];
|
||||
/// let root = "e766d5d51b89dc39d981b41bda63248d7abce4f0225eefd023792a540bcffee3";
|
||||
/// assert_eq!(ordered_trie_root(v), H256::from_str(root).unwrap());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn ordered_trie_root(input: Vec<Vec<u8>>) -> H256 {
|
||||
let gen_input = input
|
||||
// first put elements into btree to sort them by nibbles
|
||||
// optimize it later
|
||||
.into_iter()
|
||||
.fold(BTreeMap::new(), | mut acc, vec | {
|
||||
let len = acc.len();
|
||||
acc.insert(as_nibbles(&rlp::encode(&len)), vec);
|
||||
acc
|
||||
})
|
||||
// then move them to a vector
|
||||
.into_iter()
|
||||
.map(|p| p )
|
||||
.collect();
|
||||
|
||||
gen_trie_root(gen_input)
|
||||
}
|
||||
|
||||
/// Generates a trie root hash for a vector of key-values
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate ethcore_util as util;
|
||||
/// use std::str::FromStr;
|
||||
/// use util::triehash::*;
|
||||
/// use util::hash::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let v = vec![
|
||||
/// (From::from("doe"), From::from("reindeer")),
|
||||
/// (From::from("dog"), From::from("puppy")),
|
||||
/// (From::from("dogglesworth"), From::from("cat")),
|
||||
/// ];
|
||||
///
|
||||
/// let root = "8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3";
|
||||
/// assert_eq!(trie_root(v), H256::from_str(root).unwrap());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
|
||||
let gen_input = input
|
||||
.into_iter()
|
||||
.map(|(k, v)| (as_nibbles(&k), v))
|
||||
.collect();
|
||||
|
||||
gen_trie_root(gen_input)
|
||||
}
|
||||
|
||||
fn gen_trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
|
||||
let mut stream = RlpStream::new();
|
||||
hash256rlp(&input, 0, &mut stream);
|
||||
stream.out().sha3()
|
||||
}
|
||||
|
||||
/// Hex-prefix Notation. First nibble has flags: oddness = 2^0 & termination = 2^1.
|
||||
///
|
||||
/// The "termination marker" and "leaf-node" specifier are completely equivalent.
|
||||
///
|
||||
/// Input values are in range `[0, 0xf]`.
|
||||
///
|
||||
/// ```markdown
|
||||
/// [0,0,1,2,3,4,5] 0x10012345 // 7 > 4
|
||||
/// [0,1,2,3,4,5] 0x00012345 // 6 > 4
|
||||
/// [1,2,3,4,5] 0x112345 // 5 > 3
|
||||
/// [0,0,1,2,3,4] 0x00001234 // 6 > 3
|
||||
/// [0,1,2,3,4] 0x101234 // 5 > 3
|
||||
/// [1,2,3,4] 0x001234 // 4 > 3
|
||||
/// [0,0,1,2,3,4,5,T] 0x30012345 // 7 > 4
|
||||
/// [0,0,1,2,3,4,T] 0x20001234 // 6 > 4
|
||||
/// [0,1,2,3,4,5,T] 0x20012345 // 6 > 4
|
||||
/// [1,2,3,4,5,T] 0x312345 // 5 > 3
|
||||
/// [1,2,3,4,T] 0x201234 // 4 > 3
|
||||
/// ```
|
||||
fn hex_prefix_encode(nibbles: &[u8], leaf: bool) -> Vec<u8> {
|
||||
let inlen = nibbles.len();
|
||||
let oddness_factor = inlen % 2;
|
||||
// next even number divided by two
|
||||
let reslen = (inlen + 2) >> 1;
|
||||
let mut res = vec![];
|
||||
res.reserve(reslen);
|
||||
|
||||
let first_byte = {
|
||||
let mut bits = ((inlen as u8 & 1) + (2 * leaf as u8)) << 4;
|
||||
if oddness_factor == 1 {
|
||||
bits += nibbles[0];
|
||||
}
|
||||
bits
|
||||
};
|
||||
|
||||
res.push(first_byte);
|
||||
|
||||
let mut offset = oddness_factor;
|
||||
while offset < inlen {
|
||||
let byte = (nibbles[offset] << 4) + nibbles[offset + 1];
|
||||
res.push(byte);
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Converts slice of bytes to nibbles.
|
||||
fn as_nibbles(bytes: &[u8]) -> Vec<u8> {
|
||||
let mut res = vec![];
|
||||
res.reserve(bytes.len() * 2);
|
||||
for i in 0..bytes.len() {
|
||||
res.push(bytes[i] >> 4);
|
||||
res.push((bytes[i] << 4) >> 4);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn hash256rlp(input: &[(Vec<u8>, Vec<u8>)], pre_len: usize, stream: &mut RlpStream) {
|
||||
let inlen = input.len();
|
||||
|
||||
// in case of empty slice, just append null
|
||||
if inlen == 0 {
|
||||
stream.append(&"");
|
||||
return;
|
||||
}
|
||||
|
||||
// take slices
|
||||
let key: &Vec<u8> = &input[0].0;
|
||||
let value: &[u8] = &input[0].1;
|
||||
|
||||
// if the slice contains just one item, append the suffix of the key
|
||||
// and then append value
|
||||
if inlen == 1 {
|
||||
stream.append_list(2);
|
||||
stream.append(&hex_prefix_encode(&key[pre_len..], true));
|
||||
stream.append(&value);
|
||||
return;
|
||||
}
|
||||
|
||||
// get length of the longest shared prefix in slice keys
|
||||
let shared_prefix = input.iter()
|
||||
// skip first element
|
||||
.skip(1)
|
||||
// get minimum number of shared nibbles between first and each successive
|
||||
.fold(key.len(), | acc, &(ref k, _) | {
|
||||
cmp::min(key.shared_prefix_len(&k), acc)
|
||||
});
|
||||
|
||||
// if shared prefix is higher than current prefix append its
|
||||
// new part of the key to the stream
|
||||
// then recursively append suffixes of all items who had this key
|
||||
if shared_prefix > pre_len {
|
||||
stream.append_list(2);
|
||||
stream.append(&hex_prefix_encode(&key[pre_len..shared_prefix], false));
|
||||
hash256aux(input, shared_prefix, stream);
|
||||
return;
|
||||
}
|
||||
|
||||
// an item for every possible nibble/suffix
|
||||
// + 1 for data
|
||||
stream.append_list(17);
|
||||
|
||||
// if first key len is equal to prefix_len, move to next element
|
||||
let mut begin = match pre_len == key.len() {
|
||||
true => 1,
|
||||
false => 0
|
||||
};
|
||||
|
||||
// iterate over all possible nibbles
|
||||
for i in 0..16 {
|
||||
// cout how many successive elements have same next nibble
|
||||
let len = input[begin..].iter()
|
||||
.map(| pair | pair.0[pre_len] )
|
||||
.take_while(|&q| q == i).count();
|
||||
|
||||
// if at least 1 successive element has the same nibble
|
||||
// append their suffixes
|
||||
match len {
|
||||
0 => { stream.append(&""); },
|
||||
_ => hash256aux(&input[begin..(begin + len)], pre_len + 1, stream)
|
||||
}
|
||||
begin += len;
|
||||
}
|
||||
|
||||
// if fist key len is equal prefix, append it's value
|
||||
match pre_len == key.len() {
|
||||
true => { stream.append(&value); },
|
||||
false => { stream.append(&""); }
|
||||
};
|
||||
}
|
||||
|
||||
fn hash256aux(input: &[(Vec<u8>, Vec<u8>)], pre_len: usize, stream: &mut RlpStream) {
|
||||
let mut s = RlpStream::new();
|
||||
hash256rlp(input, pre_len, &mut s);
|
||||
let out = s.out();
|
||||
match out.len() {
|
||||
0...31 => stream.append_raw(&out, 1),
|
||||
_ => stream.append(&out.sha3())
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_nibbles() {
|
||||
let v = vec![0x31, 0x23, 0x45];
|
||||
let e = vec![3, 1, 2, 3, 4, 5];
|
||||
assert_eq!(as_nibbles(&v), e);
|
||||
|
||||
// A => 65 => 0x41 => [4, 1]
|
||||
let v: Vec<u8> = From::from("A");
|
||||
let e = vec![4, 1];
|
||||
assert_eq!(as_nibbles(&v), e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_prefix_encode() {
|
||||
let v = vec![0, 0, 1, 2, 3, 4, 5];
|
||||
let e = vec![0x10, 0x01, 0x23, 0x45];
|
||||
let h = hex_prefix_encode(&v, false);
|
||||
assert_eq!(h, e);
|
||||
|
||||
let v = vec![0, 1, 2, 3, 4, 5];
|
||||
let e = vec![0x00, 0x01, 0x23, 0x45];
|
||||
let h = hex_prefix_encode(&v, false);
|
||||
assert_eq!(h, e);
|
||||
|
||||
let v = vec![0, 1, 2, 3, 4, 5];
|
||||
let e = vec![0x20, 0x01, 0x23, 0x45];
|
||||
let h = hex_prefix_encode(&v, true);
|
||||
assert_eq!(h, e);
|
||||
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
let e = vec![0x31, 0x23, 0x45];
|
||||
let h = hex_prefix_encode(&v, true);
|
||||
assert_eq!(h, e);
|
||||
|
||||
let v = vec![1, 2, 3, 4];
|
||||
let e = vec![0x00, 0x12, 0x34];
|
||||
let h = hex_prefix_encode(&v, false);
|
||||
assert_eq!(h, e);
|
||||
|
||||
let v = vec![4, 1];
|
||||
let e = vec![0x20, 0x41];
|
||||
let h = hex_prefix_encode(&v, true);
|
||||
assert_eq!(h, e);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use rustc_serialize::hex::FromHex;
|
||||
use hash::*;
|
||||
use triehash::*;
|
||||
|
||||
#[test]
|
||||
fn empty_trie_root() {
|
||||
assert_eq!(trie_root(vec![]), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_trie_item() {
|
||||
let v = vec![(From::from("A"), From::from("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))];
|
||||
assert_eq!(trie_root(v), H256::from_str("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foo_trie_item() {
|
||||
|
||||
let v = vec![
|
||||
(From::from("foo"), From::from("bar")),
|
||||
(From::from("food"), From::from("bass"))
|
||||
];
|
||||
|
||||
assert_eq!(trie_root(v), H256::from_str("17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dogs_trie_item() {
|
||||
|
||||
let v = vec![
|
||||
(From::from("doe"), From::from("reindeer")),
|
||||
(From::from("dog"), From::from("puppy")),
|
||||
(From::from("dogglesworth"), From::from("cat")),
|
||||
];
|
||||
|
||||
assert_eq!(trie_root(v), H256::from_str("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn puppy_trie_items() {
|
||||
|
||||
let v = vec![
|
||||
(From::from("do"), From::from("verb")),
|
||||
(From::from("dog"), From::from("puppy")),
|
||||
(From::from("doge"), From::from("coin")),
|
||||
(From::from("horse"), From::from("stallion")),
|
||||
];
|
||||
|
||||
assert_eq!(trie_root(v), H256::from_str("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trie_root() {
|
||||
let v = vec![
|
||||
|
||||
("0000000000000000000000000000000000000000000000000000000000000045".from_hex().unwrap(),
|
||||
"22b224a1420a802ab51d326e29fa98e34c4f24ea".from_hex().unwrap()),
|
||||
|
||||
("0000000000000000000000000000000000000000000000000000000000000046".from_hex().unwrap(),
|
||||
"67706c2076330000000000000000000000000000000000000000000000000000".from_hex().unwrap()),
|
||||
|
||||
("000000000000000000000000697c7b8c961b56f675d570498424ac8de1a918f6".from_hex().unwrap(),
|
||||
"6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000".from_hex().unwrap()),
|
||||
|
||||
("0000000000000000000000007ef9e639e2733cb34e4dfc576d4b23f72db776b2".from_hex().unwrap(),
|
||||
"4655474156000000000000000000000000000000000000000000000000000000".from_hex().unwrap()),
|
||||
|
||||
("000000000000000000000000ec4f34c97e43fbb2816cfd95e388353c7181dab1".from_hex().unwrap(),
|
||||
"4e616d6552656700000000000000000000000000000000000000000000000000".from_hex().unwrap()),
|
||||
|
||||
("4655474156000000000000000000000000000000000000000000000000000000".from_hex().unwrap(),
|
||||
"7ef9e639e2733cb34e4dfc576d4b23f72db776b2".from_hex().unwrap()),
|
||||
|
||||
("4e616d6552656700000000000000000000000000000000000000000000000000".from_hex().unwrap(),
|
||||
"ec4f34c97e43fbb2816cfd95e388353c7181dab1".from_hex().unwrap()),
|
||||
|
||||
("6f6f6f6820736f2067726561742c207265616c6c6c793f000000000000000000".from_hex().unwrap(),
|
||||
"697c7b8c961b56f675d570498424ac8de1a918f6".from_hex().unwrap())
|
||||
|
||||
];
|
||||
|
||||
assert_eq!(trie_root(v), H256::from_str("9f6221ebb8efe7cff60a716ecb886e67dd042014be444669f0159d8e68b42100").unwrap());
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
//! vector util functions
|
||||
|
||||
use std::ptr;
|
||||
|
||||
@ -32,3 +33,53 @@ impl<T> InsertSlice<T> for Vec<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns len of prefix shared with elem
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate ethcore_util as util;
|
||||
/// use util::vector::SharedPrefix;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let a = vec![1,2,3,3,5];
|
||||
/// let b = vec![1,2,3];
|
||||
/// assert_eq!(a.shared_prefix_len(&b), 3);
|
||||
/// }
|
||||
/// ```
|
||||
pub trait SharedPrefix <T> {
|
||||
fn shared_prefix_len(&self, elem: &[T]) -> usize;
|
||||
}
|
||||
|
||||
impl <T> SharedPrefix<T> for Vec<T> where T: Eq {
|
||||
fn shared_prefix_len(&self, elem: &[T]) -> usize {
|
||||
use std::cmp;
|
||||
let len = cmp::min(self.len(), elem.len());
|
||||
(0..len).take_while(|&i| self[i] == elem[i]).count()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use vector::SharedPrefix;
|
||||
|
||||
#[test]
|
||||
fn test_shared_prefix() {
|
||||
let a = vec![1,2,3,4,5,6];
|
||||
let b = vec![4,2,3,4,5,6];
|
||||
assert_eq!(a.shared_prefix_len(&b), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_prefix2() {
|
||||
let a = vec![1,2,3,3,5];
|
||||
let b = vec![1,2,3];
|
||||
assert_eq!(a.shared_prefix_len(&b), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shared_prefix3() {
|
||||
let a = vec![1,2,3,4,5,6];
|
||||
let b = vec![1,2,3,4,5,6];
|
||||
assert_eq!(a.shared_prefix_len(&b), 6);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user