authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-01-10 10:13:15-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-01-10 10:13:15-05:00
logd4f791cf6c560da25a0b84b4eb2ad68f8e7d27fd
treebde223d349ae1f6251495dc5a0e03dcae6513071
parent3c094116aae459b651934663a31981cf09cdb3e4
parent19343db5936abe5e49ea340db838fe769b499944
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #680 from zig-lang/intrusiveLinkedList

Intrusive linked lists

1 files changed, 87 insertions(+), 8 deletions(-)

std/linked_list.zig+87-8
...@@ -4,8 +4,18 @@ const assert = debug.assert;...@@ -4,8 +4,18 @@ const assert = debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const Allocator = mem.Allocator;5const Allocator = mem.Allocator;
66
7/// Generic doubly linked list.7/// Generic non-intrusive doubly linked list.
8pub fn LinkedList(comptime T: type) -> type {8pub fn LinkedList(comptime T: type) -> type {
9 return BaseLinkedList(T, void, "");
10}
11
12/// Generic intrusive doubly linked list.
13pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) -> type {
14 return BaseLinkedList(void, ParentType, field_name);
15}
16
17/// Generic doubly linked list.
18fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) -> type {
9 return struct {19 return struct {
10 const Self = this;20 const Self = this;
1121
...@@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type {...@@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type {
15 next: ?&Node,25 next: ?&Node,
16 data: T,26 data: T,
1727
18 pub fn init(data: &const T) -> Node {28 pub fn init(value: &const T) -> Node {
19 return Node {29 return Node {
20 .prev = null,30 .prev = null,
21 .next = null,31 .next = null,
22 .data = *data,32 .data = *value,
23 };33 };
24 }34 }
35
36 pub fn initIntrusive() -> Node {
37 // TODO: when #678 is solved this can become `init`.
38 return Node.init({});
39 }
40
41 pub fn toData(node: &Node) -> &ParentType {
42 comptime assert(isIntrusive());
43 return @fieldParentPtr(ParentType, field_name, node);
44 }
25 };45 };
2646
27 first: ?&Node,47 first: ?&Node,
...@@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type {...@@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type {
40 };60 };
41 }61 }
4262
63 fn isIntrusive() -> bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
43 /// Insert a new node after an existing one.67 /// Insert a new node after an existing one.
44 ///68 ///
45 /// Arguments:69 /// Arguments:
...@@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type {...@@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type {
167 /// Returns:191 /// Returns:
168 /// A pointer to the new node.192 /// A pointer to the new node.
169 pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {193 pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {
194 comptime assert(!isIntrusive());
170 return allocator.create(Node);195 return allocator.create(Node);
171 }196 }
172197
...@@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type {...@@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type {
176 /// node: Pointer to the node to deallocate.201 /// node: Pointer to the node to deallocate.
177 /// allocator: Dynamic memory allocator.202 /// allocator: Dynamic memory allocator.
178 pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) {203 pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) {
204 comptime assert(!isIntrusive());
179 allocator.destroy(node);205 allocator.destroy(node);
180 }206 }
181207
...@@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type {...@@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type {
188 /// Returns:214 /// Returns:
189 /// A pointer to the new node.215 /// A pointer to the new node.
190 pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node {216 pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node {
217 comptime assert(!isIntrusive());
191 var node = try list.allocateNode(allocator);218 var node = try list.allocateNode(allocator);
192 *node = Node.init(data);219 *node = Node.init(data);
193 return node;220 return node;
...@@ -199,11 +226,11 @@ test "basic linked list test" {...@@ -199,11 +226,11 @@ test "basic linked list test" {
199 const allocator = debug.global_allocator;226 const allocator = debug.global_allocator;
200 var list = LinkedList(u32).init();227 var list = LinkedList(u32).init();
201228
202 var one = list.createNode(1, allocator) catch unreachable;229 var one = try list.createNode(1, allocator);
203 var two = list.createNode(2, allocator) catch unreachable;230 var two = try list.createNode(2, allocator);
204 var three = list.createNode(3, allocator) catch unreachable;231 var three = try list.createNode(3, allocator);
205 var four = list.createNode(4, allocator) catch unreachable;232 var four = try list.createNode(4, allocator);
206 var five = list.createNode(5, allocator) catch unreachable;233 var five = try list.createNode(5, allocator);
207 defer {234 defer {
208 list.destroyNode(one, allocator);235 list.destroyNode(one, allocator);
209 list.destroyNode(two, allocator);236 list.destroyNode(two, allocator);
...@@ -246,3 +273,55 @@ test "basic linked list test" {...@@ -246,3 +273,55 @@ test "basic linked list test" {
246 assert ((??list.last ).data == 4);273 assert ((??list.last ).data == 4);
247 assert (list.len == 2);274 assert (list.len == 2);
248}275}
276
277const link = "link";
278const ElementList = IntrusiveLinkedList(Element, link);
279const Element = struct {
280 value: u32,
281 link: IntrusiveLinkedList(Element, link).Node,
282};
283
284test "basic intrusive linked list test" {
285 const allocator = debug.global_allocator;
286 var list = ElementList.init();
287
288 var one = Element { .value = 1, .link = ElementList.Node.initIntrusive() };
289 var two = Element { .value = 2, .link = ElementList.Node.initIntrusive() };
290 var three = Element { .value = 3, .link = ElementList.Node.initIntrusive() };
291 var four = Element { .value = 4, .link = ElementList.Node.initIntrusive() };
292 var five = Element { .value = 5, .link = ElementList.Node.initIntrusive() };
293
294 list.append(&two.link); // {2}
295 list.append(&five.link); // {2, 5}
296 list.prepend(&one.link); // {1, 2, 5}
297 list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
298 list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
299
300 // Traverse forwards.
301 {
302 var it = list.first;
303 var index: u32 = 1;
304 while (it) |node| : (it = node.next) {
305 assert(node.toData().value == index);
306 index += 1;
307 }
308 }
309
310 // Traverse backwards.
311 {
312 var it = list.last;
313 var index: u32 = 1;
314 while (it) |node| : (it = node.prev) {
315 assert(node.toData().value == (6 - index));
316 index += 1;
317 }
318 }
319
320 var first = list.popFirst(); // {2, 3, 4, 5}
321 var last = list.pop(); // {2, 3, 4}
322 list.remove(&three.link); // {2, 4}
323
324 assert ((??list.first).toData().value == 2);
325 assert ((??list.last ).toData().value == 4);
326 assert (list.len == 2);
327}