| author | |
| committer | |
| log | 8acedfd5baabab705946ad097746f9183ef62420 |
| tree | c3c88949d11035d9435da7e278a2dd05b3b68596 |
| parent | 84c9cee502ff71a432fa65ff5c63fac95cb1c47e |
| parent | 810f70ef42fa013dc31b13445dd2910a43f8a0f7 |
| signature |
de-genericify linked lists9 files changed, 553 insertions(+), 543 deletions(-)
CMakeLists.txt-1| ... | @@ -444,7 +444,6 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -444,7 +444,6 @@ set(ZIG_STAGE2_SOURCES |
| 444 | lib/std/json.zig | 444 | lib/std/json.zig |
| 445 | lib/std/json/stringify.zig | 445 | lib/std/json/stringify.zig |
| 446 | lib/std/leb128.zig | 446 | lib/std/leb128.zig |
| 447 | lib/std/linked_list.zig | ||
| 448 | lib/std/log.zig | 447 | lib/std/log.zig |
| 449 | lib/std/macho.zig | 448 | lib/std/macho.zig |
| 450 | lib/std/math.zig | 449 | lib/std/math.zig |
lib/std/DoublyLinkedList.zig created+284| ... | @@ -0,0 +1,284 @@ | ||
| 1 | //! A doubly-linked list has a pair of pointers to both the head and | ||
| 2 | //! tail of the list. List elements have pointers to both the previous | ||
| 3 | //! and next elements in the sequence. The list can be traversed both | ||
| 4 | //! forward and backward. Some operations that take linear O(n) time | ||
| 5 | //! with a singly-linked list can be done without traversal in constant | ||
| 6 | //! O(1) time with a doubly-linked list: | ||
| 7 | //! | ||
| 8 | //! * Removing an element. | ||
| 9 | //! * Inserting a new element before an existing element. | ||
| 10 | //! * Pushing or popping an element from the end of the list. | ||
| 11 | |||
| 12 | const std = @import("std.zig"); | ||
| 13 | const debug = std.debug; | ||
| 14 | const assert = debug.assert; | ||
| 15 | const testing = std.testing; | ||
| 16 | const DoublyLinkedList = @This(); | ||
| 17 | |||
| 18 | first: ?*Node = null, | ||
| 19 | last: ?*Node = null, | ||
| 20 | |||
| 21 | /// This struct contains only the prev and next pointers and not any data | ||
| 22 | /// payload. The intended usage is to embed it intrusively into another data | ||
| 23 | /// structure and access the data with `@fieldParentPtr`. | ||
| 24 | pub const Node = struct { | ||
| 25 | prev: ?*Node = null, | ||
| 26 | next: ?*Node = null, | ||
| 27 | }; | ||
| 28 | |||
| 29 | pub fn insertAfter(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { | ||
| 30 | new_node.prev = existing_node; | ||
| 31 | if (existing_node.next) |next_node| { | ||
| 32 | // Intermediate node. | ||
| 33 | new_node.next = next_node; | ||
| 34 | next_node.prev = new_node; | ||
| 35 | } else { | ||
| 36 | // Last element of the list. | ||
| 37 | new_node.next = null; | ||
| 38 | list.last = new_node; | ||
| 39 | } | ||
| 40 | existing_node.next = new_node; | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { | ||
| 44 | new_node.next = existing_node; | ||
| 45 | if (existing_node.prev) |prev_node| { | ||
| 46 | // Intermediate node. | ||
| 47 | new_node.prev = prev_node; | ||
| 48 | prev_node.next = new_node; | ||
| 49 | } else { | ||
| 50 | // First element of the list. | ||
| 51 | new_node.prev = null; | ||
| 52 | list.first = new_node; | ||
| 53 | } | ||
| 54 | existing_node.prev = new_node; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | ||
| 58 | /// | ||
| 59 | /// Arguments: | ||
| 60 | /// list1: the list to concatenate onto | ||
| 61 | /// list2: the list to be concatenated | ||
| 62 | pub fn concatByMoving(list1: *DoublyLinkedList, list2: *DoublyLinkedList) void { | ||
| 63 | const l2_first = list2.first orelse return; | ||
| 64 | if (list1.last) |l1_last| { | ||
| 65 | l1_last.next = list2.first; | ||
| 66 | l2_first.prev = list1.last; | ||
| 67 | } else { | ||
| 68 | // list1 was empty | ||
| 69 | list1.first = list2.first; | ||
| 70 | } | ||
| 71 | list1.last = list2.last; | ||
| 72 | list2.first = null; | ||
| 73 | list2.last = null; | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Insert a new node at the end of the list. | ||
| 77 | /// | ||
| 78 | /// Arguments: | ||
| 79 | /// new_node: Pointer to the new node to insert. | ||
| 80 | pub fn append(list: *DoublyLinkedList, new_node: *Node) void { | ||
| 81 | if (list.last) |last| { | ||
| 82 | // Insert after last. | ||
| 83 | list.insertAfter(last, new_node); | ||
| 84 | } else { | ||
| 85 | // Empty list. | ||
| 86 | list.prepend(new_node); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Insert a new node at the beginning of the list. | ||
| 91 | /// | ||
| 92 | /// Arguments: | ||
| 93 | /// new_node: Pointer to the new node to insert. | ||
| 94 | pub fn prepend(list: *DoublyLinkedList, new_node: *Node) void { | ||
| 95 | if (list.first) |first| { | ||
| 96 | // Insert before first. | ||
| 97 | list.insertBefore(first, new_node); | ||
| 98 | } else { | ||
| 99 | // Empty list. | ||
| 100 | list.first = new_node; | ||
| 101 | list.last = new_node; | ||
| 102 | new_node.prev = null; | ||
| 103 | new_node.next = null; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Remove a node from the list. | ||
| 108 | /// | ||
| 109 | /// Arguments: | ||
| 110 | /// node: Pointer to the node to be removed. | ||
| 111 | pub fn remove(list: *DoublyLinkedList, node: *Node) void { | ||
| 112 | if (node.prev) |prev_node| { | ||
| 113 | // Intermediate node. | ||
| 114 | prev_node.next = node.next; | ||
| 115 | } else { | ||
| 116 | // First element of the list. | ||
| 117 | list.first = node.next; | ||
| 118 | } | ||
| 119 | |||
| 120 | if (node.next) |next_node| { | ||
| 121 | // Intermediate node. | ||
| 122 | next_node.prev = node.prev; | ||
| 123 | } else { | ||
| 124 | // Last element of the list. | ||
| 125 | list.last = node.prev; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Remove and return the last node in the list. | ||
| 130 | /// | ||
| 131 | /// Returns: | ||
| 132 | /// A pointer to the last node in the list. | ||
| 133 | pub fn pop(list: *DoublyLinkedList) ?*Node { | ||
| 134 | const last = list.last orelse return null; | ||
| 135 | list.remove(last); | ||
| 136 | return last; | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Remove and return the first node in the list. | ||
| 140 | /// | ||
| 141 | /// Returns: | ||
| 142 | /// A pointer to the first node in the list. | ||
| 143 | pub fn popFirst(list: *DoublyLinkedList) ?*Node { | ||
| 144 | const first = list.first orelse return null; | ||
| 145 | list.remove(first); | ||
| 146 | return first; | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Iterate over all nodes, returning the count. | ||
| 150 | /// | ||
| 151 | /// This operation is O(N). Consider tracking the length separately rather than | ||
| 152 | /// computing it. | ||
| 153 | pub fn len(list: DoublyLinkedList) usize { | ||
| 154 | var count: usize = 0; | ||
| 155 | var it: ?*const Node = list.first; | ||
| 156 | while (it) |n| : (it = n.next) count += 1; | ||
| 157 | return count; | ||
| 158 | } | ||
| 159 | |||
| 160 | test "basics" { | ||
| 161 | const L = struct { | ||
| 162 | data: u32, | ||
| 163 | node: DoublyLinkedList.Node = .{}, | ||
| 164 | }; | ||
| 165 | var list: DoublyLinkedList = .{}; | ||
| 166 | |||
| 167 | var one: L = .{ .data = 1 }; | ||
| 168 | var two: L = .{ .data = 2 }; | ||
| 169 | var three: L = .{ .data = 3 }; | ||
| 170 | var four: L = .{ .data = 4 }; | ||
| 171 | var five: L = .{ .data = 5 }; | ||
| 172 | |||
| 173 | list.append(&two.node); // {2} | ||
| 174 | list.append(&five.node); // {2, 5} | ||
| 175 | list.prepend(&one.node); // {1, 2, 5} | ||
| 176 | list.insertBefore(&five.node, &four.node); // {1, 2, 4, 5} | ||
| 177 | list.insertAfter(&two.node, &three.node); // {1, 2, 3, 4, 5} | ||
| 178 | |||
| 179 | // Traverse forwards. | ||
| 180 | { | ||
| 181 | var it = list.first; | ||
| 182 | var index: u32 = 1; | ||
| 183 | while (it) |node| : (it = node.next) { | ||
| 184 | const l: *L = @fieldParentPtr("node", node); | ||
| 185 | try testing.expect(l.data == index); | ||
| 186 | index += 1; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | // Traverse backwards. | ||
| 191 | { | ||
| 192 | var it = list.last; | ||
| 193 | var index: u32 = 1; | ||
| 194 | while (it) |node| : (it = node.prev) { | ||
| 195 | const l: *L = @fieldParentPtr("node", node); | ||
| 196 | try testing.expect(l.data == (6 - index)); | ||
| 197 | index += 1; | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | _ = list.popFirst(); // {2, 3, 4, 5} | ||
| 202 | _ = list.pop(); // {2, 3, 4} | ||
| 203 | list.remove(&three.node); // {2, 4} | ||
| 204 | |||
| 205 | try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?)).data == 2); | ||
| 206 | try testing.expect(@as(*L, @fieldParentPtr("node", list.last.?)).data == 4); | ||
| 207 | try testing.expect(list.len() == 2); | ||
| 208 | } | ||
| 209 | |||
| 210 | test "concatenation" { | ||
| 211 | const L = struct { | ||
| 212 | data: u32, | ||
| 213 | node: DoublyLinkedList.Node = .{}, | ||
| 214 | }; | ||
| 215 | var list1: DoublyLinkedList = .{}; | ||
| 216 | var list2: DoublyLinkedList = .{}; | ||
| 217 | |||
| 218 | var one: L = .{ .data = 1 }; | ||
| 219 | var two: L = .{ .data = 2 }; | ||
| 220 | var three: L = .{ .data = 3 }; | ||
| 221 | var four: L = .{ .data = 4 }; | ||
| 222 | var five: L = .{ .data = 5 }; | ||
| 223 | |||
| 224 | list1.append(&one.node); | ||
| 225 | list1.append(&two.node); | ||
| 226 | list2.append(&three.node); | ||
| 227 | list2.append(&four.node); | ||
| 228 | list2.append(&five.node); | ||
| 229 | |||
| 230 | list1.concatByMoving(&list2); | ||
| 231 | |||
| 232 | try testing.expect(list1.last == &five.node); | ||
| 233 | try testing.expect(list1.len() == 5); | ||
| 234 | try testing.expect(list2.first == null); | ||
| 235 | try testing.expect(list2.last == null); | ||
| 236 | try testing.expect(list2.len() == 0); | ||
| 237 | |||
| 238 | // Traverse forwards. | ||
| 239 | { | ||
| 240 | var it = list1.first; | ||
| 241 | var index: u32 = 1; | ||
| 242 | while (it) |node| : (it = node.next) { | ||
| 243 | const l: *L = @fieldParentPtr("node", node); | ||
| 244 | try testing.expect(l.data == index); | ||
| 245 | index += 1; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | // Traverse backwards. | ||
| 250 | { | ||
| 251 | var it = list1.last; | ||
| 252 | var index: u32 = 1; | ||
| 253 | while (it) |node| : (it = node.prev) { | ||
| 254 | const l: *L = @fieldParentPtr("node", node); | ||
| 255 | try testing.expect(l.data == (6 - index)); | ||
| 256 | index += 1; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | // Swap them back, this verifies that concatenating to an empty list works. | ||
| 261 | list2.concatByMoving(&list1); | ||
| 262 | |||
| 263 | // Traverse forwards. | ||
| 264 | { | ||
| 265 | var it = list2.first; | ||
| 266 | var index: u32 = 1; | ||
| 267 | while (it) |node| : (it = node.next) { | ||
| 268 | const l: *L = @fieldParentPtr("node", node); | ||
| 269 | try testing.expect(l.data == index); | ||
| 270 | index += 1; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | // Traverse backwards. | ||
| 275 | { | ||
| 276 | var it = list2.last; | ||
| 277 | var index: u32 = 1; | ||
| 278 | while (it) |node| : (it = node.prev) { | ||
| 279 | const l: *L = @fieldParentPtr("node", node); | ||
| 280 | try testing.expect(l.data == (6 - index)); | ||
| 281 | index += 1; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | } | ||
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,7 +5,7 @@ const WaitGroup = @import("WaitGroup.zig"); |
| 5 | 5 | ||
| 6 | mutex: std.Thread.Mutex = .{}, | 6 | mutex: std.Thread.Mutex = .{}, |
| 7 | cond: std.Thread.Condition = .{}, | 7 | cond: std.Thread.Condition = .{}, |
| 8 | run_queue: RunQueue = .{}, | 8 | run_queue: std.SinglyLinkedList = .{}, |
| 9 | is_running: bool = true, | 9 | is_running: bool = true, |
| 10 | allocator: std.mem.Allocator, | 10 | allocator: std.mem.Allocator, |
| 11 | threads: if (builtin.single_threaded) [0]std.Thread else []std.Thread, | 11 | threads: if (builtin.single_threaded) [0]std.Thread else []std.Thread, |
| ... | @@ -16,9 +16,9 @@ ids: if (builtin.single_threaded) struct { | ... | @@ -16,9 +16,9 @@ ids: if (builtin.single_threaded) struct { |
| 16 | } | 16 | } |
| 17 | } else std.AutoArrayHashMapUnmanaged(std.Thread.Id, void), | 17 | } else std.AutoArrayHashMapUnmanaged(std.Thread.Id, void), |
| 18 | 18 | ||
| 19 | const RunQueue = std.SinglyLinkedList(Runnable); | ||
| 20 | const Runnable = struct { | 19 | const Runnable = struct { |
| 21 | runFn: RunProto, | 20 | runFn: RunProto, |
| 21 | node: std.SinglyLinkedList.Node = .{}, | ||
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | const RunProto = *const fn (*Runnable, id: ?usize) void; | 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,12 +110,11 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args |
| 110 | const Closure = struct { | 110 | const Closure = struct { |
| 111 | arguments: Args, | 111 | arguments: Args, |
| 112 | pool: *Pool, | 112 | pool: *Pool, |
| 113 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | 113 | runnable: Runnable = .{ .runFn = runFn }, |
| 114 | wait_group: *WaitGroup, | 114 | wait_group: *WaitGroup, |
| 115 | 115 | ||
| 116 | fn runFn(runnable: *Runnable, _: ?usize) void { | 116 | fn runFn(runnable: *Runnable, _: ?usize) void { |
| 117 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | 117 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); |
| 118 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | ||
| 119 | @call(.auto, func, closure.arguments); | 118 | @call(.auto, func, closure.arguments); |
| 120 | closure.wait_group.finish(); | 119 | closure.wait_group.finish(); |
| 121 | 120 | ||
| ... | @@ -143,7 +142,7 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args | ... | @@ -143,7 +142,7 @@ pub fn spawnWg(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, args |
| 143 | .wait_group = wait_group, | 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 | pool.mutex.unlock(); | 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,12 +172,11 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar |
| 173 | const Closure = struct { | 172 | const Closure = struct { |
| 174 | arguments: Args, | 173 | arguments: Args, |
| 175 | pool: *Pool, | 174 | pool: *Pool, |
| 176 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | 175 | runnable: Runnable = .{ .runFn = runFn }, |
| 177 | wait_group: *WaitGroup, | 176 | wait_group: *WaitGroup, |
| 178 | 177 | ||
| 179 | fn runFn(runnable: *Runnable, id: ?usize) void { | 178 | fn runFn(runnable: *Runnable, id: ?usize) void { |
| 180 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | 179 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); |
| 181 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | ||
| 182 | @call(.auto, func, .{id.?} ++ closure.arguments); | 180 | @call(.auto, func, .{id.?} ++ closure.arguments); |
| 183 | closure.wait_group.finish(); | 181 | closure.wait_group.finish(); |
| 184 | 182 | ||
| ... | @@ -207,7 +205,7 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar | ... | @@ -207,7 +205,7 @@ pub fn spawnWgId(pool: *Pool, wait_group: *WaitGroup, comptime func: anytype, ar |
| 207 | .wait_group = wait_group, | 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 | pool.mutex.unlock(); | 209 | pool.mutex.unlock(); |
| 212 | } | 210 | } |
| 213 | 211 | ||
| ... | @@ -225,11 +223,10 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { | ... | @@ -225,11 +223,10 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { |
| 225 | const Closure = struct { | 223 | const Closure = struct { |
| 226 | arguments: Args, | 224 | arguments: Args, |
| 227 | pool: *Pool, | 225 | pool: *Pool, |
| 228 | run_node: RunQueue.Node = .{ .data = .{ .runFn = runFn } }, | 226 | runnable: Runnable = .{ .runFn = runFn }, |
| 229 | 227 | ||
| 230 | fn runFn(runnable: *Runnable, _: ?usize) void { | 228 | fn runFn(runnable: *Runnable, _: ?usize) void { |
| 231 | const run_node: *RunQueue.Node = @fieldParentPtr("data", runnable); | 229 | const closure: *@This() = @alignCast(@fieldParentPtr("runnable", runnable)); |
| 232 | const closure: *@This() = @alignCast(@fieldParentPtr("run_node", run_node)); | ||
| 233 | @call(.auto, func, closure.arguments); | 230 | @call(.auto, func, closure.arguments); |
| 234 | 231 | ||
| 235 | // The thread pool's allocator is protected by the mutex. | 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,7 +248,7 @@ pub fn spawn(pool: *Pool, comptime func: anytype, args: anytype) !void { |
| 251 | .pool = pool, | 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 | // Notify waiting threads outside the lock to try and keep the critical section small. | 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,7 +289,8 @@ fn worker(pool: *Pool) void { |
| 292 | pool.mutex.unlock(); | 289 | pool.mutex.unlock(); |
| 293 | defer pool.mutex.lock(); | 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 | // Stop executing instead of waiting if the thread pool is no longer running. | 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,7 +310,8 @@ pub fn waitAndWork(pool: *Pool, wait_group: *WaitGroup) void { |
| 312 | if (pool.run_queue.popFirst()) |run_node| { | 310 | if (pool.run_queue.popFirst()) |run_node| { |
| 313 | id = id orelse pool.ids.getIndex(std.Thread.getCurrentId()); | 311 | id = id orelse pool.ids.getIndex(std.Thread.getCurrentId()); |
| 314 | pool.mutex.unlock(); | 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 | continue; | 315 | continue; |
| 317 | } | 316 | } |
| 318 | 317 |
lib/std/heap/arena_allocator.zig+25-16| ... | @@ -14,7 +14,7 @@ pub const ArenaAllocator = struct { | ... | @@ -14,7 +14,7 @@ pub const ArenaAllocator = struct { |
| 14 | /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator | 14 | /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator |
| 15 | /// as a memory-saving optimization. | 15 | /// as a memory-saving optimization. |
| 16 | pub const State = struct { | 16 | pub const State = struct { |
| 17 | buffer_list: std.SinglyLinkedList(usize) = .{}, | 17 | buffer_list: std.SinglyLinkedList = .{}, |
| 18 | end_index: usize = 0, | 18 | end_index: usize = 0, |
| 19 | 19 | ||
| 20 | pub fn promote(self: State, child_allocator: Allocator) ArenaAllocator { | 20 | pub fn promote(self: State, child_allocator: Allocator) ArenaAllocator { |
| ... | @@ -37,7 +37,10 @@ pub const ArenaAllocator = struct { | ... | @@ -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 | const BufNode_alignment: mem.Alignment = .fromByteUnits(@alignOf(BufNode)); | 44 | const BufNode_alignment: mem.Alignment = .fromByteUnits(@alignOf(BufNode)); |
| 42 | 45 | ||
| 43 | pub fn init(child_allocator: Allocator) ArenaAllocator { | 46 | pub fn init(child_allocator: Allocator) ArenaAllocator { |
| ... | @@ -51,7 +54,8 @@ pub const ArenaAllocator = struct { | ... | @@ -51,7 +54,8 @@ pub const ArenaAllocator = struct { |
| 51 | while (it) |node| { | 54 | while (it) |node| { |
| 52 | // this has to occur before the free because the free frees node | 55 | // this has to occur before the free because the free frees node |
| 53 | const next_it = node.next; | 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 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); | 59 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); |
| 56 | it = next_it; | 60 | it = next_it; |
| 57 | } | 61 | } |
| ... | @@ -78,7 +82,8 @@ pub const ArenaAllocator = struct { | ... | @@ -78,7 +82,8 @@ pub const ArenaAllocator = struct { |
| 78 | while (it) |node| : (it = node.next) { | 82 | while (it) |node| : (it = node.next) { |
| 79 | // Compute the actually allocated size excluding the | 83 | // Compute the actually allocated size excluding the |
| 80 | // linked list node. | 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 | return size; | 88 | return size; |
| 84 | } | 89 | } |
| ... | @@ -130,7 +135,8 @@ pub const ArenaAllocator = struct { | ... | @@ -130,7 +135,8 @@ pub const ArenaAllocator = struct { |
| 130 | const next_it = node.next; | 135 | const next_it = node.next; |
| 131 | if (next_it == null) | 136 | if (next_it == null) |
| 132 | break node; | 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 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); | 140 | self.child_allocator.rawFree(alloc_buf, BufNode_alignment, @returnAddress()); |
| 135 | it = next_it; | 141 | it = next_it; |
| 136 | } else null; | 142 | } else null; |
| ... | @@ -140,12 +146,13 @@ pub const ArenaAllocator = struct { | ... | @@ -140,12 +146,13 @@ pub const ArenaAllocator = struct { |
| 140 | if (maybe_first_node) |first_node| { | 146 | if (maybe_first_node) |first_node| { |
| 141 | self.state.buffer_list.first = first_node; | 147 | self.state.buffer_list.first = first_node; |
| 142 | // perfect, no need to invoke the child_allocator | 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 | return true; | 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 | if (self.child_allocator.rawResize(first_alloc_buf, BufNode_alignment, total_size, @returnAddress())) { | 153 | if (self.child_allocator.rawResize(first_alloc_buf, BufNode_alignment, total_size, @returnAddress())) { |
| 147 | // successful resize | 154 | // successful resize |
| 148 | first_node.data = total_size; | 155 | first_buf_node.data = total_size; |
| 149 | } else { | 156 | } else { |
| 150 | // manual realloc | 157 | // manual realloc |
| 151 | const new_ptr = self.child_allocator.rawAlloc(total_size, BufNode_alignment, @returnAddress()) orelse { | 158 | const new_ptr = self.child_allocator.rawAlloc(total_size, BufNode_alignment, @returnAddress()) orelse { |
| ... | @@ -153,9 +160,9 @@ pub const ArenaAllocator = struct { | ... | @@ -153,9 +160,9 @@ pub const ArenaAllocator = struct { |
| 153 | return false; | 160 | return false; |
| 154 | }; | 161 | }; |
| 155 | self.child_allocator.rawFree(first_alloc_buf, BufNode_alignment, @returnAddress()); | 162 | self.child_allocator.rawFree(first_alloc_buf, BufNode_alignment, @returnAddress()); |
| 156 | const node: *BufNode = @ptrCast(@alignCast(new_ptr)); | 163 | const buf_node: *BufNode = @ptrCast(@alignCast(new_ptr)); |
| 157 | node.* = .{ .data = total_size }; | 164 | buf_node.* = .{ .data = total_size }; |
| 158 | self.state.buffer_list.first = node; | 165 | self.state.buffer_list.first = &buf_node.node; |
| 159 | } | 166 | } |
| 160 | } | 167 | } |
| 161 | return true; | 168 | return true; |
| ... | @@ -169,7 +176,7 @@ pub const ArenaAllocator = struct { | ... | @@ -169,7 +176,7 @@ pub const ArenaAllocator = struct { |
| 169 | return null; | 176 | return null; |
| 170 | const buf_node: *BufNode = @ptrCast(@alignCast(ptr)); | 177 | const buf_node: *BufNode = @ptrCast(@alignCast(ptr)); |
| 171 | buf_node.* = .{ .data = len }; | 178 | buf_node.* = .{ .data = len }; |
| 172 | self.state.buffer_list.prepend(buf_node); | 179 | self.state.buffer_list.prepend(&buf_node.node); |
| 173 | self.state.end_index = 0; | 180 | self.state.end_index = 0; |
| 174 | return buf_node; | 181 | return buf_node; |
| 175 | } | 182 | } |
| ... | @@ -179,8 +186,8 @@ pub const ArenaAllocator = struct { | ... | @@ -179,8 +186,8 @@ pub const ArenaAllocator = struct { |
| 179 | _ = ra; | 186 | _ = ra; |
| 180 | 187 | ||
| 181 | const ptr_align = alignment.toByteUnits(); | 188 | const ptr_align = alignment.toByteUnits(); |
| 182 | var cur_node = if (self.state.buffer_list.first) |first_node| | 189 | var cur_node: *BufNode = if (self.state.buffer_list.first) |first_node| |
| 183 | first_node | 190 | @fieldParentPtr("node", first_node) |
| 184 | else | 191 | else |
| 185 | (self.createNode(0, n + ptr_align) orelse return null); | 192 | (self.createNode(0, n + ptr_align) orelse return null); |
| 186 | while (true) { | 193 | while (true) { |
| ... | @@ -213,7 +220,8 @@ pub const ArenaAllocator = struct { | ... | @@ -213,7 +220,8 @@ pub const ArenaAllocator = struct { |
| 213 | _ = ret_addr; | 220 | _ = ret_addr; |
| 214 | 221 | ||
| 215 | const cur_node = self.state.buffer_list.first orelse return false; | 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 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index != @intFromPtr(buf.ptr) + buf.len) { | 225 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index != @intFromPtr(buf.ptr) + buf.len) { |
| 218 | // It's not the most recent allocation, so it cannot be expanded, | 226 | // It's not the most recent allocation, so it cannot be expanded, |
| 219 | // but it's fine if they want to make it smaller. | 227 | // but it's fine if they want to make it smaller. |
| ... | @@ -248,7 +256,8 @@ pub const ArenaAllocator = struct { | ... | @@ -248,7 +256,8 @@ pub const ArenaAllocator = struct { |
| 248 | const self: *ArenaAllocator = @ptrCast(@alignCast(ctx)); | 256 | const self: *ArenaAllocator = @ptrCast(@alignCast(ctx)); |
| 249 | 257 | ||
| 250 | const cur_node = self.state.buffer_list.first orelse return; | 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 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index == @intFromPtr(buf.ptr) + buf.len) { | 262 | if (@intFromPtr(cur_buf.ptr) + self.state.end_index == @intFromPtr(buf.ptr) + buf.len) { |
| 254 | self.state.end_index -= buf.len; | 263 | self.state.end_index -= buf.len; |
lib/std/http/Client.zig+44-43| ... | @@ -46,9 +46,9 @@ https_proxy: ?*Proxy = null, | ... | @@ -46,9 +46,9 @@ https_proxy: ?*Proxy = null, |
| 46 | pub const ConnectionPool = struct { | 46 | pub const ConnectionPool = struct { |
| 47 | mutex: std.Thread.Mutex = .{}, | 47 | mutex: std.Thread.Mutex = .{}, |
| 48 | /// Open connections that are currently in use. | 48 | /// Open connections that are currently in use. |
| 49 | used: Queue = .{}, | 49 | used: std.DoublyLinkedList = .{}, |
| 50 | /// Open connections that are not currently in use. | 50 | /// Open connections that are not currently in use. |
| 51 | free: Queue = .{}, | 51 | free: std.DoublyLinkedList = .{}, |
| 52 | free_len: usize = 0, | 52 | free_len: usize = 0, |
| 53 | free_size: usize = 32, | 53 | free_size: usize = 32, |
| 54 | 54 | ||
| ... | @@ -59,9 +59,6 @@ pub const ConnectionPool = struct { | ... | @@ -59,9 +59,6 @@ pub const ConnectionPool = struct { |
| 59 | protocol: Connection.Protocol, | 59 | protocol: Connection.Protocol, |
| 60 | }; | 60 | }; |
| 61 | 61 | ||
| 62 | const Queue = std.DoublyLinkedList(Connection); | ||
| 63 | pub const Node = Queue.Node; | ||
| 64 | |||
| 65 | /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. | 62 | /// Finds and acquires a connection from the connection pool matching the criteria. This function is threadsafe. |
| 66 | /// If no connection is found, null is returned. | 63 | /// If no connection is found, null is returned. |
| 67 | pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection { | 64 | pub fn findConnection(pool: *ConnectionPool, criteria: Criteria) ?*Connection { |
| ... | @@ -70,33 +67,34 @@ pub const ConnectionPool = struct { | ... | @@ -70,33 +67,34 @@ pub const ConnectionPool = struct { |
| 70 | 67 | ||
| 71 | var next = pool.free.last; | 68 | var next = pool.free.last; |
| 72 | while (next) |node| : (next = node.prev) { | 69 | while (next) |node| : (next = node.prev) { |
| 73 | if (node.data.protocol != criteria.protocol) continue; | 70 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 74 | if (node.data.port != criteria.port) continue; | 71 | if (connection.protocol != criteria.protocol) continue; |
| 72 | if (connection.port != criteria.port) continue; | ||
| 75 | 73 | ||
| 76 | // Domain names are case-insensitive (RFC 5890, Section 2.3.2.4) | 74 | // Domain names are case-insensitive (RFC 5890, Section 2.3.2.4) |
| 77 | if (!std.ascii.eqlIgnoreCase(node.data.host, criteria.host)) continue; | 75 | if (!std.ascii.eqlIgnoreCase(connection.host, criteria.host)) continue; |
| 78 | 76 | ||
| 79 | pool.acquireUnsafe(node); | 77 | pool.acquireUnsafe(connection); |
| 80 | return &node.data; | 78 | return connection; |
| 81 | } | 79 | } |
| 82 | 80 | ||
| 83 | return null; | 81 | return null; |
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | /// Acquires an existing connection from the connection pool. This function is not threadsafe. | 84 | /// Acquires an existing connection from the connection pool. This function is not threadsafe. |
| 87 | pub fn acquireUnsafe(pool: *ConnectionPool, node: *Node) void { | 85 | pub fn acquireUnsafe(pool: *ConnectionPool, connection: *Connection) void { |
| 88 | pool.free.remove(node); | 86 | pool.free.remove(&connection.pool_node); |
| 89 | pool.free_len -= 1; | 87 | pool.free_len -= 1; |
| 90 | 88 | ||
| 91 | pool.used.append(node); | 89 | pool.used.append(&connection.pool_node); |
| 92 | } | 90 | } |
| 93 | 91 | ||
| 94 | /// Acquires an existing connection from the connection pool. This function is threadsafe. | 92 | /// Acquires an existing connection from the connection pool. This function is threadsafe. |
| 95 | pub fn acquire(pool: *ConnectionPool, node: *Node) void { | 93 | pub fn acquire(pool: *ConnectionPool, connection: *Connection) void { |
| 96 | pool.mutex.lock(); | 94 | pool.mutex.lock(); |
| 97 | defer pool.mutex.unlock(); | 95 | defer pool.mutex.unlock(); |
| 98 | 96 | ||
| 99 | return pool.acquireUnsafe(node); | 97 | return pool.acquireUnsafe(connection); |
| 100 | } | 98 | } |
| 101 | 99 | ||
| 102 | /// Tries to release a connection back to the connection pool. This function is threadsafe. | 100 | /// Tries to release a connection back to the connection pool. This function is threadsafe. |
| ... | @@ -108,38 +106,37 @@ pub const ConnectionPool = struct { | ... | @@ -108,38 +106,37 @@ pub const ConnectionPool = struct { |
| 108 | pool.mutex.lock(); | 106 | pool.mutex.lock(); |
| 109 | defer pool.mutex.unlock(); | 107 | defer pool.mutex.unlock(); |
| 110 | 108 | ||
| 111 | const node: *Node = @fieldParentPtr("data", connection); | 109 | pool.used.remove(&connection.pool_node); |
| 112 | |||
| 113 | pool.used.remove(node); | ||
| 114 | 110 | ||
| 115 | if (node.data.closing or pool.free_size == 0) { | 111 | if (connection.closing or pool.free_size == 0) { |
| 116 | node.data.close(allocator); | 112 | connection.close(allocator); |
| 117 | return allocator.destroy(node); | 113 | return allocator.destroy(connection); |
| 118 | } | 114 | } |
| 119 | 115 | ||
| 120 | if (pool.free_len >= pool.free_size) { | 116 | if (pool.free_len >= pool.free_size) { |
| 121 | const popped = pool.free.popFirst() orelse unreachable; | 117 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); |
| 122 | pool.free_len -= 1; | 118 | pool.free_len -= 1; |
| 123 | 119 | ||
| 124 | popped.data.close(allocator); | 120 | popped.close(allocator); |
| 125 | allocator.destroy(popped); | 121 | allocator.destroy(popped); |
| 126 | } | 122 | } |
| 127 | 123 | ||
| 128 | if (node.data.proxied) { | 124 | if (connection.proxied) { |
| 129 | pool.free.prepend(node); // proxied connections go to the end of the queue, always try direct connections first | 125 | // proxied connections go to the end of the queue, always try direct connections first |
| 126 | pool.free.prepend(&connection.pool_node); | ||
| 130 | } else { | 127 | } else { |
| 131 | pool.free.append(node); | 128 | pool.free.append(&connection.pool_node); |
| 132 | } | 129 | } |
| 133 | 130 | ||
| 134 | pool.free_len += 1; | 131 | pool.free_len += 1; |
| 135 | } | 132 | } |
| 136 | 133 | ||
| 137 | /// Adds a newly created node to the pool of used connections. This function is threadsafe. | 134 | /// Adds a newly created node to the pool of used connections. This function is threadsafe. |
| 138 | pub fn addUsed(pool: *ConnectionPool, node: *Node) void { | 135 | pub fn addUsed(pool: *ConnectionPool, connection: *Connection) void { |
| 139 | pool.mutex.lock(); | 136 | pool.mutex.lock(); |
| 140 | defer pool.mutex.unlock(); | 137 | defer pool.mutex.unlock(); |
| 141 | 138 | ||
| 142 | pool.used.append(node); | 139 | pool.used.append(&connection.pool_node); |
| 143 | } | 140 | } |
| 144 | 141 | ||
| 145 | /// Resizes the connection pool. This function is threadsafe. | 142 | /// Resizes the connection pool. This function is threadsafe. |
| ... | @@ -170,18 +167,18 @@ pub const ConnectionPool = struct { | ... | @@ -170,18 +167,18 @@ pub const ConnectionPool = struct { |
| 170 | 167 | ||
| 171 | var next = pool.free.first; | 168 | var next = pool.free.first; |
| 172 | while (next) |node| { | 169 | while (next) |node| { |
| 173 | defer allocator.destroy(node); | 170 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 174 | next = node.next; | 171 | next = node.next; |
| 175 | 172 | connection.close(allocator); | |
| 176 | node.data.close(allocator); | 173 | allocator.destroy(connection); |
| 177 | } | 174 | } |
| 178 | 175 | ||
| 179 | next = pool.used.first; | 176 | next = pool.used.first; |
| 180 | while (next) |node| { | 177 | while (next) |node| { |
| 181 | defer allocator.destroy(node); | 178 | const connection: *Connection = @fieldParentPtr("pool_node", node); |
| 182 | next = node.next; | 179 | next = node.next; |
| 183 | 180 | connection.close(allocator); | |
| 184 | node.data.close(allocator); | 181 | allocator.destroy(node); |
| 185 | } | 182 | } |
| 186 | 183 | ||
| 187 | pool.* = undefined; | 184 | pool.* = undefined; |
| ... | @@ -194,6 +191,9 @@ pub const Connection = struct { | ... | @@ -194,6 +191,9 @@ pub const Connection = struct { |
| 194 | /// undefined unless protocol is tls. | 191 | /// undefined unless protocol is tls. |
| 195 | tls_client: if (!disable_tls) *std.crypto.tls.Client else void, | 192 | tls_client: if (!disable_tls) *std.crypto.tls.Client else void, |
| 196 | 193 | ||
| 194 | /// Entry in `ConnectionPool.used` or `ConnectionPool.free`. | ||
| 195 | pool_node: std.DoublyLinkedList.Node, | ||
| 196 | |||
| 197 | /// The protocol that this connection is using. | 197 | /// The protocol that this connection is using. |
| 198 | protocol: Protocol, | 198 | protocol: Protocol, |
| 199 | 199 | ||
| ... | @@ -1326,9 +1326,8 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec | ... | @@ -1326,9 +1326,8 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1326 | if (disable_tls and protocol == .tls) | 1326 | if (disable_tls and protocol == .tls) |
| 1327 | return error.TlsInitializationFailed; | 1327 | return error.TlsInitializationFailed; |
| 1328 | 1328 | ||
| 1329 | const conn = try client.allocator.create(ConnectionPool.Node); | 1329 | const conn = try client.allocator.create(Connection); |
| 1330 | errdefer client.allocator.destroy(conn); | 1330 | errdefer client.allocator.destroy(conn); |
| 1331 | conn.* = .{ .data = undefined }; | ||
| 1332 | 1331 | ||
| 1333 | const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { | 1332 | const stream = net.tcpConnectToHost(client.allocator, host, port) catch |err| switch (err) { |
| 1334 | error.ConnectionRefused => return error.ConnectionRefused, | 1333 | error.ConnectionRefused => return error.ConnectionRefused, |
| ... | @@ -1343,21 +1342,23 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec | ... | @@ -1343,21 +1342,23 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1343 | }; | 1342 | }; |
| 1344 | errdefer stream.close(); | 1343 | errdefer stream.close(); |
| 1345 | 1344 | ||
| 1346 | conn.data = .{ | 1345 | conn.* = .{ |
| 1347 | .stream = stream, | 1346 | .stream = stream, |
| 1348 | .tls_client = undefined, | 1347 | .tls_client = undefined, |
| 1349 | 1348 | ||
| 1350 | .protocol = protocol, | 1349 | .protocol = protocol, |
| 1351 | .host = try client.allocator.dupe(u8, host), | 1350 | .host = try client.allocator.dupe(u8, host), |
| 1352 | .port = port, | 1351 | .port = port, |
| 1352 | |||
| 1353 | .pool_node = .{}, | ||
| 1353 | }; | 1354 | }; |
| 1354 | errdefer client.allocator.free(conn.data.host); | 1355 | errdefer client.allocator.free(conn.host); |
| 1355 | 1356 | ||
| 1356 | if (protocol == .tls) { | 1357 | if (protocol == .tls) { |
| 1357 | if (disable_tls) unreachable; | 1358 | if (disable_tls) unreachable; |
| 1358 | 1359 | ||
| 1359 | conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client); | 1360 | conn.tls_client = try client.allocator.create(std.crypto.tls.Client); |
| 1360 | errdefer client.allocator.destroy(conn.data.tls_client); | 1361 | errdefer client.allocator.destroy(conn.tls_client); |
| 1361 | 1362 | ||
| 1362 | const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: { | 1363 | const ssl_key_log_file: ?std.fs.File = if (std.options.http_enable_ssl_key_log_file) ssl_key_log_file: { |
| 1363 | const ssl_key_log_path = std.process.getEnvVarOwned(client.allocator, "SSLKEYLOGFILE") catch |err| switch (err) { | 1364 | const ssl_key_log_path = std.process.getEnvVarOwned(client.allocator, "SSLKEYLOGFILE") catch |err| switch (err) { |
| ... | @@ -1375,19 +1376,19 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec | ... | @@ -1375,19 +1376,19 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec |
| 1375 | } else null; | 1376 | } else null; |
| 1376 | errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close(); | 1377 | errdefer if (ssl_key_log_file) |key_log_file| key_log_file.close(); |
| 1377 | 1378 | ||
| 1378 | conn.data.tls_client.* = std.crypto.tls.Client.init(stream, .{ | 1379 | conn.tls_client.* = std.crypto.tls.Client.init(stream, .{ |
| 1379 | .host = .{ .explicit = host }, | 1380 | .host = .{ .explicit = host }, |
| 1380 | .ca = .{ .bundle = client.ca_bundle }, | 1381 | .ca = .{ .bundle = client.ca_bundle }, |
| 1381 | .ssl_key_log_file = ssl_key_log_file, | 1382 | .ssl_key_log_file = ssl_key_log_file, |
| 1382 | }) catch return error.TlsInitializationFailed; | 1383 | }) catch return error.TlsInitializationFailed; |
| 1383 | // This is appropriate for HTTPS because the HTTP headers contain | 1384 | // This is appropriate for HTTPS because the HTTP headers contain |
| 1384 | // the content length which is used to detect truncation attacks. | 1385 | // the content length which is used to detect truncation attacks. |
| 1385 | conn.data.tls_client.allow_truncation_attacks = true; | 1386 | conn.tls_client.allow_truncation_attacks = true; |
| 1386 | } | 1387 | } |
| 1387 | 1388 | ||
| 1388 | client.connection_pool.addUsed(conn); | 1389 | client.connection_pool.addUsed(conn); |
| 1389 | 1390 | ||
| 1390 | return &conn.data; | 1391 | return conn; |
| 1391 | } | 1392 | } |
| 1392 | 1393 | ||
| 1393 | pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; | 1394 | pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; |
lib/std/linked_list.zig deleted-455| ... | @@ -1,455 +0,0 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const debug = std.debug; | ||
| 3 | const assert = debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 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 | /// A doubly-linked list has a pair of pointers to both the head and | ||
| 175 | /// tail of the list. List elements have pointers to both the previous | ||
| 176 | /// and next elements in the sequence. The list can be traversed both | ||
| 177 | /// forward and backward. Some operations that take linear O(n) time | ||
| 178 | /// with a singly-linked list can be done without traversal in constant | ||
| 179 | /// O(1) time with a doubly-linked list: | ||
| 180 | /// | ||
| 181 | /// - Removing an element. | ||
| 182 | /// - Inserting a new element before an existing element. | ||
| 183 | /// - Pushing or popping an element from the end of the list. | ||
| 184 | pub fn DoublyLinkedList(comptime T: type) type { | ||
| 185 | return struct { | ||
| 186 | const Self = @This(); | ||
| 187 | |||
| 188 | /// Node inside the linked list wrapping the actual data. | ||
| 189 | pub const Node = struct { | ||
| 190 | prev: ?*Node = null, | ||
| 191 | next: ?*Node = null, | ||
| 192 | data: T, | ||
| 193 | }; | ||
| 194 | |||
| 195 | first: ?*Node = null, | ||
| 196 | last: ?*Node = null, | ||
| 197 | len: usize = 0, | ||
| 198 | |||
| 199 | /// Insert a new node after an existing one. | ||
| 200 | /// | ||
| 201 | /// Arguments: | ||
| 202 | /// node: Pointer to a node in the list. | ||
| 203 | /// new_node: Pointer to the new node to insert. | ||
| 204 | pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void { | ||
| 205 | new_node.prev = node; | ||
| 206 | if (node.next) |next_node| { | ||
| 207 | // Intermediate node. | ||
| 208 | new_node.next = next_node; | ||
| 209 | next_node.prev = new_node; | ||
| 210 | } else { | ||
| 211 | // Last element of the list. | ||
| 212 | new_node.next = null; | ||
| 213 | list.last = new_node; | ||
| 214 | } | ||
| 215 | node.next = new_node; | ||
| 216 | |||
| 217 | list.len += 1; | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Insert a new node before an existing one. | ||
| 221 | /// | ||
| 222 | /// Arguments: | ||
| 223 | /// node: Pointer to a node in the list. | ||
| 224 | /// new_node: Pointer to the new node to insert. | ||
| 225 | pub fn insertBefore(list: *Self, node: *Node, new_node: *Node) void { | ||
| 226 | new_node.next = node; | ||
| 227 | if (node.prev) |prev_node| { | ||
| 228 | // Intermediate node. | ||
| 229 | new_node.prev = prev_node; | ||
| 230 | prev_node.next = new_node; | ||
| 231 | } else { | ||
| 232 | // First element of the list. | ||
| 233 | new_node.prev = null; | ||
| 234 | list.first = new_node; | ||
| 235 | } | ||
| 236 | node.prev = new_node; | ||
| 237 | |||
| 238 | list.len += 1; | ||
| 239 | } | ||
| 240 | |||
| 241 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | ||
| 242 | /// | ||
| 243 | /// Arguments: | ||
| 244 | /// list1: the list to concatenate onto | ||
| 245 | /// list2: the list to be concatenated | ||
| 246 | pub fn concatByMoving(list1: *Self, list2: *Self) void { | ||
| 247 | const l2_first = list2.first orelse return; | ||
| 248 | if (list1.last) |l1_last| { | ||
| 249 | l1_last.next = list2.first; | ||
| 250 | l2_first.prev = list1.last; | ||
| 251 | list1.len += list2.len; | ||
| 252 | } else { | ||
| 253 | // list1 was empty | ||
| 254 | list1.first = list2.first; | ||
| 255 | list1.len = list2.len; | ||
| 256 | } | ||
| 257 | list1.last = list2.last; | ||
| 258 | list2.first = null; | ||
| 259 | list2.last = null; | ||
| 260 | list2.len = 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Insert a new node at the end of the list. | ||
| 264 | /// | ||
| 265 | /// Arguments: | ||
| 266 | /// new_node: Pointer to the new node to insert. | ||
| 267 | pub fn append(list: *Self, new_node: *Node) void { | ||
| 268 | if (list.last) |last| { | ||
| 269 | // Insert after last. | ||
| 270 | list.insertAfter(last, new_node); | ||
| 271 | } else { | ||
| 272 | // Empty list. | ||
| 273 | list.prepend(new_node); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Insert a new node at the beginning of the list. | ||
| 278 | /// | ||
| 279 | /// Arguments: | ||
| 280 | /// new_node: Pointer to the new node to insert. | ||
| 281 | pub fn prepend(list: *Self, new_node: *Node) void { | ||
| 282 | if (list.first) |first| { | ||
| 283 | // Insert before first. | ||
| 284 | list.insertBefore(first, new_node); | ||
| 285 | } else { | ||
| 286 | // Empty list. | ||
| 287 | list.first = new_node; | ||
| 288 | list.last = new_node; | ||
| 289 | new_node.prev = null; | ||
| 290 | new_node.next = null; | ||
| 291 | |||
| 292 | list.len = 1; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | /// Remove a node from the list. | ||
| 297 | /// | ||
| 298 | /// Arguments: | ||
| 299 | /// node: Pointer to the node to be removed. | ||
| 300 | pub fn remove(list: *Self, node: *Node) void { | ||
| 301 | if (node.prev) |prev_node| { | ||
| 302 | // Intermediate node. | ||
| 303 | prev_node.next = node.next; | ||
| 304 | } else { | ||
| 305 | // First element of the list. | ||
| 306 | list.first = node.next; | ||
| 307 | } | ||
| 308 | |||
| 309 | if (node.next) |next_node| { | ||
| 310 | // Intermediate node. | ||
| 311 | next_node.prev = node.prev; | ||
| 312 | } else { | ||
| 313 | // Last element of the list. | ||
| 314 | list.last = node.prev; | ||
| 315 | } | ||
| 316 | |||
| 317 | list.len -= 1; | ||
| 318 | assert(list.len == 0 or (list.first != null and list.last != null)); | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Remove and return the last node in the list. | ||
| 322 | /// | ||
| 323 | /// Returns: | ||
| 324 | /// A pointer to the last node in the list. | ||
| 325 | pub fn pop(list: *Self) ?*Node { | ||
| 326 | const last = list.last orelse return null; | ||
| 327 | list.remove(last); | ||
| 328 | return last; | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Remove and return the first node in the list. | ||
| 332 | /// | ||
| 333 | /// Returns: | ||
| 334 | /// A pointer to the first node in the list. | ||
| 335 | pub fn popFirst(list: *Self) ?*Node { | ||
| 336 | const first = list.first orelse return null; | ||
| 337 | list.remove(first); | ||
| 338 | return first; | ||
| 339 | } | ||
| 340 | }; | ||
| 341 | } | ||
| 342 | |||
| 343 | test "basic DoublyLinkedList test" { | ||
| 344 | const L = DoublyLinkedList(u32); | ||
| 345 | var list = L{}; | ||
| 346 | |||
| 347 | var one = L.Node{ .data = 1 }; | ||
| 348 | var two = L.Node{ .data = 2 }; | ||
| 349 | var three = L.Node{ .data = 3 }; | ||
| 350 | var four = L.Node{ .data = 4 }; | ||
| 351 | var five = L.Node{ .data = 5 }; | ||
| 352 | |||
| 353 | list.append(&two); // {2} | ||
| 354 | list.append(&five); // {2, 5} | ||
| 355 | list.prepend(&one); // {1, 2, 5} | ||
| 356 | list.insertBefore(&five, &four); // {1, 2, 4, 5} | ||
| 357 | list.insertAfter(&two, &three); // {1, 2, 3, 4, 5} | ||
| 358 | |||
| 359 | // Traverse forwards. | ||
| 360 | { | ||
| 361 | var it = list.first; | ||
| 362 | var index: u32 = 1; | ||
| 363 | while (it) |node| : (it = node.next) { | ||
| 364 | try testing.expect(node.data == index); | ||
| 365 | index += 1; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | // Traverse backwards. | ||
| 370 | { | ||
| 371 | var it = list.last; | ||
| 372 | var index: u32 = 1; | ||
| 373 | while (it) |node| : (it = node.prev) { | ||
| 374 | try testing.expect(node.data == (6 - index)); | ||
| 375 | index += 1; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | _ = list.popFirst(); // {2, 3, 4, 5} | ||
| 380 | _ = list.pop(); // {2, 3, 4} | ||
| 381 | list.remove(&three); // {2, 4} | ||
| 382 | |||
| 383 | try testing.expect(list.first.?.data == 2); | ||
| 384 | try testing.expect(list.last.?.data == 4); | ||
| 385 | try testing.expect(list.len == 2); | ||
| 386 | } | ||
| 387 | |||
| 388 | test "DoublyLinkedList concatenation" { | ||
| 389 | const L = DoublyLinkedList(u32); | ||
| 390 | var list1 = L{}; | ||
| 391 | var list2 = L{}; | ||
| 392 | |||
| 393 | var one = L.Node{ .data = 1 }; | ||
| 394 | var two = L.Node{ .data = 2 }; | ||
| 395 | var three = L.Node{ .data = 3 }; | ||
| 396 | var four = L.Node{ .data = 4 }; | ||
| 397 | var five = L.Node{ .data = 5 }; | ||
| 398 | |||
| 399 | list1.append(&one); | ||
| 400 | list1.append(&two); | ||
| 401 | list2.append(&three); | ||
| 402 | list2.append(&four); | ||
| 403 | list2.append(&five); | ||
| 404 | |||
| 405 | list1.concatByMoving(&list2); | ||
| 406 | |||
| 407 | try testing.expect(list1.last == &five); | ||
| 408 | try testing.expect(list1.len == 5); | ||
| 409 | try testing.expect(list2.first == null); | ||
| 410 | try testing.expect(list2.last == null); | ||
| 411 | try testing.expect(list2.len == 0); | ||
| 412 | |||
| 413 | // Traverse forwards. | ||
| 414 | { | ||
| 415 | var it = list1.first; | ||
| 416 | var index: u32 = 1; | ||
| 417 | while (it) |node| : (it = node.next) { | ||
| 418 | try testing.expect(node.data == index); | ||
| 419 | index += 1; | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | // Traverse backwards. | ||
| 424 | { | ||
| 425 | var it = list1.last; | ||
| 426 | var index: u32 = 1; | ||
| 427 | while (it) |node| : (it = node.prev) { | ||
| 428 | try testing.expect(node.data == (6 - index)); | ||
| 429 | index += 1; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | // Swap them back, this verifies that concatenating to an empty list works. | ||
| 434 | list2.concatByMoving(&list1); | ||
| 435 | |||
| 436 | // Traverse forwards. | ||
| 437 | { | ||
| 438 | var it = list2.first; | ||
| 439 | var index: u32 = 1; | ||
| 440 | while (it) |node| : (it = node.next) { | ||
| 441 | try testing.expect(node.data == index); | ||
| 442 | index += 1; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | // Traverse backwards. | ||
| 447 | { | ||
| 448 | var it = list2.last; | ||
| 449 | var index: u32 = 1; | ||
| 450 | while (it) |node| : (it = node.prev) { | ||
| 451 | try testing.expect(node.data == (6 - index)); | ||
| 452 | index += 1; | ||
| 453 | } | ||
| 454 | } | ||
| 455 | } | ||
lib/std/std.zig+2-2| ... | @@ -16,7 +16,7 @@ pub const BufMap = @import("buf_map.zig").BufMap; | ... | @@ -16,7 +16,7 @@ pub const BufMap = @import("buf_map.zig").BufMap; |
| 16 | pub const BufSet = @import("buf_set.zig").BufSet; | 16 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 17 | pub const StaticStringMap = static_string_map.StaticStringMap; | 17 | pub const StaticStringMap = static_string_map.StaticStringMap; |
| 18 | pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql; | 18 | pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql; |
| 19 | pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList; | 19 | pub const DoublyLinkedList = @import("DoublyLinkedList.zig"); |
| 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; | 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; |
| 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; | 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; |
| ... | @@ -33,7 +33,7 @@ pub const Random = @import("Random.zig"); | ... | @@ -33,7 +33,7 @@ pub const Random = @import("Random.zig"); |
| 33 | pub const RingBuffer = @import("RingBuffer.zig"); | 33 | pub const RingBuffer = @import("RingBuffer.zig"); |
| 34 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | 34 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); | 35 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| 36 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 36 | pub const SinglyLinkedList = @import("SinglyLinkedList.zig"); |
| 37 | pub const StaticBitSet = bit_set.StaticBitSet; | 37 | pub const StaticBitSet = bit_set.StaticBitSet; |
| 38 | pub const StringHashMap = hash_map.StringHashMap; | 38 | pub const StringHashMap = hash_map.StringHashMap; |
| 39 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; | 39 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; |
src/Package/Fetch/git.zig+17-10| ... | @@ -473,14 +473,18 @@ const Object = struct { | ... | @@ -473,14 +473,18 @@ const Object = struct { |
| 473 | /// objects remaining in the cache will be freed when the cache itself is freed. | 473 | /// objects remaining in the cache will be freed when the cache itself is freed. |
| 474 | const ObjectCache = struct { | 474 | const ObjectCache = struct { |
| 475 | objects: std.AutoHashMapUnmanaged(u64, CacheEntry) = .empty, | 475 | objects: std.AutoHashMapUnmanaged(u64, CacheEntry) = .empty, |
| 476 | lru_nodes: LruList = .{}, | 476 | lru_nodes: std.DoublyLinkedList = .{}, |
| 477 | lru_nodes_len: usize = 0, | ||
| 477 | byte_size: usize = 0, | 478 | byte_size: usize = 0, |
| 478 | 479 | ||
| 479 | const max_byte_size = 128 * 1024 * 1024; // 128MiB | 480 | const max_byte_size = 128 * 1024 * 1024; // 128MiB |
| 480 | /// A list of offsets stored in the cache, with the most recently used | 481 | /// A list of offsets stored in the cache, with the most recently used |
| 481 | /// entries at the end. | 482 | /// entries at the end. |
| 482 | const LruList = std.DoublyLinkedList(u64); | 483 | const LruListNode = struct { |
| 483 | const CacheEntry = struct { object: Object, lru_node: *LruList.Node }; | 484 | data: u64, |
| 485 | node: std.DoublyLinkedList.Node, | ||
| 486 | }; | ||
| 487 | const CacheEntry = struct { object: Object, lru_node: *LruListNode }; | ||
| 484 | 488 | ||
| 485 | fn deinit(cache: *ObjectCache, allocator: Allocator) void { | 489 | fn deinit(cache: *ObjectCache, allocator: Allocator) void { |
| 486 | var object_iterator = cache.objects.iterator(); | 490 | var object_iterator = cache.objects.iterator(); |
| ... | @@ -496,8 +500,8 @@ const ObjectCache = struct { | ... | @@ -496,8 +500,8 @@ const ObjectCache = struct { |
| 496 | /// position if it is present. | 500 | /// position if it is present. |
| 497 | fn get(cache: *ObjectCache, offset: u64) ?Object { | 501 | fn get(cache: *ObjectCache, offset: u64) ?Object { |
| 498 | if (cache.objects.get(offset)) |entry| { | 502 | if (cache.objects.get(offset)) |entry| { |
| 499 | cache.lru_nodes.remove(entry.lru_node); | 503 | cache.lru_nodes.remove(&entry.lru_node.node); |
| 500 | cache.lru_nodes.append(entry.lru_node); | 504 | cache.lru_nodes.append(&entry.lru_node.node); |
| 501 | return entry.object; | 505 | return entry.object; |
| 502 | } else { | 506 | } else { |
| 503 | return null; | 507 | return null; |
| ... | @@ -510,26 +514,29 @@ const ObjectCache = struct { | ... | @@ -510,26 +514,29 @@ const ObjectCache = struct { |
| 510 | /// will not be evicted before the next call to `put` or `deinit` even if | 514 | /// will not be evicted before the next call to `put` or `deinit` even if |
| 511 | /// it exceeds the maximum cache size. | 515 | /// it exceeds the maximum cache size. |
| 512 | fn put(cache: *ObjectCache, allocator: Allocator, offset: u64, object: Object) !void { | 516 | fn put(cache: *ObjectCache, allocator: Allocator, offset: u64, object: Object) !void { |
| 513 | const lru_node = try allocator.create(LruList.Node); | 517 | const lru_node = try allocator.create(LruListNode); |
| 514 | errdefer allocator.destroy(lru_node); | 518 | errdefer allocator.destroy(lru_node); |
| 515 | lru_node.data = offset; | 519 | lru_node.data = offset; |
| 516 | 520 | ||
| 517 | const gop = try cache.objects.getOrPut(allocator, offset); | 521 | const gop = try cache.objects.getOrPut(allocator, offset); |
| 518 | if (gop.found_existing) { | 522 | if (gop.found_existing) { |
| 519 | cache.byte_size -= gop.value_ptr.object.data.len; | 523 | cache.byte_size -= gop.value_ptr.object.data.len; |
| 520 | cache.lru_nodes.remove(gop.value_ptr.lru_node); | 524 | cache.lru_nodes.remove(&gop.value_ptr.lru_node.node); |
| 525 | cache.lru_nodes_len -= 1; | ||
| 521 | allocator.destroy(gop.value_ptr.lru_node); | 526 | allocator.destroy(gop.value_ptr.lru_node); |
| 522 | allocator.free(gop.value_ptr.object.data); | 527 | allocator.free(gop.value_ptr.object.data); |
| 523 | } | 528 | } |
| 524 | gop.value_ptr.* = .{ .object = object, .lru_node = lru_node }; | 529 | gop.value_ptr.* = .{ .object = object, .lru_node = lru_node }; |
| 525 | cache.byte_size += object.data.len; | 530 | cache.byte_size += object.data.len; |
| 526 | cache.lru_nodes.append(lru_node); | 531 | cache.lru_nodes.append(&lru_node.node); |
| 532 | cache.lru_nodes_len += 1; | ||
| 527 | 533 | ||
| 528 | while (cache.byte_size > max_byte_size and cache.lru_nodes.len > 1) { | 534 | while (cache.byte_size > max_byte_size and cache.lru_nodes_len > 1) { |
| 529 | // The > 1 check is to make sure that we don't evict the most | 535 | // The > 1 check is to make sure that we don't evict the most |
| 530 | // recently added node, even if it by itself happens to exceed the | 536 | // recently added node, even if it by itself happens to exceed the |
| 531 | // maximum size of the cache. | 537 | // maximum size of the cache. |
| 532 | const evict_node = cache.lru_nodes.popFirst().?; | 538 | const evict_node: *LruListNode = @alignCast(@fieldParentPtr("node", cache.lru_nodes.popFirst().?)); |
| 539 | cache.lru_nodes_len -= 1; | ||
| 533 | const evict_offset = evict_node.data; | 540 | const evict_offset = evict_node.data; |
| 534 | allocator.destroy(evict_node); | 541 | allocator.destroy(evict_node); |
| 535 | const evict_object = cache.objects.get(evict_offset).?.object; | 542 | const evict_object = cache.objects.get(evict_offset).?.object; |