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