2017-12-24 03:08:53 +00:00
|
|
|
const std = @import("index.zig");
|
|
|
|
const debug = std.debug;
|
2017-05-03 18:28:06 +00:00
|
|
|
const assert = debug.assert;
|
2017-12-24 03:08:53 +00:00
|
|
|
const mem = std.mem;
|
2017-09-21 14:28:44 +00:00
|
|
|
const Allocator = mem.Allocator;
|
2017-05-03 18:28:06 +00:00
|
|
|
|
2018-01-10 05:33:07 +00:00
|
|
|
/// Generic non-intrusive doubly linked list.
|
2018-01-25 09:10:11 +00:00
|
|
|
pub fn LinkedList(comptime T: type) type {
|
2018-01-10 05:33:07 +00:00
|
|
|
return BaseLinkedList(T, void, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generic intrusive doubly linked list.
|
2018-01-25 09:10:11 +00:00
|
|
|
pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) type {
|
2018-01-10 05:33:07 +00:00
|
|
|
return BaseLinkedList(void, ParentType, field_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generic doubly linked list.
|
2018-01-25 09:10:11 +00:00
|
|
|
fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) type {
|
2017-12-22 05:50:30 +00:00
|
|
|
return struct {
|
2017-05-04 18:05:06 +00:00
|
|
|
const Self = this;
|
2017-05-03 18:28:06 +00:00
|
|
|
|
|
|
|
/// Node inside the linked list wrapping the actual data.
|
|
|
|
pub const Node = struct {
|
2018-05-31 14:56:59 +00:00
|
|
|
prev: ?*Node,
|
|
|
|
next: ?*Node,
|
2017-05-03 18:28:06 +00:00
|
|
|
data: T,
|
2017-09-08 03:10:23 +00:00
|
|
|
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn init(value: *const T) Node {
|
2018-05-01 05:53:04 +00:00
|
|
|
return Node{
|
2017-09-08 03:10:23 +00:00
|
|
|
.prev = null,
|
|
|
|
.next = null,
|
2018-05-01 05:53:04 +00:00
|
|
|
.data = value.*,
|
2017-12-22 05:50:30 +00:00
|
|
|
};
|
2017-09-08 03:10:23 +00:00
|
|
|
}
|
2018-01-10 05:33:07 +00:00
|
|
|
|
2018-01-25 09:10:11 +00:00
|
|
|
pub fn initIntrusive() Node {
|
2018-01-10 05:33:07 +00:00
|
|
|
// TODO: when #678 is solved this can become `init`.
|
|
|
|
return Node.init({});
|
|
|
|
}
|
|
|
|
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn toData(node: *Node) *ParentType {
|
2018-01-10 05:33:07 +00:00
|
|
|
comptime assert(isIntrusive());
|
|
|
|
return @fieldParentPtr(ParentType, field_name, node);
|
|
|
|
}
|
2017-05-03 18:28:06 +00:00
|
|
|
};
|
|
|
|
|
2018-05-31 14:56:59 +00:00
|
|
|
first: ?*Node,
|
|
|
|
last: ?*Node,
|
2018-05-01 05:53:04 +00:00
|
|
|
len: usize,
|
2017-05-03 18:28:06 +00:00
|
|
|
|
|
|
|
/// Initialize a linked list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// An empty linked list.
|
2018-01-25 09:10:11 +00:00
|
|
|
pub fn init() Self {
|
2018-05-01 05:53:04 +00:00
|
|
|
return Self{
|
2017-05-03 18:28:06 +00:00
|
|
|
.first = null,
|
2018-05-01 05:53:04 +00:00
|
|
|
.last = null,
|
|
|
|
.len = 0,
|
2017-12-22 05:50:30 +00:00
|
|
|
};
|
2017-05-03 18:28:06 +00:00
|
|
|
}
|
|
|
|
|
2018-01-25 09:10:11 +00:00
|
|
|
fn isIntrusive() bool {
|
2018-01-10 05:33:07 +00:00
|
|
|
return ParentType != void or field_name.len != 0;
|
|
|
|
}
|
|
|
|
|
2017-05-03 18:28:06 +00:00
|
|
|
/// 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.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void {
|
2017-05-03 18:28:06 +00:00
|
|
|
new_node.prev = node;
|
2017-05-03 21:23:11 +00:00
|
|
|
if (node.next) |next_node| {
|
2017-05-03 18:28:06 +00: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.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn insertBefore(list: *Self, node: *Node, new_node: *Node) void {
|
2017-05-03 18:28:06 +00:00
|
|
|
new_node.next = node;
|
2017-05-03 21:23:11 +00:00
|
|
|
if (node.prev) |prev_node| {
|
2017-05-03 18:28:06 +00: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.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn append(list: *Self, new_node: *Node) void {
|
2017-05-03 21:23:11 +00:00
|
|
|
if (list.last) |last| {
|
2017-05-03 18:28:06 +00: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.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn prepend(list: *Self, new_node: *Node) void {
|
2017-05-03 21:23:11 +00:00
|
|
|
if (list.first) |first| {
|
2017-05-03 18:28:06 +00:00
|
|
|
// Insert before first.
|
|
|
|
list.insertBefore(first, new_node);
|
|
|
|
} else {
|
|
|
|
// Empty list.
|
|
|
|
list.first = new_node;
|
2018-05-01 05:53:04 +00:00
|
|
|
list.last = new_node;
|
2017-05-03 18:28:06 +00:00
|
|
|
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.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn remove(list: *Self, node: *Node) void {
|
2017-05-03 21:23:11 +00:00
|
|
|
if (node.prev) |prev_node| {
|
2017-05-03 18:28:06 +00:00
|
|
|
// Intermediate node.
|
|
|
|
prev_node.next = node.next;
|
|
|
|
} else {
|
|
|
|
// First element of the list.
|
|
|
|
list.first = node.next;
|
|
|
|
}
|
|
|
|
|
2017-05-03 21:23:11 +00:00
|
|
|
if (node.next) |next_node| {
|
2017-05-03 18:28:06 +00:00
|
|
|
// Intermediate node.
|
|
|
|
next_node.prev = node.prev;
|
|
|
|
} else {
|
|
|
|
// Last element of the list.
|
|
|
|
list.last = node.prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
list.len -= 1;
|
2018-03-07 08:55:52 +00:00
|
|
|
assert(list.len == 0 or (list.first != null and list.last != null));
|
2017-05-03 18:28:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove and return the last node in the list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the last node in the list.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn pop(list: *Self) ?*Node {
|
2018-06-10 05:13:51 +00:00
|
|
|
const last = list.last orelse return null;
|
2017-05-03 18:28:06 +00:00
|
|
|
list.remove(last);
|
|
|
|
return last;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove and return the first node in the list.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the first node in the list.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn popFirst(list: *Self) ?*Node {
|
2018-06-10 05:13:51 +00:00
|
|
|
const first = list.first orelse return null;
|
2017-05-03 18:28:06 +00:00
|
|
|
list.remove(first);
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
2017-09-21 14:28:44 +00:00
|
|
|
/// Allocate a new node.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// allocator: Dynamic memory allocator.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the new node.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
|
2018-01-10 05:33:07 +00:00
|
|
|
comptime assert(!isIntrusive());
|
2018-06-20 15:40:21 +00:00
|
|
|
return allocator.create(Node{
|
2018-06-20 21:16:27 +00:00
|
|
|
.prev = null,
|
|
|
|
.next = null,
|
|
|
|
.data = undefined,
|
2018-06-20 15:40:21 +00:00
|
|
|
});
|
2017-09-21 14:28:44 +00:00
|
|
|
}
|
2017-05-03 18:28:06 +00:00
|
|
|
|
2017-09-21 14:28:44 +00:00
|
|
|
/// Deallocate a node.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// node: Pointer to the node to deallocate.
|
|
|
|
/// allocator: Dynamic memory allocator.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
|
2018-01-10 05:33:07 +00:00
|
|
|
comptime assert(!isIntrusive());
|
2017-09-21 14:28:44 +00:00
|
|
|
allocator.destroy(node);
|
|
|
|
}
|
2017-05-03 18:28:06 +00:00
|
|
|
|
2017-09-21 14:28:44 +00:00
|
|
|
/// Allocate and initialize a node and its data.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// data: The data to put inside the node.
|
|
|
|
/// allocator: Dynamic memory allocator.
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// A pointer to the new node.
|
2018-05-31 14:56:59 +00:00
|
|
|
pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {
|
2018-01-10 05:33:07 +00:00
|
|
|
comptime assert(!isIntrusive());
|
2018-01-07 21:51:46 +00:00
|
|
|
var node = try list.allocateNode(allocator);
|
2018-05-01 05:53:04 +00:00
|
|
|
node.* = Node.init(data);
|
2017-09-21 14:28:44 +00:00
|
|
|
return node;
|
|
|
|
}
|
2017-12-22 05:50:30 +00:00
|
|
|
};
|
2017-09-08 03:10:23 +00:00
|
|
|
}
|
2017-05-03 18:28:06 +00:00
|
|
|
|
2017-09-08 03:10:23 +00:00
|
|
|
test "basic linked list test" {
|
2017-11-10 19:02:45 +00:00
|
|
|
const allocator = debug.global_allocator;
|
2017-09-08 03:10:23 +00:00
|
|
|
var list = LinkedList(u32).init();
|
|
|
|
|
2018-05-01 05:53:04 +00:00
|
|
|
var one = try list.createNode(1, allocator);
|
|
|
|
var two = try list.createNode(2, allocator);
|
2018-01-10 05:33:07 +00:00
|
|
|
var three = try list.createNode(3, allocator);
|
2018-05-01 05:53:04 +00:00
|
|
|
var four = try list.createNode(4, allocator);
|
|
|
|
var five = try list.createNode(5, allocator);
|
2017-05-03 18:28:06 +00:00
|
|
|
defer {
|
2017-09-21 14:28:44 +00:00
|
|
|
list.destroyNode(one, allocator);
|
|
|
|
list.destroyNode(two, allocator);
|
|
|
|
list.destroyNode(three, allocator);
|
|
|
|
list.destroyNode(four, allocator);
|
|
|
|
list.destroyNode(five, allocator);
|
2017-05-03 18:28:06 +00:00
|
|
|
}
|
|
|
|
|
2018-05-01 05:53:04 +00: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-03 18:28:06 +00:00
|
|
|
|
2017-09-21 14:28:44 +00:00
|
|
|
// Traverse forwards.
|
2017-05-04 14:18:01 +00:00
|
|
|
{
|
|
|
|
var it = list.first;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.next) {
|
|
|
|
assert(node.data == index);
|
|
|
|
index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-21 14:28:44 +00:00
|
|
|
// Traverse backwards.
|
2017-05-04 14:18:01 +00:00
|
|
|
{
|
|
|
|
var it = list.last;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.prev) {
|
|
|
|
assert(node.data == (6 - index));
|
|
|
|
index += 1;
|
|
|
|
}
|
2017-05-03 18:28:06 +00:00
|
|
|
}
|
|
|
|
|
2018-05-01 05:53:04 +00:00
|
|
|
var first = list.popFirst(); // {2, 3, 4, 5}
|
|
|
|
var last = list.pop(); // {2, 3, 4}
|
|
|
|
list.remove(three); // {2, 4}
|
2017-05-03 18:28:06 +00:00
|
|
|
|
2018-06-10 03:42:14 +00:00
|
|
|
assert(list.first.?.data == 2);
|
|
|
|
assert(list.last.?.data == 4);
|
2018-05-01 05:53:04 +00:00
|
|
|
assert(list.len == 2);
|
2017-05-03 18:28:06 +00:00
|
|
|
}
|
2018-01-10 05:33:07 +00:00
|
|
|
|
2018-03-10 20:20:29 +00:00
|
|
|
const ElementList = IntrusiveLinkedList(Element, "link");
|
2018-01-10 05:33:07 +00:00
|
|
|
const Element = struct {
|
|
|
|
value: u32,
|
2018-03-10 20:20:29 +00:00
|
|
|
link: IntrusiveLinkedList(Element, "link").Node,
|
2018-01-10 05:33:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
test "basic intrusive linked list test" {
|
|
|
|
const allocator = debug.global_allocator;
|
|
|
|
var list = ElementList.init();
|
|
|
|
|
2018-05-01 05:53:04 +00:00
|
|
|
var one = Element{
|
|
|
|
.value = 1,
|
|
|
|
.link = ElementList.Node.initIntrusive(),
|
|
|
|
};
|
|
|
|
var two = Element{
|
|
|
|
.value = 2,
|
|
|
|
.link = ElementList.Node.initIntrusive(),
|
|
|
|
};
|
|
|
|
var three = Element{
|
|
|
|
.value = 3,
|
|
|
|
.link = ElementList.Node.initIntrusive(),
|
|
|
|
};
|
|
|
|
var four = Element{
|
|
|
|
.value = 4,
|
|
|
|
.link = ElementList.Node.initIntrusive(),
|
|
|
|
};
|
|
|
|
var five = Element{
|
|
|
|
.value = 5,
|
|
|
|
.link = ElementList.Node.initIntrusive(),
|
|
|
|
};
|
2018-01-10 05:33:07 +00:00
|
|
|
|
2018-05-01 05:53:04 +00:00
|
|
|
list.append(&two.link); // {2}
|
|
|
|
list.append(&five.link); // {2, 5}
|
|
|
|
list.prepend(&one.link); // {1, 2, 5}
|
|
|
|
list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
|
|
|
|
list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
|
2018-01-10 05:33:07 +00:00
|
|
|
|
|
|
|
// Traverse forwards.
|
|
|
|
{
|
|
|
|
var it = list.first;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.next) {
|
|
|
|
assert(node.toData().value == index);
|
|
|
|
index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Traverse backwards.
|
|
|
|
{
|
|
|
|
var it = list.last;
|
|
|
|
var index: u32 = 1;
|
|
|
|
while (it) |node| : (it = node.prev) {
|
|
|
|
assert(node.toData().value == (6 - index));
|
|
|
|
index += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-01 05:53:04 +00:00
|
|
|
var first = list.popFirst(); // {2, 3, 4, 5}
|
|
|
|
var last = list.pop(); // {2, 3, 4}
|
|
|
|
list.remove(&three.link); // {2, 4}
|
2018-01-10 05:33:07 +00:00
|
|
|
|
2018-06-10 03:42:14 +00:00
|
|
|
assert(list.first.?.toData().value == 2);
|
|
|
|
assert(list.last.?.toData().value == 4);
|
2018-05-01 05:53:04 +00:00
|
|
|
assert(list.len == 2);
|
2018-01-10 05:33:07 +00:00
|
|
|
}
|