Merge pull request #680 from zig-lang/intrusiveLinkedList
Intrusive linked lists
This commit is contained in:
commit
d4f791cf6c
|
@ -4,8 +4,18 @@ const assert = debug.assert;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
/// Generic doubly linked list.
|
/// Generic non-intrusive doubly linked list.
|
||||||
pub fn LinkedList(comptime T: type) -> type {
|
pub fn LinkedList(comptime T: type) -> type {
|
||||||
|
return BaseLinkedList(T, void, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic intrusive doubly linked list.
|
||||||
|
pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) -> type {
|
||||||
|
return BaseLinkedList(void, ParentType, field_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic doubly linked list.
|
||||||
|
fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) -> type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = this;
|
const Self = this;
|
||||||
|
|
||||||
|
@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||||
next: ?&Node,
|
next: ?&Node,
|
||||||
data: T,
|
data: T,
|
||||||
|
|
||||||
pub fn init(data: &const T) -> Node {
|
pub fn init(value: &const T) -> Node {
|
||||||
return Node {
|
return Node {
|
||||||
.prev = null,
|
.prev = null,
|
||||||
.next = null,
|
.next = null,
|
||||||
.data = *data,
|
.data = *value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initIntrusive() -> Node {
|
||||||
|
// TODO: when #678 is solved this can become `init`.
|
||||||
|
return Node.init({});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toData(node: &Node) -> &ParentType {
|
||||||
|
comptime assert(isIntrusive());
|
||||||
|
return @fieldParentPtr(ParentType, field_name, node);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
first: ?&Node,
|
first: ?&Node,
|
||||||
|
@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn isIntrusive() -> bool {
|
||||||
|
return ParentType != void or field_name.len != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Insert a new node after an existing one.
|
/// Insert a new node after an existing one.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
|
@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// A pointer to the new node.
|
/// A pointer to the new node.
|
||||||
pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {
|
pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {
|
||||||
|
comptime assert(!isIntrusive());
|
||||||
return allocator.create(Node);
|
return allocator.create(Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||||
/// node: Pointer to the node to deallocate.
|
/// node: Pointer to the node to deallocate.
|
||||||
/// allocator: Dynamic memory allocator.
|
/// allocator: Dynamic memory allocator.
|
||||||
pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) {
|
pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) {
|
||||||
|
comptime assert(!isIntrusive());
|
||||||
allocator.destroy(node);
|
allocator.destroy(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type {
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// A pointer to the new node.
|
/// A pointer to the new node.
|
||||||
pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node {
|
pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node {
|
||||||
|
comptime assert(!isIntrusive());
|
||||||
var node = try list.allocateNode(allocator);
|
var node = try list.allocateNode(allocator);
|
||||||
*node = Node.init(data);
|
*node = Node.init(data);
|
||||||
return node;
|
return node;
|
||||||
|
@ -199,11 +226,11 @@ test "basic linked list test" {
|
||||||
const allocator = debug.global_allocator;
|
const allocator = debug.global_allocator;
|
||||||
var list = LinkedList(u32).init();
|
var list = LinkedList(u32).init();
|
||||||
|
|
||||||
var one = list.createNode(1, allocator) catch unreachable;
|
var one = try list.createNode(1, allocator);
|
||||||
var two = list.createNode(2, allocator) catch unreachable;
|
var two = try list.createNode(2, allocator);
|
||||||
var three = list.createNode(3, allocator) catch unreachable;
|
var three = try list.createNode(3, allocator);
|
||||||
var four = list.createNode(4, allocator) catch unreachable;
|
var four = try list.createNode(4, allocator);
|
||||||
var five = list.createNode(5, allocator) catch unreachable;
|
var five = try list.createNode(5, allocator);
|
||||||
defer {
|
defer {
|
||||||
list.destroyNode(one, allocator);
|
list.destroyNode(one, allocator);
|
||||||
list.destroyNode(two, allocator);
|
list.destroyNode(two, allocator);
|
||||||
|
@ -246,3 +273,55 @@ test "basic linked list test" {
|
||||||
assert ((??list.last ).data == 4);
|
assert ((??list.last ).data == 4);
|
||||||
assert (list.len == 2);
|
assert (list.len == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const link = "link";
|
||||||
|
const ElementList = IntrusiveLinkedList(Element, link);
|
||||||
|
const Element = struct {
|
||||||
|
value: u32,
|
||||||
|
link: IntrusiveLinkedList(Element, link).Node,
|
||||||
|
};
|
||||||
|
|
||||||
|
test "basic intrusive linked list test" {
|
||||||
|
const allocator = debug.global_allocator;
|
||||||
|
var list = ElementList.init();
|
||||||
|
|
||||||
|
var one = Element { .value = 1, .link = ElementList.Node.initIntrusive() };
|
||||||
|
var two = Element { .value = 2, .link = ElementList.Node.initIntrusive() };
|
||||||
|
var three = Element { .value = 3, .link = ElementList.Node.initIntrusive() };
|
||||||
|
var four = Element { .value = 4, .link = ElementList.Node.initIntrusive() };
|
||||||
|
var five = Element { .value = 5, .link = ElementList.Node.initIntrusive() };
|
||||||
|
|
||||||
|
list.append(&two.link); // {2}
|
||||||
|
list.append(&five.link); // {2, 5}
|
||||||
|
list.prepend(&one.link); // {1, 2, 5}
|
||||||
|
list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
|
||||||
|
list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
// Traverse forwards.
|
||||||
|
{
|
||||||
|
var it = list.first;
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |node| : (it = node.next) {
|
||||||
|
assert(node.toData().value == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse backwards.
|
||||||
|
{
|
||||||
|
var it = list.last;
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |node| : (it = node.prev) {
|
||||||
|
assert(node.toData().value == (6 - index));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = list.popFirst(); // {2, 3, 4, 5}
|
||||||
|
var last = list.pop(); // {2, 3, 4}
|
||||||
|
list.remove(&three.link); // {2, 4}
|
||||||
|
|
||||||
|
assert ((??list.first).toData().value == 2);
|
||||||
|
assert ((??list.last ).toData().value == 4);
|
||||||
|
assert (list.len == 2);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user