tetsy_trie_db/nibble/
mod.rs

1// Copyright 2019 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Nibble oriented methods.
16
17use crate::node::NodeKey;
18use crate::rstd::cmp;
19
20pub use self::leftnibbleslice::LeftNibbleSlice;
21
22mod nibblevec;
23mod nibbleslice;
24mod leftnibbleslice;
25
26/// Utility methods to work on radix 16 nibble.
27pub mod nibble_ops {
28	use super::*;
29
30	/// Single nibble length in bit.
31	pub const BIT_PER_NIBBLE : usize = 4;
32	/// Number of nibble per byte.
33	pub const NIBBLE_PER_BYTE : usize = 2;
34	/// Number of child for a branch (trie radix).
35	pub const NIBBLE_LENGTH : usize = 16;
36	/// Nibble (half a byte).
37	pub const PADDING_BITMASK: u8 = 0x0F;
38	/// Size of header.
39	pub const CONTENT_HEADER_SIZE: u8 = 1;
40
41	/// Mask a byte, keeping left nibble.
42	#[inline(always)]
43	pub fn pad_left(b: u8) -> u8 {
44		b & !PADDING_BITMASK
45	}
46
47	/// Mask a byte, keeping right byte.
48	#[inline(always)]
49	pub fn pad_right(b: u8) -> u8 {
50		b & PADDING_BITMASK
51	}
52
53	/// Get u8 nibble value at a given index of a byte.
54	#[inline(always)]
55	pub fn at_left(ix: u8, b: u8) -> u8 {
56		if ix == 1 {
57			b & PADDING_BITMASK
58		} else {
59			b >> BIT_PER_NIBBLE
60		}
61	}
62
63	/// Get u8 nibble value at a given index in a left aligned array.
64	#[inline(always)]
65	pub fn left_nibble_at(v1: &[u8], ix: usize) -> u8 {
66		at_left(
67			(ix % NIBBLE_PER_BYTE) as u8,
68			v1[ix / NIBBLE_PER_BYTE]
69		)
70	}
71
72	/// Get u8 nibble value at a given index in a `NibbleSlice`.
73	#[inline(always)]
74	pub fn at(s: &NibbleSlice, i: usize) -> u8 {
75		let ix = (s.offset + i) / NIBBLE_PER_BYTE;
76		let pad = (s.offset + i) % NIBBLE_PER_BYTE;
77		at_left(pad as u8, s.data[ix])
78	}
79
80	/// Push u8 nibble value at a given index into an existing byte.
81	#[inline(always)]
82	pub fn push_at_left(ix: u8, v: u8, into: u8) -> u8 {
83		into | if ix == 1 {
84			v
85		} else {
86			v << BIT_PER_NIBBLE
87		}
88	}
89
90	#[inline]
91	/// Calculate the number of needed padding a array of nibble length `i`.
92	pub fn number_padding(i: usize) -> usize {
93		i % NIBBLE_PER_BYTE
94	}
95
96	/// The nibble shifts needed to align.
97	/// We use two value, one is a left shift and
98	/// the other is a right shift.
99	pub const SPLIT_SHIFTS: (usize, usize) = (4, 4);
100
101	/// Count the biggest common depth between two left aligned packed nibble slice.
102	pub fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize {
103		let upper_bound = cmp::min(v1.len(), v2.len());
104		for a in 0 .. upper_bound {
105			if v1[a] != v2[a] {
106				return a * NIBBLE_PER_BYTE + left_common(v1[a], v2[a]);
107			}
108		}
109		upper_bound * NIBBLE_PER_BYTE
110	}
111
112	/// Calculate the number of common nibble between two left aligned bytes.
113	#[inline(always)]
114	pub fn left_common(a: u8, b: u8) -> usize {
115		if a == b {
116			2
117		} else if pad_left(a) == pad_left(b) {
118			1
119		} else {
120			0
121		}
122	}
123
124	/// Shifts right aligned key to add a given left offset.
125	/// Resulting in possibly padding at both left and right
126	/// (example usage when combining two keys).
127	pub fn shift_key(key: &mut NodeKey, offset: usize) -> bool {
128		let old_offset = key.0;
129		key.0 = offset;
130		if old_offset > offset {
131			// shift left
132			let (s1, s2) = nibble_ops::SPLIT_SHIFTS;
133			let kl = key.1.len();
134			(0..kl - 1).for_each(|i| key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1);
135			key.1[kl - 1] = key.1[kl - 1] << s2;
136			true
137		} else if old_offset < offset {
138			// shift right
139			let (s1, s2) = nibble_ops::SPLIT_SHIFTS;
140			key.1.push(0);
141			(1..key.1.len()).rev().for_each(|i| key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2);
142			key.1[0] = key.1[0] >> s2;
143			true
144		} else {
145			false
146		}
147	}
148
149}
150
151/// Backing storage for `NibbleVec`s.
152pub(crate) type BackingByteVec = smallvec::SmallVec<[u8; 36]>;
153
154/// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`.
155/// Nibbles are always left aligned, so making a `NibbleVec` from
156/// a `NibbleSlice` can get costy.
157#[cfg_attr(feature = "std", derive(Debug))]
158#[derive(Clone, PartialEq, Eq)]
159pub struct NibbleVec {
160	inner: BackingByteVec,
161	len: usize,
162}
163
164/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets.
165///
166/// This is an immutable struct. No operations actually change it.
167///
168/// # Example
169/// ```snippet
170/// use patricia_trie::nibbleslice::NibbleSlice;
171/// fn main() {
172///   let d1 = &[0x01u8, 0x23, 0x45];
173///   let d2 = &[0x34u8, 0x50, 0x12];
174///   let d3 = &[0x00u8, 0x12];
175///   let n1 = NibbleSlice::new(d1);			// 0,1,2,3,4,5
176///   let n2 = NibbleSlice::new(d2);			// 3,4,5,0,1,2
177///   let n3 = NibbleSlice::new_offset(d3, 1);	// 0,1,2
178///   assert!(n1 > n3);							// 0,1,2,... > 0,1,2
179///   assert!(n1 < n2);							// 0,... < 3,...
180///   assert!(n2.mid(3) == n3);					// 0,1,2 == 0,1,2
181///   assert!(n1.starts_with(&n3));
182///   assert_eq!(n1.common_prefix(&n3), 3);
183///   assert_eq!(n2.mid(3).common_prefix(&n1), 3);
184/// }
185/// ```
186#[derive(Copy, Clone)]
187pub struct NibbleSlice<'a> {
188	data: &'a [u8],
189	offset: usize,
190}
191
192/// Iterator type for a nibble slice.
193pub struct NibbleSliceIterator<'a> {
194	p: &'a NibbleSlice<'a>,
195	i: usize,
196}
197