| ... | @@ -4,8 +4,18 @@ const assert = debug.assert; | ... | @@ -4,8 +4,18 @@ const assert = debug.assert; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const Allocator = mem.Allocator; | 5 | const Allocator = mem.Allocator; |
| 6 | | 6 | |
| 7 | /// Generic doubly linked list. | 7 | /// Generic non-intrusive doubly linked list. |
| 8 | pub fn LinkedList(comptime T: type) -> type { | 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 | return struct { | 19 | return struct { |
| 10 | const Self = this; | 20 | const Self = this; |
| 11 | | 21 | |
| ... | @@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type { | ... | @@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type { |
| 15 | next: ?&Node, | 25 | next: ?&Node, |
| 16 | data: T, | 26 | data: T, |
| 17 | | 27 | |
| 18 | pub fn init(data: &const T) -> Node { | 28 | pub fn init(value: &const T) -> Node { |
| 19 | return Node { | 29 | return Node { |
| 20 | .prev = null, | 30 | .prev = null, |
| 21 | .next = null, | 31 | .next = null, |
| 22 | .data = *data, | 32 | .data = *value, |
| 23 | }; | 33 | }; |
| 24 | } | 34 | } |
| | 35 | |
| | 36 | pub fn initIntrusive() -> Node { |
| | 37 | // TODO: when #678 is solved this can become `init`. |
| | 38 | return Node.init({}); |
| | 39 | } |
| | 40 | |
| | 41 | pub fn toData(node: &Node) -> &ParentType { |
| | 42 | comptime assert(isIntrusive()); |
| | 43 | return @fieldParentPtr(ParentType, field_name, node); |
| | 44 | } |
| 25 | }; | 45 | }; |
| 26 | | 46 | |
| 27 | first: ?&Node, | 47 | first: ?&Node, |
| ... | @@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type { | ... | @@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type { |
| 40 | }; | 60 | }; |
| 41 | } | 61 | } |
| 42 | | 62 | |
| | 63 | fn isIntrusive() -> bool { |
| | 64 | return ParentType != void or field_name.len != 0; |
| | 65 | } |
| | 66 | |
| 43 | /// Insert a new node after an existing one. | 67 | /// Insert a new node after an existing one. |
| 44 | /// | 68 | /// |
| 45 | /// Arguments: | 69 | /// Arguments: |
| ... | @@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type { | ... | @@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type { |
| 167 | /// Returns: | 191 | /// Returns: |
| 168 | /// A pointer to the new node. | 192 | /// A pointer to the new node. |
| 169 | pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node { | 193 | pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node { |
| | 194 | comptime assert(!isIntrusive()); |
| 170 | return allocator.create(Node); | 195 | return allocator.create(Node); |
| 171 | } | 196 | } |
| 172 | | 197 | |
| ... | @@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type { | ... | @@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type { |
| 176 | /// node: Pointer to the node to deallocate. | 201 | /// node: Pointer to the node to deallocate. |
| 177 | /// allocator: Dynamic memory allocator. | 202 | /// allocator: Dynamic memory allocator. |
| 178 | pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) { | 203 | pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) { |
| | 204 | comptime assert(!isIntrusive()); |
| 179 | allocator.destroy(node); | 205 | allocator.destroy(node); |
| 180 | } | 206 | } |
| 181 | | 207 | |
| ... | @@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type { | ... | @@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type { |
| 188 | /// Returns: | 214 | /// Returns: |
| 189 | /// A pointer to the new node. | 215 | /// A pointer to the new node. |
| 190 | pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node { | 216 | pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node { |
| | 217 | comptime assert(!isIntrusive()); |
| 191 | var node = try list.allocateNode(allocator); | 218 | var node = try list.allocateNode(allocator); |
| 192 | *node = Node.init(data); | 219 | *node = Node.init(data); |
| 193 | return node; | 220 | return node; |
| ... | @@ -199,11 +226,11 @@ test "basic linked list test" { | ... | @@ -199,11 +226,11 @@ test "basic linked list test" { |
| 199 | const allocator = debug.global_allocator; | 226 | const allocator = debug.global_allocator; |
| 200 | var list = LinkedList(u32).init(); | 227 | var list = LinkedList(u32).init(); |
| 201 | | 228 | |
| 202 | var one = list.createNode(1, allocator) catch unreachable; | 229 | var one = try list.createNode(1, allocator); |
| 203 | var two = list.createNode(2, allocator) catch unreachable; | 230 | var two = try list.createNode(2, allocator); |
| 204 | var three = list.createNode(3, allocator) catch unreachable; | 231 | var three = try list.createNode(3, allocator); |
| 205 | var four = list.createNode(4, allocator) catch unreachable; | 232 | var four = try list.createNode(4, allocator); |
| 206 | var five = list.createNode(5, allocator) catch unreachable; | 233 | var five = try list.createNode(5, allocator); |
| 207 | defer { | 234 | defer { |
| 208 | list.destroyNode(one, allocator); | 235 | list.destroyNode(one, allocator); |
| 209 | list.destroyNode(two, allocator); | 236 | list.destroyNode(two, allocator); |
| ... | @@ -246,3 +273,55 @@ test "basic linked list test" { | ... | @@ -246,3 +273,55 @@ test "basic linked list test" { |
| 246 | assert ((??list.last ).data == 4); | 273 | assert ((??list.last ).data == 4); |
| 247 | assert (list.len == 2); | 274 | assert (list.len == 2); |
| 248 | } | 275 | } |
| | 276 | |
| | 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 | } |