Browse Source

re-implements head to avoid unecessary Value::clone

Tamo 1 year ago
parent
commit
fa1104e0cf
2 changed files with 16 additions and 12 deletions
  1. 2 4
      numbat/src/ffi/lists.rs
  2. 14 8
      numbat/src/list.rs

+ 2 - 4
numbat/src/ffi/lists.rs

@@ -13,10 +13,8 @@ pub fn len(mut args: Args) -> Result<Value> {
 pub fn head(mut args: Args) -> Result<Value> {
     let list = list_arg!(args);
 
-    // We don't need to pop or drop anything, the whole allocation will
-    // be dropped if we're its last owner.
-    if let Some(first) = list.front() {
-        Ok(first.clone())
+    if let Some(first) = list.head() {
+        Ok(first)
     } else {
         Err(RuntimeError::EmptyList)
     }

+ 14 - 8
numbat/src/list.rs

@@ -74,14 +74,6 @@ impl<T> NumbatList<T> {
         }
     }
 
-    pub fn front(&self) -> Option<&T> {
-        if let Some(view) = self.view {
-            self.alloc.get(view.0)
-        } else {
-            self.alloc.front()
-        }
-    }
-
     /// Advance the view you have of the list by one.
     pub fn advance_view(&mut self) -> Result<(), RuntimeError> {
         if self.is_empty() {
@@ -120,6 +112,20 @@ impl<T: Clone> NumbatList<T> {
         (&mut self.view, Arc::make_mut(&mut self.alloc))
     }
 
+    /// Return the first element of the list. If we're the only owner of the list,
+    /// drop the list and do not copy anything. If another list is alive, only
+    /// clone the value that's being returned.
+    pub fn head(self) -> Option<T> {
+        let front = self.view.map_or(0, |(start, _end)| start);
+        if Arc::strong_count(&self.alloc) == 1 {
+            // safety: unwrap cannot fail because we ensured there was only one strong reference above
+            let mut alloc = Arc::try_unwrap(self.alloc).map_err(|_| ()).unwrap();
+            alloc.swap_remove_front(front)
+        } else {
+            self.alloc.get(front).cloned()
+        }
+    }
+
     /// Allocate iif the list being used by another value at the same time
     pub fn push_front(&mut self, element: T) {
         let (view, inner) = self.make_mut();