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

Refactor memory data #836

Merged
merged 11 commits into from
Oct 27, 2021
Prev Previous commit
Next Next commit
Rename IdAnyMap to IdTypeMap
  • Loading branch information
emilk committed Oct 26, 2021
commit 9a0a0f50f3f09b4d389a2bd8c07b7f649f46ad0d
2 changes: 1 addition & 1 deletion egui/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
pub struct Id(u64);

impl Id {
/// A special `Id`, in particular as a key to [`crate::Memory::id_map`]
/// A special `Id`, in particular as a key to [`crate::Memory::data`]
/// for when there is no particular widget to attach the data.
///
/// The null `Id` is still a valid id to use in all circumstances,
Expand Down
2 changes: 1 addition & 1 deletion egui/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct Memory {
/// This will be saved between different program runs if you use the `persistence` feature.
///
/// To store a state common for all your widgets (a singleton), use [`Id::null`] as the key.
pub data: crate::util::IdAnyMap,
pub data: crate::util::IdTypeMap,

// ------------------------------------------
/// Can be used to cache computations from one frame to another.
Expand Down
127 changes: 114 additions & 13 deletions egui/src/util/id_any_map.rs → egui/src/util/id_type_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,17 +290,46 @@ fn from_ron_str<T: serde::de::DeserializeOwned>(ron: &str) -> Option<T> {

use crate::Id;

// TODO: make generic over the key, instead of using hard-coded `Id`.
/// Stores any value identified by their type and a given [`Id`].
// TODO: make IdTypeMap generic over the key (`Id`), and make a library of IdTypeMap.
/// Stores values identified by an [`Id`] AND a the [`std::any::TypeId`] of the value.
///
/// so it maps `(Id, TypeId)` to any value you want.
///
/// Values can either be "persisted" (serializable) or "temporary" (cleared when egui is shut down).
///
/// You can store state using the key [`Id::null`]. The state will then only be identified by its type.
///
/// ```
/// # use egui::{Id, util::IdTypeMap};
/// let a = Id::new("a");
/// let b = Id::new("b");
/// let mut map: IdTypeMap = Default::default();
///
/// // `a` associated with an f64 and an i32
/// map.insert_persisted(a, 3.14);
/// map.insert_temp(a, 42);
///
/// // `b` associated with an f64 and a `&'static str`
/// map.insert_persisted(b, 6.28);
/// map.insert_temp(b, "Hello World".to_string());
///
/// // we can retrieve all four values:
/// assert_eq!(map.get_temp::<f64>(a), Some(3.14));
/// assert_eq!(map.get_temp::<i32>(a), Some(42));
/// assert_eq!(map.get_temp::<f64>(b), Some(6.28));
/// assert_eq!(map.get_temp::<String>(b), Some("Hello World".to_string()));
///
/// // we can retrieve them like so also:
/// assert_eq!(map.get_persisted::<f64>(a), Some(3.14));
/// assert_eq!(map.get_persisted::<i32>(a), Some(42));
/// assert_eq!(map.get_persisted::<f64>(b), Some(6.28));
/// assert_eq!(map.get_temp::<String>(b), Some("Hello World".to_string()));
/// ```
#[derive(Clone, Debug, Default)]
// We store use `id XOR typeid` as a key, so we don't need to hash again!
pub struct IdAnyMap(nohash_hasher::IntMap<u64, Element>);
pub struct IdTypeMap(nohash_hasher::IntMap<u64, Element>);

impl IdAnyMap {
impl IdTypeMap {
/// Insert a value that will not be persisted.
#[inline]
pub fn insert_temp<T: 'static + Any + Clone + Send + Sync>(&mut self, id: Id, value: T) {
Expand Down Expand Up @@ -458,14 +487,14 @@ fn hash(type_id: TypeId, id: Id) -> u64 {

// ----------------------------------------------------------------------------

/// How [`IdAnyMap`] is persisted.
/// How [`IdTypeMap`] is persisted.
#[cfg(feature = "persistence")]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct PersistedMap(Vec<(u64, SerializedElement)>);

#[cfg(feature = "persistence")]
impl PersistedMap {
fn from_map(map: &IdAnyMap) -> Self {
fn from_map(map: &IdTypeMap) -> Self {
// filter out the elements which cannot be serialized:
Self(
map.0
Expand All @@ -474,8 +503,8 @@ impl PersistedMap {
.collect(),
)
}
fn into_map(self) -> IdAnyMap {
IdAnyMap(
fn into_map(self) -> IdTypeMap {
IdTypeMap(
self.0
.into_iter()
.map(|(hash, SerializedElement { type_id, ron })| {
Expand All @@ -487,7 +516,7 @@ impl PersistedMap {
}

#[cfg(feature = "persistence")]
impl serde::Serialize for IdAnyMap {
impl serde::Serialize for IdTypeMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
Expand All @@ -497,7 +526,7 @@ impl serde::Serialize for IdAnyMap {
}

#[cfg(feature = "persistence")]
impl<'de> serde::Deserialize<'de> for IdAnyMap {
impl<'de> serde::Deserialize<'de> for IdTypeMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
Expand All @@ -508,6 +537,78 @@ impl<'de> serde::Deserialize<'de> for IdAnyMap {

// ----------------------------------------------------------------------------

#[test]
fn test_two_id_two_type() {
let a = Id::new("a");
let b = Id::new("b");

let mut map: IdTypeMap = Default::default();
map.insert_persisted(a, 6.28);
map.insert_temp(b, 42);
assert_eq!(map.get_persisted::<f64>(a), Some(6.28));
assert_eq!(map.get_persisted::<i32>(b), Some(42));
assert_eq!(map.get_temp::<f64>(a), Some(6.28));
assert_eq!(map.get_temp::<i32>(b), Some(42));
}

#[test]
fn test_two_id_x_two_types() {
#![allow(clippy::approx_constant)]

let a = Id::new("a");
let b = Id::new("b");
let mut map: IdTypeMap = Default::default();

// `a` associated with an f64 and an i32
map.insert_persisted(a, 3.14);
map.insert_temp(a, 42);

// `b` associated with an f64 and a `&'static str`
map.insert_persisted(b, 6.28);
map.insert_temp(b, "Hello World".to_string());

// we can retrieve all four values:
assert_eq!(map.get_temp::<f64>(a), Some(3.14));
assert_eq!(map.get_temp::<i32>(a), Some(42));
assert_eq!(map.get_temp::<f64>(b), Some(6.28));
assert_eq!(map.get_temp::<String>(b), Some("Hello World".to_string()));

// we can retrieve them like so also:
assert_eq!(map.get_persisted::<f64>(a), Some(3.14));
assert_eq!(map.get_persisted::<i32>(a), Some(42));
assert_eq!(map.get_persisted::<f64>(b), Some(6.28));
assert_eq!(map.get_temp::<String>(b), Some("Hello World".to_string()));
}

#[test]
fn test_one_id_two_types() {
let id = Id::new("a");

let mut map: IdTypeMap = Default::default();
map.insert_persisted(id, 6.28);
map.insert_temp(id, 42);

assert_eq!(map.get_temp::<f64>(id), Some(6.28));
assert_eq!(map.get_persisted::<f64>(id), Some(6.28));
assert_eq!(map.get_temp::<i32>(id), Some(42));

// ------------
// Test removal:

// We can remove:
map.remove::<i32>(id);
assert_eq!(map.get_temp::<i32>(id), None);

// Other type is still there, even though it is the same if:
assert_eq!(map.get_temp::<f64>(id), Some(6.28));
assert_eq!(map.get_persisted::<f64>(id), Some(6.28));

// But we can still remove the last:
map.remove::<f64>(id);
assert_eq!(map.get_temp::<f64>(id), None);
assert_eq!(map.get_persisted::<f64>(id), None);
}

#[test]
fn test_mix() {
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
Expand All @@ -519,7 +620,7 @@ fn test_mix() {

let id = Id::new("a");

let mut map: IdAnyMap = Default::default();
let mut map: IdTypeMap = Default::default();
map.insert_persisted(id, Foo(555));
map.insert_temp(id, Bar(1.0));

Expand Down Expand Up @@ -557,7 +658,7 @@ fn test_mix_serialize() {

let id = Id::new("a");

let mut map: IdAnyMap = Default::default();
let mut map: IdTypeMap = Default::default();
map.insert_persisted(id, Serializable(555));
map.insert_temp(id, NonSerializable(1.0));

Expand Down Expand Up @@ -597,7 +698,7 @@ fn test_mix_serialize() {
// --------------------
// Test deserialization:

let mut map: IdAnyMap = ron::from_str(&serialized).unwrap();
let mut map: IdTypeMap = ron::from_str(&serialized).unwrap();
assert_eq!(map.get_temp::<Serializable>(id), None);
assert_eq!(
map.get_persisted::<Serializable>(id),
Expand Down
4 changes: 2 additions & 2 deletions egui/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
pub mod cache;
pub(crate) mod fixed_cache;
mod history;
pub mod id_any_map;
pub mod id_type_map;
pub mod undoer;

pub use history::History;
pub use id_any_map::IdAnyMap;
pub use id_type_map::IdTypeMap;

pub use epaint::util::{hash, hash_with};