#[derive(Default)]
pub struct MemOptimizedTrieNode<CacheAlgoDataT: CacheAlgoDataTrait> {
slab_next_vacant_index: u32,
path_mask: u8,
path_size: u16,
path: MaybeInPlaceByteArray,
path_memory_manager: FieldsOffsetMaybeInPlaceByteArrayMemoryManager<
u16,
TrivialSizeFieldConverterU16,
MemOptimizedTrieNodePathMemoryManager<CacheAlgoDataT>,
MemOptimizedTrieNodePathMemoryManager<CacheAlgoDataT>,
>,
pub(super) children_table: ChildrenTableDeltaMpt,
value_size: u32,
value: MaybeInPlaceByteArray,
value_memory_manager: FieldsOffsetMaybeInPlaceByteArrayMemoryManager<
u32,
ValueSizeFieldConverter,
MemOptimizedTrieNodeValueMemoryManager<CacheAlgoDataT>,
MemOptimizedTrieNodeValueMemoryManager<CacheAlgoDataT>,
>,
merkle_hash: MerkleHash,
pub(in super::super) cache_algo_data: CacheAlgoDataT,
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> MallocSizeOf
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let path_malloc_size = if self.path_memory_manager.get_size()
> MaybeInPlaceByteArray::MAX_INPLACE_SIZE
{
unsafe { ops.malloc_size_of(self.path.ptr) }
} else {
0
};
let value_malloc_size = if self.value_memory_manager.get_size()
> MaybeInPlaceByteArray::MAX_INPLACE_SIZE
{
unsafe { ops.malloc_size_of(self.value.ptr) }
} else {
0
};
self.children_table.size_of(ops)
+ self.cache_algo_data.size_of(ops)
+ value_malloc_size
+ path_malloc_size
}
}
pub enum TrieNodeAction {
Modify,
Delete,
MergePath {
child_index: u8,
child_node_ref: NodeRefDeltaMpt,
},
}
#[cfg(test)]
use super::node_memory_manager::TrieNodeDeltaMpt;
#[test]
fn test_mem_optimized_trie_node_size() {
assert_eq!(std::mem::size_of::<TrieNodeDeltaMpt>(), 80);
assert_ne!(std::mem::size_of::<Entry<TrieNodeDeltaMpt>>(), 80)
}
make_parallel_field_maybe_in_place_byte_array_memory_manager!(
MemOptimizedTrieNodePathMemoryManager<CacheAlgoDataT> where <CacheAlgoDataT: CacheAlgoDataTrait>,
MemOptimizedTrieNode<CacheAlgoDataT>,
path_memory_manager,
path,
path_size: u16,
TrivialSizeFieldConverterU16,
);
make_parallel_field_maybe_in_place_byte_array_memory_manager!(
MemOptimizedTrieNodeValueMemoryManager<CacheAlgoDataT> where <CacheAlgoDataT: CacheAlgoDataTrait>,
MemOptimizedTrieNode<CacheAlgoDataT>,
value_memory_manager,
value,
value_size: u32,
ValueSizeFieldConverter,
);
impl<CacheAlgoDataT: CacheAlgoDataTrait> Clone
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn clone(&self) -> Self {
Self::new(
self.merkle_hash.clone(),
self.children_table.clone(),
self.value_clone().into_option(),
self.compressed_path_ref().into(),
)
}
}
unsafe impl<CacheAlgoDataT: CacheAlgoDataTrait> Send
for MemOptimizedTrieNode<CacheAlgoDataT>
{
}
unsafe impl<CacheAlgoDataT: CacheAlgoDataTrait> Sync
for MemOptimizedTrieNode<CacheAlgoDataT>
{
}
#[derive(Default, Debug)]
struct ValueSizeFieldConverter {}
impl ValueSizeFieldConverter {
const MAX_VALUE_SIZE: usize = 0xfffffffe;
const VALUE_TOMBSTONE: u32 = 0xffffffff;
}
impl SizeFieldConverterTrait<u32> for ValueSizeFieldConverter {
fn max_size() -> usize { Self::MAX_VALUE_SIZE }
fn is_size_over_limit(size: usize) -> bool { size > Self::MAX_VALUE_SIZE }
fn get(size_field: &u32) -> usize {
if *size_field == Self::VALUE_TOMBSTONE {
0
} else {
(*size_field) as usize
}
}
fn set(size_field: &mut u32, size: usize) { *size_field = size as u32; }
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> MemOptimizedTrieNode<CacheAlgoDataT> {
pub fn get_compressed_path_size(&self) -> u16 { self.path_size }
pub fn copy_compressed_path<CompressedPath: CompressedPathTrait>(
&mut self, new_path: CompressedPath,
) {
unsafe {
self.path_memory_manager.drop_value();
}
self.path_size = new_path.path_size();
self.path_mask = new_path.path_mask();
let path_slice = new_path.path_slice();
self.path =
MaybeInPlaceByteArray::copy_from(path_slice, path_slice.len());
}
pub fn value_clone(&self) -> MptValue<Box<[u8]>> {
let size = self.value_size;
if size == 0 {
MptValue::None
} else if size == ValueSizeFieldConverter::VALUE_TOMBSTONE {
MptValue::TombStone
} else {
MptValue::Some(self.value.get_slice(size as usize).into())
}
}
fn value_into_boxed_slice(&mut self) -> MptValue<Box<[u8]>> {
let size = self.value_size;
let maybe_value;
if size == 0 {
maybe_value = MptValue::None;
} else {
if size == ValueSizeFieldConverter::VALUE_TOMBSTONE {
maybe_value = MptValue::TombStone
} else {
maybe_value =
MptValue::Some(self.value.into_boxed_slice(size as usize));
}
self.value_size = 0;
}
maybe_value
}
pub fn check_value_size(value: &[u8]) -> Result<()> {
let value_size = value.len();
if ValueSizeFieldConverter::is_size_over_limit(value_size) {
return Err(Error::MPTInvalidValueLength {
length: value_size,
length_limit: ValueSizeFieldConverter::max_size(),
});
}
Ok(())
}
pub fn check_key_size(access_key: &[u8]) -> Result<()> {
let key_size = access_key.len();
if TrivialSizeFieldConverterU16::is_size_over_limit(key_size) {
return Err(Error::MPTInvalidKeyLength {
length: key_size,
length_limit: TrivialSizeFieldConverterU16::max_size(),
});
}
if key_size == 0 {
return Err(Error::MPTInvalidKeyLength {
length: key_size,
length_limit: TrivialSizeFieldConverterU16::max_size(),
});
}
Ok(())
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> TrieNodeTrait
for MemOptimizedTrieNode<CacheAlgoDataT>
{
type ChildrenTableType = ChildrenTableDeltaMpt;
type NodeRefType = NodeRefDeltaMptCompact;
fn compressed_path_ref(&self) -> CompressedPathRef {
let size = self.path_size;
CompressedPathRef::new(
self.path.get_slice(size as usize),
self.path_mask,
)
}
fn has_value(&self) -> bool { self.value_size > 0 }
fn get_children_count(&self) -> u8 {
self.children_table.get_children_count()
}
fn value_as_slice(&self) -> MptValue<&[u8]> {
let size = self.value_size;
if size == 0 {
MptValue::None
} else if size == ValueSizeFieldConverter::VALUE_TOMBSTONE {
MptValue::TombStone
} else {
MptValue::Some(self.value.get_slice(size as usize))
}
}
fn set_compressed_path(&mut self, mut path: CompressedPathRaw) {
self.path_mask = path.path_mask();
path.byte_array_memory_manager
.move_to(&mut self.path_memory_manager);
}
unsafe fn add_new_child_unchecked<T>(&mut self, child_index: u8, child: T)
where ChildrenTableItem<NodeRefDeltaMptCompact>:
WrappedCreateFrom<T, NodeRefDeltaMptCompact> {
self.children_table = CompactedChildrenTable::insert_child_unchecked(
self.children_table.to_ref(),
child_index,
ChildrenTableItem::<NodeRefDeltaMptCompact>::take(child),
);
}
unsafe fn get_child_mut_unchecked(
&mut self, child_index: u8,
) -> &mut Self::NodeRefType {
self.children_table.get_child_mut_unchecked(child_index)
}
unsafe fn replace_child_unchecked<T>(&mut self, child_index: u8, child: T)
where ChildrenTableItem<NodeRefDeltaMptCompact>:
WrappedCreateFrom<T, NodeRefDeltaMptCompact> {
self.children_table.set_child_unchecked(
child_index,
ChildrenTableItem::<NodeRefDeltaMptCompact>::take(child),
);
}
unsafe fn delete_child_unchecked(&mut self, child_index: u8) {
self.children_table = CompactedChildrenTable::delete_child_unchecked(
self.children_table.to_ref(),
child_index,
);
}
unsafe fn delete_value_unchecked(&mut self) -> Box<[u8]> {
self.value_into_boxed_slice().unwrap()
}
fn replace_value_valid(
&mut self, valid_value: Box<[u8]>,
) -> MptValue<Box<[u8]>> {
let old_value = self.value_into_boxed_slice();
let value_size = valid_value.len();
if value_size == 0 {
self.value_size = ValueSizeFieldConverter::VALUE_TOMBSTONE;
} else {
self.value = MaybeInPlaceByteArray::new(valid_value, value_size);
self.value_size = value_size as u32;
}
old_value
}
fn get_children_table_ref(&self) -> &Self::ChildrenTableType {
&self.children_table
}
}
impl<'node, CacheAlgoDataT: CacheAlgoDataTrait> GetChildTrait<'node>
for MemOptimizedTrieNode<CacheAlgoDataT>
{
type ChildIdType = NodeRefDeltaMptCompact;
fn get_child(
&'node self, child_index: u8,
) -> Option<NodeRefDeltaMptCompact> {
self.children_table.get_child(child_index)
}
}
impl<'node, CacheAlgoDataT: CacheAlgoDataTrait> TrieNodeWalkTrait<'node>
for MemOptimizedTrieNode<CacheAlgoDataT>
{
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> MemOptimizedTrieNode<CacheAlgoDataT> {
pub fn new(
merkle: MerkleHash, children_table: ChildrenTableDeltaMpt,
maybe_value: Option<Box<[u8]>>, compressed_path: CompressedPathRaw,
) -> MemOptimizedTrieNode<CacheAlgoDataT> {
let mut ret = MemOptimizedTrieNode::default();
ret.merkle_hash = merkle;
ret.children_table = children_table;
match maybe_value {
None => {}
Some(value) => {
ret.replace_value_valid(value);
}
}
ret.set_compressed_path(compressed_path);
ret
}
pub unsafe fn copy_and_replace_fields(
&self, new_value: Option<Option<Box<[u8]>>>,
new_path: Option<CompressedPathRaw>,
children_table: Option<ChildrenTableDeltaMpt>,
) -> MemOptimizedTrieNode<CacheAlgoDataT> {
let mut ret = MemOptimizedTrieNode::default();
match new_value {
Some(maybe_value) => match maybe_value {
Some(value) => {
ret.replace_value_valid(value);
}
None => {}
},
None => {
let byte_size = self.value_memory_manager.get_size();
ret.value_size = self.value_size;
ret.value = MaybeInPlaceByteArray::copy_from(
self.value.get_slice(byte_size),
byte_size,
);
}
}
match new_path {
Some(path) => ret.set_compressed_path(path),
None => ret.copy_compressed_path(self.compressed_path_ref()),
}
match children_table {
Some(table) => ret.children_table = table,
None => ret.children_table = self.children_table.clone(),
}
ret
}
pub fn check_delete_value(&self) -> Result<TrieNodeAction> {
if self.has_value() {
Ok(match self.get_children_count() {
0 => TrieNodeAction::Delete,
1 => self.merge_path_action(),
_ => TrieNodeAction::Modify,
})
} else {
Err(Error::MPTKeyNotFound.into())
}
}
fn merge_path_action(&self) -> TrieNodeAction {
let (i, node_ref) = self
.children_table
.iter()
.next()
.expect("Only called when children_count == 1");
TrieNodeAction::MergePath {
child_index: i,
child_node_ref: (*node_ref).into(),
}
}
fn merge_path_action_after_child_deletion(
&self, child_index: u8,
) -> TrieNodeAction {
for (i, node_ref) in self.children_table.iter() {
if i != child_index {
return TrieNodeAction::MergePath {
child_index: i,
child_node_ref: (*node_ref).into(),
};
}
}
unsafe { unreachable_unchecked() }
}
pub unsafe fn set_first_child_unchecked(
&mut self, child_index: u8, child: NodeRefDeltaMptCompact,
) {
self.children_table =
ChildrenTableDeltaMpt::new_from_one_child(child_index, child);
}
pub fn check_replace_or_delete_child_action(
&self, child_index: u8, new_child_node: Option<NodeRefDeltaMptCompact>,
) -> TrieNodeAction {
if new_child_node.is_none() {
match self.get_children_count() {
2 => {
return self
.merge_path_action_after_child_deletion(child_index);
}
_ => {}
}
}
return TrieNodeAction::Modify;
}
pub fn get_merkle(&self) -> &MerkleHash { &self.merkle_hash }
pub fn set_merkle(&mut self, merkle: &MerkleHash) {
self.merkle_hash = merkle.clone();
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> EntryTrait
for MemOptimizedTrieNode<CacheAlgoDataT>
{
type EntryType = MemOptimizedTrieNode<CacheAlgoDataT>;
fn from_value(value: Self) -> Self { value }
fn from_vacant_index(next: usize) -> Self {
Self {
slab_next_vacant_index: next as u32,
children_table: Default::default(),
merkle_hash: Default::default(),
path_mask: CompressedPathRaw::NO_MISSING_NIBBLE,
path_size: 0,
path: Default::default(),
path_memory_manager: Default::default(),
value_size: 0,
value: Default::default(),
value_memory_manager: Default::default(),
cache_algo_data: Default::default(),
}
}
fn is_vacant(&self) -> bool {
self.slab_next_vacant_index as CompactNodeRef
!= MaybeNodeRefDeltaMptCompact::NULL
}
fn take_occupied_and_replace_with_vacant_index(
&mut self, next: usize,
) -> MemOptimizedTrieNode<CacheAlgoDataT> {
std::mem::replace(self, Self::from_vacant_index(next))
}
fn get_next_vacant_index(&self) -> usize {
self.slab_next_vacant_index as usize
}
fn get_occupied_ref(&self) -> &MemOptimizedTrieNode<CacheAlgoDataT> { self }
fn get_occupied_mut(
&mut self,
) -> &mut MemOptimizedTrieNode<CacheAlgoDataT> {
self
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> Encodable
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_unbounded_list()
.append(self.get_merkle())
.append(&self.get_children_table_ref().to_ref())
.append(&self.value_as_slice().into_option());
let compressed_path_ref = self.compressed_path_ref();
if compressed_path_ref.path_size() > 0 {
s.append(&compressed_path_ref);
}
s.finalize_unbounded_list();
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> Decodable
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn decode(rlp: &Rlp) -> ::std::result::Result<Self, DecoderError> {
let compressed_path = if rlp.item_count()? != 4 {
CompressedPathRaw::new(&[], 0)
} else {
rlp.val_at(3)?
};
Ok(MemOptimizedTrieNode::new(
MerkleHash::from_slice(rlp.val_at::<Vec<u8>>(0)?.as_slice()),
rlp.val_at::<ChildrenTableManagedDeltaMpt>(1)?.into(),
rlp.val_at::<Option<Vec<u8>>>(2)?
.map(|v| v.into_boxed_slice()),
compressed_path,
))
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> PartialEq
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn eq(&self, other: &Self) -> bool {
self.value_as_slice() == other.value_as_slice()
&& self.children_table == other.children_table
&& self.merkle_hash == other.merkle_hash
&& self.compressed_path_ref() == other.compressed_path_ref()
}
}
impl<CacheAlgoDataT: CacheAlgoDataTrait> Debug
for MemOptimizedTrieNode<CacheAlgoDataT>
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f,
"TrieNode{{ merkle: {:?}, value: {:?}, children_table: {:?}, compressed_path: {:?} }}",
self.merkle_hash, self.value_as_slice(),
&self.children_table, self.compressed_path_ref())
}
}
use super::{
super::{
super::utils::WrappedCreateFrom,
errors::*,
merkle_patricia_trie::{maybe_in_place_byte_array::*, walk::*},
},
cache::algorithm::CacheAlgoDataTrait,
node_ref::*,
slab::*,
*,
};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use primitives::{MerkleHash, MptValue};
use rlp::*;
use std::{
fmt::{Debug, Formatter},
hint::unreachable_unchecked,
marker::{Send, Sync},
vec::Vec,
};