From 6f66691214a0709d913d15772e7b130ede7977c9 Mon Sep 17 00:00:00 2001 From: Andrea Orru Date: Wed, 3 May 2017 20:28:06 +0200 Subject: [PATCH] Generic doubly linked list. (#361) Standard linked list --- CMakeLists.txt | 1 + std/index.zig | 2 + std/linked_list.zig | 271 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 std/linked_list.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index b57c2cd78d..8e610dd7b9 100644 --- a/CMakeLists.txt +++ b/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}") diff --git a/std/index.zig b/std/index.zig index 946a08c93e..8397741b46 100644 --- a/std/index.zig +++ b/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"); diff --git a/std/linked_list.zig b/std/linked_list.zig new file mode 100644 index 0000000000..d7b8ca98fc --- /dev/null +++ b/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); +}