improve removeal code

master
Avril 4 years ago
parent dd4e231048
commit 2511939f4e
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -59,6 +59,12 @@ impl Entry
..self ..self
} }
} }
/// Clone the data needed for a refresh of this entry in a store before it is mutated.
#[inline(always)] pub(super) fn prepare_for_refresh(&self) -> (EntryKey, Box<[String]>)
{
(self.hash().clone(), self.tags.clone().into_boxed_slice())
}
} }
impl Borrow<sha256::Sha256Hash> for Entry impl Borrow<sha256::Sha256Hash> for Entry

@ -1,5 +1,6 @@
//! Handling store mutation //! Handling store mutation
use super::*; use super::*;
use std::borrow::Borrow;
impl Store impl Store
{ {
@ -10,16 +11,8 @@ impl Store
let hash_idx = self.data_hashes.insert(*ent.hash()); let hash_idx = self.data_hashes.insert(*ent.hash());
for tag in ent.tags.iter() { for tag in ent.tags.iter() {
if let Some(&ti) = self.tags.get(tag) { self.insert_tag_for_idx(tag, hash_idx);
// This tag has an entry already, append to it
self.tag_mappings.get_mut(ti).unwrap().insert(hash_idx);
} else {
// This tag has no entry, create it
let ti = self.tag_mappings.insert(iter![hash_idx].collect());
self.tags.insert(tag.clone(), ti);
} }
}
self.data.insert(ent); self.data.insert(ent);
old old
} }
@ -39,29 +32,45 @@ impl Store
where F: FnOnce(&mut Entry) -> T where F: FnOnce(&mut Entry) -> T
{ {
if let Some(mut ent) = self.data.take(ent_id) { if let Some(mut ent) = self.data.take(ent_id) {
let ohash = ent.hash().clone(); let update = ent.prepare_for_refresh();
let otags = ent.tags.clone();
let out = f(&mut ent); let out = f(&mut ent);
let new = ent; let new = ent;
let iidx = if new.hash() != &ohash { self.refresh_for_entry(update, &new);
self.data.insert(new);
Some(out)
} else {
None
}
}
/// Update an entry that may have been modified.
///
/// The entries old hash and tags are passed, and it updates the store to reflect the new entry mutation.
/// You must still insert the new entry after this.
fn refresh_for_entry(&mut self, (ohash, otags): (impl Borrow<EntryKey>, impl AsRef<[String]>), new: &Entry)
{
let ohash = ohash.borrow();
let iidx = if new.hash() != ohash {
// We need to update `data_hashes`. // We need to update `data_hashes`.
for (_, hash) in self.data_hashes.iter_mut() for (_, hash) in self.data_hashes.iter_mut()
{ {
if hash == &ohash { if hash == ohash {
*hash = *new.hash(); *hash = *new.hash();
break; break;
} }
} }
self.reverse_index_lookup(new.hash()).unwrap() self.reverse_index_lookup(new.hash()).unwrap()
} else { } else {
self.reverse_index_lookup(&ohash).unwrap() self.reverse_index_lookup(ohash).unwrap()
}; };
let otags =otags.as_ref();
if &new.tags[..] != &otags[..] { if &new.tags[..] != &otags[..] {
// We need to update tag mappings // We need to update tag mappings
let ntags: HashSet<_> = new.tags.iter().collect(); let ntags: HashSet<_> = new.tags.iter().collect();
let otags: HashSet<_> = otags.iter().collect(); let otags: HashSet<_> = otags.iter().collect();
// Find the ones that were removed and added in parallel. // Find the ones that were removed and added in parallel.
@ -78,11 +87,6 @@ impl Store
} }
} }
self.data.insert(new);
Some(out)
} else {
None
}
} }
/// Map the entry with this function, updating references to it if needed. /// Map the entry with this function, updating references to it if needed.
@ -92,18 +96,11 @@ impl Store
where F: FnOnce(Entry) -> Entry where F: FnOnce(Entry) -> Entry
{ {
if let Some(ent) = self.data.take(ent_id) { if let Some(ent) = self.data.take(ent_id) {
let ohash = ent.hash().clone(); let update = ent.prepare_for_refresh();
let new = f(ent); let new = f(ent);
if new.hash() != &ohash {
// We need to update `data_hashes`. self.refresh_for_entry(update, &new);
for (_, hash) in self.data_hashes.iter_mut()
{
if hash == &ohash {
*hash = *new.hash();
break;
}
}
}
self.data.insert(new); self.data.insert(new);
} }
} }
@ -113,7 +110,7 @@ impl Store
pub fn remove(&mut self, key: &EntryKey) -> Option<Entry> pub fn remove(&mut self, key: &EntryKey) -> Option<Entry>
{ {
if let Some(entry) = self.data.take(key) { if let Some(entry) = self.data.take(key) {
Some(self.cleanup_remove_entry(entry)) Some(self.cleanup_remove_entry(entry.with_no_cache()))
} else { } else {
None None
} }
@ -122,37 +119,19 @@ impl Store
/// Preform cleanup on an entry *already removed* from `data`. /// Preform cleanup on an entry *already removed* from `data`.
fn cleanup_remove_entry(&mut self, ent: Entry) -> Entry fn cleanup_remove_entry(&mut self, ent: Entry) -> Entry
{ {
let ent = ent.with_no_cache();
// Remove any unused tags // Remove any unused tags
for (nm, ti) in precollect!(self.tag_index_lookup(&ent.tags[..]).map(|(nm, idx)| ({ if let Some(hash_idx) = self.reverse_index_lookup(ent.hash()) {
ent.tags.iter().filter(|y| y.as_str() == nm).next().unwrap() // swap the `nm` reference to the owned reference in `ent`'s tags... There should be a better way that this eh for tag in ent.tags.iter()
}, idx))) { {
if self.purge_tag_index(ti, ent.hash()) { self.remove_tag_for_idx(tag, hash_idx);
// No more mappings, remove this tag
self.tags.remove(nm);
// And its mapping
self.tag_mappings.remove(ti);
} }
} }
// Remove from data hashes can be deferred // Remove from data hashes can be deferred
self.purge_if_needed(); self.purge_if_needed();
ent ent
} }
/// Purge this tag index from the mappings for the entry `to_remove`.
/// Returns true if there are no more references to this tag and it can be removed.
#[inline] fn purge_tag_index(&mut self, idx: ArenaIndex, to_remove: &EntryKey) -> bool
{
let data_hashes = &mut self.data_hashes;
if let Some(map) = self.tag_mappings.get_mut(idx) {
map.retain(move |&hash_idx| data_hashes.get(hash_idx).map(|x| x != to_remove).unwrap_or(false));
map.len() == 0
} else {
// There is no reference in the tag mapping itself.
false
}
}
/// Remove dead mappings from `data_hashes` to `data`. /// Remove dead mappings from `data_hashes` to `data`.
#[inline] fn purge_data_hash_mappings(&mut self) #[inline] fn purge_data_hash_mappings(&mut self)
{ {

Loading…
Cancel
Save