Created
September 5, 2018 19:37
-
-
Save steveklabnik/a7c53ddc3e357ad5cdba8559b0adfa0c to your computer and use it in GitHub Desktop.
yeah it's a linked list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::ptr; | |
pub struct DoubleLinkedList<T> { | |
head: *mut Entry<T>, | |
tail: *mut Entry<T>, | |
} | |
pub struct Entry<T> { | |
item: T, | |
next: *mut Entry<T>, | |
prev: *mut Entry<T>, | |
} | |
impl<T> Entry<T> { | |
fn alloc_new(item: T) -> *mut Entry<T> { | |
let entry = Entry { | |
item, | |
next: ptr::null_mut(), | |
prev: ptr::null_mut(), | |
}; | |
Box::into_raw(Box::new(entry)) | |
} | |
fn alloc_with_previous(item: T, prev: *mut Entry<T>) -> *mut Entry<T> { | |
let entry = Entry { | |
item, | |
next: ptr::null_mut(), | |
prev, | |
}; | |
Box::into_raw(Box::new(entry)) | |
} | |
pub fn item(&mut self) -> *mut T { | |
&mut self.item as *mut _ | |
} | |
} | |
impl<T> DoubleLinkedList<T> { | |
pub fn new() -> DoubleLinkedList<T> { | |
DoubleLinkedList { | |
head: ptr::null_mut(), | |
tail: ptr::null_mut(), | |
} | |
} | |
pub fn insert(&mut self, item: T) { | |
if self.head.is_null() { | |
let raw = Entry::alloc_new(item); | |
self.head = raw; | |
self.tail = raw; | |
} else { | |
let tail = self.tail; | |
let raw = Entry::alloc_with_previous(item, tail); | |
unsafe { | |
(*tail).next = raw; | |
self.tail = raw; | |
}; | |
} | |
} | |
} | |
impl<T> Drop for DoubleLinkedList<T> { | |
fn drop(&mut self) { | |
let mut current = self.head; | |
loop { | |
if current.is_null() { | |
break; | |
} | |
unsafe { | |
let next = (*current).next; | |
let b = Box::from_raw(current); | |
drop(b); | |
current = next; | |
} | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn insert_one() { | |
let mut list = DoubleLinkedList::new(); | |
list.insert(5); | |
unsafe { | |
let five = list.head; | |
assert!(!five.is_null()); | |
assert_eq!((*five).item, 5); | |
assert!((*five).next.is_null()); | |
assert!((*five).prev.is_null()); | |
let five = list.tail; | |
assert!(!five.is_null()); | |
assert_eq!((*five).item, 5); | |
assert!((*five).next.is_null()); | |
assert!((*five).prev.is_null()); | |
} | |
} | |
#[test] | |
fn insert_two() { | |
let mut list = DoubleLinkedList::new(); | |
list.insert(5); | |
list.insert(10); | |
unsafe { | |
let five = list.head; | |
assert!(!(*five).next.is_null()); | |
let ten = (*five).next; | |
assert!(!ten.is_null()); | |
assert_eq!((*ten).item, 10); | |
assert!((*ten).next.is_null()); | |
assert_eq!((*ten).prev, five); | |
let ten = list.tail; | |
assert!(!ten.is_null()); | |
assert_eq!((*ten).item, 10); | |
assert!((*ten).next.is_null()); | |
assert_eq!((*ten).prev, five); | |
} | |
} | |
#[test] | |
fn insert_three() { | |
let mut list = DoubleLinkedList::new(); | |
list.insert(5); | |
list.insert(10); | |
list.insert(15); | |
unsafe { | |
let five = list.head; | |
let ten = (*five).next; | |
assert!(!(*ten).next.is_null()); | |
let fifteen = (*ten).next; | |
assert!(!fifteen.is_null()); | |
assert_eq!((*fifteen).item, 15); | |
assert!((*fifteen).next.is_null()); | |
assert_eq!((*fifteen).prev, ten); | |
let fifteen = list.tail; | |
assert!(!fifteen.is_null()); | |
assert_eq!((*fifteen).item, 15); | |
assert!((*fifteen).next.is_null()); | |
assert_eq!((*fifteen).prev, ten); | |
} | |
} | |
#[test] | |
fn free() { | |
static mut COUNT: u32 = 0; | |
struct Boom; | |
impl Drop for Boom { | |
fn drop(&mut self) { | |
unsafe { | |
COUNT += 1; | |
} | |
} | |
} | |
let mut list = DoubleLinkedList::new(); | |
list.insert(Boom); | |
list.insert(Boom); | |
list.insert(Boom); | |
drop(list); | |
unsafe { | |
assert_eq!(COUNT, 3); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment