diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-01-10 10:13:15 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-01-10 10:13:15 -0500 |
| commit | d4f791cf6c560da25a0b84b4eb2ad68f8e7d27fd (patch) | |
| tree | bde223d349ae1f6251495dc5a0e03dcae6513071 /std | |
| parent | 3c094116aae459b651934663a31981cf09cdb3e4 (diff) | |
| parent | 19343db5936abe5e49ea340db838fe769b499944 (diff) | |
| download | zig-d4f791cf6c560da25a0b84b4eb2ad68f8e7d27fd.tar.gz zig-d4f791cf6c560da25a0b84b4eb2ad68f8e7d27fd.zip | |
Merge pull request #680 from zig-lang/intrusiveLinkedList
Intrusive linked lists
Diffstat (limited to 'std')
| -rw-r--r-- | std/linked_list.zig | 95 |
1 files changed, 87 insertions, 8 deletions
diff --git a/std/linked_list.zig b/std/linked_list.zig index 9536e9991a..09a01e22b4 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -4,8 +4,18 @@ const assert = debug.assert; const mem = std.mem; const Allocator = mem.Allocator; -/// Generic doubly linked list. +/// Generic non-intrusive doubly linked list. pub fn LinkedList(comptime T: type) -> type { + return BaseLinkedList(T, void, ""); +} + +/// Generic intrusive doubly linked list. +pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) -> type { + return BaseLinkedList(void, ParentType, field_name); +} + +/// Generic doubly linked list. +fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) -> type { return struct { const Self = this; @@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type { next: ?&Node, data: T, - pub fn init(data: &const T) -> Node { + pub fn init(value: &const T) -> Node { return Node { .prev = null, .next = null, - .data = *data, + .data = *value, }; } + + pub fn initIntrusive() -> Node { + // TODO: when #678 is solved this can become `init`. + return Node.init({}); + } + + pub fn toData(node: &Node) -> &ParentType { + comptime assert(isIntrusive()); + return @fieldParentPtr(ParentType, field_name, node); + } }; first: ?&Node, @@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type { }; } + fn isIntrusive() -> bool { + return ParentType != void or field_name.len != 0; + } + /// Insert a new node after an existing one. /// /// Arguments: @@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type { /// Returns: /// A pointer to the new node. pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node { + comptime assert(!isIntrusive()); return allocator.create(Node); } @@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type { /// node: Pointer to the node to deallocate. /// allocator: Dynamic memory allocator. pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) { + comptime assert(!isIntrusive()); allocator.destroy(node); } @@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type { /// Returns: /// A pointer to the new node. pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node { + comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); *node = Node.init(data); return node; @@ -199,11 +226,11 @@ test "basic linked list test" { const allocator = debug.global_allocator; var list = LinkedList(u32).init(); - var one = list.createNode(1, allocator) catch unreachable; - var two = list.createNode(2, allocator) catch unreachable; - var three = list.createNode(3, allocator) catch unreachable; - var four = list.createNode(4, allocator) catch unreachable; - var five = list.createNode(5, allocator) catch unreachable; + var one = try list.createNode(1, allocator); + var two = try list.createNode(2, allocator); + var three = try list.createNode(3, allocator); + var four = try list.createNode(4, allocator); + var five = try list.createNode(5, allocator); defer { list.destroyNode(one, allocator); list.destroyNode(two, allocator); @@ -246,3 +273,55 @@ test "basic linked list test" { assert ((??list.last ).data == 4); assert (list.len == 2); } + +const link = "link"; +const ElementList = IntrusiveLinkedList(Element, link); +const Element = struct { + value: u32, + link: IntrusiveLinkedList(Element, link).Node, +}; + +test "basic intrusive linked list test" { + const allocator = debug.global_allocator; + var list = ElementList.init(); + + 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() }; + + 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} + + // 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; + } + } + + var first = list.popFirst(); // {2, 3, 4, 5} + var last = list.pop(); // {2, 3, 4} + list.remove(&three.link); // {2, 4} + + assert ((??list.first).toData().value == 2); + assert ((??list.last ).toData().value == 4); + assert (list.len == 2); +} |
