Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEMP ON HOLD] tweak: Use a visitor pattern for more efficient SBOR traversals #1926

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions radix-common/src/data/manifest/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub type ManifestDecoder<'a> = VecDecoder<'a, ManifestCustomValueKind>;
pub type ManifestValueKind = ValueKind<ManifestCustomValueKind>;
pub type ManifestValue = Value<ManifestCustomValueKind, ManifestCustomValue>;
pub type ManifestEnumVariantValue = EnumVariantValue<ManifestCustomValueKind, ManifestCustomValue>;
#[allow(deprecated)]
pub type ManifestTraverser<'a> = VecTraverser<'a, ManifestCustomTraversal>;

pub trait ManifestCategorize: Categorize<ManifestCustomValueKind> {}
Expand Down
2 changes: 2 additions & 0 deletions radix-common/src/data/scrypto/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ pub use crate::constants::SCRYPTO_SBOR_V1_PAYLOAD_PREFIX;

pub type ScryptoEncoder<'a> = VecEncoder<'a, ScryptoCustomValueKind>;
pub type ScryptoDecoder<'a> = VecDecoder<'a, ScryptoCustomValueKind>;
#[allow(deprecated)]
pub type ScryptoTraverser<'a> = VecTraverser<'a, ScryptoCustomTraversal>;
pub type ScryptoUntypedTraverser<'a> = UntypedTraverser<'a, ScryptoCustomTraversal>;
pub type ScryptoValueKind = ValueKind<ScryptoCustomValueKind>;
pub type ScryptoValue = Value<ScryptoCustomValueKind, ScryptoCustomValue>;
// ScryptoRawValue and friends are defined in custom_payload_wrappers.rs
Expand Down
88 changes: 51 additions & 37 deletions radix-engine-interface/src/types/indexed_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,52 +17,66 @@ pub struct IndexedScryptoValue {
scrypto_value: RefCell<Option<ScryptoValue>>,
}

#[derive(Default)]
struct OwnedAndReferenceAggregator {
references: Vec<NodeId>,
owned_nodes: Vec<NodeId>,
}

impl<'de> UntypedPayloadVisitor<'de, ScryptoCustomTraversal> for OwnedAndReferenceAggregator {
type Output<'t> = Result<(), DecodeError>;

fn on_terminal_value<'t>(
&mut self,
details: OnTerminalValue<'t, 'de, ScryptoCustomTraversal>,
) -> core::ops::ControlFlow<Self::Output<'t>> {
if let traversal::TerminalValueRef::Custom(custom) = details.value {
match custom.0 {
ScryptoCustomValue::Reference(node_id) => {
self.references.push(node_id.0.into());
}
ScryptoCustomValue::Own(node_id) => {
self.owned_nodes.push(node_id.0.into());
}
ScryptoCustomValue::Decimal(_)
| ScryptoCustomValue::PreciseDecimal(_)
| ScryptoCustomValue::NonFungibleLocalId(_) => {}
}
}
core::ops::ControlFlow::Continue(())
}

fn on_error<'t>(&mut self, details: OnError<'t, ScryptoCustomTraversal>) -> Self::Output<'t> {
Err(details.error)
}

fn on_traversal_end<'t>(
&mut self,
_details: OnTraversalEnd<'t, ScryptoCustomTraversal>,
) -> Self::Output<'t> {
Ok(())
}
}

impl IndexedScryptoValue {
fn new(bytes: Vec<u8>) -> Result<Self, DecodeError> {
let mut traverser = ScryptoTraverser::new(
let mut aggregates = OwnedAndReferenceAggregator::default();
ScryptoUntypedTraverser::new(
&bytes,
ExpectedStart::PayloadPrefix(SCRYPTO_SBOR_V1_PAYLOAD_PREFIX),
VecTraverserConfig {
UntypedTraverserConfig {
max_depth: SCRYPTO_SBOR_V1_MAX_DEPTH,
check_exact_end: true,
},
);
let mut references = Vec::<NodeId>::new();
let mut owned_nodes = Vec::<NodeId>::new();
loop {
let event = traverser.next_event();
match event.event {
TraversalEvent::ContainerStart(_) => {}
TraversalEvent::ContainerEnd(_) => {}
TraversalEvent::TerminalValue(r) => {
if let traversal::TerminalValueRef::Custom(c) = r {
match c.0 {
ScryptoCustomValue::Reference(node_id) => {
references.push(node_id.0.into());
}
ScryptoCustomValue::Own(node_id) => {
owned_nodes.push(node_id.0.into());
}
ScryptoCustomValue::Decimal(_)
| ScryptoCustomValue::PreciseDecimal(_)
| ScryptoCustomValue::NonFungibleLocalId(_) => {}
}
}
}
TraversalEvent::TerminalValueBatch(_) => {}
TraversalEvent::End => {
break;
}
TraversalEvent::DecodeError(e) => {
return Err(e);
}
}
}
)
.run_from_start(
ExpectedStart::PayloadPrefix(SCRYPTO_SBOR_V1_PAYLOAD_PREFIX),
&mut aggregates,
)?;

Ok(Self {
bytes,
references,
owned_nodes,
references: aggregates.references,
owned_nodes: aggregates.owned_nodes,
scrypto_value: RefCell::new(None),
})
}
Expand Down
1 change: 1 addition & 0 deletions sbor/src/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl CustomValue<NoCustomValueKind> for NoCustomValue {

pub type BasicEncoder<'a> = VecEncoder<'a, NoCustomValueKind>;
pub type BasicDecoder<'a> = VecDecoder<'a, NoCustomValueKind>;
#[allow(deprecated)]
pub type BasicTraverser<'a> = VecTraverser<'a, NoCustomTraversal>;
pub type BasicValue = Value<NoCustomValueKind, NoCustomValue>;
pub type BasicValueKind = ValueKind<NoCustomValueKind>;
Expand Down
8 changes: 8 additions & 0 deletions sbor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,13 @@ pub(crate) mod internal_prelude {
pub use crate::prelude::*;
// These are mostly used for more advanced use cases,
// so aren't included in the general prelude
pub use crate::basic::*;
pub use crate::basic_well_known_types::*;
pub use crate::decoder::*;
pub use crate::encoder::*;
pub use crate::payload_validation::*;
pub use crate::schema::*;
pub use crate::traversal::*;
pub use crate::vec_traits::*;
pub use core::ops::ControlFlow;
}
2 changes: 2 additions & 0 deletions sbor/src/traversal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod path_formatting;
mod traversal_traits;
mod typed;
mod untyped;

pub use path_formatting::*;
pub use traversal_traits::*;
pub use typed::*;
pub use untyped::*;
191 changes: 191 additions & 0 deletions sbor/src/traversal/traversal_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use crate::internal_prelude::*;
use core::ops::ControlFlow;

pub trait CustomTraversal: Copy + Debug + Clone + PartialEq + Eq + 'static {
type CustomValueKind: CustomValueKind;
type CustomTerminalValueRef<'de>: CustomTerminalValueRef<
CustomValueKind = Self::CustomValueKind,
>;

fn read_custom_value_body<'de, R>(
custom_value_kind: Self::CustomValueKind,
reader: &mut R,
) -> Result<Self::CustomTerminalValueRef<'de>, DecodeError>
where
R: BorrowingDecoder<'de, Self::CustomValueKind>;
}

pub trait CustomTerminalValueRef: Debug + Clone + PartialEq + Eq {
type CustomValueKind: CustomValueKind;

fn custom_value_kind(&self) -> Self::CustomValueKind;
}

// We add this allow so that the placeholder names don't have to start with underscores
#[allow(unused_variables)]
pub trait UntypedPayloadVisitor<'de, T: CustomTraversal> {
type Output<'t>;

#[inline]
#[must_use]
fn on_container_start<'t>(
&mut self,
details: OnContainerStart<'t, T>,
) -> ControlFlow<Self::Output<'t>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_terminal_value<'t>(
&mut self,
details: OnTerminalValue<'t, 'de, T>,
) -> ControlFlow<Self::Output<'t>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_terminal_value_batch<'t>(
&mut self,
details: OnTerminalValueBatch<'t, 'de, T>,
) -> ControlFlow<Self::Output<'t>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_container_end<'t>(
&mut self,
details: OnContainerEnd<'t, T>,
) -> ControlFlow<Self::Output<'t>> {
ControlFlow::Continue(())
}

#[must_use]
fn on_error<'t>(&mut self, details: OnError<'t, T>) -> Self::Output<'t>;

#[must_use]
fn on_traversal_end<'t>(&mut self, details: OnTraversalEnd<'t, T>) -> Self::Output<'t>;
}

pub struct OnContainerStart<'t, T: CustomTraversal> {
pub header: ContainerHeader<T>,
pub location: Location<'t, T>,
/// If requesting to break, the traversal can be continued with this action.
/// This will be optimized out if the visitor doesn't use it.
pub resume_action: NextAction<T>,
}

pub struct OnTerminalValue<'t, 'de, T: CustomTraversal> {
pub value: TerminalValueRef<'de, T>,
pub location: Location<'t, T>,
/// If requesting to break, the traversal can be continued with this action.
/// This will be optimized out if the visitor doesn't use it.
pub resume_action: NextAction<T>,
}

pub struct OnTerminalValueBatch<'t, 'de, T: CustomTraversal> {
pub value_batch: TerminalValueBatchRef<'de>,
pub location: Location<'t, T>,
/// If requesting to break, the traversal can be continued with this action.
/// This will be optimized out if the visitor doesn't require it.
pub resume_action: NextAction<T>,
}

pub struct OnContainerEnd<'t, T: CustomTraversal> {
pub header: ContainerHeader<T>,
pub location: Location<'t, T>,
/// If requesting to break, the traversal can be continued with this action.
/// This will be optimized out if the visitor doesn't require it.
pub resume_action: NextAction<T>,
}

pub struct OnTraversalEnd<'t, T: CustomTraversal> {
pub location: Location<'t, T>,
}

pub struct OnError<'t, T: CustomTraversal> {
pub error: DecodeError,
pub location: Location<'t, T>,
}

// We add this allow so that the placeholder names don't have to start with underscores
#[allow(unused_variables)]
pub trait TypedPayloadVisitor<'de, E: CustomExtension> {
type Output<'t, 's>
where
's: 't;

#[inline]
#[must_use]
fn on_container_start<'t, 's>(
&mut self,
details: OnContainerStartTyped<'t, 's, E::CustomTraversal>,
) -> ControlFlow<Self::Output<'t, 's>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_terminal_value<'t, 's>(
&mut self,
details: OnTerminalValueTyped<'t, 's, 'de, E::CustomTraversal>,
) -> ControlFlow<Self::Output<'t, 's>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_terminal_value_batch<'t, 's>(
&mut self,
details: OnTerminalValueBatchTyped<'t, 's, 'de, E::CustomTraversal>,
) -> ControlFlow<Self::Output<'t, 's>> {
ControlFlow::Continue(())
}

#[inline]
#[must_use]
fn on_container_end<'t, 's>(
&mut self,
details: OnContainerEndTyped<'t, 's, E::CustomTraversal>,
) -> ControlFlow<Self::Output<'t, 's>> {
ControlFlow::Continue(())
}

#[must_use]
fn on_error<'t, 's>(&mut self, details: OnErrorTyped<'t, 's, E>) -> Self::Output<'t, 's>;

#[must_use]
fn on_traversal_end<'t, 's>(&mut self, details: OnTraversalEndTyped) -> Self::Output<'t, 's>;
}

pub struct OnContainerStartTyped<'t, 's, T: CustomTraversal> {
pub local_type_id: LocalTypeId,
pub header: ContainerHeader<T>,
pub location: TypedLocation<'t, 's, T>,
}

pub struct OnTerminalValueTyped<'t, 's, 'de, T: CustomTraversal> {
pub local_type_id: LocalTypeId,
pub value: TerminalValueRef<'de, T>,
pub location: TypedLocation<'t, 's, T>,
}

pub struct OnTerminalValueBatchTyped<'t, 's, 'de, T: CustomTraversal> {
pub local_type_id: LocalTypeId,
pub value_batch: TerminalValueBatchRef<'de>,
pub location: TypedLocation<'t, 's, T>,
}

pub struct OnContainerEndTyped<'t, 's, T: CustomTraversal> {
pub local_type_id: LocalTypeId,
pub location: TypedLocation<'t, 's, T>,
}

pub struct OnTraversalEndTyped {}

pub struct OnErrorTyped<'t, 's, E: CustomExtension> {
pub error: TypedTraversalError<E>,
pub location: TypedLocation<'t, 's, E::CustomTraversal>,
}
Loading
Loading