mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Merge 3ae7876a50 into e4be00f949
This commit is contained in:
commit
a35b113def
3 changed files with 369 additions and 0 deletions
|
|
@ -283,3 +283,242 @@ test "concatenation" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// implements a simple intrusive doubly linked list with a "data" field alongside
|
||||||
|
/// "node" field. This hides @fieldParentPtr complexity and adds type safety for the
|
||||||
|
/// simple case. If you need more advanced cases, for example an object being a member of
|
||||||
|
/// multiple intrusive lists, you should use DoublyLinkedList directly.
|
||||||
|
///
|
||||||
|
/// note that the signatures on the member functions of the generated datastructure take
|
||||||
|
/// pointers to the item, not the node.
|
||||||
|
pub fn Simple(T: type) type {
|
||||||
|
return struct {
|
||||||
|
const SimpleLinkedList = @This();
|
||||||
|
wrapped: DoublyLinkedList = .{},
|
||||||
|
|
||||||
|
pub const Item = struct {
|
||||||
|
data: T,
|
||||||
|
node: Node = .{},
|
||||||
|
|
||||||
|
pub fn next(item: *Item) ?*Item {
|
||||||
|
return @fieldParentPtr("node", item.node.next orelse return null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev(item: *Item) ?*Item {
|
||||||
|
return @fieldParentPtr("node", item.node.prev orelse return null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn append(list: *SimpleLinkedList, new_item: *Item) void {
|
||||||
|
list.wrapped.append(&new_item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insertAfter(list: *SimpleLinkedList, existing_item: *Item, new_item: *Item) void {
|
||||||
|
list.wrapped.insertAfter(&existing_item.node, &new_item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend(list: *SimpleLinkedList, new_item: *Item) void {
|
||||||
|
list.wrapped.prepend(&new_item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insertBefore(list: *SimpleLinkedList, existing_item: *Item, new_item: *Item) void {
|
||||||
|
list.wrapped.insertBefore(&existing_item.node, &new_item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concatByMoving(list: *SimpleLinkedList, other_list: *SimpleLinkedList) void {
|
||||||
|
list.wrapped.concatByMoving(&other_list.wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a node from the list.
|
||||||
|
pub fn remove(list: *SimpleLinkedList, item: *Item) void {
|
||||||
|
list.wrapped.remove(&item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove and return the last node in the list.
|
||||||
|
pub fn pop(list: *SimpleLinkedList) ?*Item {
|
||||||
|
const poppednode = (list.wrapped.pop()) orelse return null;
|
||||||
|
return @fieldParentPtr("node", poppednode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove and return the first node in the list.
|
||||||
|
pub fn popFirst(list: *SimpleLinkedList) ?*Item {
|
||||||
|
const poppednode = (list.wrapped.popFirst()) orelse return null;
|
||||||
|
return @fieldParentPtr("node", poppednode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a Simple list, returns the item at position <index>.
|
||||||
|
/// If the list does not have that many elements, returns `null`.
|
||||||
|
///
|
||||||
|
/// This is a linear search through the list, consider avoiding this
|
||||||
|
/// operation, except for index == 0
|
||||||
|
pub fn at(list: *SimpleLinkedList, index: usize) ?*Item {
|
||||||
|
var thisnode = list.wrapped.first orelse return null;
|
||||||
|
var ctr: usize = index;
|
||||||
|
while (ctr > 0) : (ctr -= 1) {
|
||||||
|
thisnode = thisnode.next orelse return null;
|
||||||
|
}
|
||||||
|
return @fieldParentPtr("node", thisnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a Simple list, returns the item at position <len-index-1>.
|
||||||
|
/// Note the last element is at index "0". If the list does not have
|
||||||
|
/// that many elements, returns `null`.
|
||||||
|
///
|
||||||
|
/// This is a linear search through the list, consider avoiding this
|
||||||
|
/// operation, except for index == 0
|
||||||
|
pub fn fromEnd(list: *SimpleLinkedList, index: usize) ?*Item {
|
||||||
|
var thisnode = list.wrapped.last orelse return null;
|
||||||
|
var ctr: usize = index;
|
||||||
|
while (ctr > 0) : (ctr -= 1) {
|
||||||
|
thisnode = thisnode.prev orelse return null;
|
||||||
|
}
|
||||||
|
return @fieldParentPtr("node", thisnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all nodes, returning the count.
|
||||||
|
///
|
||||||
|
/// This operation is O(N). Consider tracking the length separately rather than
|
||||||
|
/// computing it.
|
||||||
|
pub fn len(list: SimpleLinkedList) usize {
|
||||||
|
return list.wrapped.len();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Simple DLL basics" {
|
||||||
|
const List = Simple(u32);
|
||||||
|
const Item = List.Item;
|
||||||
|
var list: List = .{};
|
||||||
|
|
||||||
|
var one: Item = .{ .data = 1 };
|
||||||
|
var two: Item = .{ .data = 2 };
|
||||||
|
var three: Item = .{ .data = 3 };
|
||||||
|
var four: Item = .{ .data = 4 };
|
||||||
|
var five: Item = .{ .data = 5 };
|
||||||
|
|
||||||
|
list.append(&two); // {2}
|
||||||
|
list.append(&five); // {2, 5}
|
||||||
|
list.prepend(&one); // {1, 2, 5}
|
||||||
|
list.insertBefore(&five, &four); // {1, 2, 4, 5}
|
||||||
|
list.insertAfter(&two, &three); // {1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
// Traverse forwards.
|
||||||
|
{
|
||||||
|
var it = list.wrapped.first;
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |node| : (it = node.next) {
|
||||||
|
const l: *Item = @fieldParentPtr("node", node);
|
||||||
|
try testing.expect(l.data == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse forward, using item datastructures
|
||||||
|
{
|
||||||
|
var it = list.at(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.next()) {
|
||||||
|
try testing.expect(item.data == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse backwards.
|
||||||
|
{
|
||||||
|
var it = list.wrapped.last;
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |node| : (it = node.prev) {
|
||||||
|
const l: *Item = @fieldParentPtr("node", node);
|
||||||
|
try testing.expect(l.data == (6 - index));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse backwards, using item datastructures
|
||||||
|
{
|
||||||
|
var it = list.fromEnd(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.prev()) {
|
||||||
|
try testing.expect(item.data == (6 - index));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = list.popFirst(); // {2, 3, 4, 5}
|
||||||
|
_ = list.pop(); // {2, 3, 4}
|
||||||
|
list.remove(&three); // {2, 4}
|
||||||
|
|
||||||
|
try testing.expect(list.at(0).?.data == 2);
|
||||||
|
try testing.expect(list.fromEnd(0).?.data == 4);
|
||||||
|
try testing.expect(list.len() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Simple DLL concatenation" {
|
||||||
|
const List = Simple(u32);
|
||||||
|
const Item = List.Item;
|
||||||
|
var list1: List = .{};
|
||||||
|
var list2: List = .{};
|
||||||
|
|
||||||
|
var one: Item = .{ .data = 1 };
|
||||||
|
var two: Item = .{ .data = 2 };
|
||||||
|
var three: Item = .{ .data = 3 };
|
||||||
|
var four: Item = .{ .data = 4 };
|
||||||
|
var five: Item = .{ .data = 5 };
|
||||||
|
|
||||||
|
list1.append(&one);
|
||||||
|
list1.append(&two);
|
||||||
|
list2.append(&three);
|
||||||
|
list2.append(&four);
|
||||||
|
list2.append(&five);
|
||||||
|
|
||||||
|
list1.concatByMoving(&list2);
|
||||||
|
|
||||||
|
try testing.expect(list1.wrapped.last == &five.node);
|
||||||
|
try testing.expect(list1.len() == 5);
|
||||||
|
try testing.expect(list2.wrapped.first == null);
|
||||||
|
try testing.expect(list2.wrapped.last == null);
|
||||||
|
try testing.expect(list2.len() == 0);
|
||||||
|
|
||||||
|
// Traverse forwards.
|
||||||
|
{
|
||||||
|
var it = list1.at(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.next()) {
|
||||||
|
try testing.expect(item.data == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse backwards.
|
||||||
|
{
|
||||||
|
var it = list1.fromEnd(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.prev()) {
|
||||||
|
try testing.expect(item.data == (6 - index));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap them back, this verifies that concatenating to an empty list works.
|
||||||
|
list2.concatByMoving(&list1);
|
||||||
|
|
||||||
|
// Traverse forwards.
|
||||||
|
{
|
||||||
|
var it = list2.at(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.next()) {
|
||||||
|
try testing.expect(item.data == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse backwards.
|
||||||
|
{
|
||||||
|
var it = list2.fromEnd(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.prev()) {
|
||||||
|
try testing.expect(item.data == (6 - index));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,3 +166,131 @@ test "basics" {
|
||||||
try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?.next.?)).data == 2);
|
try testing.expect(@as(*L, @fieldParentPtr("node", list.first.?.next.?)).data == 2);
|
||||||
try testing.expect(list.first.?.next.?.next == null);
|
try testing.expect(list.first.?.next.?.next == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// implements a simple intrusive singly linked list with a "data" field alongside
|
||||||
|
/// "node" field. This hides @fieldParentPtr complexity and adds type safety for the
|
||||||
|
/// simple case. If you need more advanced cases, for example an object being a member of
|
||||||
|
/// multiple intrusive lists, you should use SinglyLinkedList directly.
|
||||||
|
///
|
||||||
|
/// note that the signatures on the member functions of the generated datastructure take
|
||||||
|
/// pointers to the item, not the node.
|
||||||
|
pub fn Simple(T: type) type {
|
||||||
|
return struct {
|
||||||
|
const SimpleLinkedList = @This();
|
||||||
|
wrapped: SinglyLinkedList = .{},
|
||||||
|
|
||||||
|
pub const Item = struct {
|
||||||
|
data: T,
|
||||||
|
node: Node = .{},
|
||||||
|
|
||||||
|
pub fn next(item: *Item) ?*Item {
|
||||||
|
return @fieldParentPtr("node", item.node.next orelse return null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insertAfter(item: *Item, new_item: *Item) void {
|
||||||
|
item.node.insertAfter(&new_item.node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn prepend(list: *SimpleLinkedList, new_item: *Item) void {
|
||||||
|
list.wrapped.prepend(&new_item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(list: *SimpleLinkedList, item: *Item) void {
|
||||||
|
list.wrapped.remove(&item.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove and return the first node in the list.
|
||||||
|
pub fn popFirst(list: *SimpleLinkedList) ?*Item {
|
||||||
|
const poppednode = (list.wrapped.popFirst()) orelse return null;
|
||||||
|
return @fieldParentPtr("node", poppednode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a Simple list, returns the item at position <index>.
|
||||||
|
/// If the list does not have that many elements, returns `null`.
|
||||||
|
///
|
||||||
|
/// This is a linear search through the list, consider avoiding this
|
||||||
|
/// operation, except for index == 0
|
||||||
|
pub fn at(list: *SimpleLinkedList, index: usize) ?*Item {
|
||||||
|
var thisnode = list.wrapped.first orelse return null;
|
||||||
|
var ctr: usize = index;
|
||||||
|
while (ctr > 0) : (ctr -= 1) {
|
||||||
|
thisnode = thisnode.next orelse return null;
|
||||||
|
}
|
||||||
|
return @fieldParentPtr("node", thisnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all nodes, returning the count.
|
||||||
|
///
|
||||||
|
/// This operation is O(N). Consider tracking the length separately rather than
|
||||||
|
/// computing it.
|
||||||
|
pub fn len(list: SimpleLinkedList) usize {
|
||||||
|
return list.wrapped.len();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Simple singly linked list" {
|
||||||
|
const SimpleList = Simple(u32);
|
||||||
|
const L = SimpleList.Item;
|
||||||
|
|
||||||
|
var list: SimpleList = .{};
|
||||||
|
|
||||||
|
try testing.expect(list.len() == 0);
|
||||||
|
|
||||||
|
var one: L = .{ .data = 1 };
|
||||||
|
var two: L = .{ .data = 2 };
|
||||||
|
var three: L = .{ .data = 3 };
|
||||||
|
var four: L = .{ .data = 4 };
|
||||||
|
var five: L = .{ .data = 5 };
|
||||||
|
|
||||||
|
try testing.expect(list.at(0) == null);
|
||||||
|
|
||||||
|
list.prepend(&two); // {2}
|
||||||
|
two.node.insertAfter(&five.node); // {2, 5}
|
||||||
|
list.prepend(&one); // {1, 2, 5}
|
||||||
|
two.insertAfter(&three); // {1, 2, 3, 5}
|
||||||
|
three.node.insertAfter(&four.node); // {1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
try testing.expect(list.len() == 5);
|
||||||
|
|
||||||
|
try testing.expect(list.at(0).?.data == 1);
|
||||||
|
try testing.expect(list.at(3).?.data == 4);
|
||||||
|
try testing.expect(list.at(7) == null);
|
||||||
|
|
||||||
|
try testing.expect(one.next().?.data == 2);
|
||||||
|
try testing.expect(two.next().?.data == 3);
|
||||||
|
try testing.expect(three.next().?.data == 4);
|
||||||
|
try testing.expect(four.next().?.data == 5);
|
||||||
|
try testing.expect(five.next() == null);
|
||||||
|
|
||||||
|
// Traverse forwards.
|
||||||
|
{
|
||||||
|
var it = list.at(0);
|
||||||
|
var index: u32 = 1;
|
||||||
|
while (it) |item| : (it = item.next()) {
|
||||||
|
try testing.expect(item.data == index);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = list.popFirst(); // {2, 3, 4, 5}
|
||||||
|
_ = list.remove(&five); // {2, 3, 4}
|
||||||
|
_ = two.node.removeNext(); // {2, 4}
|
||||||
|
|
||||||
|
try testing.expect(@as(*L, @fieldParentPtr("node", list.wrapped.first.?)).data == 2);
|
||||||
|
try testing.expect(list.at(0).?.data == 2);
|
||||||
|
try testing.expect(@as(*L, @fieldParentPtr("node", list.wrapped.first.?.next.?)).data == 4);
|
||||||
|
try testing.expect(list.at(1).?.data == 4);
|
||||||
|
|
||||||
|
try testing.expect(list.wrapped.first.?.next.?.next == null);
|
||||||
|
|
||||||
|
SinglyLinkedList.Node.reverse(&list.wrapped.first);
|
||||||
|
|
||||||
|
try testing.expect(@as(*L, @fieldParentPtr("node", list.wrapped.first.?)).data == 4);
|
||||||
|
try testing.expect(list.at(0).?.data == 4);
|
||||||
|
try testing.expect(@as(*L, @fieldParentPtr("node", list.wrapped.first.?.next.?)).data == 2);
|
||||||
|
try testing.expect(list.at(1).?.data == 2);
|
||||||
|
try testing.expect(list.wrapped.first.?.next.?.next == null);
|
||||||
|
try testing.expect(list.at(2) == null);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub const StaticStringMap = static_string_map.StaticStringMap;
|
||||||
pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql;
|
pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql;
|
||||||
pub const Deque = @import("deque.zig").Deque;
|
pub const Deque = @import("deque.zig").Deque;
|
||||||
pub const DoublyLinkedList = @import("DoublyLinkedList.zig");
|
pub const DoublyLinkedList = @import("DoublyLinkedList.zig");
|
||||||
|
pub const SimpleDoublyLinkedList = DoublyLinkedList.Simple;
|
||||||
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||||
pub const DynamicBitSet = bit_set.DynamicBitSet;
|
pub const DynamicBitSet = bit_set.DynamicBitSet;
|
||||||
pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
|
pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
|
||||||
|
|
@ -28,6 +29,7 @@ pub const Progress = @import("Progress.zig");
|
||||||
pub const Random = @import("Random.zig");
|
pub const Random = @import("Random.zig");
|
||||||
pub const SemanticVersion = @import("SemanticVersion.zig");
|
pub const SemanticVersion = @import("SemanticVersion.zig");
|
||||||
pub const SinglyLinkedList = @import("SinglyLinkedList.zig");
|
pub const SinglyLinkedList = @import("SinglyLinkedList.zig");
|
||||||
|
pub const SimpleSinglyLinkedList = SinglyLinkedList.Simple;
|
||||||
pub const StaticBitSet = bit_set.StaticBitSet;
|
pub const StaticBitSet = bit_set.StaticBitSet;
|
||||||
pub const StringHashMap = hash_map.StringHashMap;
|
pub const StringHashMap = hash_map.StringHashMap;
|
||||||
pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;
|
pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue