From df447b733735af4f188204cb113c50e5afb13150 Mon Sep 17 00:00:00 2001 From: guoweikang Date: Wed, 23 Oct 2024 18:13:48 +0800 Subject: [PATCH] linked list support push front -------- Implementing push_front in a circular doubly linked list is very simple. It behaves the same as pushback, and only requires updating the head. Signed-off-by: guoweikang --- src/linked_list.rs | 14 ++++++++++++++ src/raw_list.rs | 31 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/linked_list.rs b/src/linked_list.rs index 1f4b9fb..d8f239d 100644 --- a/src/linked_list.rs +++ b/src/linked_list.rs @@ -161,6 +161,20 @@ impl List { } } + /// Adds the given object to the first (front) of the list. + /// + /// It is dropped if it's already on this (or another) list; this can happen for + /// reference-counted objects, so dropping means decrementing the reference count. + pub fn push_front(&mut self, data: G::Wrapped) { + let ptr = data.into_pointer(); + + // SAFETY: We took ownership of the entry, so it is safe to insert it. + if !unsafe { self.list.push_front(ptr.as_ref()) } { + // If insertion failed, rebuild object so that it can be freed. + unsafe { G::Wrapped::from_pointer(ptr) }; + } + } + /// Inserts the given object after `existing`. /// /// It is dropped if it's already on this (or another) list; this can happen for diff --git a/src/raw_list.rs b/src/raw_list.rs index 4218ce1..c1f9dcd 100644 --- a/src/raw_list.rs +++ b/src/raw_list.rs @@ -152,7 +152,7 @@ impl RawList { true } - fn push_back_internal(&mut self, new: &G::EntryType) -> bool { + fn push_back_internal(&mut self, new: &G::EntryType, front: bool) -> bool { let links = G::get_links(new); if !links.acquire_for_insertion() { // Nothing to do if already inserted. @@ -164,7 +164,13 @@ impl RawList { let new_ptr = Some(NonNull::from(new)); match self.back() { // SAFETY: `back` is valid as the list cannot change. - Some(back) => self.insert_after_priv(unsafe { back.as_ref() }, new_entry, new_ptr), + Some(back) => { + self.insert_after_priv(unsafe { back.as_ref() }, new_entry, new_ptr); + // if push front, update head + if front { + self.head = new_ptr; + } + } None => { self.head = new_ptr; new_entry.next = new_ptr; @@ -175,7 +181,11 @@ impl RawList { } pub(crate) unsafe fn push_back(&mut self, new: &G::EntryType) -> bool { - self.push_back_internal(new) + self.push_back_internal(new, false) + } + + pub(crate) unsafe fn push_front(&mut self, new: &G::EntryType) -> bool { + self.push_back_internal(new, true) } fn remove_internal(&mut self, data: &G::EntryType) -> bool { @@ -538,6 +548,21 @@ mod tests { } } + #[test] + fn test_push_front() { + const MAX: usize = 10; + let v = build_vector(MAX); + let mut list = super::RawList::::new(); + + for n in 1..=MAX { + // SAFETY: The entry was allocated above, it's not in any lists yet, is never moved, + // and outlives the list. + println!("push front: {}", MAX - n); + unsafe { list.push_front(&v[MAX - n]) }; + assert_list_contents(&v[MAX - n..MAX], &list); + } + } + #[test] fn test_one_removal() { test_each_element(1, 10, |v, list, i, _| {