| author | |
| committer | |
| log | 1639fcea43549853f1fded32aa1d711d21771e1c |
| tree | 0c053fb3b858757e49ae234f7167c65fe310a1e7 |
| parent | e9220525e836810425e925722d1beaba9bfe9d91 |
by making it always intrusive, we make it a more broadly useful API, and
avoid binary bloat.6 files changed, 207 insertions(+), 202 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -444,7 +444,6 @@ set(ZIG_STAGE2_SOURCES |
| 444 | 444 | lib/std/json.zig |
| 445 | 445 | lib/std/json/stringify.zig |
| 446 | 446 | lib/std/leb128.zig |
| 447 | lib/std/linked_list.zig | |
| 448 | 447 | lib/std/log.zig |
| 449 | 448 | lib/std/macho.zig |
| 450 | 449 | lib/std/math.zig |
lib/std/SinglyLinkedList.zig created+166| ... | ... | @@ -0,0 +1,166 @@ |
| 1 | //! A singly-linked list is headed by a single forward pointer. The elements | |
| 2 | //! are singly-linked for minimum space and pointer manipulation overhead at | |
| 3 | //! the expense of O(n) removal for arbitrary elements. New elements can be | |
| 4 | //! added to the list after an existing element or at the head of the list. | |
| 5 | //! | |
| 6 | //! A singly-linked list may only be traversed in the forward direction. | |
| 7 | //! | |
| 8 | //! Singly-linked lists are useful under these conditions: | |
| 9 | //! * Ability to preallocate elements / requirement of infallibility for | |
| 10 | //! insertion. | |
| 11 | //! * Ability to allocate elements intrusively along with other data. | |
| 12 | //! * Homogenous elements. | |
| 13 | ||
| 14 | const std = @import("std.zig"); | |
| 15 | const debug = std.debug; | |
| 16 | const assert = debug.assert; | |
| 17 | const testing = std.testing; | |
| 18 | const SinglyLinkedList = @This(); | |
| 19 | ||
| 20 | first: ?*Node = null, | |
| 21 | ||
| 22 | /// This struct contains only a next pointer and not any data payload. The | |
| 23 | /// intended usage is to embed it intrusively into another data structure and | |
| 24 | /// access the data with `@fieldParentPtr`. | |
| 25 | pub const Node = struct { | |
| 26 | next: ?*Node = null, | |
| 27 | ||
| 28 | pub fn insertAfter(node: *Node, new_node: *Node) void { | |
| 29 | new_node.next = node.next; | |
| 30 | node.next = new_node; | |
| 31 | } | |
| 32 | ||
| 33 | /// Remove the node after the one provided, returning it. | |
| 34 | pub fn removeNext(node: *Node) ?*Node { | |
| 35 | const next_node = node.next orelse return null; | |
| 36 | node.next = next_node.next; | |
| 37 | return next_node; | |
| 38 | } | |
| 39 | ||
| 40 | /// Iterate over the singly-linked list from this node, until the final | |
| 41 | /// node is found. | |
| 42 | /// | |
| 43 | /// This operation is O(N). Instead of calling this function, consider | |
| 44 | /// using a different data structure. | |
| 45 | pub fn findLast(node: *Node) *Node { | |
| 46 | var it = node; | |
| 47 | while (true) { | |
| 48 | it = it.next orelse return it; | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | /// Iterate over each next node, returning the count of all nodes except | |
| 53 | /// the starting one. | |
| 54 | /// | |
| 55 | /// This operation is O(N). Instead of calling this function, consider | |
| 56 | /// using a different data structure. | |
| 57 | pub fn countChildren(node: *const Node) usize { | |
| 58 | var count: usize = 0; | |
| 59 | var it: ?*const Node = node.next; | |
| 60 | while (it) |n| : (it = n.next) { | |
| 61 | count += 1; | |
| 62 | } | |
| 63 | return count; | |
| 64 | } | |
| 65 | ||
| 66 | /// Reverse the list starting from this node in-place. | |
| 67 | /// | |
| 68 | /// This operation is O(N). Instead of calling this function, consider | |
| 69 | /// using a different data structure. | |
| 70 | pub fn reverse(indirect: *?*Node) void { | |
| 71 | if (indirect.* == null) { | |
| 72 | return; | |
| 73 | } | |
| 74 | var current: *Node = indirect.*.?; | |
| 75 | while (current.next) |next| { | |
| 76 | current.next = next.next; | |
| 77 | next.next = indirect.*; | |
| 78 | indirect.* = next; | |
| 79 | } | |
| 80 | } | |
| 81 | }; | |
| 82 | ||
| 83 | pub fn prepend(list: *SinglyLinkedList, new_node: *Node) void { | |
| 84 | new_node.next = list.first; | |
| 85 | list.first = new_node; | |
| 86 | } | |
| 87 | ||
| 88 | pub fn remove(list: *SinglyLinkedList, node: *Node) void { | |
| 89 | if (list.first == node) { | |
| 90 | list.first = node.next; | |
| 91 | } else { | |
| 92 | var current_elm = list.first.?; | |
| 93 | while (current_elm.next != node) { | |
| 94 | current_elm = current_elm.next.?; | |
| 95 | } | |
| 96 | current_elm.next = node.next; | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | /// Remove and return the first node in the list. | |
| 101 | pub fn popFirst(list: *SinglyLinkedList) ?*Node { | |
| 102 | const first = list.first orelse return null; | |
| 103 | list.first = first.next; | |
| 104 | return first; | |
| 105 | } | |
| 106 | ||
| 107 | /// Iterate over all nodes, returning the count. | |
| 108 | /// | |
| 109 | /// This operation is O(N). Consider tracking the length separately rather than | |
| 110 | /// computing it. | |
| 111 | pub fn len(list: SinglyLinkedList) usize { | |
| 112 | if (list.first) |n| { | |
| 113 | return 1 + n.countChildren(); | |
| 114 | } else { | |
| 115 | return 0; | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | test "basics" { | |
| 120 | const L = struct { | |
| 121 | data: u32, | |
| 122 | node: SinglyLinkedList.Node = .{}, | |
| 123 | }; | |
| 124 | var list: SinglyLinkedList = .{}; | |
| 125 | ||
| 126 | try testing.expect(list.len() == 0); | |
| 127 | ||
| 128 | var one: L = .{ .data = 1 }; | |
| 129 | var two: L = .{ .data = 2 }; | |
| 130 | var three: L = .{ .data = 3 }; | |
| 131 | var four: L = .{ .data = 4 }; | |
| 132 | var five: L = .{ .data = 5 }; | |
| 133 | ||
| 134 | list.prepend(&two.node); // {2} | |
| 135 | two.node.insertAfter(&five.node); // {2, 5} | |
| 136 | list.prepend(&one.node); // {1, 2, 5} | |
| 137 | two.node.insertAfter(&three.node); // {1, 2, 3, 5} | |
| 138 | three.node.insertAfter(&four.node); // {1, 2, 3, 4, 5} | |
| 139 | ||
| 140 | try testing.expect(list.len() == 5); | |
| 141 | ||
| 142 | // Traverse forwards. | |
| 143 | { | |
| 144 | var it = list.first; | |
| 145 | var index: u32 = 1; | |
| 146 | while (it) |node| : (it = node.next) { | |
| 147 | const l: *L = @fieldParentPtr("node", node); | |
| 148 | try testing.expect(l.data == index); | |
| 149 | index += 1; | |
| 150 | } | |
| 151 | } | |
| 152 | ||
| 153 | _ = list.popFirst(); // {2, 3, 4, 5} | |
| 154 | _ = list.remove(&five.node); // {2, 3, 4} | |
| 155 | _ = two.node.removeNext(); // {2, 4} | |
| 156 | ||
| 157 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?)).data == 2); | |
| 158 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?.next.?)).data == 4); | |
| 159 | try testing.expect(list.first.?.next.?.next == null); | |
| 160 | ||
| 161 | SinglyLinkedList.Node.reverse(&list.first); | |
| 162 | ||
| 163 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?)).data == 4); | |
| 164 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?.next.?)).data == 2); | |
| 165 | try testing.expect(list.first.?.next.?.next == null); | |
| 166 | } |
lib/std/Thread/Pool.zig+15-16| ... | ... | @@ -5,7 +5,7 @@ const WaitGroup = @import("WaitGroup.zig"); |
| 5 | 5 | |
| 6 | 6 | mutex: std.Thread.Mutex = .{}, |
| 7 | 7 | cond: std.Thread.Condition = .{}, |
| 8 | run_queue: RunQueue = .{}, | |
| 8 | run_queue: std.SinglyLinkedList = .{}, | |
| 9 | 9 | is_running: bool = true, |
| 10 | 10 | allocator: std.mem.Allocator, |
| 11 | 11 | threads: if (builtin.single_threaded) [0]std.Thread else []std.Thread, |
| ... | ... | @@ -16,9 +16,9 @@ ids: if (builtin.single_threaded) struct { |
| 16 | 16 | } |
| 17 | 17 | } else std.AutoArrayHashMapUnmanaged(std.Thread.Id, void), |
| 18 | 18 | |
| 19 | const RunQueue = std.SinglyLinkedList(Runnable); | |
| 20 | 19 | const Runnable = struct { |
| 21 | 20 | runFn: RunProto, |
| 21 | node: std.SinglyLinkedList.Node = .{}, | |
| 22 | 22 | }; |
| 23 | 23 | |
| 24 | 24 | const RunProto = *const fn (*Runnable, id: ?usize) void; |
| ... | ... | @@ -110,12 +110,11 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args |
| 110 | 110 | const Closure = struct { |
| 111 | 111 | arguments: Args, |
| 112 | 112 | pool: *Pool, |
| 113 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 113 | runnable: Runnable = .{ .runFn = runFn }, | |
| 114 | 114 | wait_group: *WaitGroup, |
| 115 | 115 | |
| 116 | 116 | fn runFn(runnable: *Runnable, _: ?usize) void { |
| 117 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | |
| 118 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | |
| 117 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); | |
| 119 | 118 | @call(.auto, func, closure.arguments); |
| 120 | 119 | closure.wait_group.finish(); |
| 121 | 120 | |
| ... | ... | @@ -143,7 +142,7 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args |
| 143 | 142 | .wait_group = wait_group, |
| 144 | 143 | }; |
| 145 | 144 | |
| 146 | pool.run_queue.prepend(&closure.run_node); | |
| 145 | pool.run_queue.prepend(&closure.runnable.node); | |
| 147 | 146 | pool.mutex.unlock(); |
| 148 | 147 | } |
| 149 | 148 | |
| ... | ... | @@ -173,12 +172,11 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar |
| 173 | 172 | const Closure = struct { |
| 174 | 173 | arguments: Args, |
| 175 | 174 | pool: *Pool, |
| 176 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 175 | runnable: Runnable = .{ .runFn = runFn }, | |
| 177 | 176 | wait_group: *WaitGroup, |
| 178 | 177 | |
| 179 | 178 | fn runFn(runnable: *Runnable, id: ?usize) void { |
| 180 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | |
| 181 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | |
| 179 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); | |
| 182 | 180 | @call(.auto, func, .{id.?} ++ closure.arguments); |
| 183 | 181 | closure.wait_group.finish(); |
| 184 | 182 | |
| ... | ... | @@ -207,7 +205,7 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar |
| 207 | 205 | .wait_group = wait_group, |
| 208 | 206 | }; |
| 209 | 207 | |
| 210 | pool.run_queue.prepend(&closure.run_node); | |
| 208 | pool.run_queue.prepend(&closure.runnable.node); | |
| 211 | 209 | pool.mutex.unlock(); |
| 212 | 210 | } |
| 213 | 211 | |
| ... | ... | @@ -225,11 +223,10 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { |
| 225 | 223 | const Closure = struct { |
| 226 | 224 | arguments: Args, |
| 227 | 225 | pool: *Pool, |
| 228 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | |
| 226 | runnable: Runnable = .{ .runFn = runFn }, | |
| 229 | 227 | |
| 230 | 228 | fn runFn(runnable: *Runnable, _: ?usize) void { |
| 231 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | |
| 232 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | |
| 229 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); | |
| 233 | 230 | @call(.auto, func, closure.arguments); |
| 234 | 231 | |
| 235 | 232 | // The thread pool's allocator is protected by the mutex. |
| ... | ... | @@ -251,7 +248,7 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { |
| 251 | 248 | .pool = pool, |
| 252 | 249 | }; |
| 253 | 250 | |
| 254 | pool.run_queue.prepend(&closure.run_node); | |
| 251 | pool.run_queue.prepend(&closure.runnable.node); | |
| 255 | 252 | } |
| 256 | 253 | |
| 257 | 254 | // Notify waiting threads outside the lock to try and keep the critical section small. |
| ... | ... | @@ -292,7 +289,8 @@ fn worker(pool: *Pool) void { |
| 292 | 289 | pool.mutex.unlock(); |
| 293 | 290 | defer pool.mutex.lock(); |
| 294 | 291 | |
| 295 | run_node.data.runFn(&run_node.data, id); | |
| 292 | const runnable: *Runnable = @fieldParentPtr("node", run_node); | |
| 293 | runnable.runFn(runnable, id); | |
| 296 | 294 | } |
| 297 | 295 | |
| 298 | 296 | // Stop executing instead of waiting if the thread pool is no longer running. |
| ... | ... | @@ -312,7 +310,8 @@ pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { |
| 312 | 310 | if (pool.run_queue.popFirst()) |run_node| { |
| 313 | 311 | id = id orelse pool.ids.getIndex(std.Thread.getCurrentId()); |
| 314 | 312 | pool.mutex.unlock(); |
| 315 | run_node.data.runFn(&run_node.data, id); | |
| 313 | const runnable: *Runnable = @fieldParentPtr("node", run_node); | |
| 314 | runnable.runFn(runnable, id); | |
| 316 | 315 | continue; |
| 317 | 316 | } |
| 318 | 317 |
lib/std/heap/arena_allocator.zig+25-16| ... | ... | @@ -14,7 +14,7 @@ pub const ArenaAllocator = struct { |
| 14 | 14 | /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator |
| 15 | 15 | /// as a memory-saving optimization. |
| 16 | 16 | pub const State = struct { |
| 17 | buffer_list: std.SinglyLinkedList(usize) = .{}, | |
| 17 | buffer_list: std.SinglyLinkedList = .{}, | |
| 18 | 18 | end_index: usize = 0, |
| 19 | 19 | |
| 20 | 20 | pub fn promote(self: State, child_allocator: Allocator) ArenaAllocator { |
| ... | ... | @@ -37,7 +37,10 @@ pub const ArenaAllocator = struct { |
| 37 | 37 | }; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | const BufNode = std.SinglyLinkedList(usize).Node; | |
| 40 | const BufNode = struct { | |
| 41 | data: usize, | |
| 42 | node: std.SinglyLinkedList.Node = .{}, | |
| 43 | }; | |
| 41 | 44 | const BufNode_alignment: mem.Alignment = .fromByteUnits(@alignOf(BufNode)); |
| 42 | 45 | |
| 43 | 46 | pub fn init(child_allocator: Allocator) ArenaAllocator { |
| ... | ... | @@ -51,7 +54,8 @@ pub const ArenaAllocator = struct { |
| 51 | 54 | while (it) |node| { |
| 52 | 55 | // this has to occur before the free because the free frees node |
| 53 | 56 | const next_it = node.next; |
| 54 | const alloc_buf = @as([*]u8, @ptrCast(node))[0..node.data]; | |
| 57 | const buf_node: *BufNode = @fieldParentPtr("node", node); | |
| 58 | const alloc_buf = @as([*]u8, @ptrCast(buf_node))[0..buf_node.data]; | |
| 55 | 59 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); |
| 56 | 60 | it = next_it; |
| 57 | 61 | } |
| ... | ... | @@ -78,7 +82,8 @@ pub const ArenaAllocator = struct { |
| 78 | 82 | while (it) |node| : (it = node.next) { |
| 79 | 83 | // Compute the actually allocated size excluding the |
| 80 | 84 | // linked list node. |
| 81 | size += node.data - @sizeOf(BufNode); | |
| 85 | const buf_node: *BufNode = @fieldParentPtr("node", node); | |
| 86 | size += buf_node.data - @sizeOf(BufNode); | |
| 82 | 87 | } |
| 83 | 88 | return size; |
| 84 | 89 | } |
| ... | ... | @@ -130,7 +135,8 @@ pub const ArenaAllocator = struct { |
| 130 | 135 | const next_it = node.next; |
| 131 | 136 | if (next_it == null) |
| 132 | 137 | break node; |
| 133 | const alloc_buf = @as([*]u8, @ptrCast(node))[0..node.data]; | |
| 138 | const buf_node: *BufNode = @fieldParentPtr("node", node); | |
| 139 | const alloc_buf = @as([*]u8, @ptrCast(buf_node))[0..buf_node.data]; | |
| 134 | 140 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); |
| 135 | 141 | it = next_it; |
| 136 | 142 | } else null; |
| ... | ... | @@ -140,12 +146,13 @@ pub const ArenaAllocator = struct { |
| 140 | 146 | if (maybe_first_node) |first_node| { |
| 141 | 147 | self.state.buffer_list.first = first_node; |
| 142 | 148 | // perfect, no need to invoke the child_allocator |
| 143 | if (first_node.data == total_size) | |
| 149 | const first_buf_node: *BufNode = @fieldParentPtr("node", first_node); | |
| 150 | if (first_buf_node.data == total_size) | |
| 144 | 151 | return true; |
| 145 | const first_alloc_buf = @as([*]u8, @ptrCast(first_node))[0..first_node.data]; | |
| 152 | const first_alloc_buf = @as([*]u8, @ptrCast(first_buf_node))[0..first_buf_node.data]; | |
| 146 | 153 | if (self.child_allocator.rawResize(first_alloc_buf, BufNode_alignment, total_size, @returnAddress())) { |
| 147 | 154 | // successful resize |
| 148 | first_node.data = total_size; | |
| 155 | first_buf_node.data = total_size; | |
| 149 | 156 | } else { |
| 150 | 157 | // manual realloc |
| 151 | 158 | const new_ptr = self.child_allocator.rawAlloc(total_size, BufNode_alignment, @returnAddress()) orelse { |
| ... | ... | @@ -153,9 +160,9 @@ pub const ArenaAllocator = struct { |
| 153 | 160 | return false; |
| 154 | 161 | }; |
| 155 | 162 | self.child_allocator.rawFree(first_alloc_buf, BufNode_alignment, @returnAddress()); |
| 156 | const node: *BufNode = @ptrCast(@alignCast(new_ptr)); | |
| 157 | node.* = .{ .data = total_size }; | |
| 158 | self.state.buffer_list.first = node; | |
| 163 | const buf_node: *BufNode = @ptrCast(@alignCast(new_ptr)); | |
| 164 | buf_node.* = .{ .data = total_size }; | |
| 165 | self.state.buffer_list.first = &buf_node.node; | |
| 159 | 166 | } |
| 160 | 167 | } |
| 161 | 168 | return true; |
| ... | ... | @@ -169,7 +176,7 @@ pub const ArenaAllocator = struct { |
| 169 | 176 | return null; |
| 170 | 177 | const buf_node: *BufNode = @ptrCast(@alignCast(ptr)); |
| 171 | 178 | buf_node.* = .{ .data = len }; |
| 172 | self.state.buffer_list.prepend(buf_node); | |
| 179 | self.state.buffer_list.prepend(&buf_node.node); | |
| 173 | 180 | self.state.end_index = 0; |
| 174 | 181 | return buf_node; |
| 175 | 182 | } |
| ... | ... | @@ -179,8 +186,8 @@ pub const ArenaAllocator = struct { |
| 179 | 186 | _ = ra; |
| 180 | 187 | |
| 181 | 188 | const ptr_align = alignment.toByteUnits(); |
| 182 | var cur_node = if (self.state.buffer_list.first) |first_node| | |
| 183 | first_node | |
| 189 | var cur_node: *BufNode = if (self.state.buffer_list.first) |first_node| | |
| 190 | @fieldParentPtr("node", first_node) | |
| 184 | 191 | else |
| 185 | 192 | (self.createNode(0, n + ptr_align) orelse return null); |
| 186 | 193 | while (true) { |
| ... | ... | @@ -213,7 +220,8 @@ pub const ArenaAllocator = struct { |
| 213 | 220 | _ = ret_addr; |
| 214 | 221 | |
| 215 | 222 | const cur_node = self.state.buffer_list.first orelse return false; |
| 216 | const cur_buf = @as([*]u8, @ptrCast(cur_node))[@sizeOf(BufNode)..cur_node.data]; | |
| 223 | const cur_buf_node: *BufNode = @fieldParentPtr("node", cur_node); | |
| 224 | const cur_buf = @as([*]u8, @ptrCast(cur_buf_node))[@sizeOf(BufNode)..cur_buf_node.data]; | |
| 217 | 225 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index != @intFromPtr(buf.ptr) + buf.len) { |
| 218 | 226 | // It's not the most recent allocation, so it cannot be expanded, |
| 219 | 227 | // but it's fine if they want to make it smaller. |
| ... | ... | @@ -248,7 +256,8 @@ pub const ArenaAllocator = struct { |
| 248 | 256 | const self: *ArenaAllocator = @ptrCast(@alignCast(ctx)); |
| 249 | 257 | |
| 250 | 258 | const cur_node = self.state.buffer_list.first orelse return; |
| 251 | const cur_buf = @as([*]u8, @ptrCast(cur_node))[@sizeOf(BufNode)..cur_node.data]; | |
| 259 | const cur_buf_node: *BufNode = @fieldParentPtr("node", cur_node); | |
| 260 | const cur_buf = @as([*]u8, @ptrCast(cur_buf_node))[@sizeOf(BufNode)..cur_buf_node.data]; | |
| 252 | 261 | |
| 253 | 262 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index == @intFromPtr(buf.ptr) + buf.len) { |
| 254 | 263 | self.state.end_index -= buf.len; |
lib/std/linked_list.zig-168| ... | ... | @@ -3,174 +3,6 @@ const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 4 | const testing = std.testing; |
| 5 | 5 | |
| 6 | /// A singly-linked list is headed by a single forward pointer. The elements | |
| 7 | /// are singly-linked for minimum space and pointer manipulation overhead at | |
| 8 | /// the expense of O(n) removal for arbitrary elements. New elements can be | |
| 9 | /// added to the list after an existing element or at the head of the list. | |
| 10 | /// A singly-linked list may only be traversed in the forward direction. | |
| 11 | /// Singly-linked lists are ideal for applications with large datasets and | |
| 12 | /// few or no removals or for implementing a LIFO queue. | |
| 13 | pub fn SinglyLinkedList(comptime T: type) type { | |
| 14 | return struct { | |
| 15 | const Self = @This(); | |
| 16 | ||
| 17 | /// Node inside the linked list wrapping the actual data. | |
| 18 | pub const Node = struct { | |
| 19 | next: ?*Node = null, | |
| 20 | data: T, | |
| 21 | ||
| 22 | pub const Data = T; | |
| 23 | ||
| 24 | /// Insert a new node after the current one. | |
| 25 | /// | |
| 26 | /// Arguments: | |
| 27 | /// new_node: Pointer to the new node to insert. | |
| 28 | pub fn insertAfter(node: *Node, new_node: *Node) void { | |
| 29 | new_node.next = node.next; | |
| 30 | node.next = new_node; | |
| 31 | } | |
| 32 | ||
| 33 | /// Remove a node from the list. | |
| 34 | /// | |
| 35 | /// Arguments: | |
| 36 | /// node: Pointer to the node to be removed. | |
| 37 | /// Returns: | |
| 38 | /// node removed | |
| 39 | pub fn removeNext(node: *Node) ?*Node { | |
| 40 | const next_node = node.next orelse return null; | |
| 41 | node.next = next_node.next; | |
| 42 | return next_node; | |
| 43 | } | |
| 44 | ||
| 45 | /// Iterate over the singly-linked list from this node, until the final node is found. | |
| 46 | /// This operation is O(N). | |
| 47 | pub fn findLast(node: *Node) *Node { | |
| 48 | var it = node; | |
| 49 | while (true) { | |
| 50 | it = it.next orelse return it; | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | /// Iterate over each next node, returning the count of all nodes except the starting one. | |
| 55 | /// This operation is O(N). | |
| 56 | pub fn countChildren(node: *const Node) usize { | |
| 57 | var count: usize = 0; | |
| 58 | var it: ?*const Node = node.next; | |
| 59 | while (it) |n| : (it = n.next) { | |
| 60 | count += 1; | |
| 61 | } | |
| 62 | return count; | |
| 63 | } | |
| 64 | ||
| 65 | /// Reverse the list starting from this node in-place. | |
| 66 | /// This operation is O(N). | |
| 67 | pub fn reverse(indirect: *?*Node) void { | |
| 68 | if (indirect.* == null) { | |
| 69 | return; | |
| 70 | } | |
| 71 | var current: *Node = indirect.*.?; | |
| 72 | while (current.next) |next| { | |
| 73 | current.next = next.next; | |
| 74 | next.next = indirect.*; | |
| 75 | indirect.* = next; | |
| 76 | } | |
| 77 | } | |
| 78 | }; | |
| 79 | ||
| 80 | first: ?*Node = null, | |
| 81 | ||
| 82 | /// Insert a new node at the head. | |
| 83 | /// | |
| 84 | /// Arguments: | |
| 85 | /// new_node: Pointer to the new node to insert. | |
| 86 | pub fn prepend(list: *Self, new_node: *Node) void { | |
| 87 | new_node.next = list.first; | |
| 88 | list.first = new_node; | |
| 89 | } | |
| 90 | ||
| 91 | /// Remove a node from the list. | |
| 92 | /// | |
| 93 | /// Arguments: | |
| 94 | /// node: Pointer to the node to be removed. | |
| 95 | pub fn remove(list: *Self, node: *Node) void { | |
| 96 | if (list.first == node) { | |
| 97 | list.first = node.next; | |
| 98 | } else { | |
| 99 | var current_elm = list.first.?; | |
| 100 | while (current_elm.next != node) { | |
| 101 | current_elm = current_elm.next.?; | |
| 102 | } | |
| 103 | current_elm.next = node.next; | |
| 104 | } | |
| 105 | } | |
| 106 | ||
| 107 | /// Remove and return the first node in the list. | |
| 108 | /// | |
| 109 | /// Returns: | |
| 110 | /// A pointer to the first node in the list. | |
| 111 | pub fn popFirst(list: *Self) ?*Node { | |
| 112 | const first = list.first orelse return null; | |
| 113 | list.first = first.next; | |
| 114 | return first; | |
| 115 | } | |
| 116 | ||
| 117 | /// Iterate over all nodes, returning the count. | |
| 118 | /// This operation is O(N). | |
| 119 | pub fn len(list: Self) usize { | |
| 120 | if (list.first) |n| { | |
| 121 | return 1 + n.countChildren(); | |
| 122 | } else { | |
| 123 | return 0; | |
| 124 | } | |
| 125 | } | |
| 126 | }; | |
| 127 | } | |
| 128 | ||
| 129 | test "basic SinglyLinkedList test" { | |
| 130 | const L = SinglyLinkedList(u32); | |
| 131 | var list = L{}; | |
| 132 | ||
| 133 | try testing.expect(list.len() == 0); | |
| 134 | ||
| 135 | var one = L.Node{ .data = 1 }; | |
| 136 | var two = L.Node{ .data = 2 }; | |
| 137 | var three = L.Node{ .data = 3 }; | |
| 138 | var four = L.Node{ .data = 4 }; | |
| 139 | var five = L.Node{ .data = 5 }; | |
| 140 | ||
| 141 | list.prepend(&two); // {2} | |
| 142 | two.insertAfter(&five); // {2, 5} | |
| 143 | list.prepend(&one); // {1, 2, 5} | |
| 144 | two.insertAfter(&three); // {1, 2, 3, 5} | |
| 145 | three.insertAfter(&four); // {1, 2, 3, 4, 5} | |
| 146 | ||
| 147 | try testing.expect(list.len() == 5); | |
| 148 | ||
| 149 | // Traverse forwards. | |
| 150 | { | |
| 151 | var it = list.first; | |
| 152 | var index: u32 = 1; | |
| 153 | while (it) |node| : (it = node.next) { | |
| 154 | try testing.expect(node.data == index); | |
| 155 | index += 1; | |
| 156 | } | |
| 157 | } | |
| 158 | ||
| 159 | _ = list.popFirst(); // {2, 3, 4, 5} | |
| 160 | _ = list.remove(&five); // {2, 3, 4} | |
| 161 | _ = two.removeNext(); // {2, 4} | |
| 162 | ||
| 163 | try testing.expect(list.first.?.data == 2); | |
| 164 | try testing.expect(list.first.?.next.?.data == 4); | |
| 165 | try testing.expect(list.first.?.next.?.next == null); | |
| 166 | ||
| 167 | L.Node.reverse(&list.first); | |
| 168 | ||
| 169 | try testing.expect(list.first.?.data == 4); | |
| 170 | try testing.expect(list.first.?.next.?.data == 2); | |
| 171 | try testing.expect(list.first.?.next.?.next == null); | |
| 172 | } | |
| 173 | ||
| 174 | 6 | /// A doubly-linked list has a pair of pointers to both the head and |
| 175 | 7 | /// tail of the list. List elements have pointers to both the previous |
| 176 | 8 | /// and next elements in the sequence. The list can be traversed both |
lib/std/std.zig+1-1| ... | ... | @@ -33,7 +33,7 @@ pub const Random = @import("Random.zig"); |
| 33 | 33 | pub const RingBuffer = @import("RingBuffer.zig"); |
| 34 | 34 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 35 | 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 36 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | |
| 36 | pub const SinglyLinkedList = @import("SinglyLinkedList.zig"); | |
| 37 | 37 | pub const StaticBitSet = bit_set.StaticBitSet; |
| 38 | 38 | pub const StringHashMap = hash_map.StringHashMap; |
| 39 | 39 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |