authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-06-10 09:54:03-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-06-10 09:54:03-04:00
log12b2950bf294b3cfda0616b2e71b739cbda684d9
tree25a5a2da7a6527cf61096e236baf26c58d1ccc05
parentb7811d32690a9f3b4912635e16e6aa5ace25362c
parentb0648bfbd31cad73e00ee72b430dcf2c2907e08f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2424 from daurnimator/single-linked-list

Add SinglyLinkedList

8 files changed, 205 insertions(+), 21 deletions(-)

src-self-hosted/compilation.zig+1-1
...@@ -160,7 +160,7 @@ pub const Compilation = struct {...@@ -160,7 +160,7 @@ pub const Compilation = struct {
160 /// it uses an optional pointer so that tombstone removals are possible160 /// it uses an optional pointer so that tombstone removals are possible
161 fn_link_set: event.Locked(FnLinkSet),161 fn_link_set: event.Locked(FnLinkSet),
162162
163 pub const FnLinkSet = std.LinkedList(?*Value.Fn);163 pub const FnLinkSet = std.TailQueue(?*Value.Fn);
164164
165 windows_subsystem_windows: bool,165 windows_subsystem_windows: bool,
166 windows_subsystem_console: bool,166 windows_subsystem_console: bool,
src-self-hosted/value.zig+1-1
...@@ -186,7 +186,7 @@ pub const Value = struct {...@@ -186,7 +186,7 @@ pub const Value = struct {
186 /// Path to the object file that contains this function186 /// Path to the object file that contains this function
187 containing_object: Buffer,187 containing_object: Buffer,
188188
189 link_set_node: *std.LinkedList(?*Value.Fn).Node,189 link_set_node: *std.TailQueue(?*Value.Fn).Node,
190190
191 /// Creates a Fn value with 1 ref191 /// Creates a Fn value with 1 ref
192 /// Takes ownership of symbol_name192 /// Takes ownership of symbol_name
std/atomic/queue.zig+1-1
...@@ -14,7 +14,7 @@ pub fn Queue(comptime T: type) type {...@@ -14,7 +14,7 @@ pub fn Queue(comptime T: type) type {
14 mutex: std.Mutex,14 mutex: std.Mutex,
1515
16 pub const Self = @This();16 pub const Self = @This();
17 pub const Node = std.LinkedList(T).Node;17 pub const Node = std.TailQueue(T).Node;
1818
19 pub fn init() Self {19 pub fn init() Self {
20 return Self{20 return Self{
std/child_process.zig+3-3
...@@ -13,7 +13,7 @@ const BufMap = std.BufMap;...@@ -13,7 +13,7 @@ const BufMap = std.BufMap;
13const Buffer = std.Buffer;13const Buffer = std.Buffer;
14const builtin = @import("builtin");14const builtin = @import("builtin");
15const Os = builtin.Os;15const Os = builtin.Os;
16const LinkedList = std.LinkedList;16const TailQueue = std.TailQueue;
17const maxInt = std.math.maxInt;17const maxInt = std.math.maxInt;
1818
19pub const ChildProcess = struct {19pub const ChildProcess = struct {
...@@ -48,7 +48,7 @@ pub const ChildProcess = struct {...@@ -48,7 +48,7 @@ pub const ChildProcess = struct {
48 pub cwd: ?[]const u8,48 pub cwd: ?[]const u8,
4949
50 err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,50 err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,
51 llnode: if (os.windows.is_the_target) void else LinkedList(*ChildProcess).Node,51 llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node,
5252
53 pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||53 pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||
54 os.ChangeCurDirError || windows.CreateProcessError;54 os.ChangeCurDirError || windows.CreateProcessError;
...@@ -388,7 +388,7 @@ pub const ChildProcess = struct {...@@ -388,7 +388,7 @@ pub const ChildProcess = struct {
388388
389 self.pid = pid;389 self.pid = pid;
390 self.err_pipe = err_pipe;390 self.err_pipe = err_pipe;
391 self.llnode = LinkedList(*ChildProcess).Node.init(self);391 self.llnode = TailQueue(*ChildProcess).Node.init(self);
392 self.term = null;392 self.term = null;
393393
394 if (self.stdin_behavior == StdIo.Pipe) {394 if (self.stdin_behavior == StdIo.Pipe) {
std/event/net.zig+1-1
...@@ -19,7 +19,7 @@ pub const Server = struct {...@@ -19,7 +19,7 @@ pub const Server = struct {
19 waiting_for_emfile_node: PromiseNode,19 waiting_for_emfile_node: PromiseNode,
20 listen_resume_node: event.Loop.ResumeNode,20 listen_resume_node: event.Loop.ResumeNode,
2121
22 const PromiseNode = std.LinkedList(promise).Node;22 const PromiseNode = std.TailQueue(promise).Node;
2323
24 pub fn init(loop: *Loop) Server {24 pub fn init(loop: *Loop) Server {
25 // TODO can't initialize handler coroutine here because we need well defined copy elision25 // TODO can't initialize handler coroutine here because we need well defined copy elision
std/heap.zig+5-6
...@@ -347,10 +347,10 @@ pub const ArenaAllocator = struct {...@@ -347,10 +347,10 @@ pub const ArenaAllocator = struct {
347 pub allocator: Allocator,347 pub allocator: Allocator,
348348
349 child_allocator: *Allocator,349 child_allocator: *Allocator,
350 buffer_list: std.LinkedList([]u8),350 buffer_list: std.SinglyLinkedList([]u8),
351 end_index: usize,351 end_index: usize,
352352
353 const BufNode = std.LinkedList([]u8).Node;353 const BufNode = std.SinglyLinkedList([]u8).Node;
354354
355 pub fn init(child_allocator: *Allocator) ArenaAllocator {355 pub fn init(child_allocator: *Allocator) ArenaAllocator {
356 return ArenaAllocator{356 return ArenaAllocator{
...@@ -359,7 +359,7 @@ pub const ArenaAllocator = struct {...@@ -359,7 +359,7 @@ pub const ArenaAllocator = struct {
359 .shrinkFn = shrink,359 .shrinkFn = shrink,
360 },360 },
361 .child_allocator = child_allocator,361 .child_allocator = child_allocator,
362 .buffer_list = std.LinkedList([]u8).init(),362 .buffer_list = std.SinglyLinkedList([]u8).init(),
363 .end_index = 0,363 .end_index = 0,
364 };364 };
365 }365 }
...@@ -387,10 +387,9 @@ pub const ArenaAllocator = struct {...@@ -387,10 +387,9 @@ pub const ArenaAllocator = struct {
387 const buf_node = &buf_node_slice[0];387 const buf_node = &buf_node_slice[0];
388 buf_node.* = BufNode{388 buf_node.* = BufNode{
389 .data = buf,389 .data = buf,
390 .prev = null,
391 .next = null,390 .next = null,
392 };391 };
393 self.buffer_list.append(buf_node);392 self.buffer_list.prepend(buf_node);
394 self.end_index = 0;393 self.end_index = 0;
395 return buf_node;394 return buf_node;
396 }395 }
...@@ -398,7 +397,7 @@ pub const ArenaAllocator = struct {...@@ -398,7 +397,7 @@ pub const ArenaAllocator = struct {
398 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {397 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
399 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);398 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
400399
401 var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment);400 var cur_node = if (self.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
402 while (true) {401 while (true) {
403 const cur_buf = cur_node.data[@sizeOf(BufNode)..];402 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
404 const addr = @ptrToInt(cur_buf.ptr) + self.end_index;403 const addr = @ptrToInt(cur_buf.ptr) + self.end_index;
std/linked_list.zig+191-7
...@@ -5,8 +5,192 @@ const testing = std.testing;...@@ -5,8 +5,192 @@ const testing = std.testing;
5const mem = std.mem;5const mem = std.mem;
6const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
77
8/// Generic doubly linked list.8/// A singly-linked list is headed by a single forward pointer. The elements
9pub fn LinkedList(comptime T: type) type {9/// are singly linked for minimum space and pointer manipulation overhead at
10/// the expense of O(n) removal for arbitrary elements. New elements can be
11/// added to the list after an existing element or at the head of the list.
12/// A singly-linked list may only be traversed in the forward direction.
13/// Singly-linked lists are ideal for applications with large datasets and
14/// few or no removals or for implementing a LIFO queue.
15pub fn SinglyLinkedList(comptime T: type) type {
16 return struct {
17 const Self = @This();
18
19 /// Node inside the linked list wrapping the actual data.
20 pub const Node = struct {
21 next: ?*Node,
22 data: T,
23
24 pub fn init(data: T) Node {
25 return Node{
26 .next = null,
27 .data = data,
28 };
29 }
30
31 /// Insert a new node after the current one.
32 ///
33 /// Arguments:
34 /// new_node: Pointer to the new node to insert.
35 pub fn insertAfter(node: *Node, new_node: *Node) void {
36 new_node.next = node.next;
37 node.next = new_node;
38 }
39
40 /// Remove a node from the list.
41 ///
42 /// Arguments:
43 /// node: Pointer to the node to be removed.
44 /// Returns:
45 /// node removed
46 pub fn removeNext(node: *Node) ?*Node {
47 const next_node = node.next orelse return null;
48 node.next = next_node.next;
49 return next_node;
50 }
51 };
52
53 first: ?*Node,
54
55 /// Initialize a linked list.
56 ///
57 /// Returns:
58 /// An empty linked list.
59 pub fn init() Self {
60 return Self{
61 .first = null,
62 };
63 }
64
65 /// Insert a new node after an existing one.
66 ///
67 /// Arguments:
68 /// node: Pointer to a node in the list.
69 /// new_node: Pointer to the new node to insert.
70 pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void {
71 node.insertAfter(new_node);
72 }
73
74 /// Insert a new node at the head.
75 ///
76 /// Arguments:
77 /// new_node: Pointer to the new node to insert.
78 pub fn prepend(list: *Self, new_node: *Node) void {
79 new_node.next = list.first;
80 list.first = new_node;
81 }
82
83 /// Remove a node from the list.
84 ///
85 /// Arguments:
86 /// node: Pointer to the node to be removed.
87 pub fn remove(list: *Self, node: *Node) void {
88 if (list.first == node) {
89 list.first = node.next;
90 } else {
91 var current_elm = list.first.?;
92 while (current_elm.next != node) {
93 current_elm = current_elm.next.?;
94 }
95 current_elm.next = node.next;
96 }
97 }
98
99 /// Remove and return the first node in the list.
100 ///
101 /// Returns:
102 /// A pointer to the first node in the list.
103 pub fn popFirst(list: *Self) ?*Node {
104 const first = list.first orelse return null;
105 list.first = first.next;
106 return first;
107 }
108
109 /// Allocate a new node.
110 ///
111 /// Arguments:
112 /// allocator: Dynamic memory allocator.
113 ///
114 /// Returns:
115 /// A pointer to the new node.
116 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
117 return allocator.create(Node);
118 }
119
120 /// Deallocate a node.
121 ///
122 /// Arguments:
123 /// node: Pointer to the node to deallocate.
124 /// allocator: Dynamic memory allocator.
125 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
126 allocator.destroy(node);
127 }
128
129 /// Allocate and initialize a node and its data.
130 ///
131 /// Arguments:
132 /// data: The data to put inside the node.
133 /// allocator: Dynamic memory allocator.
134 ///
135 /// Returns:
136 /// A pointer to the new node.
137 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
138 var node = try list.allocateNode(allocator);
139 node.* = Node.init(data);
140 return node;
141 }
142 };
143}
144
145test "basic SinglyLinkedList test" {
146 const allocator = debug.global_allocator;
147 var list = SinglyLinkedList(u32).init();
148
149 var one = try list.createNode(1, allocator);
150 var two = try list.createNode(2, allocator);
151 var three = try list.createNode(3, allocator);
152 var four = try list.createNode(4, allocator);
153 var five = try list.createNode(5, allocator);
154 defer {
155 list.destroyNode(one, allocator);
156 list.destroyNode(two, allocator);
157 list.destroyNode(three, allocator);
158 list.destroyNode(four, allocator);
159 list.destroyNode(five, allocator);
160 }
161
162 list.prepend(two); // {2}
163 list.insertAfter(two, five); // {2, 5}
164 list.prepend(one); // {1, 2, 5}
165 list.insertAfter(two, three); // {1, 2, 3, 5}
166 list.insertAfter(three, four); // {1, 2, 3, 4, 5}
167
168 // Traverse forwards.
169 {
170 var it = list.first;
171 var index: u32 = 1;
172 while (it) |node| : (it = node.next) {
173 testing.expect(node.data == index);
174 index += 1;
175 }
176 }
177
178 _ = list.popFirst(); // {2, 3, 4, 5}
179 _ = list.remove(five); // {2, 3, 4}
180 _ = two.removeNext(); // {2, 4}
181
182 testing.expect(list.first.?.data == 2);
183 testing.expect(list.first.?.next.?.data == 4);
184 testing.expect(list.first.?.next.?.next == null);
185}
186
187/// A tail queue is headed by a pair of pointers, one to the head of the
188/// list and the other to the tail of the list. The elements are doubly
189/// linked so that an arbitrary element can be removed without a need to
190/// traverse the list. New elements can be added to the list before or
191/// after an existing element, at the head of the list, or at the end of
192/// the list. A tail queue may be traversed in either direction.
193pub fn TailQueue(comptime T: type) type {
10 return struct {194 return struct {
11 const Self = @This();195 const Self = @This();
12196
...@@ -219,9 +403,9 @@ pub fn LinkedList(comptime T: type) type {...@@ -219,9 +403,9 @@ pub fn LinkedList(comptime T: type) type {
219 };403 };
220}404}
221405
222test "basic linked list test" {406test "basic TailQueue test" {
223 const allocator = debug.global_allocator;407 const allocator = debug.global_allocator;
224 var list = LinkedList(u32).init();408 var list = TailQueue(u32).init();
225409
226 var one = try list.createNode(1, allocator);410 var one = try list.createNode(1, allocator);
227 var two = try list.createNode(2, allocator);411 var two = try list.createNode(2, allocator);
...@@ -271,10 +455,10 @@ test "basic linked list test" {...@@ -271,10 +455,10 @@ test "basic linked list test" {
271 testing.expect(list.len == 2);455 testing.expect(list.len == 2);
272}456}
273457
274test "linked list concatenation" {458test "TailQueue concatenation" {
275 const allocator = debug.global_allocator;459 const allocator = debug.global_allocator;
276 var list1 = LinkedList(u32).init();460 var list1 = TailQueue(u32).init();
277 var list2 = LinkedList(u32).init();461 var list2 = TailQueue(u32).init();
278462
279 var one = try list1.createNode(1, allocator);463 var one = try list1.createNode(1, allocator);
280 defer list1.destroyNode(one, allocator);464 defer list1.destroyNode(one, allocator);
std/std.zig+2-1
...@@ -7,17 +7,18 @@ pub const Buffer = @import("buffer.zig").Buffer;...@@ -7,17 +7,18 @@ pub const Buffer = @import("buffer.zig").Buffer;
7pub const BufferOutStream = @import("io.zig").BufferOutStream;7pub const BufferOutStream = @import("io.zig").BufferOutStream;
8pub const DynLib = @import("dynamic_library.zig").DynLib;8pub const DynLib = @import("dynamic_library.zig").DynLib;
9pub const HashMap = @import("hash_map.zig").HashMap;9pub const HashMap = @import("hash_map.zig").HashMap;
10pub const LinkedList = @import("linked_list.zig").LinkedList;
11pub const Mutex = @import("mutex.zig").Mutex;10pub const Mutex = @import("mutex.zig").Mutex;
12pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;11pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
13pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;12pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
14pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;13pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
15pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;14pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
16pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;15pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
16pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
17pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;17pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
18pub const SegmentedList = @import("segmented_list.zig").SegmentedList;18pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
19pub const SpinLock = @import("spinlock.zig").SpinLock;19pub const SpinLock = @import("spinlock.zig").SpinLock;
20pub const ChildProcess = @import("child_process.zig").ChildProcess;20pub const ChildProcess = @import("child_process.zig").ChildProcess;
21pub const TailQueue = @import("linked_list.zig").TailQueue;
21pub const Thread = @import("thread.zig").Thread;22pub const Thread = @import("thread.zig").Thread;
2223
23pub const atomic = @import("atomic.zig");24pub const atomic = @import("atomic.zig");