mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 03:24:46 +01:00
Configuration: implement UnionList storage
This commit is contained in:
parent
f45c4925a5
commit
e911add06f
5 changed files with 301 additions and 66 deletions
|
|
@ -15,6 +15,7 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
|
|||
std.log.err("TODO also print unlazy deps", .{});
|
||||
std.log.err("TODO also print system integrations", .{});
|
||||
std.log.err("TODO also print available options", .{});
|
||||
std.log.err("TODO also print modules", .{});
|
||||
const c = &sc.configuration;
|
||||
var serializer: Serializer = .{ .writer = w };
|
||||
var s = try serializer.beginStruct(.{});
|
||||
|
|
@ -83,6 +84,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
|
|||
.flag_optional => comptime unreachable,
|
||||
.flag_length_prefixed_list => comptime unreachable,
|
||||
.enum_optional => comptime unreachable,
|
||||
.union_list => comptime unreachable,
|
||||
} else if (std.enums.tagName(Field, field_value)) |name| {
|
||||
try s.ident(name);
|
||||
} else {
|
||||
|
|
@ -105,6 +107,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
|
|||
try printValue(sc, s, @TypeOf(field_value.slice), field_value.slice);
|
||||
},
|
||||
.extended => @compileError("TODO"),
|
||||
.union_list => @compileError("TODO"),
|
||||
},
|
||||
else => @compileError("not implemented: " ++ @typeName(Field)),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ const Serialize = struct {
|
|||
wc: *Configuration.Wip,
|
||||
module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty,
|
||||
package_map: std.AutoArrayHashMapUnmanaged(*std.Build, Configuration.Package.Index) = .empty,
|
||||
/// Index corresponds to `Configuration.steps` index.
|
||||
step_map: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty,
|
||||
|
||||
fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
|
||||
if (b.pkg_hash.len == 0) return .root;
|
||||
|
|
@ -291,6 +293,56 @@ const Serialize = struct {
|
|||
return if (opt_slice) |slice| try s.wc.addString(slice) else null;
|
||||
}
|
||||
|
||||
fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {
|
||||
log.err("TODO deduplicate addSystemLib", .{});
|
||||
const wc = s.wc;
|
||||
return @enumFromInt(try wc.addExtra(@as(Configuration.SystemLib, .{
|
||||
.flags = .{
|
||||
.needed = sl.needed,
|
||||
.weak = sl.weak,
|
||||
.use_pkg_config = sl.use_pkg_config,
|
||||
.preferred_link_mode = sl.preferred_link_mode,
|
||||
.search_strategy = sl.search_strategy,
|
||||
},
|
||||
.name = try wc.addString(sl.name),
|
||||
})));
|
||||
}
|
||||
|
||||
fn addCSourceFile(s: *Serialize, csf: *const std.Build.Module.CSourceFile) !Configuration.CSourceFile.Index {
|
||||
log.err("TODO addCSourceFile trailing data", .{});
|
||||
const wc = s.wc;
|
||||
return @enumFromInt(try wc.addExtra(@as(Configuration.CSourceFile, .{
|
||||
.flags = .{
|
||||
.args_len = @intCast(csf.flags.len),
|
||||
.lang = .init(csf.language),
|
||||
},
|
||||
.file = try addLazyPath(s, csf.file),
|
||||
})));
|
||||
}
|
||||
|
||||
fn addCSourceFiles(s: *Serialize, csf: *const std.Build.Module.CSourceFiles) !Configuration.CSourceFiles.Index {
|
||||
log.err("TODO addCSourceFiles trailing data", .{});
|
||||
const wc = s.wc;
|
||||
return @enumFromInt(try wc.addExtra(@as(Configuration.CSourceFiles, .{
|
||||
.flags = .{
|
||||
.args_len = @intCast(csf.flags.len),
|
||||
.lang = .init(csf.language),
|
||||
},
|
||||
.root = try addLazyPath(s, csf.root),
|
||||
.files_len = @intCast(csf.files.len),
|
||||
})));
|
||||
}
|
||||
|
||||
fn addRcSourceFile(s: *Serialize, rsf: *const std.Build.Module.RcSourceFile) !Configuration.RcSourceFile.Index {
|
||||
log.err("TODO addRcSourceFile trailing data", .{});
|
||||
const wc = s.wc;
|
||||
return @enumFromInt(try wc.addExtra(@as(Configuration.RcSourceFile, .{
|
||||
.file = try addLazyPath(s, rsf.file),
|
||||
.args_len = @intCast(rsf.flags.len),
|
||||
.include_paths_len = @intCast(rsf.include_paths.len),
|
||||
})));
|
||||
}
|
||||
|
||||
fn initStringList(s: *Serialize, list: []const []const u8) ![]const Configuration.String {
|
||||
const wc = s.wc;
|
||||
const result = try s.arena.alloc(Configuration.String, list.len);
|
||||
|
|
@ -312,6 +364,35 @@ const Serialize = struct {
|
|||
const arena = s.arena;
|
||||
const gpa = wc.gpa;
|
||||
|
||||
const include_dirs = try arena.alloc(Configuration.Module.IncludeDir, m.include_dirs.items.len);
|
||||
for (include_dirs, m.include_dirs.items) |*dest, src| dest.* = switch (src) {
|
||||
.path => |lp| .{ .path = try addLazyPath(s, lp) },
|
||||
.path_system => |lp| .{ .path_system = try addLazyPath(s, lp) },
|
||||
.path_after => |lp| .{ .path_after = try addLazyPath(s, lp) },
|
||||
.framework_path => |lp| .{ .framework_path = try addLazyPath(s, lp) },
|
||||
.framework_path_system => |lp| .{ .framework_path_system = try addLazyPath(s, lp) },
|
||||
.embed_path => |lp| .{ .embed_path = try addLazyPath(s, lp) },
|
||||
.other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
|
||||
.config_header_step => |chs| .{ .config_header_step = stepIndex(s, &chs.step) },
|
||||
};
|
||||
|
||||
const rpaths = try arena.alloc(Configuration.Module.RPath, m.rpaths.items.len);
|
||||
for (rpaths, m.rpaths.items) |*dest, src| dest.* = switch (src) {
|
||||
.lazy_path => |lp| .{ .lazy_path = try addLazyPath(s, lp) },
|
||||
.special => |slice| .{ .special = try wc.addString(slice) },
|
||||
};
|
||||
|
||||
const link_objects = try arena.alloc(Configuration.Module.LinkObject, m.link_objects.items.len);
|
||||
for (link_objects, m.link_objects.items) |*dest, *src| dest.* = switch (src.*) {
|
||||
.static_path => |lp| .{ .static_path = try addLazyPath(s, lp) },
|
||||
.other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
|
||||
.system_lib => |*sl| .{ .system_lib = try addSystemLib(s, sl) },
|
||||
.assembly_file => |lp| .{ .assembly_file = try addLazyPath(s, lp) },
|
||||
.c_source_file => |csf| .{ .c_source_file = try addCSourceFile(s, csf) },
|
||||
.c_source_files => |csf| .{ .c_source_files = try addCSourceFiles(s, csf) },
|
||||
.win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },
|
||||
};
|
||||
|
||||
const lib_paths = try arena.alloc(Configuration.LazyPath, m.lib_paths.items.len);
|
||||
for (lib_paths, m.lib_paths.items) |*dest, src| dest.* = try addLazyPath(s, src);
|
||||
|
||||
|
|
@ -352,11 +433,11 @@ const Serialize = struct {
|
|||
.fuzz = .init(m.strip),
|
||||
.code_model = m.code_model,
|
||||
.c_macros = c_macros.len != 0,
|
||||
.include_dirs = m.include_dirs.items.len != 0,
|
||||
.include_dirs = include_dirs.len != 0,
|
||||
.lib_paths = lib_paths.len != 0,
|
||||
.rpaths = m.rpaths.items.len != 0,
|
||||
.rpaths = rpaths.len != 0,
|
||||
.frameworks = m.frameworks.entries.len != 0,
|
||||
.link_objects = m.link_objects.items.len != 0,
|
||||
.link_objects = link_objects.len != 0,
|
||||
.export_symbol_names = export_symbol_names.len != 0,
|
||||
},
|
||||
.flags2 = .{
|
||||
|
|
@ -376,6 +457,9 @@ const Serialize = struct {
|
|||
.c_macros = .{ .slice = c_macros },
|
||||
.lib_paths = .{ .slice = lib_paths },
|
||||
.export_symbol_names = .{ .slice = export_symbol_names },
|
||||
.include_dirs = .init(include_dirs),
|
||||
.rpaths = .init(rpaths),
|
||||
.link_objects = .init(link_objects),
|
||||
})));
|
||||
|
||||
log.err("TODO serialize the trailing Module data", .{});
|
||||
|
|
@ -384,6 +468,10 @@ const Serialize = struct {
|
|||
|
||||
return module_index;
|
||||
}
|
||||
|
||||
fn stepIndex(s: *const Serialize, step: *Step) Configuration.Step.Index {
|
||||
return @enumFromInt(s.step_map.getIndex(step).?);
|
||||
}
|
||||
};
|
||||
|
||||
fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
|
||||
|
|
@ -396,34 +484,32 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
|
|||
// Starting from all top-level steps in `b`, traverse the entire step graph
|
||||
// and add all step dependencies implied by module graphs.
|
||||
const top_level_steps = b.top_level_steps.values();
|
||||
// Index corresponds to `Configuration.steps` index.
|
||||
var step_map: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty;
|
||||
try step_map.ensureUnusedCapacity(arena, top_level_steps.len);
|
||||
try s.step_map.ensureUnusedCapacity(arena, top_level_steps.len);
|
||||
for (top_level_steps) |tls| {
|
||||
step_map.putAssumeCapacityNoClobber(&tls.step, {});
|
||||
s.step_map.putAssumeCapacityNoClobber(&tls.step, {});
|
||||
}
|
||||
{
|
||||
while (wc.steps.items.len < step_map.count()) {
|
||||
const step = step_map.keys()[wc.steps.items.len];
|
||||
while (wc.steps.items.len < s.step_map.count()) {
|
||||
const step = s.step_map.keys()[wc.steps.items.len];
|
||||
|
||||
// Set up any implied dependencies for this step. It's important that we do this first, so
|
||||
// that the loop below discovers steps implied by the module graph.
|
||||
try createModuleDependenciesForStep(step);
|
||||
|
||||
try step_map.ensureUnusedCapacity(arena, step.dependencies.items.len);
|
||||
try s.step_map.ensureUnusedCapacity(arena, step.dependencies.items.len);
|
||||
for (step.dependencies.items) |other_step| {
|
||||
step_map.putAssumeCapacity(other_step, {});
|
||||
s.step_map.putAssumeCapacity(other_step, {});
|
||||
}
|
||||
|
||||
// Add and then de-duplicate dependencies.
|
||||
const deps = d: {
|
||||
const deps: Configuration.Deps = @enumFromInt(wc.extra.items.len);
|
||||
for (try wc.reserveLengthPrefixed(step.dependencies.items.len), step.dependencies.items) |*dep, dep_step|
|
||||
dep.* = @intCast(step_map.getIndex(dep_step).?);
|
||||
dep.* = @intCast(s.step_map.getIndex(dep_step).?);
|
||||
break :d try wc.dedupeDeps(deps);
|
||||
};
|
||||
|
||||
try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity);
|
||||
try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);
|
||||
wc.steps.appendAssumeCapacity(.{
|
||||
.name = try wc.addString(step.name),
|
||||
.owner = try s.builderToPackage(step.owner),
|
||||
|
|
@ -613,7 +699,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
|
|||
.emitted_pdb = try s.addOptionalLazyPathEnum(ia.emitted_pdb),
|
||||
.h_dir = try addInstallDir(wc, ia.h_dir),
|
||||
.emitted_h = try s.addOptionalLazyPathEnum(ia.emitted_h),
|
||||
.artifact = stepIndex(&step_map, &ia.artifact.step),
|
||||
.artifact = s.stepIndex(&ia.artifact.step),
|
||||
})));
|
||||
},
|
||||
.install_file => @panic("TODO"),
|
||||
|
|
@ -688,7 +774,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
|
|||
}
|
||||
|
||||
try wc.write(writer, .{
|
||||
.default_step = stepIndex(&step_map, b.default_step),
|
||||
.default_step = s.stepIndex(b.default_step),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -714,10 +800,6 @@ fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Co
|
|||
}
|
||||
}
|
||||
|
||||
fn stepIndex(step_map: *const std.AutoArrayHashMapUnmanaged(*Step, void), step: *Step) Configuration.Step.Index {
|
||||
return @enumFromInt(step_map.getIndex(step).?);
|
||||
}
|
||||
|
||||
/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
|
||||
/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
|
||||
fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
|
||||
|
|
|
|||
|
|
@ -73,18 +73,8 @@ pub const SystemLib = struct {
|
|||
preferred_link_mode: std.builtin.LinkMode,
|
||||
search_strategy: SystemLib.SearchStrategy,
|
||||
|
||||
pub const UsePkgConfig = enum {
|
||||
/// Don't use pkg-config, just pass -lfoo where foo is name.
|
||||
no,
|
||||
/// Try to get information on how to link the library from pkg-config.
|
||||
/// If that fails, fall back to passing -lfoo where foo is name.
|
||||
yes,
|
||||
/// Try to get information on how to link the library from pkg-config.
|
||||
/// If that fails, error out.
|
||||
force,
|
||||
};
|
||||
|
||||
pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
|
||||
pub const UsePkgConfig = std.Build.Configuration.SystemLib.UsePkgConfig;
|
||||
pub const SearchStrategy = std.Build.Configuration.SystemLib.SearchStrategy;
|
||||
};
|
||||
|
||||
pub const CSourceLanguage = enum {
|
||||
|
|
|
|||
|
|
@ -863,7 +863,7 @@ pub const OutputMode = enum {
|
|||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub const LinkMode = enum {
|
||||
pub const LinkMode = enum(u1) {
|
||||
static,
|
||||
dynamic,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1071,9 +1071,6 @@ pub const Package = struct {
|
|||
|
||||
/// Trailing:
|
||||
/// * frameworks: FlagsPrefixedList(FrameworkFlags), // if flag is set
|
||||
/// * include_dirs: UnionList(IncludeDir), // if flag is set
|
||||
/// * rpaths: UnionList(RPath), // if flag is set
|
||||
/// * link_objects: UnionList(LinkObject), // if flag is set
|
||||
pub const Module = struct {
|
||||
flags: Flags,
|
||||
flags2: Flags2,
|
||||
|
|
@ -1084,6 +1081,9 @@ pub const Module = struct {
|
|||
c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
|
||||
lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath),
|
||||
export_symbol_names: Storage.FlagLengthPrefixedList(.flags, .export_symbol_names, String),
|
||||
include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir),
|
||||
rpaths: Storage.UnionList(.flags, .rpaths, RPath),
|
||||
link_objects: Storage.UnionList(.flags, .link_objects, LinkObject),
|
||||
|
||||
pub const Optimize = enum(u3) {
|
||||
debug,
|
||||
|
|
@ -1204,7 +1204,7 @@ pub const Module = struct {
|
|||
static_path: LazyPath,
|
||||
/// Always `Step.Tag.compile`.
|
||||
other_step: Step.Index,
|
||||
system_lib: SystemLib,
|
||||
system_lib: SystemLib.Index,
|
||||
assembly_file: LazyPath,
|
||||
c_source_file: CSourceFile.Index,
|
||||
c_source_files: CSourceFiles.Index,
|
||||
|
|
@ -1328,8 +1328,18 @@ pub const SystemLib = struct {
|
|||
_,
|
||||
};
|
||||
|
||||
pub const UsePkgConfig = enum(u2) { no, yes, force };
|
||||
pub const LinkMode = enum { static, dynamic };
|
||||
pub const UsePkgConfig = enum(u2) {
|
||||
/// Don't use pkg-config, just pass -lfoo where foo is name.
|
||||
no,
|
||||
/// Try to get information on how to link the library from pkg-config.
|
||||
/// If that fails, fall back to passing -lfoo where foo is name.
|
||||
yes,
|
||||
/// Try to get information on how to link the library from pkg-config.
|
||||
/// If that fails, error out.
|
||||
force,
|
||||
};
|
||||
|
||||
pub const LinkMode = std.builtin.LinkMode;
|
||||
|
||||
pub const Flags = packed struct(u32) {
|
||||
needed: bool,
|
||||
|
|
@ -1337,18 +1347,19 @@ pub const SystemLib = struct {
|
|||
use_pkg_config: UsePkgConfig,
|
||||
preferred_link_mode: LinkMode,
|
||||
search_strategy: SearchStrategy,
|
||||
_: u25 = 0,
|
||||
};
|
||||
|
||||
pub const SearchStrategy = enum(u2) { paths_first, mode_first, no_fallback };
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// * flag: String, // for each flags_len
|
||||
/// * arg: String, // for each args_len
|
||||
/// * sub_path: String, // for each files_len
|
||||
pub const CSourceFiles = struct {
|
||||
flags: Flags,
|
||||
root: LazyPath,
|
||||
files_len: u32,
|
||||
flags: Flags,
|
||||
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
|
|
@ -1356,16 +1367,16 @@ pub const CSourceFiles = struct {
|
|||
|
||||
pub const Flags = packed struct(u32) {
|
||||
/// C compiler CLI flags.
|
||||
flags_len: u29,
|
||||
args_len: u29,
|
||||
lang: OptionalCSourceLanguage,
|
||||
};
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// * flag: String, // for each flags_len
|
||||
/// * arg: String, // for each args_len
|
||||
pub const CSourceFile = struct {
|
||||
file: LazyPath,
|
||||
flags: Flags,
|
||||
file: LazyPath,
|
||||
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
|
|
@ -1373,11 +1384,24 @@ pub const CSourceFile = struct {
|
|||
|
||||
pub const Flags = packed struct(u32) {
|
||||
/// C compiler CLI flags.
|
||||
flags_len: u29,
|
||||
args_len: u29,
|
||||
lang: OptionalCSourceLanguage,
|
||||
};
|
||||
};
|
||||
|
||||
/// Trailing:
|
||||
/// * arg: String, // for each args_len
|
||||
/// * include_path: String, // for each include_paths_len
|
||||
pub const RcSourceFile = struct {
|
||||
file: LazyPath,
|
||||
args_len: u32,
|
||||
include_paths_len: u32,
|
||||
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
};
|
||||
};
|
||||
|
||||
pub const OptionalCSourceLanguage = enum(u3) {
|
||||
c,
|
||||
cpp,
|
||||
|
|
@ -1386,29 +1410,17 @@ pub const OptionalCSourceLanguage = enum(u3) {
|
|||
assembly,
|
||||
assembly_with_preprocessor,
|
||||
default,
|
||||
};
|
||||
|
||||
pub const RcSourceFile = struct {
|
||||
file: LazyPath,
|
||||
/// Any option that rc.exe accepts will work here, with the exception of:
|
||||
/// - `/fo`: The output filename is set by the build system
|
||||
/// - `/p`: Only running the preprocessor is not supported in this context
|
||||
/// - `/:no-preprocess` (non-standard option): Not supported in this context
|
||||
/// - Any MUI-related option
|
||||
/// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line-
|
||||
///
|
||||
/// Implicitly defined options:
|
||||
/// /x (ignore the INCLUDE environment variable)
|
||||
/// /D_DEBUG or /DNDEBUG depending on the optimization mode
|
||||
flags: []const []const u8 = &.{},
|
||||
/// Include paths that may or may not exist yet and therefore need to be
|
||||
/// specified as a LazyPath. Each path will be appended to the flags
|
||||
/// as `/I <resolved path>`.
|
||||
include_paths: []const LazyPath = &.{},
|
||||
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
};
|
||||
pub fn init(x: ?std.Build.Module.CSourceLanguage) @This() {
|
||||
return switch (x orelse return .default) {
|
||||
.c => .c,
|
||||
.cpp => .cpp,
|
||||
.objective_c => .objective_c,
|
||||
.objective_cpp => .objective_cpp,
|
||||
.assembly => .assembly,
|
||||
.assembly_with_preprocessor => .assembly_with_preprocessor,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ResolvedTarget = struct {
|
||||
|
|
@ -1691,6 +1703,7 @@ pub const Storage = enum {
|
|||
enum_optional,
|
||||
extended,
|
||||
flag_length_prefixed_list,
|
||||
union_list,
|
||||
|
||||
/// The presence of the field is determined by a boolean within a packed
|
||||
/// struct.
|
||||
|
|
@ -1769,6 +1782,63 @@ pub const Storage = enum {
|
|||
};
|
||||
}
|
||||
|
||||
/// `UnionArg` is a tagged union with a small integer for the enum tag.
|
||||
///
|
||||
/// A field in flags determines whether the metadata is present.
|
||||
///
|
||||
/// The metadata is bit-packed consecutive packed struct which is the
|
||||
/// `UnionArg` enum tag combined with a "last" marker boolean field.
|
||||
/// When "last" is true, the element is the last one, providing
|
||||
/// the length of the list.
|
||||
///
|
||||
/// Following is each element of the list; each bitcastable to u32.
|
||||
pub fn UnionList(
|
||||
comptime flags_arg: @EnumLiteral(),
|
||||
comptime flag_arg: @EnumLiteral(),
|
||||
comptime UnionArg: type,
|
||||
) type {
|
||||
return struct {
|
||||
/// When serializing it is UnionArg slice pointer.
|
||||
/// When deserializing it is extra index of first UnionArg element.
|
||||
data: ?*const anyopaque,
|
||||
len: usize,
|
||||
|
||||
pub const storage: Storage = .union_list;
|
||||
pub const flags = flags_arg;
|
||||
pub const flag = flag_arg;
|
||||
pub const Union = UnionArg;
|
||||
|
||||
pub const Tag = @typeInfo(Union).@"union".tag_type.?;
|
||||
pub const MetaInt = @Int(.unsigned, @bitSizeOf(Tag) + 1);
|
||||
pub const Meta = packed struct(MetaInt) {
|
||||
tag: Tag,
|
||||
last: bool,
|
||||
};
|
||||
|
||||
/// Valid to call only when serializing.
|
||||
pub fn init(slice: []const Union) @This() {
|
||||
return .{ .data = slice.ptr, .len = slice.len };
|
||||
}
|
||||
|
||||
/// Valid to call only when deserializing.
|
||||
pub fn get(this: *const @This(), extra: []const u32) []const u32 {
|
||||
return extra[@intFromPtr(this.data)..][0..this.len];
|
||||
}
|
||||
|
||||
/// Valid to call only when deserializing.
|
||||
pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag {
|
||||
_ = this;
|
||||
_ = extra;
|
||||
_ = i;
|
||||
@panic("TODO implement UnionList.tag");
|
||||
}
|
||||
|
||||
fn extraLen(len: usize) usize {
|
||||
return len + (len * @bitSizeOf(Meta) + 31) / 32;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {
|
||||
var end = i;
|
||||
_ = data(buffer, &end, S);
|
||||
|
|
@ -1848,6 +1918,23 @@ pub const Storage = enum {
|
|||
defer i.* = data_start + len;
|
||||
return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };
|
||||
},
|
||||
.union_list => {
|
||||
const flags = @field(container, @tagName(Field.flags));
|
||||
const flag = @field(flags, @tagName(Field.flag));
|
||||
if (!flag) return .{ .data = null, .len = 0 };
|
||||
const meta_start = i.*;
|
||||
const meta_buffer = buffer[meta_start..];
|
||||
var len: u32 = 0;
|
||||
var bit_offset: usize = 0;
|
||||
while (true) : (bit_offset += @bitSizeOf(Field.Meta)) {
|
||||
const meta = loadBits(u32, meta_buffer, bit_offset, Field.Meta);
|
||||
len += 1;
|
||||
if (meta.last) break;
|
||||
}
|
||||
const end = meta_start + Field.extraLen(len);
|
||||
i.* = end;
|
||||
return .{ .data = end - len, .len = len };
|
||||
},
|
||||
},
|
||||
},
|
||||
.@"extern" => comptime unreachable,
|
||||
|
|
@ -1884,6 +1971,7 @@ pub const Storage = enum {
|
|||
.auto => switch (Field.storage) {
|
||||
.flag_optional, .enum_optional, .extended => 1,
|
||||
.flag_length_prefixed_list => field.slice.len + 1,
|
||||
.union_list => Field.extraLen(field.len),
|
||||
},
|
||||
.@"extern" => comptime unreachable,
|
||||
},
|
||||
|
|
@ -1947,6 +2035,33 @@ pub const Storage = enum {
|
|||
@memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice)));
|
||||
return len + 1;
|
||||
},
|
||||
.union_list => {
|
||||
if (value.len == 0) return 0;
|
||||
const Tag = @typeInfo(Field.Union).@"union".tag_type.?;
|
||||
const slice_ptr: [*]const Field.Union = @ptrCast(@alignCast(value.data));
|
||||
const slice = slice_ptr[0..value.len];
|
||||
const meta_buffer = buffer[i..][0 .. (slice.len * @bitSizeOf(Field.Meta) + 31) / 32];
|
||||
for (slice[0 .. slice.len - 1], 0..) |elem, elem_index| {
|
||||
const union_tag: Tag = elem;
|
||||
storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
|
||||
.tag = union_tag,
|
||||
.last = false,
|
||||
}));
|
||||
} else {
|
||||
const elem_index = slice.len - 1;
|
||||
const elem = slice[elem_index];
|
||||
const union_tag: Tag = elem;
|
||||
storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
|
||||
.tag = union_tag,
|
||||
.last = true,
|
||||
}));
|
||||
}
|
||||
var total: usize = meta_buffer.len;
|
||||
for (i + meta_buffer.len.., slice) |elem_index, src| switch (src) {
|
||||
inline else => |x| total += setExtraField(buffer, elem_index, @TypeOf(x), x),
|
||||
};
|
||||
return total;
|
||||
},
|
||||
},
|
||||
},
|
||||
.@"extern" => comptime unreachable,
|
||||
|
|
@ -2000,3 +2115,48 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {
|
|||
try reader.readVecAll(&vecs);
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn loadBits(comptime Int: type, buffer: []const Int, bit_offset: usize, comptime Result: type) Result {
|
||||
const index = bit_offset / @bitSizeOf(Int);
|
||||
const small_bit_offset = bit_offset % @bitSizeOf(Int);
|
||||
const ResultInt = @Int(.unsigned, @bitSizeOf(Result));
|
||||
const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset));
|
||||
const available_bits = @bitSizeOf(Int) - small_bit_offset;
|
||||
if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result);
|
||||
const missing_bits = @bitSizeOf(ResultInt) - available_bits;
|
||||
const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1));
|
||||
return @bitCast(result | (upper << @intCast(available_bits)));
|
||||
}
|
||||
|
||||
pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void {
|
||||
const Value = @TypeOf(value);
|
||||
const ValueInt = @Int(.unsigned, @bitSizeOf(Value));
|
||||
const value_int: ValueInt = @bitCast(value);
|
||||
const index = bit_offset / @bitSizeOf(Int);
|
||||
const small_bit_offset = bit_offset % @bitSizeOf(Int);
|
||||
const available_bits = @bitSizeOf(Int) - small_bit_offset;
|
||||
if (available_bits >= @bitSizeOf(ValueInt)) {
|
||||
buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset));
|
||||
buffer[index] |= @as(Int, value_int) << @intCast(small_bit_offset);
|
||||
} else {
|
||||
const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2);
|
||||
const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]);
|
||||
ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset));
|
||||
ptr.* |= @as(DoubleInt, value_int) << @intCast(small_bit_offset);
|
||||
}
|
||||
}
|
||||
|
||||
test "loadBits and storeBits" {
|
||||
var buffer: [2]u32 = .{
|
||||
0b01111111000000001111111100000000,
|
||||
0b11111111000000001111111100000100,
|
||||
};
|
||||
try std.testing.expectEqual(0b100, loadBits(u32, &buffer, 6, u3));
|
||||
try std.testing.expectEqual(0b100011, loadBits(u32, &buffer, 29, u6));
|
||||
|
||||
storeBits(u32, &buffer, 6, @as(u3, 0b010));
|
||||
storeBits(u32, &buffer, 29, @as(u6, 0b010010));
|
||||
|
||||
try std.testing.expectEqual(0b010, loadBits(u32, &buffer, 6, u3));
|
||||
try std.testing.expectEqual(0b010010, loadBits(u32, &buffer, 29, u6));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue