Skip to content

Commit

Permalink
fine grained control over cache hit behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
tadeohepperle committed Dec 7, 2023
1 parent 8904669 commit 119bf73
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 63 deletions.
25 changes: 18 additions & 7 deletions description/src/description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use scale_info::{
TypeDefCompact, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, TypeDefVariant, Variant,
};

use crate::transformer::{CacheHitPolicy, Transformer};
use crate::transformer::Transformer;

use super::formatting::format_type_description;

Expand All @@ -19,21 +19,32 @@ pub fn type_description(
type_registry: &PortableRegistry,
format: bool,
) -> anyhow::Result<String> {
fn return_type_name_on_recurse(
fn return_type_name(
_type_id: u32,
ty: &Type<PortableForm>,
_transformer: &Transformer<String>,
) -> anyhow::Result<String> {
) -> Option<anyhow::Result<String>> {
if let Some(type_name) = ty.path.ident() {
return Ok(type_name);
return Some(Ok(type_name));
}
Err(anyhow!("Recursive type that did not get handled properly"))
None
}

fn return_type_name_on_cache_hit(
type_id: u32,
ty: &Type<PortableForm>,
cached: &String,
transformer: &Transformer<String>,
) -> Option<anyhow::Result<String>> {
if let Some(type_name) = ty.path.ident() {
return Some(Ok(type_name));
}
Some(Ok(cached.clone()))
}
let transformer = Transformer::new(
ty_description,
return_type_name_on_recurse,
CacheHitPolicy::ExecuteRecursePolicy, // returns the type name in this case...
return_type_name,
return_type_name_on_cache_hit,
(),
type_registry,
);
Expand Down
6 changes: 1 addition & 5 deletions description/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ mod tests {
}
>,
t: u8,
operation: enum Operation {
Add,
Intersect,
Difference
}
operation: Operation
}
}"}
);
Expand Down
58 changes: 17 additions & 41 deletions description/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,17 @@ pub struct Transformer<'a, R, S = ()> {
/// The `recurse_policy` defines, how to handle cases, where a type has been
/// visited before, and is visited *again*, before a representation of this type could be computed.
/// It is up the implementation to return an error in these cases, or some other value.
recurse_policy: fn(u32, &Type<PortableForm>, &Self) -> anyhow::Result<R>,
///
/// You can return None to sidestep recursion protection and let the transformer continue.
recurse_policy: fn(u32, &Type<PortableForm>, &Self) -> Option<anyhow::Result<R>>,
/// Describe the policy to apply when encountering a cache hit.
/// A cache hit is, when the representation of a type has already been computed.
///
/// In this case there are 2 options:
/// - ReturnCached => return the cached value
/// - ExecuteRecursePolicy => execute the recurse policy
cache_hit_policy: CacheHitPolicy,
/// You can return None to sidestep recursion protection and let the transformer continue.
cache_hit_policy: fn(u32, &Type<PortableForm>, &R, &Self) -> Option<anyhow::Result<R>>,
registry: &'a PortableRegistry,
}

/// The transformer stores computed representations of types in a cache.
/// Sometimes we encounter types, where this representation is already computed.
///
/// The `CacheHitPolicy` defines, how to handle these cases.
#[derive(Debug, Clone, Copy)]
pub enum CacheHitPolicy {
/// Returns the computed value from the cache.
ReturnCached,
/// Ignore the cached value and just compute the representation of the type again.
///
/// Note: This is safe from a recursion standpoint.
/// It is useful for generating multiple different type examples for one type instead of just returning the same one every time.
ComputeAgain,
/// Act like we were dealing with a recursive type. This will lead to the recurse policy being executed.
/// It can for example be used to return a placeholder value, e.g. the type name, when a type is encountered for the second time.
ExecuteRecursePolicy,
}

