From df447b733735af4f188204cb113c50e5afb13150 Mon Sep 17 00:00:00 2001 From: guoweikang Date: Wed, 23 Oct 2024 18:13:48 +0800 Subject: [PATCH 1/2] 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, _| { From f5d3aa1707454a57f8f1aa68708bf1ce6eafea3b Mon Sep 17 00:00:00 2001 From: guoweikang Date: Thu, 24 Oct 2024 15:56:19 +0800 Subject: [PATCH 2/2] RawList as public vis ------- 1 update to 0.2.1 2 RawList as a public type Signed-off-by: guoweikang --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/raw_list.rs | 52 ++++++++++++++++++++++++++++--------------------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d6a6f69..c7e5c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linked_list_r4l" -version = "0.2.0" +version = "0.2.1" edition = "2021" authors = ["Wedson Almeida Filho ", "WeiKang Guo "] description = "Linked lists that supports arbitrary removal in constant time" diff --git a/src/lib.rs b/src/lib.rs index 8fb8e00..9783861 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ mod linked_list; mod raw_list; pub use linked_list::List; -pub use raw_list::{GetLinks, Links}; +pub use raw_list::{GetLinks, Links, RawList}; #[macro_export(local_inner_macros)] #[doc(hidden)] diff --git a/src/raw_list.rs b/src/raw_list.rs index c1f9dcd..2895136 100644 --- a/src/raw_list.rs +++ b/src/raw_list.rs @@ -90,21 +90,23 @@ impl ListEntry { /// # Invariants /// /// The links of objects added to a list are owned by the list. -pub(crate) struct RawList { +pub struct RawList { head: Option>, } impl RawList { - pub(crate) const fn new() -> Self { + /// Constructs a new empty RawList. + pub const fn new() -> Self { Self { head: None } } /// Returns an iterator for the list starting at the first entry. - pub(crate) fn iter(&self) -> Iterator<'_, G> { + pub fn iter(&self) -> Iterator<'_, G> { Iterator::new(self.cursor_front(), self.cursor_back()) } - pub(crate) const fn is_empty(&self) -> bool { + /// Returns whether the RawList is empty. + pub const fn is_empty(&self) -> bool { self.head.is_none() } @@ -135,11 +137,7 @@ impl RawList { /// # Safety /// /// Callers must ensure that `existing` points to a valid entry that is on the list. - pub(crate) unsafe fn insert_after( - &mut self, - existing: &G::EntryType, - new: &G::EntryType, - ) -> bool { + pub unsafe fn insert_after(&mut self, existing: &G::EntryType, new: &G::EntryType) -> bool { let links = G::get_links(new); if !links.acquire_for_insertion() { // Nothing to do if already inserted. @@ -180,11 +178,21 @@ impl RawList { true } - pub(crate) unsafe fn push_back(&mut self, new: &G::EntryType) -> bool { + /// Adds the given object to the end (back) of the list. + /// + /// Rawlist will save the reference as node ptr. + /// The caller must ensure the validity of the reference while it is on + /// the linked list. + pub unsafe fn push_back(&mut self, new: &G::EntryType) -> bool { self.push_back_internal(new, false) } - pub(crate) unsafe fn push_front(&mut self, new: &G::EntryType) -> bool { + /// Adds the given object to the first (front) of the list. + /// + /// Rawlist will save the reference as node ptr. + /// The caller must ensure the validity of the reference while it is on + /// the linked list. + pub unsafe fn push_front(&mut self, new: &G::EntryType) -> bool { self.push_back_internal(new, true) } @@ -232,7 +240,7 @@ impl RawList { /// /// Callers must ensure that `data` is either on this list or in no list. It being on another /// list leads to memory unsafety. - pub(crate) unsafe fn remove(&mut self, data: &G::EntryType) -> bool { + pub unsafe fn remove(&mut self, data: &G::EntryType) -> bool { self.remove_internal(data) } @@ -244,7 +252,7 @@ impl RawList { } /// Get and Remove the first element of the list. - pub(crate) fn pop_front(&mut self) -> Option> { + pub fn pop_front(&mut self) -> Option> { self.pop_front_internal() } @@ -270,7 +278,7 @@ impl RawList { } /// Returns a mut cursor starting on the first element of the list. - pub(crate) fn cursor_front_mut(&mut self) -> CursorMut<'_, G> { + pub fn cursor_front_mut(&mut self) -> CursorMut<'_, G> { CursorMut::new(self, self.front()) } } @@ -361,7 +369,7 @@ impl<'a, G: GetLinks> Cursor<'a, G> { } } -pub(crate) struct CursorMut<'a, G: GetLinks> { +pub struct CursorMut<'a, G: GetLinks> { cursor: CommonCursor, list: &'a mut RawList, } @@ -374,7 +382,7 @@ impl<'a, G: GetLinks> CursorMut<'a, G> { } } - pub(crate) fn current(&mut self) -> Option<&mut G::EntryType> { + pub fn current(&mut self) -> Option<&mut G::EntryType> { let cur = self.cursor.cur?; // SAFETY: Objects must be kept alive while on the list. Some(unsafe { &mut *cur.as_ptr() }) @@ -382,7 +390,7 @@ impl<'a, G: GetLinks> CursorMut<'a, G> { /// Removes the entry the cursor is pointing to and advances the cursor to the next entry. It /// returns a raw pointer to the removed element (if one is removed). - pub(crate) fn remove_current(&mut self) -> Option> { + pub fn remove_current(&mut self) -> Option> { let entry = self.cursor.cur?; self.cursor.move_next(self.list); // SAFETY: The entry is on the list as we just got it from there and it cannot change. @@ -390,26 +398,26 @@ impl<'a, G: GetLinks> CursorMut<'a, G> { Some(entry) } - pub(crate) fn peek_next(&mut self) -> Option<&mut G::EntryType> { + pub fn peek_next(&mut self) -> Option<&mut G::EntryType> { let mut new = CommonCursor::new(self.cursor.cur); new.move_next(self.list); // SAFETY: Objects must be kept alive while on the list. Some(unsafe { &mut *new.cur?.as_ptr() }) } - pub(crate) fn peek_prev(&mut self) -> Option<&mut G::EntryType> { + pub fn peek_prev(&mut self) -> Option<&mut G::EntryType> { let mut new = CommonCursor::new(self.cursor.cur); new.move_prev(self.list); // SAFETY: Objects must be kept alive while on the list. Some(unsafe { &mut *new.cur?.as_ptr() }) } - pub(crate) fn move_next(&mut self) { + pub fn move_next(&mut self) { self.cursor.move_next(self.list); } #[allow(dead_code)] - pub(crate) fn move_prev(&mut self) { + pub fn move_prev(&mut self) { self.cursor.move_prev(self.list); } } @@ -423,7 +431,7 @@ impl<'a, G: GetLinks> iter::IntoIterator for &'a RawList { } /// An iterator for the linked list. -pub(crate) struct Iterator<'a, G: GetLinks> { +pub struct Iterator<'a, G: GetLinks> { cursor_front: Cursor<'a, G>, cursor_back: Cursor<'a, G>, }