Skip to content

Instantly share code, notes, and snippets.

@MartinKavik
Created August 12, 2020 12:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MartinKavik/678ad94e8a71dcea439c10a9f63387d9 to your computer and use it in GitHub Desktop.
Save MartinKavik/678ad94e8a71dcea439c10a9f63387d9 to your computer and use it in GitHub Desktop.
Seed `view` optimization by tracking data changes in `view` functions arguments
#![allow(dead_code)]
use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
struct Model {
todos: BTreeMap<String, Todo>,
new_todo_title: String,
selected_todo: Option<SelectedTodo>,
filter: Filter,
}
struct Todo {
title: String,
completed: bool,
}
struct SelectedTodo {
title: String,
}
enum Filter {
All,
Active,
}
fn main() {
let mut view_arg_1_hash: Option<u64> = None;
let mut view_new_todo_title_arg_1_hash: Option<u64> = None;
let mut model = Model {
todos: BTreeMap::new(),
new_todo_title: "my todo".to_owned(),
selected_todo: None,
filter: Filter::All,
};
println!("--- 1. view call ---");
view(&model, &mut view_arg_1_hash, &mut view_new_todo_title_arg_1_hash);
println!("--- 2. view call --- ");
view(&model, &mut view_arg_1_hash, &mut view_new_todo_title_arg_1_hash);
println!("--- 3. view call --- ");
// change `Model` a bit to trigger at least root `view` logic
model.filter = Filter::Active;
view(&model, &mut view_arg_1_hash, &mut view_new_todo_title_arg_1_hash);
println!("--- 4. view call --- ");
// change `new_todo_title` to trigger root `view` logic and also `view_new_todo_title` logic
model.new_todo_title = "change my todo title".to_owned();
view(&model, &mut view_arg_1_hash, &mut view_new_todo_title_arg_1_hash);
}
// Arguments `view_arg_1_hash` and `view_new_todo_title_arg_1_hash` + hashing + match
// should be refactored and then hidden by the attribute `#[view]`
fn view(model: &Model, view_arg_1_hash: &mut Option<u64>, view_new_todo_title_arg_1_hash: &mut Option<u64>) {
let hash = as_bytes_and_calculate_hash(model);
let old_hash = view_arg_1_hash.replace(hash);
match old_hash {
Some(old_hash) if hash == old_hash => return,
_ => ()
}
println!("view called");
view_new_todo_title(&model.new_todo_title, view_new_todo_title_arg_1_hash);
}
fn view_new_todo_title(title: &str, view_new_todo_title_arg_1_hash: &mut Option<u64>) {
// Note: `to_owned` is needed because of `::std::mem::size_of::<T>(),` in `any_as_u8_slice`
// but there is probably a way how to get bytes from reference to unsized types.
let hash = as_bytes_and_calculate_hash(&title.to_owned());
let old_hash = view_new_todo_title_arg_1_hash.replace(hash);
match old_hash {
Some(old_hash) if hash == old_hash => return,
_ => ()
}
println!("view new todo title called");
}
fn calculate_hash<T: Hash + ?Sized>(t: &T) -> u64 {
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
fn as_bytes_and_calculate_hash<T>(t: &T) -> u64 {
let bytes: &[u8] = unsafe { any_as_u8_slice(t) };
calculate_hash(bytes)
}
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::std::slice::from_raw_parts(
(p as *const T) as *const u8,
::std::mem::size_of::<T>(),
)
}
@MartinKavik
Copy link
Author

It can be run in https://play.rust-lang.org/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment