Handle bitflags

This commit is contained in:
Robin Voetter
2020-06-20 04:15:31 +02:00
parent 0d6e65eeec
commit 3bef667e24
3 changed files with 98 additions and 16 deletions

View File

@@ -155,30 +155,30 @@ const DeclarationResolver = struct {
pub const Generator = struct { pub const Generator = struct {
gpa: *Allocator, gpa: *Allocator,
registry_arena: std.heap.ArenaAllocator, reg_arena: std.heap.ArenaAllocator,
registry: reg.Registry, registry: reg.Registry,
pub fn init(allocator: *Allocator, spec: *xml.Element) !Generator { pub fn init(allocator: *Allocator, spec: *xml.Element) !Generator {
const result = try parseXml(allocator, spec); const result = try parseXml(allocator, spec);
return Generator{ return Generator{
.gpa = allocator, .gpa = allocator,
.registry_arena = result.arena, .reg_arena = result.arena,
.registry = result.registry, .registry = result.registry,
}; };
} }
pub fn deinit(self: Generator) void { pub fn deinit(self: Generator) void {
self.registry_arena.deinit(); self.reg_arena.deinit();
} }
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`. // Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
pub fn resolveDeclarations(self: *Generator) !void { pub fn resolveDeclarations(self: *Generator) !void {
var resolver = DeclarationResolver.init(self.gpa, &self.registry_arena.allocator, &self.registry); var resolver = DeclarationResolver.init(self.gpa, &self.reg_arena.allocator, &self.registry);
defer resolver.deinit(); defer resolver.deinit();
try resolver.resolve(); try resolver.resolve();
} }
pub fn render(self: *Generator, out_stream: var) !void { pub fn render(self: *Generator, out_stream: var) !void {
try renderRegistry(out_stream, self.gpa, &self.registry); try renderRegistry(out_stream, &self.reg_arena.allocator, &self.registry);
} }
}; };

View File

@@ -9,7 +9,6 @@ const preamble =
\\const std = @import("std"); \\const std = @import("std");
\\const builtin = @import("builtin"); \\const builtin = @import("builtin");
\\const root = @import("root"); \\const root = @import("root");
\\
\\pub const vulkan_call_conv: builtin.CallingConvention = if (builtin.os.tag == .windows) \\pub const vulkan_call_conv: builtin.CallingConvention = if (builtin.os.tag == .windows)
\\ .Stdcall \\ .Stdcall
\\ else if (builtin.abi == .android and (builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb()) and builtin.Target.arm.featureSetHas(builtin.cpu.features, .has_v7) and builtin.cpu.arch.ptrBitWidth() == 32) \\ else if (builtin.abi == .android and (builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb()) and builtin.Target.arm.featureSetHas(builtin.cpu.features, .has_v7) and builtin.cpu.arch.ptrBitWidth() == 32)
@@ -80,6 +79,15 @@ fn Renderer(comptime WriterType: type) type {
OutOfMemory, OutOfMemory,
}; };
const BitflagName = struct {
/// Name without FlagBits, so VkSurfaceTransformFlagBitsKHR
/// becomes VkSurfaceTransform
base_name: []const u8,
/// Optional tag of the flag
tag: ?[]const u8,
};
writer: WriterType, writer: WriterType,
allocator: *Allocator, allocator: *Allocator,
registry: *const reg.Registry, registry: *const reg.Registry,
@@ -107,11 +115,7 @@ fn Renderer(comptime WriterType: type) type {
} }
fn extractEnumFieldName(self: Self, enum_name: []const u8, field_name: []const u8) ![]const u8 { fn extractEnumFieldName(self: Self, enum_name: []const u8, field_name: []const u8) ![]const u8 {
const tag = util.getAuthorTag(enum_name, self.registry.tags); const adjusted_enum_name = util.stripAuthorTag(enum_name, self.registry.tags);
const adjusted_enum_name = if (tag) |name|
enum_name[0 .. enum_name.len - name.len]
else
enum_name;
var enum_it = util.SegmentIterator.init(adjusted_enum_name); var enum_it = util.SegmentIterator.init(adjusted_enum_name);
var field_it = util.SegmentIterator.init(field_name); var field_it = util.SegmentIterator.init(field_name);
@@ -127,6 +131,20 @@ fn Renderer(comptime WriterType: type) type {
} }
} }
fn exctractBitflagName(self: Self, name: []const u8) ?BitflagName {
const tag = util.getAuthorTag(name, self.registry.tags);
const base_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name;
if (!mem.endsWith(u8, base_name, "FlagBits")) {
return null;
}
return BitflagName{
.base_name = base_name[0 .. base_name.len - "FlagBits".len],
.tag = tag,
};
}
fn render(self: *Self) !void { fn render(self: *Self) !void {
try self.writer.writeAll(preamble); try self.writer.writeAll(preamble);
@@ -218,9 +236,13 @@ fn Renderer(comptime WriterType: type) type {
if (builtin_types.get(name)) |zig_name| { if (builtin_types.get(name)) |zig_name| {
try self.writer.writeAll(zig_name); try self.writer.writeAll(zig_name);
return; return;
} } else if (self.exctractBitflagName(name)) |bitflag_name| {
try self.writer.print("{}Flags{}", .{
if (mem.startsWith(u8, name, "vk")) { util.trimVkNamespace(bitflag_name.base_name),
@as([]const u8, if (bitflag_name.tag) |tag| tag else "")
});
return;
} else if (mem.startsWith(u8, name, "vk")) {
// Function type, always render with the exact same text for linking purposes. // Function type, always render with the exact same text for linking purposes.
try self.writeIdentifier(name); try self.writeIdentifier(name);
return; return;
@@ -288,7 +310,7 @@ fn Renderer(comptime WriterType: type) type {
switch (decl.decl_type) { switch (decl.decl_type) {
.container => |container| try self.renderContainer(decl.name, container), .container => |container| try self.renderContainer(decl.name, container),
.enumeration => |enumeration| try self.renderEnumeration(decl.name, enumeration), .enumeration => |enumeration| try self.renderEnumeration(decl.name, enumeration),
.bitmask => {}, .bitmask => |bitmask| try self.renderBitmask(decl.name, bitmask),
.handle => |handle| try self.renderHandle(decl.name, handle), .handle => |handle| try self.renderHandle(decl.name, handle),
.command => {}, .command => {},
.alias => |alias| try self.renderAlias(decl.name, alias), .alias => |alias| try self.renderAlias(decl.name, alias),
@@ -348,7 +370,11 @@ fn Renderer(comptime WriterType: type) type {
} }
fn renderEnumeration(self: *Self, name: []const u8, enumeration: reg.Enum) !void { fn renderEnumeration(self: *Self, name: []const u8, enumeration: reg.Enum) !void {
// TODO: Handle bitmasks if (enumeration.is_bitmask) {
try self.renderBitmaskBits(name, enumeration);
return;
}
try self.writer.writeAll("const "); try self.writer.writeAll("const ");
try self.renderTypeName(name); try self.renderTypeName(name);
try self.writer.writeAll(" = extern enum {"); try self.writer.writeAll(" = extern enum {");
@@ -383,6 +409,51 @@ fn Renderer(comptime WriterType: type) type {
try self.writer.writeAll("};\n"); try self.writer.writeAll("};\n");
} }
fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void {
try self.writer.writeAll("const ");
try self.renderTypeName(name);
try self.writer.writeAll(" = packed struct {");
var flags_by_bitpos = [_]?[]const u8{null} ** 32;
for (bits.fields) |field| {
if (field.value == .bitpos) {
flags_by_bitpos[field.value.bitpos] = field.name;
}
}
for (flags_by_bitpos) |opt_flag_name, bitpos| {
if (opt_flag_name) |flag_name| {
try self.writeIdentifierWithCase(.snake, try self.extractEnumFieldName(name, flag_name));
} else {
try self.writer.print("__reserved_bit_{}", .{bitpos});
}
try self.writer.writeAll(": bool ");
if (bitpos == 0) { // Force alignment to integer boundaries
try self.writer.writeAll("align(@alignOf(u32)) ");
}
try self.writer.writeAll("= false, ");
}
try self.writer.writeAll("};\n");
}
fn renderBitmask(self: *Self, name: []const u8, bitmask: reg.Bitmask) !void {
if (bitmask.bits_enum == null) {
// The bits structure is generated by renderBitmaskBits, but that wont
// output flags with no associated bits type.
try self.writer.writeAll("const ");
try self.renderTypeName(name);
try self.writer.writeAll(
\\ = packed struct {
\\__reserved_bits: u32 = 0,
\\};
\\
);
}
}
fn renderHandle(self: *Self, name: []const u8, handle: reg.Handle) !void { fn renderHandle(self: *Self, name: []const u8, handle: reg.Handle) !void {
const backing_type: []const u8 = if (handle.is_dispatchable) "usize" else "u64"; const backing_type: []const u8 = if (handle.is_dispatchable) "usize" else "u64";
@@ -394,6 +465,8 @@ fn Renderer(comptime WriterType: type) type {
fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void { fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void {
if (alias.target == .other_command) { if (alias.target == .other_command) {
return; // TODO: Decide on how to tackle commands return; // TODO: Decide on how to tackle commands
} else if (self.exctractBitflagName(name) != null) {
return; // Don't make aliases of the bitflag names, as those are replaced by just the flags type
} }
try self.writer.writeAll("const "); try self.writer.writeAll("const ");

View File

@@ -86,6 +86,14 @@ pub fn getAuthorTag(id: []const u8, tags: []const reg.Tag) ?[]const u8 {
return null; return null;
} }
pub fn stripAuthorTag(id: []const u8, tags: []const reg.Tag) []const u8 {
if (getAuthorTag(id, tags)) |tag| {
return id[0 .. id.len - tag.len];
}
return id;
}
pub const SegmentIterator = struct { pub const SegmentIterator = struct {
text: []const u8, text: []const u8,
offset: usize, offset: usize,
@@ -218,6 +226,7 @@ pub const IdRenderer = struct {
pub fn render(self: *IdRenderer, out: var, case_style: CaseStyle, id: []const u8) !void { pub fn render(self: *IdRenderer, out: var, case_style: CaseStyle, id: []const u8) !void {
const tag = getAuthorTag(id, self.tags); const tag = getAuthorTag(id, self.tags);
const adjusted_id = if (tag) |name| id[0 .. id.len - name.len] else id; const adjusted_id = if (tag) |name| id[0 .. id.len - name.len] else id;
self.text_cache.items.len = 0; self.text_cache.items.len = 0;
switch (case_style) { switch (case_style) {