Skip to content

Commit

Permalink
linked list support push front
Browse files Browse the repository at this point in the history
--------
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 <[email protected]>
  • Loading branch information
guoweikang committed Oct 23, 2024
1 parent d18c1d0 commit df447b7
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
14 changes: 14 additions & 0 deletions src/linked_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ impl<G: GetLinksWrapped> List<G> {
}
}

/// 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
Expand Down
31 changes: 28 additions & 3 deletions src/raw_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl<G: GetLinks> RawList<G> {
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.
Expand All @@ -164,7 +164,13 @@ impl<G: GetLinks> RawList<G> {
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;
Expand All @@ -175,7 +181,11 @@ impl<G: GetLinks> RawList<G> {
}

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 {
Expand Down Expand Up @@ -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::<Example>::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, _| {
Expand Down

0 comments on commit df447b7

Please sign in to comment.