From 3a98f214edd73dbf0849f5642c5cb57b45ffab75 Mon Sep 17 00:00:00 2001 From: Graham Keenan Date: Sun, 5 Jun 2022 18:25:50 +0100 Subject: [PATCH 1/4] feat(cordyceps): added new push_back and pop_front methods to list --- cordyceps/src/list.rs | 105 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index 68e1b0c4..d943b290 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -158,6 +158,49 @@ impl> + ?Sized> List { // tracing::trace!(?self, "push_front: pushed"); } + /// Appends an item to the tail of the list + pub fn push_back(&mut self, item: T::Handle) { + let ptr = T::into_ptr(item); + // tracing::trace!(?self, ?ptr, "push_back"); + assert_ne!(self.tail, Some(ptr)); + unsafe { + T::links(ptr).as_mut().set_next(None); + T::links(ptr).as_mut().set_prev(self.tail); + // tracing::trace!(?links); + if let Some(tail) = self.tail { + T::links(tail).as_mut().set_next(Some(ptr)); + // tracing::trace!(tail.links = ?T::links(tail).as_ref(), "set tail next ptr"); + } + } + + self.tail = Some(ptr); + if self.head.is_none() { + self.head = Some(ptr); + } + + // tracing::trace!(?self, "push_back: pushed"); + } + + /// Remove an item from the head of the list + pub fn pop_front(&mut self) -> Option { + let head = self.head?; + + unsafe { + let mut head_links = T::links(head); + // tracing::trace!(?self, head.addr = ?head, head.links = ?head_links, "pop_front"); + self.head = head_links.as_ref().next(); + if let Some(next) = head_links.as_mut().next() { + T::links(next).as_mut().set_prev(None); + } else { + self.tail = None; + } + + head_links.as_mut().unlink(); + // tracing::trace!(?self, head.links = ?head.links, "pop_front: popped"); + Some(T::from_ptr(head)) + } + } + /// Removes an item from the tail of the list. pub fn pop_back(&mut self) -> Option { let tail = self.tail?; @@ -563,6 +606,68 @@ mod tests { assert!(list.is_empty()); } + #[test] + fn pop_front() { + let _trace = trace_init(); + + let a = entry(5); + let b = entry(7); + let c = entry(9); + let mut list = List::::new(); + + list.push_front(a.as_ref()); + list.assert_valid(); + + list.push_front(b.as_ref()); + list.assert_valid(); + + list.push_front(c.as_ref()); + list.assert_valid(); + + let d = list.pop_front().unwrap(); + assert_eq!(9, d.val); + + let e = list.pop_front().unwrap(); + assert_eq!(7, e.val); + + let f = list.pop_front().unwrap(); + assert_eq!(5, f.val); + + assert!(list.is_empty()); + assert!(list.pop_front().is_none()); + list.assert_valid(); + } + + #[test] + fn push_back() { + let _trace = trace_init(); + + let a = entry(5); + let b = entry(7); + let c = entry(9); + let mut list = List::::new(); + + list.push_back(a.as_ref()); + list.assert_valid(); + + list.push_back(b.as_ref()); + list.assert_valid(); + + list.push_back(c.as_ref()); + list.assert_valid(); + + let d = list.pop_back().unwrap(); + assert_eq!(9, d.val); + + let e = list.pop_back().unwrap(); + assert_eq!(7, e.val); + + let f = list.pop_back().unwrap(); + assert_eq!(5, f.val); + + list.assert_valid(); + } + #[test] fn push_pop_push_pop() { let _trace = trace_init(); From e40bb2337529e7f98d5bafcf4467bbb78de10f44 Mon Sep 17 00:00:00 2001 From: Graham Keenan Date: Sun, 5 Jun 2022 18:37:16 +0100 Subject: [PATCH 2/4] test(cordyceps): added missing checks to push_back test --- cordyceps/src/list.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index d943b290..712c4bb7 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -665,6 +665,9 @@ mod tests { let f = list.pop_back().unwrap(); assert_eq!(5, f.val); + assert!(list.is_empty()); + assert!(list.pop_back().is_none()); + list.assert_valid(); } From 882b7673c2830f22c82bc3d18cef5afd25d2ace3 Mon Sep 17 00:00:00 2001 From: Graham Keenan Date: Sun, 5 Jun 2022 21:26:34 +0100 Subject: [PATCH 3/4] test(cordyceps): added tests for new methods to fuzz_linked_list proptest. updated documentation to clarify the List can now be used as a stack or Doubly-Linked List --- cordyceps/src/list.rs | 46 +++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index 712c4bb7..95f0349d 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -16,6 +16,9 @@ use core::{ /// This data structure may be used as a first-in, first-out queue by using the /// [`List::push_front`] and [`List::pop_back`] methods. It also supports /// random-access removals using the [`List::remove`] method. +/// +/// This data structure can also be used as a stack or doubly-linked list by using +/// the [`List::pop_front`] and [`List::push_back`] methods. /// /// In order to be part of a `List`, a type `T` must implement [`Linked`] for /// [`list::Links`]. @@ -161,15 +164,12 @@ impl> + ?Sized> List { /// Appends an item to the tail of the list pub fn push_back(&mut self, item: T::Handle) { let ptr = T::into_ptr(item); - // tracing::trace!(?self, ?ptr, "push_back"); assert_ne!(self.tail, Some(ptr)); unsafe { T::links(ptr).as_mut().set_next(None); T::links(ptr).as_mut().set_prev(self.tail); - // tracing::trace!(?links); if let Some(tail) = self.tail { T::links(tail).as_mut().set_next(Some(ptr)); - // tracing::trace!(tail.links = ?T::links(tail).as_ref(), "set tail next ptr"); } } @@ -177,8 +177,6 @@ impl> + ?Sized> List { if self.head.is_none() { self.head = Some(ptr); } - - // tracing::trace!(?self, "push_back: pushed"); } /// Remove an item from the head of the list @@ -187,7 +185,6 @@ impl> + ?Sized> List { unsafe { let mut head_links = T::links(head); - // tracing::trace!(?self, head.addr = ?head, head.links = ?head_links, "pop_front"); self.head = head_links.as_ref().next(); if let Some(next) = head_links.as_mut().next() { T::links(next).as_mut().set_prev(None); @@ -196,7 +193,6 @@ impl> + ?Sized> List { } head_links.as_mut().unlink(); - // tracing::trace!(?self, head.links = ?head.links, "pop_front: popped"); Some(T::from_ptr(head)) } } @@ -923,8 +919,10 @@ mod tests { #[derive(Debug)] enum Op { - Push, - Pop, + PushFront, + PopBack, + PushBack, + PopFront, Remove(usize), } @@ -955,10 +953,12 @@ mod tests { let ops = ops .iter() - .map(|i| match i % 3 { - 0 => Op::Push, - 1 => Op::Pop, - 2 => Op::Remove(i / 3), + .map(|i| match i % 5 { + 0 => Op::PushFront, + 1 => Op::PopBack, + 2 => Op::PushBack, + 3 => Op::PopFront, + 4 => Op::Remove(i / 5), _ => unreachable!(), }) .collect::>(); @@ -982,13 +982,13 @@ mod tests { let _span = tracing::info_span!("op", ?i, ?op).entered(); tracing::info!(?op); match op { - Op::Push => { + Op::PushFront => { reference.push_front(i as i32); assert_eq!(entries[i].val, i as i32); ll.push_front(entries[i].as_ref()); } - Op::Pop => { + Op::PopBack => { if reference.is_empty() { assert!(ll.is_empty()); tracing::debug!("skipping pop; list is empty"); @@ -998,6 +998,22 @@ mod tests { let v = reference.pop_back(); assert_eq!(v, ll.pop_back().map(|v| v.val)); } + Op::PushBack => { + reference.push_back(i as i32); + assert_eq!(entries[i].val, i as i32); + + ll.push_back(entries[i].as_ref()); + } + Op::PopFront => { + if reference.is_empty() { + assert!(ll.is_empty()); + tracing::debug!("skipping pop: list is empty"); + continue; + } + + let v = reference.pop_front(); + assert_eq!(v, ll.pop_front().map(|v| v.val)); + } Op::Remove(n) => { if reference.is_empty() { assert!(ll.is_empty()); From 1c0e6abd255b97a5fc30753567745f37f51ad4bc Mon Sep 17 00:00:00 2001 From: Graham Keenan Date: Sun, 5 Jun 2022 23:21:23 +0100 Subject: [PATCH 4/4] lint(cordyceps): run rustfmt --- cordyceps/src/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index 95f0349d..e85e2daf 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -16,7 +16,7 @@ use core::{ /// This data structure may be used as a first-in, first-out queue by using the /// [`List::push_front`] and [`List::pop_back`] methods. It also supports /// random-access removals using the [`List::remove`] method. -/// +/// /// This data structure can also be used as a stack or doubly-linked list by using /// the [`List::pop_front`] and [`List::push_back`] methods. ///