diff --git a/Cargo.toml b/Cargo.toml index 5ea2296a..c855a331 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,9 @@ rust-version = "1.71.0" [workspace] members = [ + "memory", "sealable-trie", + "stdx", ] resolver = "2" @@ -17,3 +19,7 @@ pretty_assertions = "1.4.0" rand = { version = "0.8.5" } sha2 = { version = "0.10.7", default-features = false } strum = { version = "0.25.0", default-features = false, features = ["derive"] } + +memory = { path = "memory" } +sealable-trie = { path = "sealable-trie" } +stdx = { path = "stdx" } diff --git a/memory/Cargo.toml b/memory/Cargo.toml new file mode 100644 index 00000000..ff3169d5 --- /dev/null +++ b/memory/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "memory" +authors = ["Michal Nazarewicz "] +version = "0.0.0" +edition = "2021" + +[dependencies] +derive_more.workspace = true +stdx = { workspace = true, optional = true } + +[dev-dependencies] +stdx.workspace = true + +[features] +test_utils = ["stdx"] diff --git a/sealable-trie/src/memory.rs b/memory/src/lib.rs similarity index 66% rename from sealable-trie/src/memory.rs rename to memory/src/lib.rs index 16492844..b4b1e9bc 100644 --- a/sealable-trie/src/memory.rs +++ b/memory/src/lib.rs @@ -1,9 +1,9 @@ +extern crate alloc; + use alloc::vec::Vec; use core::fmt; use core::num::NonZeroU32; -use crate::nodes::RawNode; - /// A pointer value. The value is 30-bit and always non-zero. #[derive( Copy, @@ -30,7 +30,7 @@ impl Ptr { /// Largest value that can be stored in the pointer. // The two most significant bits are used internally in RawNode encoding // thus the max value is 30-bit. - const MAX: u32 = u32::MAX >> 2; + pub const MAX: u32 = u32::MAX >> 2; /// Constructs a new pointer from given address. /// @@ -42,14 +42,13 @@ impl Ptr { /// /// ``` /// # use core::num::NonZeroU32; - /// # use sealable_trie::memory::*; /// - /// assert_eq!(Ok(None), Ptr::new(0)); - /// assert_eq!(42, Ptr::new(42).unwrap().unwrap().get()); + /// assert_eq!(Ok(None), memory::Ptr::new(0)); + /// assert_eq!(42, memory::Ptr::new(42).unwrap().unwrap().get()); /// assert_eq!((1 << 30) - 1, - /// Ptr::new((1 << 30) - 1).unwrap().unwrap().get()); - /// assert_eq!(Err(AddressTooLarge(NonZeroU32::new(1 << 30).unwrap())), - /// Ptr::new(1 << 30)); + /// memory::Ptr::new((1 << 30) - 1).unwrap().unwrap().get()); + /// assert_eq!(Err(memory::AddressTooLarge(NonZeroU32::new(1 << 30).unwrap())), + /// memory::Ptr::new(1 << 30)); /// ``` pub const fn new(ptr: u32) -> Result, AddressTooLarge> { // Using match so the function is const @@ -64,7 +63,7 @@ impl Ptr { /// /// Two most significant bits of the address are masked out thus ensuring /// that the value is never too large. - pub(crate) fn new_truncated(ptr: u32) -> Option { + pub fn new_truncated(ptr: u32) -> Option { NonZeroU32::new(ptr & Self::MAX).map(Self) } } @@ -81,13 +80,12 @@ impl TryFrom for Ptr { /// /// ``` /// # use core::num::NonZeroU32; - /// # use sealable_trie::memory::*; /// /// let answer = NonZeroU32::new(42).unwrap(); - /// assert_eq!(42, Ptr::try_from(answer).unwrap().get()); + /// assert_eq!(42, memory::Ptr::try_from(answer).unwrap().get()); /// /// let large = NonZeroU32::new(1 << 30).unwrap(); - /// assert_eq!(Err(AddressTooLarge(large)), Ptr::try_from(large)); + /// assert_eq!(Err(memory::AddressTooLarge(large)), memory::Ptr::try_from(large)); /// ``` fn try_from(num: NonZeroU32) -> Result { if num.get() <= Ptr::MAX { @@ -112,16 +110,25 @@ impl fmt::Debug for Ptr { /// An interface for memory management used by the trie. pub trait Allocator { + type Value; + /// Allocates a new block and initialise it to given value. - fn alloc(&mut self, value: RawNode) -> Result; + fn alloc(&mut self, value: Self::Value) -> Result; + + /// Returns shared reference to value stored at given pointer. + /// + /// May panic or return garbage if `ptr` is invalid. + fn get(&self, ptr: Ptr) -> &Self::Value; - /// Returns value stored at given pointer. + /// Returns exclusive reference to value stored at given pointer. /// /// May panic or return garbage if `ptr` is invalid. - fn get(&self, ptr: Ptr) -> RawNode; + fn get_mut(&mut self, ptr: Ptr) -> &mut Self::Value; /// Sets value at given pointer. - fn set(&mut self, ptr: Ptr, value: RawNode); + fn set(&mut self, ptr: Ptr, value: Self::Value) { + *self.get_mut(ptr) = value; + } /// Frees a block. fn free(&mut self, ptr: Ptr); @@ -151,7 +158,7 @@ pub struct WriteLog<'a, A: Allocator> { alloc: &'a mut A, /// List of changes in the transaction. - write_log: Vec<(Ptr, RawNode)>, + write_log: Vec<(Ptr, A::Value)>, /// List pointers to nodes allocated during the transaction. allocated: Vec, @@ -187,13 +194,13 @@ impl<'a, A: Allocator> WriteLog<'a, A> { /// Returns underlying allocator. pub fn allocator(&self) -> &A { &*self.alloc } - pub fn alloc(&mut self, value: RawNode) -> Result { + pub fn alloc(&mut self, value: A::Value) -> Result { let ptr = self.alloc.alloc(value)?; self.allocated.push(ptr); Ok(ptr) } - pub fn set(&mut self, ptr: Ptr, value: RawNode) { + pub fn set(&mut self, ptr: Ptr, value: A::Value) { self.write_log.push((ptr, value)) } @@ -210,83 +217,104 @@ impl<'a, A: Allocator> core::ops::Drop for WriteLog<'a, A> { } } -#[cfg(test)] -pub(crate) mod test_utils { +#[cfg(any(test, feature = "test_utils"))] +pub mod test_utils { use super::*; - use crate::stdx; - pub struct TestAllocator { + pub struct TestAllocator { count: usize, - free: Option, - pool: alloc::vec::Vec, - allocated: std::collections::HashMap, + pool: alloc::vec::Vec, + free_list: std::collections::HashSet, } - impl TestAllocator { + impl TestAllocator { pub fn new(capacity: usize) -> Self { - let max_cap = usize::try_from(Ptr::MAX).unwrap_or(usize::MAX); + let max_cap = usize::try_from(Ptr::MAX - 1).unwrap_or(usize::MAX); let capacity = capacity.min(max_cap); - let mut pool = alloc::vec::Vec::with_capacity(capacity); - pool.push(RawNode([0xAA; 72])); - Self { count: 0, free: None, pool, allocated: Default::default() } + let pool = Vec::with_capacity(capacity); + Self { count: 0, pool, free_list: Default::default() } } pub fn count(&self) -> usize { self.count } + /// Gets index in the memory pool for the given pointer. + /// + /// Panics if the value of the pointer overflows `usize`. This can only + /// happen if `usize` is smaller than `u32` and unallocated pointer was + /// given. + fn index_from_ptr(ptr: Ptr) -> usize { + usize::try_from(ptr.get() - 1).unwrap() + } + + /// Converts index in the memory pool into a pointer. + /// + /// Panics if the resulting pointer’s value would be higher than + /// [`Ptr::MAX`]. + fn ptr_from_index(index: usize) -> Ptr { + Ptr::new(u32::try_from(index + 1).unwrap()).unwrap().unwrap() + } + /// Verifies that block has been allocated. Panics if it hasn’t. + #[track_caller] fn check_allocated(&self, action: &str, ptr: Ptr) -> usize { - let adj = match self.allocated.get(&ptr.get()).copied() { - None => "unallocated", - Some(false) => "freed", - Some(true) => return usize::try_from(ptr.get()).unwrap(), + let index = Self::index_from_ptr(ptr); + let adj = if index >= self.pool.len() { + "unallocated" + } else if self.free_list.contains(&ptr) { + "freed" + } else { + return index; }; panic!("Tried to {action} {adj} block at {ptr}") } } - impl Allocator for TestAllocator { - fn alloc(&mut self, value: RawNode) -> Result { - let ptr = if let Some(ptr) = self.free { - // Grab node from free list - let node = &mut self.pool[ptr.get() as usize]; - let bytes = stdx::split_array_ref::<4, 68, 72>(&node.0).0; - self.free = Ptr::new(u32::from_ne_bytes(*bytes)).unwrap(); - *node = value; - ptr + impl Allocator for TestAllocator { + type Value = T; + + fn alloc(&mut self, value: T) -> Result { + // HashSet doesn’t have pop method so we need to do iter and remove. + if let Some(ptr) = self.free_list.iter().next().copied() { + // Grab node from free list. + self.free_list.remove(&ptr); + self.pool[Self::index_from_ptr(ptr)] = value; + self.count += 1; + Ok(ptr) } else if self.pool.len() < self.pool.capacity() { // Grab new node self.pool.push(value); - Ptr::new((self.pool.len() - 1) as u32).unwrap().unwrap() + self.count += 1; + Ok(Self::ptr_from_index(self.pool.len() - 1)) } else { // No free node to allocate - return Err(OutOfMemory); - }; + Err(OutOfMemory) + } + } - assert!( - self.allocated.insert(ptr.get(), true) != Some(true), - "Internal error: Allocated the same block twice at {ptr}", - ); - self.count += 1; - Ok(ptr) + #[track_caller] + fn get(&self, ptr: Ptr) -> &T { + &self.pool[self.check_allocated("read", ptr)] } #[track_caller] - fn get(&self, ptr: Ptr) -> RawNode { - self.pool[self.check_allocated("read", ptr)].clone() + fn get_mut(&mut self, ptr: Ptr) -> &mut T { + let idx = self.check_allocated("access", ptr); + &mut self.pool[idx] } #[track_caller] - fn set(&mut self, ptr: Ptr, value: RawNode) { - let idx = self.check_allocated("read", ptr); - self.pool[idx] = value + fn set(&mut self, ptr: Ptr, value: T) { + let idx = self.check_allocated("set", ptr); + self.pool[idx] = value; } + #[track_caller] fn free(&mut self, ptr: Ptr) { - let idx = self.check_allocated("free", ptr); - self.allocated.insert(ptr.get(), false); - *stdx::split_array_mut::<4, 68, 72>(&mut self.pool[idx].0).0 = - self.free.map_or(0, |ptr| ptr.get()).to_ne_bytes(); - self.free = Some(ptr); + if self.check_allocated("free", ptr) == self.pool.len() - 1 { + self.pool.pop(); + } else { + self.free_list.insert(ptr); + } self.count -= 1; } } @@ -294,37 +322,28 @@ pub(crate) mod test_utils { #[cfg(test)] mod test_write_log { - use super::test_utils::TestAllocator; use super::*; - use crate::hash::CryptoHash; - fn make_allocator() -> (TestAllocator, Vec) { - let mut alloc = TestAllocator::new(100); - let ptrs = (0..10) - .map(|num| alloc.alloc(make_node(num)).unwrap()) - .collect::>(); + fn make_allocator() -> (test_utils::TestAllocator, Vec) { + let mut alloc = test_utils::TestAllocator::new(100); + let ptrs = + (0..10).map(|num| alloc.alloc(num).unwrap()).collect::>(); assert_nodes(10, &alloc, &ptrs, 0); (alloc, ptrs) } - fn make_node(num: usize) -> RawNode { - let hash = CryptoHash::test(num); - let child = crate::nodes::Reference::node(None, &hash); - RawNode::branch(child, child) - } - #[track_caller] fn assert_nodes( count: usize, - alloc: &TestAllocator, + alloc: &test_utils::TestAllocator, ptrs: &[Ptr], offset: usize, ) { assert_eq!(count, alloc.count()); for (idx, ptr) in ptrs.iter().enumerate() { assert_eq!( - make_node(idx + offset), - alloc.get(*ptr), + idx + offset, + *alloc.get(*ptr), "Invalid value when reading {ptr}" ); } @@ -335,7 +354,7 @@ mod test_write_log { let (mut alloc, ptrs) = make_allocator(); let mut wlog = WriteLog::new(&mut alloc); for (idx, &ptr) in ptrs.iter().take(5).enumerate() { - wlog.set(ptr, make_node(idx + 10)); + wlog.set(ptr, idx + 10); } assert_nodes(10, wlog.allocator(), &ptrs, 0); wlog.commit(); @@ -348,7 +367,7 @@ mod test_write_log { let (mut alloc, ptrs) = make_allocator(); let mut wlog = WriteLog::new(&mut alloc); for (idx, &ptr) in ptrs.iter().take(5).enumerate() { - wlog.set(ptr, make_node(idx + 10)); + wlog.set(ptr, idx + 10); } assert_nodes(10, wlog.allocator(), &ptrs, 0); core::mem::drop(wlog); @@ -359,9 +378,8 @@ mod test_write_log { fn test_alloc_commit() { let (mut alloc, ptrs) = make_allocator(); let mut wlog = WriteLog::new(&mut alloc); - let new_ptrs = (10..20) - .map(|num| wlog.alloc(make_node(num)).unwrap()) - .collect::>(); + let new_ptrs = + (10..20).map(|num| wlog.alloc(num).unwrap()).collect::>(); assert_nodes(20, &wlog.allocator(), &ptrs, 0); assert_nodes(20, &wlog.allocator(), &new_ptrs, 10); wlog.commit(); @@ -373,9 +391,8 @@ mod test_write_log { fn test_alloc_rollback() { let (mut alloc, ptrs) = make_allocator(); let mut wlog = WriteLog::new(&mut alloc); - let new_ptrs = (10..20) - .map(|num| wlog.alloc(make_node(num)).unwrap()) - .collect::>(); + let new_ptrs = + (10..20).map(|num| wlog.alloc(num).unwrap()).collect::>(); assert_nodes(20, &wlog.allocator(), &ptrs, 0); assert_nodes(20, &wlog.allocator(), &new_ptrs, 10); core::mem::drop(wlog); diff --git a/sealable-trie/Cargo.toml b/sealable-trie/Cargo.toml index b6e04c9b..507e7e27 100644 --- a/sealable-trie/Cargo.toml +++ b/sealable-trie/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "sealable-trie" +authors = ["Michal Nazarewicz "] version = "0.0.0" edition = "2021" @@ -9,6 +10,11 @@ derive_more.workspace = true sha2.workspace = true strum.workspace = true +memory.workspace = true +stdx.workspace = true + [dev-dependencies] pretty_assertions.workspace = true rand.workspace = true + +memory = { workspace = true, features = ["test_utils"] } diff --git a/sealable-trie/src/bits.rs b/sealable-trie/src/bits.rs index ffd3dcba..a9145450 100644 --- a/sealable-trie/src/bits.rs +++ b/sealable-trie/src/bits.rs @@ -3,7 +3,7 @@ use core::fmt; #[cfg(test)] use pretty_assertions::assert_eq; -use crate::{nodes, stdx}; +use crate::nodes; /// Representation of a slice of bits. /// diff --git a/sealable-trie/src/lib.rs b/sealable-trie/src/lib.rs index 49500c3b..995dc4ce 100644 --- a/sealable-trie/src/lib.rs +++ b/sealable-trie/src/lib.rs @@ -5,10 +5,8 @@ extern crate std; pub mod bits; pub mod hash; -pub mod memory; pub mod nodes; pub mod proof; -pub(crate) mod stdx; pub mod trie; #[cfg(test)] diff --git a/sealable-trie/src/nodes.rs b/sealable-trie/src/nodes.rs index 88ddae53..fe48b877 100644 --- a/sealable-trie/src/nodes.rs +++ b/sealable-trie/src/nodes.rs @@ -1,7 +1,8 @@ +use memory::Ptr; + +use crate::bits; use crate::bits::Slice; use crate::hash::CryptoHash; -use crate::memory::Ptr; -use crate::{bits, stdx}; #[cfg(test)] mod stress_tests; @@ -103,7 +104,7 @@ pub enum Node<'a, P = Option, S = bool> { // The actual pointer value is therefore 30-bit long. #[derive(Clone, Copy, PartialEq, derive_more::Deref)] #[repr(transparent)] -pub struct RawNode(pub(crate) [u8; 72]); +pub struct RawNode(pub(crate) [u8; RawNode::SIZE]); /// Reference either to a node or a value as held in Branch or Extension nodes. /// @@ -250,9 +251,12 @@ fn hash_extension_slow_path( } impl RawNode { + /// Size of the byte buffer used for a node encoding. + pub const SIZE: usize = 72; + /// Constructs a Branch node with specified children. pub fn branch(left: Reference, right: Reference) -> Self { - let mut res = Self([0; 72]); + let mut res = Self([0; RawNode::SIZE]); let (lft, rht) = res.halfs_mut(); *lft = left.encode(); *rht = right.encode(); @@ -265,7 +269,7 @@ impl RawNode { /// slice is too long. The slice must not exceed [`MAX_EXTENSION_KEY_SIZE`] /// to be valid. pub fn extension(key: Slice, child: Reference) -> Option { - let mut res = Self([0; 72]); + let mut res = Self([0; RawNode::SIZE]); let (lft, rht) = res.halfs_mut(); key.encode_into(lft, 0x80)?; *rht = child.encode(); @@ -274,7 +278,7 @@ impl RawNode { /// Constructs a Value node with given value hash and child. pub fn value(value: ValueRef, child: NodeRef) -> Self { - let mut res = Self([0; 72]); + let mut res = Self([0; RawNode::SIZE]); let (lft, rht) = res.halfs_mut(); *lft = Reference::Value(value).encode(); lft[0] |= 0x80; @@ -323,13 +327,11 @@ impl RawNode { fn first(&self) -> u8 { self.0[0] } /// Splits the raw byte representation in two halfs. - fn halfs(&self) -> (&[u8; 36], &[u8; 36]) { - stdx::split_array_ref::<36, 36, 72>(&self.0) - } + fn halfs(&self) -> (&[u8; 36], &[u8; 36]) { stdx::split_array_ref(&self.0) } /// Splits the raw byte representation in two halfs. fn halfs_mut(&mut self) -> (&mut [u8; 36], &mut [u8; 36]) { - stdx::split_array_mut::<36, 36, 72>(&mut self.0) + stdx::split_array_mut(&mut self.0) } } @@ -467,6 +469,16 @@ impl<'a> ValueRef<'a, bool> { } +// ============================================================================= +// Conversions + +impl<'a> From<&'a [u8; RawNode::SIZE]> for &'a RawNode { + fn from(bytes: &'a [u8; RawNode::SIZE]) -> &'a RawNode { + // SAFETY: RawNode is repr(transparent). + unsafe { &*bytes.as_ptr().cast() } + } +} + // ============================================================================= // PartialEq diff --git a/sealable-trie/src/nodes/stress_tests.rs b/sealable-trie/src/nodes/stress_tests.rs index ba73d74e..7a36525e 100644 --- a/sealable-trie/src/nodes/stress_tests.rs +++ b/sealable-trie/src/nodes/stress_tests.rs @@ -5,18 +5,18 @@ //! it performs can be controlled by STRESS_TEST_ITERATIONS environment //! variable. +use memory::Ptr; use pretty_assertions::assert_eq; -use crate::memory::Ptr; +use crate::bits; use crate::nodes::{self, Node, NodeRef, RawNode, Reference, ValueRef}; use crate::test_utils::get_iteration_count; -use crate::{bits, stdx}; /// Generates random raw representation and checks decode→encode round-trip. #[test] fn stress_test_raw_encoding_round_trip() { let mut rng = rand::thread_rng(); - let mut raw = RawNode([0; 72]); + let mut raw = RawNode([0; RawNode::SIZE]); for _ in 0..get_iteration_count() { gen_random_raw_node(&mut rng, &mut raw.0); let node = raw.decode(); @@ -26,7 +26,10 @@ fn stress_test_raw_encoding_round_trip() { } /// Generates a random raw node representation in canonical representation. -fn gen_random_raw_node(rng: &mut impl rand::Rng, bytes: &mut [u8; 72]) { +fn gen_random_raw_node( + rng: &mut impl rand::Rng, + bytes: &mut [u8; RawNode::SIZE], +) { fn make_ref_canonical(bytes: &mut [u8]) { if bytes[0] & 0x40 == 0 { // Node reference. Pointer can be non-zero. diff --git a/sealable-trie/src/nodes/tests.rs b/sealable-trie/src/nodes/tests.rs index e1e8dd5e..51cca366 100644 --- a/sealable-trie/src/nodes/tests.rs +++ b/sealable-trie/src/nodes/tests.rs @@ -1,10 +1,10 @@ use base64::engine::general_purpose::STANDARD as BASE64_ENGINE; use base64::Engine; +use memory::Ptr; use pretty_assertions::assert_eq; use crate::bits; use crate::hash::CryptoHash; -use crate::memory::Ptr; use crate::nodes::{Node, NodeRef, RawNode, Reference, ValueRef}; const DEAD: Ptr = match Ptr::new(0xDEAD) { @@ -45,7 +45,7 @@ pub(super) fn raw_from_node(node: &Node) -> RawNode { /// 4. If node is an Extension, checks if slow path hash calculation produces /// the same hash. #[track_caller] -fn check_node_encoding(node: Node, want: [u8; 72], want_hash: &str) { +fn check_node_encoding(node: Node, want: [u8; RawNode::SIZE], want_hash: &str) { let raw = raw_from_node(&node); assert_eq!(want, raw.0, "Unexpected raw representation"); assert_eq!(node, RawNode(want).decode(), "Bad Raw→Node conversion"); diff --git a/sealable-trie/src/trie.rs b/sealable-trie/src/trie.rs index 396430c8..58b79c11 100644 --- a/sealable-trie/src/trie.rs +++ b/sealable-trie/src/trie.rs @@ -1,9 +1,10 @@ use core::num::NonZeroU16; +use memory::Ptr; + use crate::hash::CryptoHash; -use crate::memory::Ptr; -use crate::nodes::{Node, NodeRef, Reference}; -use crate::{bits, memory, proof}; +use crate::nodes::{Node, NodeRef, RawNode, Reference}; +use crate::{bits, proof}; mod seal; mod set; @@ -90,6 +91,7 @@ impl From for Error { } type Result = ::core::result::Result; +type Value = [u8; crate::nodes::RawNode::SIZE]; macro_rules! proof { ($proof:ident push $item:expr) => { @@ -103,7 +105,7 @@ macro_rules! proof { }; } -impl Trie { +impl> Trie { /// Creates a new trie using given allocator. pub fn new(alloc: A) -> Self { Self { root_ptr: None, root_hash: EMPTY_TRIE_ROOT, alloc } @@ -152,7 +154,7 @@ impl Trie { let mut node_hash = self.root_hash.clone(); loop { let node = self.alloc.get(node_ptr.ok_or(Error::Sealed)?); - let node = node.decode(); + let node = <&RawNode>::from(node).decode(); debug_assert_eq!(node_hash, node.hash()); let child = match node { @@ -288,7 +290,7 @@ impl Trie { println!(" (sealed)"); return; }; - match self.alloc.get(ptr).decode() { + match <&RawNode>::from(self.alloc.get(ptr)).decode() { Node::Branch { children } => { println!(" Branch"); print_ref(children[0], depth + 2); @@ -309,7 +311,7 @@ impl Trie { #[cfg(test)] -impl Trie { +impl Trie> { /// Creates a test trie using a TestAllocator with given capacity. pub(crate) fn test(capacity: usize) -> Self { Self::new(memory::test_utils::TestAllocator::new(capacity)) diff --git a/sealable-trie/src/trie/seal.rs b/sealable-trie/src/trie/seal.rs index 50e68fdb..e29e2018 100644 --- a/sealable-trie/src/trie/seal.rs +++ b/sealable-trie/src/trie/seal.rs @@ -1,9 +1,10 @@ use alloc::vec::Vec; +use memory::Ptr; + use super::{Error, Result}; -use crate::memory::Ptr; +use crate::bits; use crate::nodes::{Node, NodeRef, RawNode, Reference, ValueRef}; -use crate::{bits, memory}; /// Context for [`Trie::seal`] operation. pub(super) struct SealContext<'a, A> { @@ -17,7 +18,7 @@ pub(super) struct SealContext<'a, A> { alloc: &'a mut A, } -impl<'a, A: memory::Allocator> SealContext<'a, A> { +impl<'a, A: memory::Allocator> SealContext<'a, A> { pub(super) fn new(alloc: &'a mut A, key: bits::Slice<'a>) -> Self { Self { key, alloc } } @@ -29,7 +30,7 @@ impl<'a, A: memory::Allocator> SealContext<'a, A> { /// that `ptr` has been freed and it has to update references to it. pub(super) fn seal(&mut self, nref: NodeRef) -> Result { let ptr = nref.ptr.ok_or(Error::Sealed)?; - let node = self.alloc.get(ptr); + let node = RawNode(self.alloc.get(ptr).clone()); let node = node.decode(); debug_assert_eq!(*nref.hash, node.hash()); @@ -41,7 +42,7 @@ impl<'a, A: memory::Allocator> SealContext<'a, A> { match result { SealResult::Replace(node) => { - self.alloc.set(ptr, node); + self.alloc.set(ptr, *node); Ok(false) } SealResult::Free => { @@ -136,14 +137,17 @@ enum SealResult { } /// Frees node and all its descendants from the allocator. -fn prune(alloc: &mut impl memory::Allocator, ptr: Option) { +fn prune( + alloc: &mut impl memory::Allocator, + ptr: Option, +) { let mut ptr = match ptr { Some(ptr) => ptr, None => return, }; let mut queue = Vec::new(); loop { - let children = get_children(&alloc.get(ptr)); + let children = get_children(alloc.get(ptr).into()); alloc.free(ptr); match children { (None, None) => match queue.pop() { diff --git a/sealable-trie/src/trie/set.rs b/sealable-trie/src/trie/set.rs index 2a4cbd39..f02355b0 100644 --- a/sealable-trie/src/trie/set.rs +++ b/sealable-trie/src/trie/set.rs @@ -1,11 +1,12 @@ +use memory::Ptr; + use super::{Error, Result}; +use crate::bits; use crate::hash::CryptoHash; -use crate::memory::Ptr; use crate::nodes::{Node, NodeRef, RawNode, Reference, ValueRef}; -use crate::{bits, memory}; /// Context for [`Trie::set`] operation. -pub(super) struct SetContext<'a, A: memory::Allocator> { +pub(super) struct SetContext<'a, A: memory::Allocator> { /// Part of the key yet to be traversed. /// /// It starts as the key user provided and as trie is traversed bits are @@ -19,7 +20,7 @@ pub(super) struct SetContext<'a, A: memory::Allocator> { wlog: memory::WriteLog<'a, A>, } -impl<'a, A: memory::Allocator> SetContext<'a, A> { +impl<'a, A: memory::Allocator> SetContext<'a, A> { pub(super) fn new( alloc: &'a mut A, key: bits::Slice<'a>, @@ -62,7 +63,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { /// Inserts value into the trie starting at node pointed by given reference. fn handle(&mut self, nref: NodeRef) -> Result<(Ptr, CryptoHash)> { let nref = (nref.ptr.ok_or(Error::Sealed)?, nref.hash); - let node = self.wlog.allocator().get(nref.0); + let node = RawNode(self.wlog.allocator().get(nref.0).clone()); let node = node.decode(); debug_assert_eq!(*nref.1, node.hash()); match node { @@ -317,14 +318,14 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { /// Sets value of a node cell at given address and returns its hash. fn set_node(&mut self, ptr: Ptr, node: RawNode) -> (Ptr, CryptoHash) { let hash = node.decode().hash(); - self.wlog.set(ptr, node); + self.wlog.set(ptr, *node); (ptr, hash) } /// Allocates a new node and sets it to given value. fn alloc_node(&mut self, node: RawNode) -> Result<(Ptr, CryptoHash)> { let hash = node.decode().hash(); - let ptr = self.wlog.alloc(node)?; + let ptr = self.wlog.alloc(*node)?; Ok((ptr, hash)) } } diff --git a/sealable-trie/src/trie/tests.rs b/sealable-trie/src/trie/tests.rs index a88d634b..9410651e 100644 --- a/sealable-trie/src/trie/tests.rs +++ b/sealable-trie/src/trie/tests.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; use std::println; +use memory::test_utils::TestAllocator; use rand::Rng; use crate::hash::CryptoHash; -use crate::memory::test_utils::TestAllocator; fn do_test_inserts<'a>( keys: impl IntoIterator, @@ -114,7 +114,7 @@ impl core::fmt::Debug for Key { } struct TestTrie { - trie: super::Trie, + trie: super::Trie>, mapping: HashMap, count: usize, } diff --git a/stdx/Cargo.toml b/stdx/Cargo.toml new file mode 100644 index 00000000..8bcf7c03 --- /dev/null +++ b/stdx/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "stdx" +version = "0.0.0" +edition = "2021" diff --git a/sealable-trie/src/stdx.rs b/stdx/src/lib.rs similarity index 76% rename from sealable-trie/src/stdx.rs rename to stdx/src/lib.rs index 5431d01d..bff33cfb 100644 --- a/sealable-trie/src/stdx.rs +++ b/stdx/src/lib.rs @@ -1,9 +1,5 @@ /// Splits `&[u8; L + R]` into `(&[u8; L], &[u8; R])`. -pub(crate) fn split_array_ref< - const L: usize, - const R: usize, - const N: usize, ->( +pub fn split_array_ref( xs: &[u8; N], ) -> (&[u8; L], &[u8; R]) { let () = AssertEqSum::::OK; @@ -13,11 +9,7 @@ pub(crate) fn split_array_ref< } /// Splits `&mut [u8; L + R]` into `(&mut [u8; L], &mut [u8; R])`. -pub(crate) fn split_array_mut< - const L: usize, - const R: usize, - const N: usize, ->( +pub fn split_array_mut( xs: &mut [u8; N], ) -> (&mut [u8; L], &mut [u8; R]) { let () = AssertEqSum::::OK; @@ -28,7 +20,7 @@ pub(crate) fn split_array_mut< /// Splits `&[u8]` into `(&[u8; L], &[u8])`. Returns `None` if input is too /// shorter. -pub(crate) fn split_at(xs: &[u8]) -> Option<(&[u8; L], &[u8])> { +pub fn split_at(xs: &[u8]) -> Option<(&[u8; L], &[u8])> { if xs.len() < L { return None; } @@ -39,9 +31,7 @@ pub(crate) fn split_at(xs: &[u8]) -> Option<(&[u8; L], &[u8])> { /// Splits `&[u8]` into `(&[u8], &[u8; R])`. Returns `None` if input is too /// shorter. #[allow(dead_code)] -pub(crate) fn rsplit_at( - xs: &[u8], -) -> Option<(&[u8], &[u8; R])> { +pub fn rsplit_at(xs: &[u8]) -> Option<(&[u8], &[u8; R])> { let (head, tail) = xs.split_at(xs.len().checked_sub(R)?); Some((head, tail.try_into().unwrap())) }