cleanup + properly handle saving function data

This commit is contained in:
Simon Gardling 2022-05-25 12:24:25 -04:00
parent 24526d138d
commit 123c49a5d8
4 changed files with 56 additions and 34 deletions

View File

@ -11,6 +11,7 @@ use parsing::{process_func_str, BackingFunction};
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{self, Debug},
hash::{Hash, Hasher},
intrinsics::assume,
};
@ -62,6 +63,16 @@ pub struct FunctionEntry {
pub settings_opened: bool,
}
impl Hash for FunctionEntry {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw_func_str.hash(state);
self.integral.hash(state);
self.nth_derviative.hash(state);
self.curr_nth.hash(state);
self.settings_opened.hash(state);
}
}
impl Serialize for FunctionEntry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View File

@ -9,6 +9,9 @@ use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::BitXorAssign;
pub struct FunctionManager {
@ -19,7 +22,7 @@ impl Default for FunctionManager {
fn default() -> Self {
Self {
functions: vec![(
Id::new_from_u64(11414819524356497634), // Random number here to avoid call to random
Id::new_from_u64(11414819524356497634), // Random number here to avoid call to crate::misc::random_u64()
FunctionEntry::EMPTY,
)],
}
@ -72,7 +75,17 @@ const fn button_area_button(text: impl Into<WidgetText>) -> Button {
}
impl FunctionManager {
pub fn display_entries(&mut self, ui: &mut egui::Ui) {
#[inline]
fn get_hash(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.functions.hash(&mut hasher);
hasher.finish()
}
/// Displays function entries alongside returning whether or not functions have been modified
pub fn display_entries(&mut self, ui: &mut egui::Ui) -> bool {
let initial_hash = self.get_hash();
let can_remove = self.functions.len() > 1;
let available_width = ui.available_width();
@ -127,6 +140,8 @@ impl FunctionManager {
if movement != Movement::Complete && let Some(hints) = function.autocomplete.hint.many() {
// Doesn't need to have a number in id as there should only be 1 autocomplete popup in the entire gui
// hashed "autocomplete_popup"
const POPUP_ID: Id = Id::new_from_u64(7574801616484505465);
let mut clicked = false;
@ -217,6 +232,10 @@ impl FunctionManager {
if let Some(remove_i_unwrap) = remove_i {
self.functions.remove(remove_i_unwrap);
}
let final_hash = self.get_hash();
initial_hash != final_hash
}
/// Create and push new empty function entry

View File

@ -386,7 +386,23 @@ impl MathApp {
);
});
self.functions.display_entries(ui);
if self.functions.display_entries(ui) {
#[cfg(target_arch = "wasm32")]
{
tracing::info!("Saving function data");
let hash: crate::misc::HashBytes = unsafe {
std::mem::transmute::<&str, crate::misc::HashBytes>(build::SHORT_COMMIT)
};
let saved_data = &crate::misc::hashed_storage_create(
hash,
bincode::serialize(&self.functions).unwrap().as_slice(),
);
// tracing::info!("Bytes: {}", saved_data.len());
get_localstorage()
.set_item(FUNC_NAME, saved_data)
.expect("failed to set local function storage");
}
}
// Only render if there's enough space
if ui.available_height() > crate::data::FONT_SIZE {
@ -633,31 +649,7 @@ impl App for MathApp {
self.last_info.1 = start.map(|a| format!("Took: {:?}", a.elapsed()));
}
fn save(&mut self, _: &mut dyn eframe::Storage) {
#[cfg(target_arch = "wasm32")]
self.save_functions();
}
fn auto_save_interval(&self) -> std::time::Duration { std::time::Duration::from_secs(10) }
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
crate::style::STYLE.window_fill().into()
}
}
impl MathApp {
#[cfg(target_arch = "wasm32")]
fn save_functions(&self) {
tracing::info!("Saving function data");
let hash: crate::misc::HashBytes =
unsafe { std::mem::transmute::<&str, crate::misc::HashBytes>(build::SHORT_COMMIT) };
let saved_data = &crate::misc::hashed_storage_create(
hash,
bincode::serialize(&self.functions).unwrap().as_slice(),
);
// tracing::info!("Bytes: {}", saved_data.len());
get_localstorage()
.set_item(FUNC_NAME, saved_data)
.expect("failed to set local function storage");
}
}

View File

@ -1,5 +1,3 @@
use std::intrinsics::assume;
use egui::plot::{Line, Points, Value, Values};
use getrandom::getrandom;
use itertools::Itertools;
@ -163,25 +161,27 @@ pub fn hashed_storage_create(hash: HashBytes, data: &[u8]) -> String {
#[allow(dead_code)]
pub const fn hashed_storage_read(data: &str) -> Option<(HashBytes, &[u8])> {
// Make sure data is long enough to decode
if HASH_LENGTH >= data.len() {
return None;
}
unsafe {
assume(!data.is_empty());
assume(data.len() > HASH_LENGTH);
}
// Transmute data into slice
let decoded_1: &[u8] = unsafe { std::mem::transmute::<&str, &[u8]>(data) };
// return hash and decoded data
Some((
unsafe { *(decoded_1[..HASH_LENGTH].as_ptr() as *const HashBytes) },
&decoded_1[HASH_LENGTH..],
))
}
/// Creates and returns random u64
pub fn random_u64() -> Result<u64, getrandom::Error> {
// Buffer of 8 `u8`s that are later merged into one u64
let mut buf = [0u8; 8];
// Populate buffer with random values
getrandom(&mut buf)?;
// Merge buffer into u64
Ok(u64::from_be_bytes(buf))
}