patricia_trie/
nibbleslice.rs

1// Copyright 2015-2018 Parity Technologies (UK) Ltd.
2// This file is part of Parity.
3
4// Parity is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets.
18
19use std::cmp::*;
20use std::fmt;
21use elastic_array::ElasticArray36;
22
23/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets.
24///
25/// This is an immutable struct. No operations actually change it.
26///
27/// # Example
28/// ```snippet
29/// use patricia_trie::nibbleslice::NibbleSlice;
30/// fn main() {
31///   let d1 = &[0x01u8, 0x23, 0x45];
32///   let d2 = &[0x34u8, 0x50, 0x12];
33///   let d3 = &[0x00u8, 0x12];
34///   let n1 = NibbleSlice::new(d1);			// 0,1,2,3,4,5
35///   let n2 = NibbleSlice::new(d2);			// 3,4,5,0,1,2
36///   let n3 = NibbleSlice::new_offset(d3, 1);	// 0,1,2
37///   assert!(n1 > n3);							// 0,1,2,... > 0,1,2
38///   assert!(n1 < n2);							// 0,... < 3,...
39///   assert!(n2.mid(3) == n3);					// 0,1,2 == 0,1,2
40///   assert!(n1.starts_with(&n3));
41///   assert_eq!(n1.common_prefix(&n3), 3);
42///   assert_eq!(n2.mid(3).common_prefix(&n1), 3);
43/// }
44/// ```
45#[derive(Copy, Clone, Eq, Ord)]
46pub struct NibbleSlice<'a> {
47	data: &'a [u8],
48	offset: usize,
49	data_encode_suffix: &'a [u8],
50	offset_encode_suffix: usize,
51}
52
53/// Iterator type for a nibble slice.
54pub struct NibbleSliceIterator<'a> {
55	p: &'a NibbleSlice<'a>,
56	i: usize,
57}
58
59impl<'a> Iterator for NibbleSliceIterator<'a> {
60	type Item = u8;
61	fn next(&mut self) -> Option<u8> {
62		self.i += 1;
63		match self.i <= self.p.len() {
64			true => Some(self.p.at(self.i - 1)),
65			false => None,
66		}
67	}
68}
69
70impl<'a> NibbleSlice<'a> {
71	/// Create a new nibble slice with the given byte-slice.
72	pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_offset(data, 0) }
73
74	/// Create a new nibble slice with the given byte-slice with a nibble offset.
75	pub fn new_offset(data: &'a [u8], offset: usize) -> Self {
76		NibbleSlice {
77			data,
78			offset,
79			data_encode_suffix: &b""[..],
80			offset_encode_suffix: 0
81		}
82	}
83
84	/// Create a composed nibble slice; one followed by the other.
85	pub fn new_composed(a: &NibbleSlice<'a>, b: &NibbleSlice<'a>) -> Self {
86		NibbleSlice {
87			data: a.data,
88			offset: a.offset,
89			data_encode_suffix: b.data,
90			offset_encode_suffix: b.offset
91		}
92	}
93
94	/// Get an iterator for the series of nibbles.
95	pub fn iter(&'a self) -> NibbleSliceIterator<'a> {
96		NibbleSliceIterator { p: self, i: 0 }
97	}
98
99	/// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`).
100	pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) {
101		(Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32)
102	}
103
104	/// Is this an empty slice?
105	pub fn is_empty(&self) -> bool { self.len() == 0 }
106
107	/// Get the length (in nibbles, naturally) of this slice.
108	#[inline]
109	pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * 2 - self.offset - self.offset_encode_suffix }
110
111	/// Get the nibble at position `i`.
112	#[inline(always)]
113	pub fn at(&self, i: usize) -> u8 {
114		let l = self.data.len() * 2 - self.offset;
115		if i < l {
116			if (self.offset + i) & 1 == 1 {
117				self.data[(self.offset + i) / 2] & 15u8
118			}
119			else {
120				self.data[(self.offset + i) / 2] >> 4
121			}
122		}
123		else {
124			let i = i - l;
125			if (self.offset_encode_suffix + i) & 1 == 1 {
126				self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] & 15u8
127			}
128			else {
129				self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] >> 4
130			}
131		}
132	}
133
134	/// Return object which represents a view on to this slice (further) offset by `i` nibbles.
135	pub fn mid(&self, i: usize) -> NibbleSlice<'a> {
136		NibbleSlice {
137			data: self.data,
138			offset: self.offset + i,
139			data_encode_suffix: &b""[..],
140			offset_encode_suffix: 0
141		}
142	}
143
144	/// Do we start with the same nibbles as the whole of `them`?
145 	pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() }
146
147 	/// How many of the same nibbles at the beginning do we match with `them`?
148	pub fn common_prefix(&self, them: &Self) -> usize {
149		let s = min(self.len(), them.len());
150		let mut i = 0usize;
151		while i < s {
152			if self.at(i) != them.at(i) { break; }
153			i += 1;
154		}
155		i
156	}
157
158	/// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`.
159	#[inline]
160	pub fn encoded(&self, is_leaf: bool) -> ElasticArray36<u8> {
161		let l = self.len();
162		let mut r = ElasticArray36::new();
163		let mut i = l % 2;
164		r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0});
165		while i < l {
166			r.push(self.at(i) * 16 + self.at(i + 1));
167			i += 2;
168		}
169		r
170	}
171
172	/// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation,
173	/// noting whether it `is_leaf`.
174	pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36<u8> {
175		let l = min(self.len(), n);
176		let mut r = ElasticArray36::new();
177		let mut i = l % 2;
178		r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0});
179		while i < l {
180			r.push(self.at(i) * 16 + self.at(i + 1));
181			i += 2;
182		}
183		r
184	}
185}
186
187impl<'a> PartialEq for NibbleSlice<'a> {
188	fn eq(&self, them: &Self) -> bool {
189		self.len() == them.len() && self.starts_with(them)
190	}
191}
192
193impl<'a> PartialOrd for NibbleSlice<'a> {
194	fn partial_cmp(&self, them: &Self) -> Option<Ordering> {
195		let s = min(self.len(), them.len());
196		let mut i = 0usize;
197		while i < s {
198			match self.at(i).partial_cmp(&them.at(i)).unwrap() {
199				Ordering::Less => return Some(Ordering::Less),
200				Ordering::Greater => return Some(Ordering::Greater),
201				_ => i += 1,
202			}
203		}
204		self.len().partial_cmp(&them.len())
205	}
206}
207
208impl<'a> fmt::Debug for NibbleSlice<'a> {
209	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210		for i in 0..self.len() {
211			match i {
212				0 => write!(f, "{:01x}", self.at(i))?,
213				_ => write!(f, "'{:01x}", self.at(i))?,
214			}
215		}
216		Ok(())
217	}
218}
219
220#[cfg(test)]
221mod tests {
222	use super::NibbleSlice;
223	use elastic_array::ElasticArray36;
224	static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45];
225
226	#[test]
227	fn basics() {
228		let n = NibbleSlice::new(D);
229		assert_eq!(n.len(), 6);
230		assert!(!n.is_empty());
231
232		let n = NibbleSlice::new_offset(D, 6);
233		assert!(n.is_empty());
234
235		let n = NibbleSlice::new_offset(D, 3);
236		assert_eq!(n.len(), 3);
237		for i in 0..3 {
238			assert_eq!(n.at(i), i as u8 + 3);
239		}
240	}
241
242	#[test]
243	fn iterator() {
244		let n = NibbleSlice::new(D);
245		let mut nibbles: Vec<u8> = vec![];
246		nibbles.extend(n.iter());
247		assert_eq!(nibbles, (0u8..6).collect::<Vec<_>>())
248	}
249
250	#[test]
251	fn mid() {
252		let n = NibbleSlice::new(D);
253		let m = n.mid(2);
254		for i in 0..4 {
255			assert_eq!(m.at(i), i as u8 + 2);
256		}
257		let m = n.mid(3);
258		for i in 0..3 {
259			assert_eq!(m.at(i), i as u8 + 3);
260		}
261	}
262
263	#[test]
264	fn encoded() {
265		let n = NibbleSlice::new(D);
266		assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45]));
267		assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45]));
268		assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45]));
269		assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x31, 0x23, 0x45]));
270	}
271
272	#[test]
273	fn from_encoded() {
274		let n = NibbleSlice::new(D);
275		assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45]));
276		assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45]));
277		assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45]));
278		assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45]));
279	}
280
281	#[test]
282	fn find_length_of_common_prefix() {
283		let n = NibbleSlice::new(D);
284
285		let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67];
286		let m = NibbleSlice::new(other);
287
288		assert_eq!(n.common_prefix(&m), 4);
289		assert_eq!(m.common_prefix(&n), 4);
290		assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3);
291		assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0);
292		assert_eq!(n.common_prefix(&m.mid(4)), 6);
293		assert!(!n.starts_with(&m.mid(4)));
294		assert!(m.mid(4).starts_with(&n));
295	}
296
297	#[test]
298	fn compare_sizes() {
299		let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45];
300		let n = NibbleSlice::new(D);
301		let m = NibbleSlice::new(other);
302
303		assert!(n != m);
304		assert!(n > m);
305		assert!(m < n);
306
307		assert!(n == m.mid(4));
308		assert!(n >= m.mid(4));
309		assert!(n <= m.mid(4));
310	}
311
312	#[test]
313	fn common_prefix_for_concatenated_slices() {
314		let first = NibbleSlice::new(&[0x01u8, 0x23, 0x45]); // 0'1'2'3'4'5'
315		let first_ext = NibbleSlice::new(&[0x22u8, 0x44, 0x55]); // 2'2'4'4'5'5
316		let concat = NibbleSlice::new_composed(&first, &first_ext);
317		assert!(concat.len() == first.len() + first_ext.len());
318
319		// 0'1'2'3'4'5'2'2'9'9'5'5'
320		let second = NibbleSlice::new(&[0x01u8, 0x23, 0x45, 0x22, 0x99, 0x55]);
321
322		let common_prefix_length = first.common_prefix(&second);
323		assert_eq!(common_prefix_length, 6);
324
325		let common_prefix_length = concat.common_prefix(&second);
326		assert_eq!(common_prefix_length, 8);
327	}
328}