2017-05-03 11:28:06 -07:00
|
|
|
const debug = @import("debug.zig");
|
|
|
|
const assert = debug.assert;
|
|
|
|
const mem = @import("mem.zig");
|
|
|
|
|
|
|
|
/// Generic doubly linked list.
|
|
|
|
pub fn LinkedList(comptime T: type) -> type {
|
|
|
|
struct {
|
2017-05-04 11:05:06 -07:00
|
|
|
const Self = this;
|
2017-05-03 11:28:06 -07:00
|
|
|
|
|
|
|
/// Node inside the linked list wrapping the actual data.
|
|
|
|
pub const Node = struct {
|
|
|
|
prev: ?&Node,
|
|
|
|
next: ?&Node,
|
|
|
|
data: T,
|
2017-09-07 20:10:23 -07:00
|
|
|
|
|
|
|
pub fn init(data: &const T) -> Node {
|
|
|
|
Node {
|
|
|
|
.data = *data,
|
|
|
|
.prev = null,
|
|
|
|
.next = null,
|
|
|
|
}
|
|
|
|
}
|
2017-05-03 11:28:06 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
first: ?&Node,
|
|
|
|
last: ?&Node,
|
|
|
|
len: usize,
|
|
|
|
|
|
|
|
/// Initialize a linked list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// An empty linked list.
|
2017-09-07 20:10:23 -07:00
|
|
|
pub fn init() -> Self {
|
2017-05-04 11:05:06 -07:00
|
|
|
Self {
|
2017-05-03 11:28:06 -07:00
|
|
|
.first = null,
|
|
|
|
.last = null,
|
|
|
|
.len = 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a new node after an existing one.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// node: Pointer to a node in the list.
|
|
|
|
/// new_node: Pointer to the new node to insert.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn insertAfter(list: &Self, node: &Node, new_node: &Node) {
|
2017-05-03 11:28:06 -07:00
|
|
|
new_node.prev = node;
|
2017-05-03 14:23:11 -07:00
|
|
|
if (node.next) |next_node| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Intermediate node.
|
|
|
|
new_node.next = next_node;
|
|
|
|
next_node.prev = new_node;
|
|
|
|
} else {
|
|
|
|
// Last element of the list.
|
|
|
|
new_node.next = null;
|
|
|
|
list.last = new_node;
|
|
|
|
}
|
|
|
|
node.next = new_node;
|
|
|
|
|
|
|
|
list.len += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a new node before an existing one.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// node: Pointer to a node in the list.
|
|
|
|
/// new_node: Pointer to the new node to insert.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn insertBefore(list: &Self, node: &Node, new_node: &Node) {
|
2017-05-03 11:28:06 -07:00
|
|
|
new_node.next = node;
|
2017-05-03 14:23:11 -07:00
|
|
|
if (node.prev) |prev_node| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Intermediate node.
|
|
|
|
new_node.prev = prev_node;
|
|
|
|
prev_node.next = new_node;
|
|
|
|
} else {
|
|
|
|
// First element of the list.
|
|
|
|
new_node.prev = null;
|
|
|
|
list.first = new_node;
|
|
|
|
}
|
|
|
|
node.prev = new_node;
|
|
|
|
|
|
|
|
list.len += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a new node at the end of the list.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// new_node: Pointer to the new node to insert.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn append(list: &Self, new_node: &Node) {
|
2017-05-03 14:23:11 -07:00
|
|
|
if (list.last) |last| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Insert after last.
|
|
|
|
list.insertAfter(last, new_node);
|
|
|
|
} else {
|
|
|
|
// Empty list.
|
|
|
|
list.prepend(new_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert a new node at the beginning of the list.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// new_node: Pointer to the new node to insert.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn prepend(list: &Self, new_node: &Node) {
|
2017-05-03 14:23:11 -07:00
|
|
|
if (list.first) |first| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Insert before first.
|
|
|
|
list.insertBefore(first, new_node);
|
|
|
|
} else {
|
|
|
|
// Empty list.
|
|
|
|
list.first = new_node;
|
|
|
|
list.last = new_node;
|
|
|
|
new_node.prev = null;
|
|
|
|
new_node.next = null;
|
|
|
|
|
|
|
|
list.len = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove a node from the list.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// node: Pointer to the node to be removed.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn remove(list: &Self, node: &Node) {
|
2017-05-03 14:23:11 -07:00
|
|
|
if (node.prev) |prev_node| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Intermediate node.
|
|
|
|
prev_node.next = node.next;
|
|
|
|
} else {
|
|
|
|
// First element of the list.
|
|
|
|
list.first = node.next;
|
|
|
|
}
|
|
|
|
|
2017-05-03 14:23:11 -07:00
|
|
|
if (node.next) |next_node| {
|
2017-05-03 11:28:06 -07:00
|
|
|
// Intermediate node.
|
|
|
|
next_node.prev = node.prev;
|
|
|
|
} else {
|
|
|
|
// Last element of the list.
|
|
|
|
list.last = node.prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
list.len -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove and return the last node in the list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the last node in the list.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn pop(list: &Self) -> ?&Node {
|
2017-05-03 11:28:06 -07:00
|
|
|
const last = list.last ?? return null;
|
|
|
|
list.remove(last);
|
|
|
|
return last;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove and return the first node in the list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the first node in the list.
|
2017-05-04 11:05:06 -07:00
|
|
|
pub fn popFirst(list: &Self) -> ?&Node {
|
2017-05-03 11:28:06 -07:00
|
|
|
const first = list.first ?? return null;
|
|
|
|
list.remove(first);
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
2017-09-07 20:10:23 -07:00
|
|
|
}
|
|
|
|
}
|
2017-05-03 11:28:06 -07:00
|
|
|
|
2017-09-07 20:10:23 -07:00
|
|
|
pub fn testAllocateNode(comptime T: type, list: &LinkedList(T), allocator: &mem.Allocator) -> %&LinkedList(T).Node {
|
|
|
|
allocator.create(LinkedList(T).Node)
|
|
|
|
}
|
2017-05-03 11:28:06 -07:00
|
|
|
|
2017-09-07 20:10:23 -07:00
|
|
|
pub fn testDestroyNode(comptime T: type, list: &LinkedList(T), node: &LinkedList(T).Node, allocator: &mem.Allocator) {
|
|
|
|
allocator.destroy(node);
|
2017-05-03 11:28:06 -07:00
|
|
|
}
|
|
|
|
|
2017-09-07 20:10:23 -07:00
|
|
|
pub fn testCreateNode(comptime T: type, list: &LinkedList(T), data: &const T, allocator: &mem.Allocator) -> %&LinkedList(T).Node {
|
|
|
|
var node = %return testAllocateNode(T, list, allocator);
|
|
|
|
*node = LinkedList(T).Node.init(data);
|
|
|
|
return node;
|
|
|
|
}
|
2017-05-03 11:28:06 -07:00
|
|
|
|
2017-09-07 20:10:23 -07:00
|
|
|
test "basic linked list test" {
|
|
|
|
const allocator = &debug.global_allocator;
|
|
|
|
var list = LinkedList(u32).init();
|
|
|
|
|
|
|
|
var one = %%testCreateNode(u32, &list, 1, allocator);
|
|
|
|
var two = %%testCreateNode(u32, &list, 2, allocator);
|
|
|
|
var three = %%testCreateNode(u32, &list, 3, allocator);
|
|
|
|
var four = %%testCreateNode(u32, &list, 4, allocator);
|
|
|
|
var five = %%testCreateNode(u32, &list, 5, allocator);
|
2017-05-03 11:28:06 -07:00
|
|
|
defer {
|
2017-09-07 20:10:23 -07:00
|
|
|
testDestroyNode(u32, &list, one, allocator);
|
|
|
|
testDestroyNode(u32, &list, two, allocator);
|
|
|
|
testDestroyNode(u32, &list, three, allocator);
|
|
|
|
testDestroyNode(u32, &list, four, allocator);
|
|
|
|
testDestroyNode(u32, &list, five, allocator);
|
2017-05-03 11:28:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
list.append(two); // {2}
|
|
|
|
list.append(five); // {2, 5}
|
|
|
|
list.prepend(one); // {1, 2, 5}
|
|
|
|
list.insertBefore(five, four); // {1, 2, 4, 5}
|
|
|
|
list.insertAfter(two, three); // {1, 2, 3, 4, 5}
|
|
|
|
|
2017-05-04 07:18:01 -07:00
|
|
|
// traverse forwards
|
|
|
|
{
|
|
|
|
var it = list.first;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.next) {
|
|
|
|
assert(node.data == index);
|
|
|
|
index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// traverse backwards
|
|
|
|
{
|
|
|
|
var it = list.last;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.prev) {
|
|
|
|
assert(node.data == (6 - index));
|
|
|
|
index += 1;
|
|
|
|
}
|
2017-05-03 11:28:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
var first = list.popFirst(); // {2, 3, 4, 5}
|
|
|
|
var last = list.pop(); // {2, 3, 4}
|
|
|
|
list.remove(three); // {2, 4}
|
|
|
|
|
|
|
|
assert ((??list.first).data == 2);
|
|
|
|
assert ((??list.last ).data == 4);
|
|
|
|
assert (list.len == 2);
|
|
|
|
}
|