Commit 6f66691214
Changed files (3)
std/index.zig
@@ -8,6 +8,7 @@ pub const empty_import = @import("empty.zig");
pub const fmt = @import("fmt.zig");
pub const hash_map = @import("hash_map.zig");
pub const io = @import("io.zig");
+pub const linked_list = @import("linked_list.zig");
pub const list = @import("list.zig");
pub const math = @import("math.zig");
pub const mem = @import("mem.zig");
@@ -28,6 +29,7 @@ test "std" {
_ = @import("fmt.zig");
_ = @import("hash_map.zig");
_ = @import("io.zig");
+ _ = @import("linked_list.zig");
_ = @import("list.zig");
_ = @import("math.zig");
_ = @import("mem.zig");
std/linked_list.zig
@@ -0,0 +1,271 @@
+const debug = @import("debug.zig");
+const assert = debug.assert;
+const mem = @import("mem.zig");
+const Allocator = mem.Allocator;
+
+/// Generic doubly linked list.
+pub fn LinkedList(comptime T: type) -> type {
+ struct {
+ const List = this;
+
+ /// Node inside the linked list wrapping the actual data.
+ pub const Node = struct {
+ prev: ?&Node,
+ next: ?&Node,
+ data: T,
+ };
+
+ first: ?&Node,
+ last: ?&Node,
+ len: usize,
+ allocator: &Allocator,
+
+ /// Initialize a linked list.
+ ///
+ /// Arguments:
+ /// allocator: Dynamic memory allocator.
+ ///
+ /// Returns:
+ /// An empty linked list.
+ pub fn init(allocator: &Allocator) -> List {
+ List {
+ .first = null,
+ .last = null,
+ .len = 0,
+ .allocator = allocator,
+ }
+ }
+
+ /// 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.
+ pub fn insertAfter(list: &List, node: &Node, new_node: &Node) {
+ new_node.prev = node;
+ test (node.next) |next_node| {
+ // 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.
+ pub fn insertBefore(list: &List, node: &Node, new_node: &Node) {
+ new_node.next = node;
+ test (node.prev) |prev_node| {
+ // 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.
+ pub fn append(list: &List, new_node: &Node) {
+ test (list.last) |last| {
+ // 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.
+ pub fn prepend(list: &List, new_node: &Node) {
+ test (list.first) |first| {
+ // 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.
+ pub fn remove(list: &List, node: &Node) {
+ test (node.prev) |prev_node| {
+ // Intermediate node.
+ prev_node.next = node.next;
+ } else {
+ // First element of the list.
+ list.first = node.next;
+ }
+
+ test (node.next) |next_node| {
+ // 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.
+ pub fn pop(list: &List) -> ?&Node {
+ 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.
+ pub fn popFirst(list: &List) -> ?&Node {
+ const first = list.first ?? return null;
+ list.remove(first);
+ return first;
+ }
+
+ /// Allocate a new node.
+ ///
+ /// Returns:
+ /// A pointer to the new node.
+ pub fn allocateNode(list: &List) -> %&Node {
+ list.allocator.create(Node)
+ }
+
+ /// Deallocate a node.
+ ///
+ /// Arguments:
+ /// node: Pointer to the node to deallocate.
+ pub fn destroyNode(list: &List, node: &Node) {
+ list.allocator.destroy(node);
+ }
+
+ /// Allocate and initialize a node and its data.
+ ///
+ /// Arguments:
+ /// data: The data to put inside the node.
+ ///
+ /// Returns:
+ /// A pointer to the new node.
+ pub fn createNode(list: &List, data: &const T) -> %&Node {
+ var node = %return list.allocateNode();
+ *node = Node {
+ .prev = null,
+ .next = null,
+ .data = *data,
+ };
+ return node;
+ }
+
+ /// Iterate through the elements of the list.
+ ///
+ /// Returns:
+ /// A list iterator with a next() method.
+ pub fn iterate(list: &List) -> List.Iterator(false) {
+ List.Iterator(false) {
+ .node = list.first,
+ }
+ }
+
+ /// Iterate through the elements of the list backwards.
+ ///
+ /// Returns:
+ /// A list iterator with a next() method.
+ pub fn iterateBackwards(list: &List) -> List.Iterator(true) {
+ List.Iterator(true) {
+ .node = list.last,
+ }
+ }
+
+ /// Abstract iteration over a linked list.
+ pub fn Iterator(comptime backwards: bool) -> type {
+ struct {
+ const It = this;
+
+ node: ?&Node,
+
+ /// Return the next element of the list, until the end.
+ /// When no more elements are available, return null.
+ pub fn next(it: &It) -> ?&Node {
+ const current = it.node ?? return null;
+ it.node = if (backwards) current.prev else current.next;
+ return current;
+ }
+ }
+ }
+ }
+}
+
+test "basic linked list test" {
+ var list = LinkedList(u32).init(&debug.global_allocator);
+
+ var one = %%list.createNode(1);
+ var two = %%list.createNode(2);
+ var three = %%list.createNode(3);
+ var four = %%list.createNode(4);
+ var five = %%list.createNode(5);
+ defer {
+ list.destroyNode(one);
+ list.destroyNode(two);
+ list.destroyNode(three);
+ list.destroyNode(four);
+ list.destroyNode(five);
+ }
+
+ 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}
+
+ // Traverse the list forwards and backwards.
+ var it = list.iterate();
+ var it_reverse = list.iterateBackwards();
+ var index: u32 = 1;
+ while (true) {
+ const node = it.next() ?? break;
+ const node_reverse = it_reverse.next() ?? break;
+ assert (node.data == index);
+ assert (node_reverse.data == (6 - index));
+ index += 1;
+ }
+
+ 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);
+}
CMakeLists.txt
@@ -215,6 +215,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/fmt.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")
+install(FILES "${CMAKE_SOURCE_DIR}/std/linked_list.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/list.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/math.zig" DESTINATION "${ZIG_STD_DEST}")
install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")