Update to zig version 0.9.0

This commit is contained in:
Isaac Freund
2021-12-23 18:11:10 +00:00
parent 38b5de89b3
commit 398059895f
12 changed files with 219 additions and 232 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# zig-wayland
Zig 0.8.0 bindings and protocol scanner for libwayland.
Zig 0.9 bindings and protocol scanner for libwayland.
## Usage
+29 -22
View File
@@ -23,14 +23,16 @@ pub fn build(b: *zbs.Builder) void {
}
const test_step = b.step("test", "Run the tests");
for ([_][]const u8{ "src/scanner.zig", "src/common_core.zig" }) |file| {
const t = b.addTest(file);
t.setTarget(target);
t.setBuildMode(mode);
{
const scanner_tests = b.addTest("src/scanner.zig");
scanner_tests.setTarget(target);
scanner_tests.setBuildMode(mode);
test_step.dependOn(&t.step);
scanner_tests.step.dependOn(&scanner.step);
scanner_tests.addPackage(scanner.getPkg());
test_step.dependOn(&scanner_tests.step);
}
{
const ref_all = b.addTest("src/ref_all.zig");
ref_all.setTarget(target);
@@ -53,9 +55,7 @@ pub const ScanProtocolsStep = struct {
builder: *zbs.Builder,
step: zbs.Step,
/// zig-cache/zig-wayland of the importing project
out_path: []const u8,
result: zbs.GeneratedFile,
/// Slice of absolute paths of protocol xml files to be scanned
protocol_paths: std.ArrayList([]const u8),
@@ -65,12 +65,8 @@ pub const ScanProtocolsStep = struct {
const self = ally.create(ScanProtocolsStep) catch unreachable;
self.* = .{
.builder = builder,
.step = zbs.Step.init(.Custom, "Scan Protocols", ally, make),
.out_path = fs.path.resolve(ally, &[_][]const u8{
builder.build_root,
builder.cache_root,
"zig-wayland",
}) catch unreachable,
.step = zbs.Step.init(.custom, "Scan Protocols", ally, make),
.result = .{ .step = &self.step, .path = null },
.protocol_paths = std.ArrayList([]const u8).init(ally),
};
return self;
@@ -102,9 +98,13 @@ pub const ScanProtocolsStep = struct {
), &std.ascii.spaces);
const wayland_xml = try fs.path.join(ally, &[_][]const u8{ wayland_dir, "wayland.xml" });
var root = try fs.cwd().openDir(self.builder.build_root, .{});
defer root.close();
try scanner.scan(root, self.out_path, wayland_xml, self.protocol_paths.items);
const out_path = try fs.path.join(ally, &[_][]const u8{ self.builder.cache_root, "zig-wayland" });
var root_dir = try fs.cwd().openDir(self.builder.build_root, .{});
defer root_dir.close();
var out_dir = try root_dir.makeOpenPath(out_path, .{});
defer out_dir.close();
try scanner.scan(root_dir, out_dir, wayland_xml, self.protocol_paths.items);
// Once https://github.com/ziglang/zig/issues/131 is implemented
// we can stop generating/linking C code.
@@ -113,19 +113,21 @@ pub const ScanProtocolsStep = struct {
&[_][]const u8{ "wayland-scanner", "private-code", path, self.getCodePath(path) },
);
}
self.result.path = try fs.path.join(ally, &[_][]const u8{ out_path, "wayland.zig" });
}
/// Add the necessary C source to the compilation unit.
/// Once https://github.com/ziglang/zig/issues/131 we can remove this.
pub fn addCSource(self: *ScanProtocolsStep, obj: *zbs.LibExeObjStep) void {
for (self.protocol_paths.items) |path|
for (self.protocol_paths.items) |path| {
obj.addCSourceFile(self.getCodePath(path), &[_][]const u8{"-std=c99"});
}
}
pub fn getPkg(self: *ScanProtocolsStep) zbs.Pkg {
const ally = self.builder.allocator;
return .{
.name = "wayland",
.path = fs.path.join(ally, &[_][]const u8{ self.out_path, "wayland.zig" }) catch unreachable,
.path = .{ .generated = &self.result },
};
}
@@ -135,6 +137,11 @@ pub const ScanProtocolsStep = struct {
const basename = fs.path.basename(xml_in_path);
const basename_no_ext = basename[0..(basename.len - 4)];
const code_filename = std.fmt.allocPrint(ally, "{s}-protocol.c", .{basename_no_ext}) catch unreachable;
return fs.path.join(ally, &[_][]const u8{ self.out_path, code_filename }) catch unreachable;
return fs.path.join(ally, &[_][]const u8{
self.builder.build_root,
self.builder.cache_root,
"zig-wayland",
code_filename,
}) catch unreachable;
}
};
+3 -3
View File
@@ -10,7 +10,7 @@ pub fn main() !void {
_ = try display.roundtrip();
}
fn listener(registry: *wl.Registry, event: wl.Registry.Event, data: *u32) void {
std.debug.warn("foo is {}\n", .{data.*});
std.debug.warn("event is {}\n", .{event});
fn listener(_: *wl.Registry, event: wl.Registry.Event, data: *u32) void {
std.debug.print("foo is {}\n", .{data.*});
std.debug.print("event is {}\n", .{event});
}
+2 -2
View File
@@ -32,10 +32,10 @@ pub fn main() anyerror!void {
signal2.emit(&foo2);
}
fn not(listen: *wl.Listener(void)) void {
fn not(_: *wl.Listener(void)) void {
std.debug.print("notified\n", .{});
}
fn not2(listen: *wl.Listener(*Foo), foo: *Foo) void {
fn not2(_: *wl.Listener(*Foo), foo: *Foo) void {
std.debug.print("{s}\n", .{foo.bar});
}
+1 -1
View File
@@ -24,7 +24,7 @@ fn listener(registry: *wl.Registry, event: wl.Registry.Event, running: *bool) vo
}
}
fn seatListener(seat: *wl.Seat, event: wl.Seat.Event, running: *bool) void {
fn seatListener(_: *wl.Seat, event: wl.Seat.Event, running: *bool) void {
switch (event) {
.capabilities => |data| {
std.debug.print("Seat capabilities\n Pointer {}\n Keyboard {}\n Touch {}\n", .{
+48 -48
View File
@@ -19,27 +19,27 @@ pub fn dispatch(display: *Display) !u32 {
const rc = wl_display_dispatch(display);
// poll(2), sendmsg(2), recvmsg(2), EOVERFLOW, E2BIG
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
os.EFAULT => unreachable,
os.EINTR => unreachable,
os.EINVAL => unreachable,
os.ENOMEM => error.SystemResources,
os.EACCES => error.AccessDenied,
os.EAGAIN => unreachable,
os.EALREADY => error.FastOpenAlreadyInProgress,
os.EBADF => unreachable,
os.ECONNRESET => error.ConnectionResetByPeer,
os.EDESTADDRREQ => unreachable,
os.EISCONN => unreachable,
os.EMSGSIZE => error.MessageTooBig,
os.ENOBUFS => error.SystemResources,
os.ENOTCONN => unreachable,
os.ENOTSOCK => unreachable,
os.EOPNOTSUPP => unreachable,
os.EPIPE => error.BrokenPipe,
os.ECONNREFUSED => error.ConnectionRefused,
os.EOVERFLOW => error.BufferOverflow,
os.E2BIG => error.BufferOverflow,
.SUCCESS => @intCast(u32, rc),
.FAULT => unreachable,
.INTR => unreachable,
.INVAL => unreachable,
.NOMEM => error.SystemResources,
.ACCES => error.AccessDenied,
.AGAIN => unreachable,
.ALREADY => error.FastOpenAlreadyInProgress,
.BADF => unreachable,
.CONNRESET => error.ConnectionResetByPeer,
.DESTADDRREQ => unreachable,
.ISCONN => unreachable,
.MSGSIZE => error.MessageTooBig,
.NOBUFS => error.SystemResources,
.NOTCONN => unreachable,
.NOTSOCK => unreachable,
.OPNOTSUPP => unreachable,
.PIPE => error.BrokenPipe,
.CONNREFUSED => error.ConnectionRefused,
.OVERFLOW => error.BufferOverflow,
.@"2BIG" => error.BufferOverflow,
else => |err| os.unexpectedErrno(err),
};
}
@@ -48,27 +48,27 @@ extern fn wl_display_dispatch_queue(display: *Display, queue: *client.wl.EventQu
pub fn dispatchQueue(display: *Display, queue: *client.wl.EventQueue) !u32 {
const rc = wl_display_dispatch_queue(display, queue);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
os.EFAULT => unreachable,
os.EINTR => unreachable,
os.EINVAL => unreachable,
os.ENOMEM => error.SystemResources,
os.EACCES => error.AccessDenied,
os.EAGAIN => unreachable,
os.EALREADY => error.FastOpenAlreadyInProgress,
os.EBADF => unreachable,
os.ECONNRESET => error.ConnectionResetByPeer,
os.EDESTADDRREQ => unreachable,
os.EISCONN => unreachable,
os.EMSGSIZE => error.MessageTooBig,
os.ENOBUFS => error.SystemResources,
os.ENOTCONN => unreachable,
os.ENOTSOCK => unreachable,
os.EOPNOTSUPP => unreachable,
os.EPIPE => error.BrokenPipe,
os.ECONNREFUSED => error.ConnectionRefused,
os.EOVERFLOW => error.BufferOverflow,
os.E2BIG => error.BufferOverflow,
.SUCCESS => @intCast(u32, rc),
.FAULT => unreachable,
.INTR => unreachable,
.INVAL => unreachable,
.NOMEM => error.SystemResources,
.ACCES => error.AccessDenied,
.AGAIN => unreachable,
.ALREADY => error.FastOpenAlreadyInProgress,
.BADF => unreachable,
.CONNRESET => error.ConnectionResetByPeer,
.DESTADDRREQ => unreachable,
.ISCONN => unreachable,
.MSGSIZE => error.MessageTooBig,
.NOBUFS => error.SystemResources,
.NOTCONN => unreachable,
.NOTSOCK => unreachable,
.OPNOTSUPP => unreachable,
.PIPE => error.BrokenPipe,
.CONNREFUSED => error.ConnectionRefused,
.OVERFLOW => error.BufferOverflow,
.@"2BIG" => error.BufferOverflow,
else => |err| os.unexpectedErrno(err),
};
}
@@ -77,7 +77,7 @@ extern fn wl_display_dispatch_pending(display: *Display) c_int;
pub fn dispatchPending(display: *Display) !u32 {
const rc = wl_display_dispatch_pending(display);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
.SUCCESS => @intCast(u32, rc),
// TODO
else => |err| os.unexpectedErrno(err),
};
@@ -87,7 +87,7 @@ extern fn wl_display_dispatch_queue_pending(display: *Display, queue: *client.wl
pub fn dispatchQueuePending(display: *Display, queue: *client.wl.EventQueue) !u32 {
const rc = wl_display_dispatch_queue_pending(display, queue);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
.SUCCESS => @intCast(u32, rc),
// TODO
else => |err| os.unexpectedErrno(err),
};
@@ -97,7 +97,7 @@ extern fn wl_display_roundtrip(display: *Display) c_int;
pub fn roundtrip(display: *Display) !u32 {
const rc = wl_display_roundtrip(display);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
.SUCCESS => @intCast(u32, rc),
// TODO
else => |err| os.unexpectedErrno(err),
};
@@ -107,7 +107,7 @@ extern fn wl_display_roundtrip_queue(display: *Display, queue: *client.wl.EventQ
pub fn roundtripQueue(display: *Display, queue: *client.wl.EventQueue) !u32 {
const rc = wl_display_roundtrip_queue(display, queue);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
.SUCCESS => @intCast(u32, rc),
// TODO
else => |err| os.unexpectedErrno(err),
};
@@ -117,8 +117,8 @@ extern fn wl_display_flush(display: *Display) c_int;
pub fn flush(display: *Display) error{WouldBlock}!u32 {
const rc = wl_display_flush(display);
return switch (os.errno(rc)) {
0 => @intCast(u32, rc),
os.EAGAIN => error.WouldBlock,
.SUCCESS => @intCast(u32, rc),
.AGAIN => error.WouldBlock,
else => unreachable,
};
}
+4 -21
View File
@@ -21,7 +21,7 @@ pub const Interface = extern struct {
pub const Array = extern struct {
size: usize,
alloc: usize,
data: ?*c_void,
data: ?*anyopaque,
/// Does not clone memory
pub fn fromArrayList(comptime T: type, list: std.ArrayList(T)) Array {
@@ -39,7 +39,7 @@ pub const Array = extern struct {
};
/// A 24.8 signed fixed-point number.
pub const Fixed = extern enum(i32) {
pub const Fixed = enum(i32) {
_,
pub fn toInt(f: Fixed) i24 {
@@ -75,10 +75,10 @@ pub fn Dispatcher(comptime Obj: type, comptime Data: type) type {
const Payload = if (client) Obj.Event else Obj.Request;
return struct {
pub fn dispatcher(
implementation: ?*const c_void,
implementation: ?*const anyopaque,
object: if (client) *wayland.client.wl.Proxy else *wayland.server.wl.Resource,
opcode: u32,
message: *const Message,
_: *const Message,
args: [*]Argument,
) callconv(.C) c_int {
inline for (@typeInfo(Payload).Union.fields) |payload_field, payload_num| {
@@ -111,20 +111,3 @@ pub fn Dispatcher(comptime Obj: type, comptime Data: type) type {
}
};
}
test "Fixed" {
const testing = @import("std").testing;
{
const initial: f64 = 10.5301837;
const val = Fixed.fromDouble(initial);
try testing.expectApproxEqAbs(initial, val.toDouble(), 1.0 / 256.0);
try testing.expectEqual(@as(i24, 10), val.toInt());
}
{
const val = Fixed.fromInt(10);
try testing.expectEqual(@as(f64, 10.0), val.toDouble());
try testing.expectEqual(@as(i24, 10), val.toInt());
}
}
+57 -57
View File
@@ -5,15 +5,15 @@ const fmtId = std.zig.fmtId;
const xml = @import("xml.zig");
const gpa = &allocator_instance.allocator;
const gpa = allocator_instance.allocator();
var allocator_instance = std.heap.GeneralPurposeAllocator(.{}){};
pub fn scan(root_dir: fs.Dir, out_path: []const u8, wayland_xml: []const u8, protocols: []const []const u8) !void {
var out_dir = try root_dir.makeOpenPath(out_path, .{});
defer out_dir.close();
pub fn scan(root_dir: fs.Dir, out_dir: fs.Dir, wayland_xml: []const u8, protocols: []const []const u8) !void {
const wayland_file = try out_dir.createFile("wayland.zig", .{});
try wayland_file.writeAll(@embedFile("wayland.zig"));
try wayland_file.writeAll(
\\pub const client = @import("client.zig");
\\pub const server = @import("server.zig");
);
defer wayland_file.close();
var scanner = Scanner{};
@@ -97,8 +97,8 @@ const Scanner = struct {
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
const xml_bytes = try xml_file.readToEndAlloc(&arena.allocator, 512 * 4096);
const protocol = try Protocol.parseXML(&arena.allocator, xml_bytes);
const xml_bytes = try xml_file.readToEndAlloc(arena.allocator(), 512 * 4096);
const protocol = try Protocol.parseXML(arena.allocator(), xml_bytes);
const protocol_name = try gpa.dupe(u8, protocol.name);
const protocol_namespace = try gpa.dupe(u8, protocol.namespace);
@@ -136,20 +136,20 @@ const Protocol = struct {
toplevel_description: ?[]const u8,
interfaces: std.ArrayList(Interface),
fn parseXML(allocator: *mem.Allocator, xml_bytes: []const u8) !Protocol {
fn parseXML(arena: mem.Allocator, xml_bytes: []const u8) !Protocol {
var parser = xml.Parser.init(xml_bytes);
while (parser.next()) |ev| switch (ev) {
.open_tag => |tag| if (mem.eql(u8, tag, "protocol")) return parse(allocator, &parser),
.open_tag => |tag| if (mem.eql(u8, tag, "protocol")) return parse(arena, &parser),
else => {},
};
return error.UnexpectedEndOfFile;
}
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Protocol {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Protocol {
var name: ?[]const u8 = null;
var copyright: ?[]const u8 = null;
var toplevel_description: ?[]const u8 = null;
var interfaces = std.ArrayList(Interface).init(allocator);
var interfaces = std.ArrayList(Interface).init(arena);
while (parser.next()) |ev| switch (ev) {
.open_tag => |tag| {
if (mem.eql(u8, tag, "copyright")) {
@@ -157,7 +157,7 @@ const Protocol = struct {
return error.DuplicateCopyright;
const e = parser.next() orelse return error.UnexpectedEndOfFile;
switch (e) {
.character_data => |data| copyright = try allocator.dupe(u8, data),
.character_data => |data| copyright = try arena.dupe(u8, data),
else => return error.BadCopyright,
}
} else if (mem.eql(u8, tag, "description")) {
@@ -166,7 +166,7 @@ const Protocol = struct {
while (parser.next()) |e| {
switch (e) {
.character_data => |data| {
toplevel_description = try allocator.dupe(u8, data);
toplevel_description = try arena.dupe(u8, data);
break;
},
.attribute => continue,
@@ -176,12 +176,12 @@ const Protocol = struct {
return error.UnexpectedEndOfFile;
}
} else if (mem.eql(u8, tag, "interface")) {
try interfaces.append(try Interface.parse(allocator, parser));
try interfaces.append(try Interface.parse(arena, parser));
}
},
.attribute => |attr| if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
},
.close_tag => |tag| if (mem.eql(u8, tag, "protocol")) {
if (interfaces.items.len == 0) return error.NoInterfaces;
@@ -204,14 +204,14 @@ const Protocol = struct {
fn emitCopyrightAndToplevelDescription(protocol: Protocol, writer: anytype) !void {
try writer.writeAll("// Generated by zig-wayland\n\n");
if (protocol.copyright) |copyright| {
var it = mem.split(copyright, "\n");
var it = mem.split(u8, copyright, "\n");
while (it.next()) |line| {
try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.spaces)});
}
try writer.writeByte('\n');
}
if (protocol.toplevel_description) |toplevel_description| {
var it = mem.split(toplevel_description, "\n");
var it = mem.split(u8, toplevel_description, "\n");
while (it.next()) |line| {
try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.spaces)});
}
@@ -258,30 +258,30 @@ const Interface = struct {
events: std.ArrayList(Message),
enums: std.ArrayList(Enum),
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Interface {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Interface {
var name: ?[]const u8 = null;
var version: ?u32 = null;
var requests = std.ArrayList(Message).init(allocator);
var events = std.ArrayList(Message).init(allocator);
var enums = std.ArrayList(Enum).init(allocator);
var requests = std.ArrayList(Message).init(arena);
var events = std.ArrayList(Message).init(arena);
var enums = std.ArrayList(Enum).init(arena);
while (parser.next()) |ev| switch (ev) {
.open_tag => |tag| {
// TODO: parse description
if (mem.eql(u8, tag, "request"))
try requests.append(try Message.parse(allocator, parser))
try requests.append(try Message.parse(arena, parser))
else if (mem.eql(u8, tag, "event"))
try events.append(try Message.parse(allocator, parser))
try events.append(try Message.parse(arena, parser))
else if (mem.eql(u8, tag, "enum"))
try enums.append(try Enum.parse(allocator, parser));
try enums.append(try Enum.parse(arena, parser));
},
.attribute => |attr| {
if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "version")) {
if (version != null) return error.DuplicateVersion;
version = try std.fmt.parseInt(u32, try attr.dupeValue(allocator), 10);
version = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
}
},
.close_tag => |tag| if (mem.eql(u8, tag, "interface")) {
@@ -338,7 +338,7 @@ const Interface = struct {
\\ _data: T,
\\) void {{
\\ const _proxy = @ptrCast(*client.wl.Proxy, _{[interface]});
\\ const _mut_data = @intToPtr(?*c_void, @ptrToInt(_data));
\\ const _mut_data = @intToPtr(?*anyopaque, @ptrToInt(_data));
\\ _proxy.addDispatcher(common.Dispatcher({[type]}, T).dispatcher, _listener, _mut_data);
\\}}
, .{
@@ -421,15 +421,15 @@ const Interface = struct {
\\pub inline fn setHandler(
\\ _{[interface]}: *{[type]},
\\ comptime T: type,
\\ handle_request: fn ({[interface]}: *{[type]}, request: Request, data: T) void,
\\ comptime handle_destroy: ?fn ({[interface]}: *{[type]}, data: T) void,
\\ handle_request: fn (_{[interface]}: *{[type]}, request: Request, data: T) void,
\\ comptime handle_destroy: ?fn (_{[interface]}: *{[type]}, data: T) void,
\\ _data: T,
\\) void {{
\\ const _resource = @ptrCast(*server.wl.Resource, _{[interface]});
\\ _resource.setDispatcher(
\\ common.Dispatcher({[type]}, T).dispatcher,
\\ handle_request,
\\ @intToPtr(?*c_void, @ptrToInt(_data)),
\\ @intToPtr(?*anyopaque, @ptrToInt(_data)),
\\ if (handle_destroy) |_handler| struct {{
\\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
\\ @call(.{{ .modifier = .always_inline }}, _handler, .{{
@@ -449,14 +449,14 @@ const Interface = struct {
\\pub inline fn setHandler(
\\ _{[interface]}: *{[type]},
\\ comptime T: type,
\\ comptime handle_destroy: ?fn ({[interface]}: *{[type]}, data: T) void,
\\ comptime handle_destroy: ?fn (_{[interface]}: *{[type]}, data: T) void,
\\ _data: T,
\\) void {{
\\ const _resource = @ptrCast(*server.wl.Resource, {[interface]});
\\ const _resource = @ptrCast(*server.wl.Resource, _{[interface]});
\\ _resource.setDispatcher(
\\ null,
\\ null,
\\ @intToPtr(?*c_void, @ptrToInt(_data)),
\\ @intToPtr(?*anyopaque, @ptrToInt(_data)),
\\ if (handle_destroy) |_handler| struct {{
\\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
\\ @call(.{{ .modifier = .always_inline }}, _handler, .{{
@@ -537,25 +537,25 @@ const Message = struct {
destructor: void,
},
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Message {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Message {
var name: ?[]const u8 = null;
var since: ?u32 = null;
var args = std.ArrayList(Arg).init(allocator);
var args = std.ArrayList(Arg).init(arena);
var destructor = false;
while (parser.next()) |ev| switch (ev) {
.open_tag => |tag| {
// TODO: parse description
if (mem.eql(u8, tag, "arg"))
try args.append(try Arg.parse(allocator, parser));
try args.append(try Arg.parse(arena, parser));
},
.attribute => |attr| {
if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "since")) {
if (since != null) return error.DuplicateSince;
since = try std.fmt.parseInt(u32, try attr.dupeValue(allocator), 10);
since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
} else if (mem.eql(u8, attr.name, "type")) {
if (attr.valueEql("destructor")) {
destructor = true;
@@ -758,7 +758,7 @@ const Arg = struct {
allow_null: bool,
enum_name: ?[]const u8,
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Arg {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Arg {
var name: ?[]const u8 = null;
var kind: ?std.meta.Tag(Type) = null;
var interface: ?[]const u8 = null;
@@ -769,21 +769,21 @@ const Arg = struct {
.attribute => |attr| {
if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "type")) {
if (kind != null) return error.DuplicateType;
kind = std.meta.stringToEnum(std.meta.Tag(Type), try attr.dupeValue(allocator)) orelse
kind = std.meta.stringToEnum(std.meta.Tag(Type), try attr.dupeValue(arena)) orelse
return error.InvalidType;
} else if (mem.eql(u8, attr.name, "interface")) {
if (interface != null) return error.DuplicateInterface;
interface = try attr.dupeValue(allocator);
interface = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "allow-null")) {
if (allow_null != null) return error.DuplicateAllowNull;
if (!attr.valueEql("true") and !attr.valueEql("false")) return error.InvalidBoolValue;
allow_null = attr.valueEql("true");
} else if (mem.eql(u8, attr.name, "enum")) {
if (enum_name != null) return error.DuplicateEnum;
enum_name = try attr.dupeValue(allocator);
enum_name = try attr.dupeValue(arena);
}
},
.close_tag => |tag| if (mem.eql(u8, tag, "arg")) {
@@ -873,25 +873,25 @@ const Enum = struct {
entries: std.ArrayList(Entry),
bitfield: bool,
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Enum {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Enum {
var name: ?[]const u8 = null;
var since: ?u32 = null;
var entries = std.ArrayList(Entry).init(allocator);
var entries = std.ArrayList(Entry).init(arena);
var bitfield: ?bool = null;
while (parser.next()) |ev| switch (ev) {
.open_tag => |tag| {
// TODO: parse description
if (mem.eql(u8, tag, "entry"))
try entries.append(try Entry.parse(allocator, parser));
try entries.append(try Entry.parse(arena, parser));
},
.attribute => |attr| {
if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "since")) {
if (since != null) return error.DuplicateSince;
since = try std.fmt.parseInt(u32, try attr.dupeValue(allocator), 10);
since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
} else if (mem.eql(u8, attr.name, "bitfield")) {
if (bitfield != null) return error.DuplicateBitfield;
if (!attr.valueEql("true") and !attr.valueEql("false")) return error.InvalidBoolValue;
@@ -944,7 +944,7 @@ const Enum = struct {
try writer.writeAll("pub const Enum ");
}
try writer.writeAll(" = extern enum(c_int) {");
try writer.writeAll(" = enum(c_int) {");
for (e.entries.items) |entry| {
try writer.print("{s}= {s},", .{ fmtId(entry.name), entry.value });
}
@@ -961,7 +961,7 @@ const Entry = struct {
since: u32,
value: []const u8,
fn parse(allocator: *mem.Allocator, parser: *xml.Parser) !Entry {
fn parse(arena: mem.Allocator, parser: *xml.Parser) !Entry {
var name: ?[]const u8 = null;
var since: ?u32 = null;
var value: ?[]const u8 = null;
@@ -970,13 +970,13 @@ const Entry = struct {
.attribute => |attr| {
if (mem.eql(u8, attr.name, "name")) {
if (name != null) return error.DuplicateName;
name = try attr.dupeValue(allocator);
name = try attr.dupeValue(arena);
} else if (mem.eql(u8, attr.name, "since")) {
if (since != null) return error.DuplicateSince;
since = try std.fmt.parseInt(u32, try attr.dupeValue(allocator), 10);
since = try std.fmt.parseInt(u32, try attr.dupeValue(arena), 10);
} else if (mem.eql(u8, attr.name, "value")) {
if (value != null) return error.DuplicateName;
value = try attr.dupeValue(allocator);
value = try attr.dupeValue(arena);
}
},
.close_tag => |tag| if (mem.eql(u8, tag, "entry")) {
@@ -1014,8 +1014,8 @@ fn formatCaseImpl(case: Case, comptime trim: bool) type {
return struct {
pub fn f(
bytes: []const u8,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
var upper = case == .title;
@@ -1061,7 +1061,7 @@ test "parsing" {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const protocol = try Protocol.parseXML(&arena.allocator, @embedFile("../protocol/wayland.xml"));
const protocol = try Protocol.parseXML(arena.allocator(), @embedFile("../protocol/wayland.xml"));
try testing.expectEqualSlices(u8, "wayland", protocol.name);
try testing.expectEqual(@as(usize, 22), protocol.interfaces.items.len);
-4
View File
@@ -1,4 +0,0 @@
// These files are generated by the scanner from the src/foo_core.zig
// files and protocol xml
pub const client = @import("client.zig");
pub const server = @import("server.zig");
+6 -6
View File
@@ -55,7 +55,7 @@ pub const Proxy = opaque {
}
const DispatcherFn = fn (
implementation: ?*const c_void,
implementation: ?*const anyopaque,
proxy: *Proxy,
opcode: u32,
message: *const Message,
@@ -64,14 +64,14 @@ pub const Proxy = opaque {
extern fn wl_proxy_add_dispatcher(
proxy: *Proxy,
dispatcher: DispatcherFn,
implementation: ?*const c_void,
data: ?*c_void,
implementation: ?*const anyopaque,
data: ?*anyopaque,
) c_int;
pub fn addDispatcher(
proxy: *Proxy,
dispatcher: DispatcherFn,
implementation: ?*const c_void,
data: ?*c_void,
implementation: ?*const anyopaque,
data: ?*anyopaque,
) void {
const ret = wl_proxy_add_dispatcher(proxy, dispatcher, implementation, data);
// Since there is no way to remove listeners, adding a listener to
@@ -80,7 +80,7 @@ pub const Proxy = opaque {
assert(ret != -1); // If this fails, a listener was already added
}
extern fn wl_proxy_get_user_data(proxy: *Proxy) ?*c_void;
extern fn wl_proxy_get_user_data(proxy: *Proxy) ?*anyopaque;
pub const getUserData = wl_proxy_get_user_data;
extern fn wl_proxy_get_version(proxy: *Proxy) u32;
+38 -38
View File
@@ -82,8 +82,8 @@ pub const Server = opaque {
extern fn wl_display_set_global_filter(
server: *Server,
filter: fn (client: *const Client, global: *const Global, data: ?*c_void) callconv(.C) bool,
data: ?*c_void,
filter: fn (client: *const Client, global: *const Global, data: ?*anyopaque) callconv(.C) bool,
data: ?*anyopaque,
) void;
pub inline fn setGlobalFilter(
server: *Server,
@@ -109,8 +109,8 @@ pub const Server = opaque {
extern fn wl_display_add_protocol_logger(
server: *Server,
func: fn (data: ?*c_void, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) callconv(.C) void,
data: ?*c_void,
func: fn (data: ?*anyopaque, direction: ProtocolLogger.Type, message: *const ProtocolLogger.LogMessage) callconv(.C) void,
data: ?*anyopaque,
) void;
pub inline fn addProtocolLogger(
server: *Server,
@@ -169,11 +169,11 @@ pub const Client = opaque {
extern fn wl_client_add_resource_created_listener(client: *Client, listener: *Listener(*Resource)) void;
pub const addResourceCreatedListener = wl_client_add_resource_created_listener;
const IteratorResult = extern enum { stop, cont };
const IteratorResult = enum(c_int) { stop, cont };
extern fn wl_client_for_each_resource(
client: *Client,
iterator: fn (resource: *Resource, data: ?*c_void) callconv(.C) IteratorResult,
data: ?*c_void,
iterator: fn (resource: *Resource, data: ?*anyopaque) callconv(.C) IteratorResult,
data: ?*anyopaque,
) void;
pub inline fn forEachResource(
client: *Client,
@@ -196,8 +196,8 @@ pub const Global = opaque {
server: *Server,
interface: *const Interface,
version: c_int,
data: ?*c_void,
bind: fn (client: *Client, data: ?*c_void, version: u32, id: u32) callconv(.C) void,
data: ?*anyopaque,
bind: fn (client: *Client, data: ?*anyopaque, version: u32, id: u32) callconv(.C) void,
) ?*Global;
pub inline fn create(
server: *Server,
@@ -212,7 +212,7 @@ pub const Global = opaque {
T.getInterface(),
@intCast(c_int, version),
data,
@ptrCast(fn (client: *Client, data: ?*c_void, version: u32, id: u32) callconv(.C) void, bind),
@ptrCast(fn (client: *Client, data: ?*anyopaque, version: u32, id: u32) callconv(.C) void, bind),
) orelse error.GlobalCreateFailed;
}
@@ -225,7 +225,7 @@ pub const Global = opaque {
extern fn wl_global_get_interface(global: *const Global) *const Interface;
pub const getInterface = wl_global_get_interface;
extern fn wl_global_get_user_data(global: *const Global) ?*c_void;
extern fn wl_global_get_user_data(global: *const Global) ?*anyopaque;
pub const getUserData = wl_global_get_user_data;
};
@@ -253,7 +253,7 @@ pub const Resource = opaque {
pub const postNoMemory = wl_resource_post_no_memory;
const DispatcherFn = fn (
implementation: ?*const c_void,
implementation: ?*const anyopaque,
resource: *Resource,
opcode: u32,
message: *const Message,
@@ -263,21 +263,21 @@ pub const Resource = opaque {
extern fn wl_resource_set_dispatcher(
resource: *Resource,
dispatcher: ?DispatcherFn,
implementation: ?*const c_void,
data: ?*c_void,
implementation: ?*const anyopaque,
data: ?*anyopaque,
destroy_fn: ?DestroyFn,
) void;
pub fn setDispatcher(
resource: *Resource,
dispatcher: ?DispatcherFn,
implementation: ?*const c_void,
data: ?*c_void,
implementation: ?*const anyopaque,
data: ?*anyopaque,
destroy_fn: ?DestroyFn,
) void {
wl_resource_set_dispatcher(resource, dispatcher, implementation, data, destroy_fn);
}
extern fn wl_resource_get_user_data(resource: *Resource) ?*c_void;
extern fn wl_resource_get_user_data(resource: *Resource) ?*anyopaque;
pub const getUserData = wl_resource_get_user_data;
extern fn wl_resource_get_id(resource: *Resource) u32;
@@ -318,7 +318,7 @@ pub const Resource = opaque {
};
pub const ProtocolLogger = opaque {
pub const Type = extern enum {
pub const Type = enum(c_int) {
request,
event,
};
@@ -471,7 +471,7 @@ pub fn Listener(comptime T: type) type {
fn (listener: *Self, data: T) void;
link: list.Link,
notify: fn (listener: *Self, data: ?*c_void) callconv(.C) void,
notify: fn (listener: *Self, data: ?*anyopaque) callconv(.C) void,
pub fn init(comptime notify: NotifyFn) Self {
var self: Self = undefined;
@@ -482,13 +482,13 @@ pub fn Listener(comptime T: type) type {
pub fn setNotify(self: *Self, comptime notify: NotifyFn) void {
self.notify = if (T == void)
struct {
fn wrapper(listener: *Self, _: ?*c_void) callconv(.C) void {
fn wrapper(listener: *Self, _: ?*anyopaque) callconv(.C) void {
@call(.{ .modifier = .always_inline }, notify, .{listener});
}
}.wrapper
else
struct {
fn wrapper(listener: *Self, data: ?*c_void) callconv(.C) void {
fn wrapper(listener: *Self, data: ?*anyopaque) callconv(.C) void {
@call(.{ .modifier = .always_inline }, notify, .{ listener, @intToPtr(T, @ptrToInt(data)) });
}
}.wrapper;
@@ -533,7 +533,7 @@ pub fn Signal(comptime T: type) type {
/// This is similar to wlroots' wlr_signal_emit_safe. It handles
/// removal of any element in the list during iteration and stops at
/// whatever the last element was when iteration started.
fn emitInner(signal: *Self, data: ?*c_void) void {
fn emitInner(signal: *Self, data: ?*anyopaque) void {
var cursor: Listener(T) = undefined;
signal.listener_list.prepend(&cursor);
@@ -569,8 +569,8 @@ pub const EventLoop = opaque {
loop: *EventLoop,
fd: c_int,
mask: u32,
func: fn (fd: c_int, mask: u32, data: ?*c_void) callconv(.C) c_int,
data: ?*c_void,
func: fn (fd: c_int, mask: u32, data: ?*anyopaque) callconv(.C) c_int,
data: ?*anyopaque,
) ?*EventSource;
pub inline fn addFd(
loop: *EventLoop,
@@ -584,15 +584,15 @@ pub const EventLoop = opaque {
loop,
fd,
mask,
@ptrCast(fn (fd: c_int, mask: u32, data: ?*c_void) callconv(.C) c_int, func),
@ptrCast(fn (fd: c_int, mask: u32, data: ?*anyopaque) callconv(.C) c_int, func),
data,
) orelse error.AddFdFailed;
}
extern fn wl_event_loop_add_timer(
loop: *EventLoop,
func: fn (data: ?*c_void) callconv(.C) c_int,
data: ?*c_void,
func: fn (data: ?*anyopaque) callconv(.C) c_int,
data: ?*anyopaque,
) ?*EventSource;
pub inline fn addTimer(
loop: *EventLoop,
@@ -602,7 +602,7 @@ pub const EventLoop = opaque {
) !*EventSource {
return wl_event_loop_add_timer(
loop,
@ptrCast(fn (?*c_void) callconv(.C) c_int, func),
@ptrCast(fn (?*anyopaque) callconv(.C) c_int, func),
data,
) orelse error.AddTimerFailed;
}
@@ -610,8 +610,8 @@ pub const EventLoop = opaque {
extern fn wl_event_loop_add_signal(
loop: *EventLoop,
signal_number: c_int,
func: fn (c_int, ?*c_void) callconv(.C) c_int,
data: ?*c_void,
func: fn (c_int, ?*anyopaque) callconv(.C) c_int,
data: ?*anyopaque,
) ?*EventSource;
pub inline fn addSignal(
loop: *EventLoop,
@@ -623,15 +623,15 @@ pub const EventLoop = opaque {
return wl_event_loop_add_signal(
loop,
signal_number,
@ptrCast(fn (c_int, ?*c_void) callconv(.C) c_int, func),
@ptrCast(fn (c_int, ?*anyopaque) callconv(.C) c_int, func),
data,
) orelse error.AddSignalFailed;
}
extern fn wl_event_loop_add_idle(
loop: *EventLoop,
func: fn (data: ?*c_void) callconv(.C) void,
data: ?*c_void,
func: fn (data: ?*anyopaque) callconv(.C) void,
data: ?*anyopaque,
) ?*EventSource;
pub inline fn addIdle(
loop: *EventLoop,
@@ -641,7 +641,7 @@ pub const EventLoop = opaque {
) error{OutOfMemory}!*EventSource {
return wl_event_loop_add_idle(
loop,
@ptrCast(fn (?*c_void) callconv(.C) void, func),
@ptrCast(fn (?*anyopaque) callconv(.C) void, func),
data,
) orelse error.OutOfMemory;
}
@@ -650,7 +650,7 @@ pub const EventLoop = opaque {
pub fn dispatch(loop: *EventLoop, timeout: c_int) !void {
const rc = wl_event_loop_dispatch(loop, timeout);
switch (os.errno(rc)) {
0 => return,
.SUCCESS => return,
// TODO
else => |err| return os.unexpectedErrno(err),
}
@@ -682,7 +682,7 @@ pub const EventSource = opaque {
pub fn fdUpdate(source: *EventSource, mask: u32) !void {
const rc = wl_event_source_fd_update(source, mask);
switch (os.errno(rc)) {
0 => return,
.SUCCESS => return,
// TODO
else => |err| return os.unexpectedErrno(err),
}
@@ -692,7 +692,7 @@ pub const EventSource = opaque {
pub fn timerUpdate(source: *EventSource, ms_delay: c_int) !void {
const rc = wl_event_source_timer_update(source, ms_delay);
switch (os.errno(rc)) {
0 => return,
.SUCCESS => return,
// TODO
else => |err| return os.unexpectedErrno(err),
}
@@ -710,7 +710,7 @@ pub const shm = struct {
extern fn wl_shm_buffer_end_access(buffer: *Buffer) void;
pub const endAccess = wl_shm_buffer_end_access;
extern fn wl_shm_buffer_get_data(buffer: *Buffer) ?*c_void;
extern fn wl_shm_buffer_get_data(buffer: *Buffer) ?*anyopaque;
pub const getData = wl_shm_buffer_get_data;
extern fn wl_shm_buffer_get_format(buffer: *Buffer) u32;
+30 -29
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const mem = std.mem;
pub const Parser = struct {
document: []const u8,
@@ -26,7 +27,7 @@ pub const Parser = struct {
switch (p.peek(0) orelse return null) {
'<' => switch (p.peek(1) orelse return null) {
'?' => {
if (std.mem.indexOf(u8, p.document[2..], "?>")) |end| {
if (mem.indexOf(u8, p.document[2..], "?>")) |end| {
const ev = Event{ .processing_instruction = p.document[2 .. end + 2] };
p.document = p.document[end + 4 ..];
return ev;
@@ -34,7 +35,7 @@ pub const Parser = struct {
},
'/' => switch (p.peek(2) orelse return null) {
':', 'A'...'Z', '_', 'a'...'z' => {
if (std.mem.indexOfScalar(u8, p.document[3..], '>')) |end| {
if (mem.indexOfScalar(u8, p.document[3..], '>')) |end| {
const ev = Event{ .close_tag = p.document[2 .. end + 3] };
p.document = p.document[end + 4 ..];
return ev;
@@ -44,14 +45,14 @@ pub const Parser = struct {
},
'!' => switch (p.peek(2) orelse return null) {
'-' => if ((p.peek(3) orelse return null) == '-') {
if (std.mem.indexOf(u8, p.document[3..], "-->")) |end| {
if (mem.indexOf(u8, p.document[3..], "-->")) |end| {
const ev = Event{ .comment = p.document[4 .. end + 3] };
p.document = p.document[end + 6 ..];
return ev;
}
},
'[' => if (std.mem.startsWith(u8, p.document[3..], "CDATA[")) {
if (std.mem.indexOf(u8, p.document, "]]>")) |end| {
'[' => if (mem.startsWith(u8, p.document[3..], "CDATA[")) {
if (mem.indexOf(u8, p.document, "]]>")) |end| {
const ev = Event{ .character_data = p.document[9..end] };
p.document = p.document[end + 3 ..];
return ev;
@@ -60,15 +61,15 @@ pub const Parser = struct {
else => {},
},
':', 'A'...'Z', '_', 'a'...'z' => {
const angle = std.mem.indexOfScalar(u8, p.document, '>') orelse return null;
if (std.mem.indexOfScalar(u8, p.document[0..angle], ' ')) |space| {
const angle = mem.indexOfScalar(u8, p.document, '>') orelse return null;
if (mem.indexOfScalar(u8, p.document[0..angle], ' ')) |space| {
const ev = Event{ .open_tag = p.document[1..space] };
p.current_tag = ev.open_tag;
p.document = p.document[space..];
p.mode = .attrs;
return ev;
}
if (std.mem.indexOfScalar(u8, p.document[0..angle], '/')) |slash| {
if (mem.indexOfScalar(u8, p.document[0..angle], '/')) |slash| {
const ev = Event{ .open_tag = p.document[1..slash] };
p.current_tag = ev.open_tag;
p.document = p.document[slash..];
@@ -126,7 +127,7 @@ pub const Parser = struct {
const c = p.peek(0) orelse return null;
switch (c) {
'\'', '"' => {
if (std.mem.indexOfScalar(u8, p.document[1..], c)) |end| {
if (mem.indexOfScalar(u8, p.document[1..], c)) |end| {
const ev = Event{
.attribute = .{
.name = name,
@@ -166,24 +167,24 @@ pub const Parser = struct {
}
fn parseEntity(s: []const u8, buf: *[4]u8) ?usize {
const semi = std.mem.indexOfScalar(u8, s, ';') orelse return null;
const semi = mem.indexOfScalar(u8, s, ';') orelse return null;
const entity = s[0..semi];
if (std.mem.eql(u8, entity, "lt")) {
buf.* = std.mem.toBytes(@as(u32, '<'));
} else if (std.mem.eql(u8, entity, "gt")) {
buf.* = std.mem.toBytes(@as(u32, '>'));
} else if (std.mem.eql(u8, entity, "amp")) {
buf.* = std.mem.toBytes(@as(u32, '&'));
} else if (std.mem.eql(u8, entity, "apos")) {
buf.* = std.mem.toBytes(@as(u32, '\''));
} else if (std.mem.eql(u8, entity, "quot")) {
buf.* = std.mem.toBytes(@as(u32, '"'));
} else if (std.mem.startsWith(u8, entity, "#x")) {
if (mem.eql(u8, entity, "lt")) {
buf.* = mem.toBytes(@as(u32, '<'));
} else if (mem.eql(u8, entity, "gt")) {
buf.* = mem.toBytes(@as(u32, '>'));
} else if (mem.eql(u8, entity, "amp")) {
buf.* = mem.toBytes(@as(u32, '&'));
} else if (mem.eql(u8, entity, "apos")) {
buf.* = mem.toBytes(@as(u32, '\''));
} else if (mem.eql(u8, entity, "quot")) {
buf.* = mem.toBytes(@as(u32, '"'));
} else if (mem.startsWith(u8, entity, "#x")) {
const codepoint = std.fmt.parseInt(u21, entity[2..semi], 16) catch return null;
buf.* = std.mem.toBytes(@as(u32, codepoint));
} else if (std.mem.startsWith(u8, entity, "#")) {
buf.* = mem.toBytes(@as(u32, codepoint));
} else if (mem.startsWith(u8, entity, "#")) {
const codepoint = std.fmt.parseInt(u21, entity[1..semi], 10) catch return null;
buf.* = std.mem.toBytes(@as(u32, codepoint));
buf.* = mem.toBytes(@as(u32, codepoint));
} else {
return null;
}
@@ -195,7 +196,7 @@ pub const Parser = struct {
return null;
if (parseEntity(p.document[1..], &p.char_buffer)) |semi| {
const codepoint = std.mem.bytesToValue(u32, &p.char_buffer);
const codepoint = mem.bytesToValue(u32, &p.char_buffer);
const n = std.unicode.utf8Encode(@intCast(u21, codepoint), &p.char_buffer) catch return null;
p.document = p.document[semi + 2 ..];
p.mode = .chars;
@@ -247,7 +248,7 @@ pub const Attribute = struct {
raw_value: []const u8,
char_buffer: [4]u8 = undefined,
pub fn dupeValue(attr: Attribute, allocator: *std.mem.Allocator) error{OutOfMemory}![]u8 {
pub fn dupeValue(attr: Attribute, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
var list = std.ArrayList(u8).init(allocator);
errdefer list.deinit();
var attr_copy = attr;
@@ -261,7 +262,7 @@ pub const Attribute = struct {
var i: usize = 0;
while (attr_copy.next()) |fragment| {
if (std.mem.startsWith(u8, fragment, prefix[i..])) {
if (mem.startsWith(u8, fragment, prefix[i..])) {
i += fragment.len;
} else {
return false;
@@ -276,7 +277,7 @@ pub const Attribute = struct {
var i: usize = 0;
while (attr_copy.next()) |fragment| {
if (std.mem.startsWith(u8, value[i..], fragment)) {
if (mem.startsWith(u8, value[i..], fragment)) {
i += fragment.len;
} else {
return false;
@@ -292,7 +293,7 @@ pub const Attribute = struct {
if (attr.raw_value[0] == '&') {
if (Parser.parseEntity(attr.raw_value[1..], &attr.char_buffer)) |semi| {
const codepoint = std.mem.bytesToValue(u32, &attr.char_buffer);
const codepoint = mem.bytesToValue(u32, &attr.char_buffer);
const n = std.unicode.utf8Encode(@intCast(u21, codepoint), &attr.char_buffer) catch return null;
attr.raw_value = attr.raw_value[semi + 2 ..];
return attr.char_buffer[0..n];