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