From 90badfe81f797f303343b86ca11024c44e77807c Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 10 Aug 2022 20:24:39 -0400 Subject: [PATCH] Don't require Clone on as many RocList methods --- crates/roc_std/src/roc_list.rs | 170 +++++++++++++++++---------------- 1 file changed, 86 insertions(+), 84 deletions(-) diff --git a/crates/roc_std/src/roc_list.rs b/crates/roc_std/src/roc_list.rs index e0ae78ed0f..a434035d53 100644 --- a/crates/roc_std/src/roc_list.rs +++ b/crates/roc_std/src/roc_list.rs @@ -147,6 +147,92 @@ impl RocList where T: Clone, { + pub fn from_slice(slice: &[T]) -> Self { + let mut list = Self::empty(); + list.extend_from_slice(slice); + list + } + + pub fn extend_from_slice(&mut self, slice: &[T]) { + // TODO: Can we do better for ZSTs? Alignment might be a problem. + if slice.is_empty() { + return; + } + + let new_len = self.len() + slice.len(); + let non_null_elements = if let Some((elements, storage)) = self.elements_and_storage() { + // Decrement the list's refence count. + let mut copy = storage.get(); + let is_unique = copy.decrease(); + + if is_unique { + // If we have enough capacity, we can add to the existing elements in-place. + if self.capacity() >= slice.len() { + elements + } else { + // There wasn't enough capacity, so we need a new allocation. + // Since this is a unique RocList, we can use realloc here. + let new_ptr = unsafe { + roc_realloc( + storage.as_ptr().cast(), + Self::alloc_bytes(new_len), + Self::alloc_bytes(self.capacity), + Self::alloc_alignment(), + ) + }; + + self.capacity = new_len; + + Self::elems_from_allocation(NonNull::new(new_ptr).unwrap_or_else(|| { + todo!("Reallocation failed"); + })) + } + } else { + if !copy.is_readonly() { + // Write the decremented reference count back. + storage.set(copy); + } + + // Allocate new memory. + let new_elements = Self::elems_with_capacity(slice.len()); + + // Copy the old elements to the new allocation. + unsafe { + copy_nonoverlapping(elements.as_ptr(), new_elements.as_ptr(), self.length); + } + + new_elements + } + } else { + Self::elems_with_capacity(slice.len()) + }; + + self.elements = Some(non_null_elements); + + let elements = self.elements.unwrap().as_ptr(); + + let append_ptr = unsafe { elements.add(self.len()) }; + + // Use .cloned() to increment the elements' reference counts, if needed. + for (i, new_elem) in slice.iter().cloned().enumerate() { + unsafe { + // Write the element into the slot, without dropping it. + append_ptr + .add(i) + .write(ptr::read(&ManuallyDrop::new(new_elem))); + } + + // It's important that the length is increased one by one, to + // make sure that we don't drop uninitialized elements, even when + // a incrementing the reference count panics. + self.length += 1; + } + + self.capacity = self.length + } +} + +impl RocList { /// Increase a RocList's capacity by at least the requested number of elements (possibly more). /// /// May return a new RocList, if the provided one was not unique. @@ -232,90 +318,6 @@ where }); } - pub fn from_slice(slice: &[T]) -> Self { - let mut list = Self::empty(); - list.extend_from_slice(slice); - list - } - - pub fn extend_from_slice(&mut self, slice: &[T]) { - // TODO: Can we do better for ZSTs? Alignment might be a problem. - if slice.is_empty() { - return; - } - - let new_len = self.len() + slice.len(); - let non_null_elements = if let Some((elements, storage)) = self.elements_and_storage() { - // Decrement the list's refence count. - let mut copy = storage.get(); - let is_unique = copy.decrease(); - - if is_unique { - // If we have enough capacity, we can add to the existing elements in-place. - if self.capacity() >= slice.len() { - elements - } else { - // There wasn't enough capacity, so we need a new allocation. - // Since this is a unique RocList, we can use realloc here. - let new_ptr = unsafe { - roc_realloc( - storage.as_ptr().cast(), - Self::alloc_bytes(new_len), - Self::alloc_bytes(self.capacity), - Self::alloc_alignment(), - ) - }; - - self.capacity = new_len; - - Self::elems_from_allocation(NonNull::new(new_ptr).unwrap_or_else(|| { - todo!("Reallocation failed"); - })) - } - } else { - if !copy.is_readonly() { - // Write the decremented reference count back. - storage.set(copy); - } - - // Allocate new memory. - let new_elements = Self::elems_with_capacity(slice.len()); - - // Copy the old elements to the new allocation. - unsafe { - copy_nonoverlapping(elements.as_ptr(), new_elements.as_ptr(), self.length); - } - - new_elements - } - } else { - Self::elems_with_capacity(slice.len()) - }; - - self.elements = Some(non_null_elements); - - let elements = self.elements.unwrap().as_ptr(); - - let append_ptr = unsafe { elements.add(self.len()) }; - - // Use .cloned() to increment the elements' reference counts, if needed. - for (i, new_elem) in slice.iter().cloned().enumerate() { - unsafe { - // Write the element into the slot, without dropping it. - append_ptr - .add(i) - .write(ptr::read(&ManuallyDrop::new(new_elem))); - } - - // It's important that the length is increased one by one, to - // make sure that we don't drop uninitialized elements, even when - // a incrementing the reference count panics. - self.length += 1; - } - - self.capacity = self.length - } - /// Replace self with a new version, without letting `drop` run in between. fn update_to(&mut self, mut updated: Self) { // We want to replace `self` with `updated` in a way that makes sure