diff --git a/cranelift/entity/src/list.rs b/cranelift/entity/src/list.rs index 3e583c5fd7..d37ea8ae09 100644 --- a/cranelift/entity/src/list.rs +++ b/cranelift/entity/src/list.rs @@ -307,6 +307,24 @@ impl EntityList { self.as_mut_slice(pool).get_mut(index) } + /// Create a deep clone of the list, which does not alias the original list. + pub fn deep_clone(&self, pool: &mut ListPool) -> Self { + match pool.len_of(self) { + None => return Self::new(), + Some(len) => { + let src = self.index as usize; + let block = pool.alloc(sclass_for_length(len)); + pool.data[block] = T::new(len); + pool.data.copy_within(src..src + len, block + 1); + + Self { + index: (block + 1) as u32, + unused: PhantomData, + } + } + } + } + /// Removes all elements from the list. /// /// The memory used by the list is put back in the pool. @@ -445,20 +463,8 @@ impl EntityList { } } - /// Removes the element at position `index` from the list. Potentially linear complexity. - pub fn remove(&mut self, index: usize, pool: &mut ListPool) { - let len; - { - let seq = self.as_mut_slice(pool); - len = seq.len(); - debug_assert!(index < len); - - // Copy elements down. - for i in index..len - 1 { - seq[i] = seq[i + 1]; - } - } - + /// Removes the last element from the list. + fn remove_last(&mut self, len: usize, pool: &mut ListPool) { // Check if we deleted the last element. if len == 1 { self.clear(pool); @@ -477,19 +483,64 @@ impl EntityList { pool.data[block] = T::new(len - 1); } + /// Removes the element at position `index` from the list. Potentially linear complexity. + pub fn remove(&mut self, index: usize, pool: &mut ListPool) { + let len; + { + let seq = self.as_mut_slice(pool); + len = seq.len(); + debug_assert!(index < len); + + // Copy elements down. + for i in index..len - 1 { + seq[i] = seq[i + 1]; + } + } + + self.remove_last(len, pool); + } + /// Removes the element at `index` in constant time by switching it with the last element of /// the list. pub fn swap_remove(&mut self, index: usize, pool: &mut ListPool) { - let len = self.len(pool); + let seq = self.as_mut_slice(pool); + let len = seq.len(); debug_assert!(index < len); - if index == len - 1 { - self.remove(index, pool); - } else { - { - let seq = self.as_mut_slice(pool); - seq.swap(index, len - 1); + if index != len - 1 { + seq.swap(index, len - 1); + } + + self.remove_last(len, pool); + } + + /// Shortens the list down to `len` elements. + /// + /// Does nothing if the list is already shorter than `len`. + pub fn truncate(&mut self, new_len: usize, pool: &mut ListPool) { + if new_len == 0 { + self.clear(pool); + return; + } + + match pool.len_of(self) { + None => return, + Some(len) => { + if len <= new_len { + return; + } + + let block; + let idx = self.index as usize; + let sclass = sclass_for_length(len); + let new_sclass = sclass_for_length(new_len); + if sclass != new_sclass { + block = pool.realloc(idx - 1, sclass, new_sclass, new_len + 1); + self.index = (block + 1) as u32; + } else { + block = idx - 1; + } + pool.data[block] = T::new(new_len); } - self.remove(len - 1, pool); } } @@ -736,4 +787,44 @@ mod tests { list.as_mut_slice(pool)[3] = i4; assert_eq!(list.as_slice(pool), &[i2, i1, i3, i4]); } + + #[test] + fn deep_clone() { + let pool = &mut ListPool::::new(); + + let i1 = Inst::new(1); + let i2 = Inst::new(2); + let i3 = Inst::new(3); + let i4 = Inst::new(4); + + let mut list1 = EntityList::from_slice(&[i1, i2, i3], pool); + let list2 = list1.deep_clone(pool); + assert_eq!(list1.as_slice(pool), &[i1, i2, i3]); + assert_eq!(list2.as_slice(pool), &[i1, i2, i3]); + + list1.as_mut_slice(pool)[0] = i4; + assert_eq!(list1.as_slice(pool), &[i4, i2, i3]); + assert_eq!(list2.as_slice(pool), &[i1, i2, i3]); + } + + #[test] + fn truncate() { + let pool = &mut ListPool::::new(); + + let i1 = Inst::new(1); + let i2 = Inst::new(2); + let i3 = Inst::new(3); + let i4 = Inst::new(4); + + let mut list = EntityList::from_slice(&[i1, i2, i3, i4, i1, i2, i3, i4], pool); + assert_eq!(list.as_slice(pool), &[i1, i2, i3, i4, i1, i2, i3, i4]); + list.truncate(6, pool); + assert_eq!(list.as_slice(pool), &[i1, i2, i3, i4, i1, i2]); + list.truncate(9, pool); + assert_eq!(list.as_slice(pool), &[i1, i2, i3, i4, i1, i2]); + list.truncate(2, pool); + assert_eq!(list.as_slice(pool), &[i1, i2]); + list.truncate(0, pool); + assert!(list.is_empty()); + } }