Improve type safe list API

- Add support for lists of wl.Resource/wl.Client
- Greatly simplify code
This commit is contained in:
Isaac Freund
2020-11-18 18:25:22 +01:00
parent ba49b2b6f9
commit f77821b1aa
2 changed files with 143 additions and 93 deletions
+80 -30
View File
@@ -1,43 +1,93 @@
const std = @import("std");
const wl = @import("wayland").server.wl;
const A = struct {
list: wl.List(B, "link").Head,
};
const B = struct {
data: u32,
link: wl.List(B, "link").Link = undefined,
link: wl.list.Link = undefined,
};
const C = struct {
data: u32,
link: wl.list.Link = undefined,
pub fn getLink(c: *C) *wl.list.Link {
return &c.link;
}
pub fn fromLink(link: *wl.list.Link) *C {
return @fieldParentPtr(C, "link", link);
}
};
pub fn main() void {
var a: A = undefined;
a.list.init();
var one = B{ .data = 1 };
var two = B{ .data = 2 };
var three = B{ .data = 3 };
var four = B{ .data = 4 };
var five = B{ .data = 5 };
a.list.append(&one.link);
a.list.append(&two.link);
a.list.append(&three.link);
a.list.append(&four.link);
a.list.append(&five.link);
{
std.debug.print("forward\n", .{});
var it = a.list.iterator(.forward);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
var a: wl.list.Head(B, "link") = undefined;
a.init();
var one = B{ .data = 1 };
var two = B{ .data = 2 };
var three = B{ .data = 3 };
var four = B{ .data = 4 };
var five = B{ .data = 5 };
std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
a.append(&one);
a.append(&two);
a.append(&three);
a.append(&four);
a.append(&five);
std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
{
std.debug.print("forward\n", .{});
var it = a.iterator(.forward);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
}
three.link.remove();
a.prepend(&three);
{
std.debug.print("reverse moved three\n", .{});
var it = a.iterator(.reverse);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
}
}
three.link.remove();
a.list.prepend(&three.link);
{
std.debug.print("reverse moved three\n", .{});
var it = a.list.iterator(.reverse);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
var a: wl.list.Head(C, null) = undefined;
a.init();
var one = C{ .data = 1 };
var two = C{ .data = 2 };
var three = C{ .data = 3 };
var four = C{ .data = 4 };
var five = C{ .data = 5 };
std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
a.append(&one);
a.append(&two);
a.append(&three);
a.append(&four);
a.append(&five);
std.debug.print("length: {} empty: {}\n", .{ a.length(), a.empty() });
{
std.debug.print("forward\n", .{});
var it = a.iterator(.forward);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
}
three.link.remove();
a.prepend(&three);
{
std.debug.print("reverse moved three\n", .{});
var it = a.iterator(.reverse);
while (it.next()) |b| std.debug.print("{}\n", .{b.data});
}
}
}
+63 -63
View File
@@ -132,10 +132,10 @@ pub const Client = opaque {
extern fn wl_client_flush(client: *Client) void;
pub const flush = wl_client_flush;
extern fn wl_client_get_link(client: *Client) *List;
extern fn wl_client_get_link(client: *Client) *list.Link;
pub const getLink = wl_client_get_link;
extern fn wl_client_from_link(link: *List) *Client;
extern fn wl_client_from_link(link: *list.Link) *Client;
pub const fromLink = wl_client_from_link;
const Credentials = struct {
@@ -283,12 +283,13 @@ pub const Resource = opaque {
extern fn wl_resource_get_id(resource: *Resource) u32;
pub const getId = wl_resource_get_id;
extern fn wl_resource_get_link(resource: *Resource) *List;
extern fn wl_resource_get_link(resource: *Resource) *list.Link;
pub const getLink = wl_resource_get_link;
extern fn wl_resource_from_link(link: *List) *Resource;
extern fn wl_resource_from_link(link: *list.Link) *Resource;
pub const fromLink = wl_resource_from_link;
// TODO
extern fn wl_resource_find_for_client(list: *List, client: *Client) ?*Resource;
pub const findForClient = wl_resource_find_for_client;
@@ -334,91 +335,92 @@ pub const ProtocolLogger = opaque {
pub const destroy = wl_protocol_logger_destroy;
};
pub fn List(comptime T: type, comptime link_field: []const u8) type {
return extern struct {
const Self = @This();
pub const list = struct {
pub const Link = extern struct {
prev: ?*Link,
next: ?*Link,
prev: ?*Self,
next: ?*Self,
pub fn insertAfter(link: *Link, other: *Link) void {
other.prev = link;
other.next = link.next;
link.next = other;
other.next.?.prev = other;
}
/// This has the same ABI as wl_list
pub const Head = extern struct {
list: Self,
pub fn remove(link: *Link) void {
link.prev.?.next = link.next;
link.next.?.prev = link.prev;
link.* = .{ .prev = null, .next = null };
}
};
pub fn init(head: *Head) void {
head.* = .{ .list = .{ .prev = &head.list, .next = &head.list } };
/// This has the same ABI as wl.list.Link/wl_list. If link_field is null, then
/// T.getLink()/T.fromLink() will be used. This allows for compatiability
/// with wl.Client and wl.Resource
pub fn Head(comptime T: type, comptime link_field: ?[]const u8) type {
return extern struct {
const Self = @This();
link: Link,
pub fn init(head: *Self) void {
head.* = .{ .link = .{ .prev = &head.link, .next = &head.link } };
}
pub fn prepend(head: *Head, link: *Link) void {
@fieldParentPtr(Link, "list", &head.list).insertAfter(link);
pub fn prepend(head: *Self, elem: *T) void {
const link = if (link_field) |f| &@field(elem, f) else elem.getLink();
head.link.insertAfter(link);
}
pub fn append(head: *Head, link: *Link) void {
@fieldParentPtr(Link, "list", head.list.prev.?).insertAfter(link);
pub fn append(head: *Self, elem: *T) void {
const link = if (link_field) |f| &@field(elem, f) else elem.getLink();
head.link.prev.?.insertAfter(link);
}
pub fn length(head: *const Head) usize {
pub fn length(head: *const Self) usize {
var count: usize = 0;
var current = head.list.next;
while (current != head.list) : (current = current.next) {
var current = head.link.next.?;
while (current != &head.link) : (current = current.next.?) {
count += 1;
}
return count;
}
pub fn empty(head: *const Head) bool {
return head.list.next == head.list;
pub fn empty(head: *const Self) bool {
return head.link.next == &head.link;
}
pub fn insertList(head: *Head, other: *Head) void {
pub fn insertList(head: *Self, other: *Self) void {
if (other.empty()) return;
other.list.next.?.prev = head.list;
other.list.prev.?.next = head.list.next;
head.list.next.?.prev = other.list.prev;
head.list.next = other.list.next;
other.link.next.?.prev = head.link;
other.link.prev.?.next = head.link.next;
head.link.next.?.prev = other.link.prev;
head.link.next = other.link.next;
}
const Direction = enum { forward, reverse };
fn Iterator(comptime direction: Direction) type {
return struct {
head: *Head,
current: *Self,
head: *Link,
current: *Link,
pub fn next(it: *@This()) ?*T {
it.current = switch (direction) {
.forward => it.current.next.?,
.reverse => it.current.prev.?,
};
if (it.current == &it.head.list) return null;
return @fieldParentPtr(T, link_field, @fieldParentPtr(Link, "list", it.current));
if (it.current == it.head) return null;
return if (link_field) |f| @fieldParentPtr(T, f, it.current) else T.fromLink(it.current);
}
};
}
pub fn iterator(head: *Head, comptime direction: Direction) Iterator(direction) {
return .{ .head = head, .current = &head.list };
pub fn iterator(head: *Self, comptime direction: Direction) Iterator(direction) {
return .{ .head = &head.link, .current = &head.link };
}
};
/// This has the same ABI as wl_list
pub const Link = extern struct {
list: Self,
pub fn insertAfter(link: *Link, other: *Link) void {
other.list.prev = &link.list;
other.list.next = link.list.next;
link.list.next = &other.list;
other.list.next.?.prev = &other.list;
}
pub fn remove(link: *Link) void {
link.list.prev.?.next = link.list.next;
link.list.next.?.prev = link.list.prev;
link.* = .{ .list = .{ .prev = null, .next = null } };
}
};
};
}
}
};
pub fn Listener(comptime T: type) type {
return extern struct {
@@ -429,7 +431,7 @@ pub fn Listener(comptime T: type) type {
else
fn (listener: *Self, data: T) void;
link: List(Self, "link").Link,
link: list.Link,
notify: fn (listener: *Self, data: ?*c_void) callconv(.C) void,
pub fn setNotify(self: *Self, comptime notify: NotifyFn) void {
@@ -453,14 +455,14 @@ pub fn Signal(comptime T: type) type {
return extern struct {
const Self = @This();
listener_list: List(Listener(T), "link").Head,
listener_list: list.Head(Listener(T), "link"),
pub fn init(signal: *Self) void {
signal.listener_list.init();
}
pub fn add(signal: *Self, listener: *Listener(T)) void {
signal.listener_list.append(&listener.link);
signal.listener_list.append(listener);
}
pub fn get(signal: *Self, notify: @TypeOf(Listener(T).notify)) ?*Listener(T) {
@@ -488,15 +490,13 @@ pub fn Signal(comptime T: type) type {
/// whatever the last element was when iteration started.
fn emitInner(signal: *Self, data: ?*c_void) void {
var cursor: Listener(T) = undefined;
signal.listener_list.prepend(&cursor.link);
signal.listener_list.prepend(&cursor);
var end: Listener(T) = undefined;
signal.listener_list.append(&end.link);
signal.listener_list.append(&end);
while (cursor.link.list.next != &end.link.list) {
// this is a little ugly, using @typeInfo here works around
// what I assume to be a stage1 compiler bug.
const pos = @ptrCast(*@typeInfo(Listener(T)).Struct.fields[0].field_type, cursor.link.list.next.?);
while (cursor.link.next != &end.link) {
const pos = cursor.link.next.?;
const listener = @fieldParentPtr(Listener(T), "link", pos);
cursor.link.remove();