#[derive(Clone, Debug)]
enum Cached<Out> {
/// not known yet, but computation has already started
Expand All @@ -61,10 +43,11 @@ impl<'a, R, S> Transformer<'a, R, S>
where
R: Clone + std::fmt::Debug,
{
/// Create a new transformer.
pub fn new(
policy: fn(u32, &Type<PortableForm>, &Self) -> anyhow::Result<R>,
recurse_policy: fn(u32, &Type<PortableForm>, &Self) -> anyhow::Result<R>,
cache_hit_policy: CacheHitPolicy,
recurse_policy: fn(u32, &Type<PortableForm>, &Self) -> Option<anyhow::Result<R>>,
cache_hit_policy: fn(u32, &Type<PortableForm>, &R, &Self) -> Option<anyhow::Result<R>>,
state: S,
registry: &'a PortableRegistry,
) -> Self {
Expand All @@ -90,24 +73,17 @@ where
))?;

match self.cache.borrow().get(&type_id) {
Some(Cached::Recursive) => {
if !recursion_should_continue(&ty.type_def) {
return (self.recurse_policy)(type_id, ty, self);
}
}
Some(Cached::Computed(repr)) => {
match self.cache_hit_policy {
CacheHitPolicy::ReturnCached => return Ok(repr.clone()),
CacheHitPolicy::ExecuteRecursePolicy => {
if !recursion_should_continue(&ty.type_def) {
return (self.recurse_policy)(type_id, ty, self);
}
}
CacheHitPolicy::ComputeAgain => {
// ..continue with the computation
}
Some(cache_value) => {
let result_or_continue = match cache_value {
Cached::Recursive => (self.recurse_policy)(type_id, ty, self),
Cached::Computed(repr) => (self.cache_hit_policy)(type_id, ty, repr, self),
};

if let Some(result) = result_or_continue {
return result;
}
}
Some(Cached::Computed(repr)) => {}
_ => {}
};

Expand Down
20 changes: 15 additions & 5 deletions description/src/type_example/rust_value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::transformer::{CacheHitPolicy, Transformer};
use crate::transformer::Transformer;
use anyhow::anyhow;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, ToTokens};
Expand Down Expand Up @@ -131,10 +131,20 @@ pub fn example_from_seed(
_type_id: u32,
ty: &Type<PortableForm>,
_transformer: &CodeTransformer,
) -> anyhow::Result<TokenStream> {
Err(anyhow!(
) -> Option<anyhow::Result<TokenStream>> {
Some(Err(anyhow!(
"Cannot generate rust type example for recursive type: {ty:?}"
))
)))
}

/// Note: because None is returned here, the transformer will just continue its work.
fn compute_another_example(
type_id: u32,
ty: &Type<PortableForm>,
cached_value: &TokenStream,
transformer: &CodeTransformer,
) -> Option<anyhow::Result<TokenStream>> {
None
}

let state = CodeTransformerState {
Expand All @@ -147,7 +157,7 @@ pub fn example_from_seed(
let transformer = CodeTransformer::new(
ty_example,
error_on_recurse,
CacheHitPolicy::ComputeAgain,
compute_another_example,
state,
types,
);
Expand Down
21 changes: 16 additions & 5 deletions description/src/type_example/scale_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rand::{seq::SliceRandom, Rng};
use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef, TypeDefPrimitive};
use scale_value::{BitSequence, Composite, Primitive, Value, ValueDef, Variant};

use crate::transformer::{CacheHitPolicy, Transformer};
use crate::transformer::Transformer;

type ValueTransformer<'a> = Transformer<'a, Value, RefCell<rand_chacha::ChaCha8Rng>>;

Expand All @@ -23,16 +23,27 @@ pub fn example_from_seed(id: u32, types: &PortableRegistry, seed: u64) -> anyhow
_type_id: u32,
ty: &Type<PortableForm>,
_transformer: &ValueTransformer,
) -> anyhow::Result<Value> {
Err(anyhow!(
) -> Option<anyhow::Result<Value>> {
Some(Err(anyhow!(
"Cannot generate scale value example for recursive type: {ty:?}"
))
)))
}

/// Note: because None is returned here, the transformer will just continue its work.
fn compute_another_example(
type_id: u32,
ty: &Type<PortableForm>,
cached_value: &Value,
transformer: &ValueTransformer,
) -> Option<anyhow::Result<Value>> {
None
}

let state = RefCell::new(rand_chacha::ChaCha8Rng::seed_from_u64(seed));
let transformer = ValueTransformer::new(
ty_example,
error_on_recurse,
CacheHitPolicy::ComputeAgain,
compute_another_example,
state,
types,
);
Expand Down

0 comments on commit 119bf73

Please sign in to comment.