diff --git a/src/context.rs b/src/context.rs index 35aef36..26d0dc8 100644 --- a/src/context.rs +++ b/src/context.rs @@ -403,7 +403,9 @@ impl, V> EntityOrientedDenseMap { Self::default() } - pub fn insert(&mut self, key: K, value: V) -> Option { + // FIXME(eddyb) this should not allocate space unconditionally, but offer an + // API where "vacant entry" may or may not have a `&mut Option` in it. + pub fn entry(&mut self, key: K) -> &mut Option { let entity = K::to_entity(key); let (chunk_start, intra_chunk_idx) = entity.to_chunk_start_and_intra_chunk_idx(); let chunk_value_slots = self @@ -417,7 +419,11 @@ impl, V> EntityOrientedDenseMap { } let value_slots = &mut chunk_value_slots[intra_chunk_idx]; - K::get_dense_value_slot_mut(key, value_slots).replace(value) + K::get_dense_value_slot_mut(key, value_slots) + } + + pub fn insert(&mut self, key: K, value: V) -> Option { + self.entry(key).replace(value) } pub fn get(&self, key: K) -> Option<&V> { @@ -438,6 +444,7 @@ impl, V> EntityOrientedDenseMap { self.get_slot_mut(key)?.take() } + // FIXME(eddyb) deduplicate with `entry`. fn get_slot_mut(&mut self, key: K) -> Option<&mut Option> { let entity = K::to_entity(key); let (chunk_start, intra_chunk_idx) = entity.to_chunk_start_and_intra_chunk_idx(); @@ -514,7 +521,9 @@ impl>, D> EntityList { let old_first_def = &mut defs[old_first]; // FIXME(eddyb) this situation should be impossible anyway, as it - // involves the `EntityListNode`s links, which should be unforgeable. + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) assert!( old_first_def.prev.is_none(), "invalid EntityList: `first->prev != None`" @@ -543,7 +552,9 @@ impl>, D> EntityList { let old_last_def = &mut defs[old_last]; // FIXME(eddyb) this situation should be impossible anyway, as it - // involves the `EntityListNode`s links, which should be unforgeable. + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) assert!( old_last_def.next.is_none(), "invalid EntityList: `last->next != None`" @@ -558,6 +569,49 @@ impl>, D> EntityList { }); } + /// Insert `new_node` (defined in `defs`) into `self`, before `next`. + // + // FIXME(eddyb) unify this with the other insert methods, maybe with a new + // "insert position" type? + #[track_caller] + pub fn insert_before(&mut self, new_node: E, next: E, defs: &mut EntityDefs) { + let prev = defs[next].prev.replace(new_node); + + let new_node_def = &mut defs[new_node]; + assert!( + new_node_def.prev.is_none() && new_node_def.next.is_none(), + "EntityList::insert_before: new node already linked into a (different?) list" + ); + + new_node_def.prev = prev; + new_node_def.next = Some(next); + + match prev { + Some(prev) => { + let old_prev_next = defs[prev].next.replace(new_node); + + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable. + assert!( + old_prev_next == Some(next), + "invalid EntityListNode: `node->prev->next != node`" + ); + } + None => { + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) + assert!( + self.0.map(|this| this.first) == Some(next), + "invalid EntityList: `node->prev == None` but `node != first`" + ); + + self.0.as_mut().unwrap().first = new_node; + } + } + } + /// Insert all of `list_to_prepend`'s nodes at the start of `self`. #[track_caller] pub fn prepend(&mut self, list_to_prepend: Self, defs: &mut EntityDefs) { @@ -582,7 +636,9 @@ impl>, D> EntityList { let a_last_def = &mut defs[a.last]; // FIXME(eddyb) this situation should be impossible anyway, as it - // involves the `EntityListNode`s links, which should be unforgeable. + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) assert!( a_last_def.next.is_none(), "invalid EntityList: `last->next != None`" @@ -594,7 +650,9 @@ impl>, D> EntityList { let b_first_def = &mut defs[b.first]; // FIXME(eddyb) this situation should be impossible anyway, as it - // involves the `EntityListNode`s links, which should be unforgeable. + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) assert!( b_first_def.prev.is_none(), "invalid EntityList: `first->prev != None`" @@ -608,6 +666,72 @@ impl>, D> EntityList { last: b.last, })) } + + /// Remove `node` (defined in `defs`) from `self`. + #[track_caller] + pub fn remove(&mut self, node: E, defs: &mut EntityDefs) { + // Unlink `node->{prev,next}` first (also allowing re-insertion elsewhere). + let (prev, next) = { + let node_def = &mut defs[node]; + (node_def.prev.take(), node_def.next.take()) + }; + + // Unlink `prev->next = node` (or validate `first = node`). + match prev { + Some(prev) => { + let old_prev_next = mem::replace(&mut defs[prev].next, next); + + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable. + assert!( + old_prev_next == Some(node), + "invalid EntityListNode: `node->prev->next != node`" + ); + } + None => { + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) + assert!( + self.0.map(|this| this.first) == Some(node), + "invalid EntityList: `node->prev == None` but `node != first`" + ); + } + } + + // Unlink `next->prev = node` (or validate `last = node`). + match next { + Some(next) => { + let old_next_prev = mem::replace(&mut defs[next].prev, prev); + + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable. + assert!( + old_next_prev == Some(node), + "invalid EntityListNode: `node->next->prev != node`" + ); + } + None => { + // FIXME(eddyb) this situation should be impossible anyway, as it + // involves the `EntityListNode`s links, which should be unforgeable, + // but it's still possible to keep around outdated `EntityList`s + // (should `EntityList` not implement `Copy`/`Clone` *at all*?) + assert!( + self.0.map(|this| this.last) == Some(node), + "invalid EntityList: `node->next == None` but `node != last`" + ); + } + } + + // Update list end-points (overwritten `first`/`last` validated above). + match (prev, next) { + (Some(_), Some(_)) => {} + (None, Some(next)) => self.0.as_mut().unwrap().first = next, + (Some(prev), None) => self.0.as_mut().unwrap().last = prev, + (None, None) => self.0 = None, + } + } } /// [`EntityList`] iterator, but with a different API than [`Iterator`]. diff --git a/src/func_at.rs b/src/func_at.rs index f5e1e62..fba3eea 100644 --- a/src/func_at.rs +++ b/src/func_at.rs @@ -91,6 +91,14 @@ impl<'a> Iterator for FuncAt<'a, EntityListIter> { } } +impl<'a> DoubleEndedIterator for FuncAt<'a, EntityListIter> { + fn next_back(&mut self) -> Option { + let (prev, rest) = self.position.split_last(self.data_insts)?; + self.position = rest; + Some(self.at(prev)) + } +} + impl<'a> FuncAt<'a, DataInst> { pub fn def(self) -> &'a DataInstDef { &self.data_insts[self.position] @@ -146,6 +154,24 @@ impl<'a, P: Copy> FuncAtMut<'a, P> { position: new_position, } } + + /// Demote to a `FuncAt`, with the same `position`. + // + // FIXME(eddyb) maybe find a better name for this? + pub fn freeze(self) -> FuncAt<'a, P> { + let FuncAtMut { + control_regions, + control_nodes, + data_insts, + position, + } = self; + FuncAt { + control_regions, + control_nodes, + data_insts, + position, + } + } } impl<'a> FuncAtMut<'a, ControlRegion> {