authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-02 19:46:22-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
log8aec13b6ab1f0a8586f3e5cf5600d70697ac209d
treea22b8daa2c6eefa3299b0be35d5495b04a640402
parent8331c59ee2ec3e57ffcc4c0ae57792a995afa865

Configuration: serialize remaining Module information

also handle properly Module circular references and introduce a general deduplication mechanism.

4 files changed, 172 insertions(+), 109 deletions(-)

lib/compiler/Maker/ScannedConfig.zig+2
...@@ -87,6 +87,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi...@@ -87,6 +87,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
87 .union_list => comptime unreachable,87 .union_list => comptime unreachable,
88 .length_prefixed_list => comptime unreachable,88 .length_prefixed_list => comptime unreachable,
89 .flag_union => comptime unreachable,89 .flag_union => comptime unreachable,
90 .multi_list => comptime unreachable,
90 } else if (std.enums.tagName(Field, field_value)) |name| {91 } else if (std.enums.tagName(Field, field_value)) |name| {
91 try s.ident(name);92 try s.ident(name);
92 } else {93 } else {
...@@ -111,6 +112,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi...@@ -111,6 +112,7 @@ fn printValue(sc: *const ScannedConfig, s: *Serializer, comptime Field: type, fi
111 .extended => @compileError("TODO"),112 .extended => @compileError("TODO"),
112 .union_list => @compileError("TODO"),113 .union_list => @compileError("TODO"),
113 .flag_union => try printValue(sc, s, Field.Union, field_value.u),114 .flag_union => try printValue(sc, s, Field.Union, field_value.u),
115 .multi_list => @compileError("TODO"),
114 },116 },
115 else => @compileError("not implemented: " ++ @typeName(Field)),117 else => @compileError("not implemented: " ++ @typeName(Field)),
116 },118 },
lib/compiler/configurer.zig+55-48
...@@ -294,9 +294,8 @@ const Serialize = struct {...@@ -294,9 +294,8 @@ const Serialize = struct {
294 }294 }
295295
296 fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {296 fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {
297 log.err("TODO deduplicate addSystemLib", .{});
298 const wc = s.wc;297 const wc = s.wc;
299 return @enumFromInt(try wc.addExtra(@as(Configuration.SystemLib, .{298 return @enumFromInt(try wc.addDeduped(@as(Configuration.SystemLib, .{
300 .flags = .{299 .flags = .{
301 .needed = sl.needed,300 .needed = sl.needed,
302 .weak = sl.weak,301 .weak = sl.weak,
...@@ -362,7 +361,6 @@ const Serialize = struct {...@@ -362,7 +361,6 @@ const Serialize = struct {
362361
363 const wc = s.wc;362 const wc = s.wc;
364 const arena = s.arena;363 const arena = s.arena;
365 const gpa = wc.gpa;
366364
367 const include_dirs = try arena.alloc(Configuration.Module.IncludeDir, m.include_dirs.items.len);365 const include_dirs = try arena.alloc(Configuration.Module.IncludeDir, m.include_dirs.items.len);
368 for (include_dirs, m.include_dirs.items) |*dest, src| dest.* = switch (src) {366 for (include_dirs, m.include_dirs.items) |*dest, src| dest.* = switch (src) {
...@@ -393,66 +391,55 @@ const Serialize = struct {...@@ -393,66 +391,55 @@ const Serialize = struct {
393 .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },391 .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },
394 };392 };
395393
394 const frameworks = try arena.alloc(Configuration.Module.Framework, m.frameworks.entries.len);
395 for (frameworks, m.frameworks.keys(), m.frameworks.values()) |*dest, name, options| dest.* = .{
396 .flags = .{
397 .needed = options.needed,
398 .weak = options.weak,
399 },
400 .name = try wc.addString(name),
401 };
402
396 const lib_paths = try arena.alloc(Configuration.LazyPath, m.lib_paths.items.len);403 const lib_paths = try arena.alloc(Configuration.LazyPath, m.lib_paths.items.len);
397 for (lib_paths, m.lib_paths.items) |*dest, src| dest.* = try addLazyPath(s, src);404 for (lib_paths, m.lib_paths.items) |*dest, src| dest.* = try addLazyPath(s, src);
398405
399 const c_macros = try initStringList(s, m.c_macros.items);406 const c_macros = try initStringList(s, m.c_macros.items);
400 const export_symbol_names = try initStringList(s, m.export_symbol_names);407 const export_symbol_names = try initStringList(s, m.export_symbol_names);
401408
402 const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len);
403 const import_table_extra_len = 1 + 2 * m.import_table.entries.len;
404 try wc.extra.ensureUnusedCapacity(gpa, import_table_extra_len);
405 wc.extra.items.len += import_table_extra_len;
406 wc.extra.appendAssumeCapacity(@intCast(m.import_table.entries.len));
407 wc.extra.items[@intFromEnum(import_table)] = @intCast(m.import_table.entries.len);
408 for (
409 m.import_table.keys(),
410 @intFromEnum(import_table) + 1..,
411 ) |mod_name, extra_index| {
412 wc.extra.items[extra_index] = @intFromEnum(try wc.addString(mod_name));
413 }
414 for (
415 m.import_table.values(),
416 @intFromEnum(import_table) + 1 + m.import_table.entries.len..,
417 ) |dep, extra_index| {
418 log.err("TODO module dependencies can be cyclic", .{});
419 wc.extra.items[extra_index] = @intFromEnum(try addModule(s, dep));
420 }
421
422 const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{409 const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{
423 .flags = .{410 .flags = .{
424 .optimize = .init(m.optimize),411 .optimize = .init(m.optimize),
425 .strip = .init(m.strip),412 .strip = .init(m.strip),
426 .unwind_tables = .init(m.unwind_tables),413 .unwind_tables = .init(m.unwind_tables),
427 .dwarf_format = .init(m.dwarf_format),414 .dwarf_format = .init(m.dwarf_format),
428 .single_threaded = .init(m.strip),415 .single_threaded = .init(m.single_threaded),
429 .stack_protector = .init(m.strip),416 .stack_protector = .init(m.stack_protector),
430 .stack_check = .init(m.strip),417 .stack_check = .init(m.stack_check),
431 .sanitize_c = .init(m.sanitize_c),418 .sanitize_c = .init(m.sanitize_c),
432 .sanitize_thread = .init(m.strip),419 .sanitize_thread = .init(m.sanitize_thread),
433 .fuzz = .init(m.strip),420 .fuzz = .init(m.fuzz),
434 .code_model = m.code_model,421 .code_model = m.code_model,
435 .c_macros = c_macros.len != 0,422 .c_macros = c_macros.len != 0,
436 .include_dirs = include_dirs.len != 0,423 .include_dirs = include_dirs.len != 0,
437 .lib_paths = lib_paths.len != 0,424 .lib_paths = lib_paths.len != 0,
438 .rpaths = rpaths.len != 0,425 .rpaths = rpaths.len != 0,
439 .frameworks = m.frameworks.entries.len != 0,426 .frameworks = frameworks.len != 0,
440 .link_objects = link_objects.len != 0,427 .link_objects = link_objects.len != 0,
441 .export_symbol_names = export_symbol_names.len != 0,428 .export_symbol_names = export_symbol_names.len != 0,
442 },429 },
443 .flags2 = .{430 .flags2 = .{
444 .valgrind = .init(m.strip),431 .valgrind = .init(m.valgrind),
445 .pic = .init(m.strip),432 .pic = .init(m.pic),
446 .red_zone = .init(m.strip),433 .red_zone = .init(m.red_zone),
447 .omit_frame_pointer = .init(m.strip),434 .omit_frame_pointer = .init(m.omit_frame_pointer),
448 .error_tracing = .init(m.strip),435 .error_tracing = .init(m.error_tracing),
449 .link_libc = .init(m.strip),436 .link_libc = .init(m.link_libc),
450 .link_libcpp = .init(m.strip),437 .link_libcpp = .init(m.link_libcpp),
451 .no_builtin = .init(m.strip),438 .no_builtin = .init(m.no_builtin),
452 },439 },
453 .owner = try s.builderToPackage(m.owner),440 .owner = try s.builderToPackage(m.owner),
454 .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file),441 .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file),
455 .import_table = import_table,442 .import_table = .invalid,
456 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),443 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
457 .c_macros = .{ .slice = c_macros },444 .c_macros = .{ .slice = c_macros },
458 .lib_paths = .{ .slice = lib_paths },445 .lib_paths = .{ .slice = lib_paths },
...@@ -460,12 +447,33 @@ const Serialize = struct {...@@ -460,12 +447,33 @@ const Serialize = struct {
460 .include_dirs = .init(include_dirs),447 .include_dirs = .init(include_dirs),
461 .rpaths = .init(rpaths),448 .rpaths = .init(rpaths),
462 .link_objects = .init(link_objects),449 .link_objects = .init(link_objects),
450 .frameworks = .{ .slice = frameworks },
463 })));451 })));
464452
465 log.err("TODO serialize the trailing Module data", .{});453 // The import table is the only place that modules can form dependency
466454 // loops. Therefore, we populate the module indexes only after adding
455 // the module to module_map.
467 try s.module_map.putNoClobber(arena, m, module_index);456 try s.module_map.putNoClobber(arena, m, module_index);
468457
458 var imports = try std.MultiArrayList(Configuration.ImportTable.Import).initCapacity(arena, m.import_table.entries.len);
459 imports.len = m.import_table.entries.len;
460 for (
461 imports.items(.name),
462 imports.items(.module),
463 m.import_table.keys(),
464 m.import_table.values(),
465 ) |*dest_name, *dest_module, src_name, src_module| {
466 dest_name.* = try wc.addString(src_name);
467 dest_module.* = try addModule(s, src_module);
468 }
469
470 comptime assert(std.mem.eql(u8, @typeInfo(Configuration.Module).@"struct".fields[2].name, "import_table"));
471 comptime assert(@typeInfo(Configuration.Module).@"struct".fields[2].type == Configuration.ImportTable.Index);
472 assert(wc.extra.items[@intFromEnum(module_index) + 2] == @intFromEnum(Configuration.ImportTable.Index.invalid));
473 wc.extra.items[@intFromEnum(module_index) + 2] = try wc.addDeduped(@as(Configuration.ImportTable, .{
474 .imports = .{ .mal = imports },
475 }));
476
469 return module_index;477 return module_index;
470 }478 }
471479
...@@ -502,12 +510,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -502,12 +510,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
502 }510 }
503511
504 // Add and then de-duplicate dependencies.512 // Add and then de-duplicate dependencies.
505 const deps = d: {513 const dep_steps = try arena.alloc(Configuration.Step.Index, step.dependencies.items.len);
506 const deps: Configuration.Deps = @enumFromInt(wc.extra.items.len);514 for (dep_steps, step.dependencies.items) |*dest, src|
507 for (try wc.reserveLengthPrefixed(step.dependencies.items.len), step.dependencies.items) |*dep, dep_step|515 dest.* = @enumFromInt(s.step_map.getIndex(src).?);
508 dep.* = @intCast(s.step_map.getIndex(dep_step).?);516 const deps: Configuration.Deps.Index = @enumFromInt(try wc.addDeduped(@as(Configuration.Deps, .{
509 break :d try wc.dedupeDeps(deps);517 .steps = .{ .slice = dep_steps },
510 };518 })));
511519
512 try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);520 try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);
513 wc.steps.appendAssumeCapacity(.{521 wc.steps.appendAssumeCapacity(.{
...@@ -791,8 +799,7 @@ fn addOptionalResolvedTarget(...@@ -791,8 +799,7 @@ fn addOptionalResolvedTarget(
791 optional_resolved_target: ?std.Build.ResolvedTarget,799 optional_resolved_target: ?std.Build.ResolvedTarget,
792) !Configuration.ResolvedTarget.OptionalIndex {800) !Configuration.ResolvedTarget.OptionalIndex {
793 const resolved_target = optional_resolved_target orelse return .none;801 const resolved_target = optional_resolved_target orelse return .none;
794 log.debug("TODO deduplicate resolved targets", .{});802 return @enumFromInt(try wc.addDeduped(@as(Configuration.ResolvedTarget, .{
795 return @enumFromInt(try wc.addExtra(@as(Configuration.ResolvedTarget, .{
796 .query = try wc.addTargetQuery(resolved_target.query),803 .query = try wc.addTargetQuery(resolved_target.query),
797 .result = try wc.addTarget(resolved_target.result),804 .result = try wc.addTarget(resolved_target.result),
798 })));805 })));
lib/std/Build/Step/Compile.zig+1
...@@ -43,6 +43,7 @@ export_memory: bool = false,...@@ -43,6 +43,7 @@ export_memory: bool = false,
43/// For WebAssembly targets, this will allow for undefined symbols to43/// For WebAssembly targets, this will allow for undefined symbols to
44/// be imported from the host environment.44/// be imported from the host environment.
45import_symbols: bool = false,45import_symbols: bool = false,
46/// (WebAssembly) import function table from the host environment
46import_table: bool = false,47import_table: bool = false,
47export_table: bool = false,48export_table: bool = false,
48initial_memory: ?u64 = null,49initial_memory: ?u64 = null,
lib/std/zig/Configuration.zig+114-61
...@@ -33,9 +33,8 @@ pub const Header = extern struct {...@@ -33,9 +33,8 @@ pub const Header = extern struct {
33pub const Wip = struct {33pub const Wip = struct {
34 gpa: Allocator,34 gpa: Allocator,
35 string_table: StringTable = .empty,35 string_table: StringTable = .empty,
36 /// De-duplicates an array inside `extra` that has first element length36 /// De-duplicates an array inside `extra`.
37 /// followed by length elements.37 dedupe_table: DedupeTable = .empty,
38 length_prefixed_table: LengthPrefixedTable = .empty,
39 targets_table: TargetsTable = .empty,38 targets_table: TargetsTable = .empty,
4039
41 string_bytes: std.ArrayList(u8) = .empty,40 string_bytes: std.ArrayList(u8) = .empty,
...@@ -46,25 +45,27 @@ pub const Wip = struct {...@@ -46,25 +45,27 @@ pub const Wip = struct {
46 path_deps: std.MultiArrayList(Path) = .empty,45 path_deps: std.MultiArrayList(Path) = .empty,
47 extra: std.ArrayList(u32) = .empty,46 extra: std.ArrayList(u32) = .empty,
4847
49 const LengthPrefixedTable = std.HashMapUnmanaged(u32, void, LengthPrefixedContext, std.hash_map.default_max_load_percentage);48 const DedupeTable = std.HashMapUnmanaged(ExtraSlice, void, ExtraSlice.Context, std.hash_map.default_max_load_percentage);
50 const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage);49 const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage);
5150
52 const LengthPrefixedContext = struct {51 const ExtraSlice = struct {
53 extra: []const u32,52 index: u32,
53 len: u32,
5454
55 pub fn eql(ctx: @This(), a: u32, b: u32) bool {55 const Context = struct {
56 const len_a = ctx.extra[a];56 extra: []const u32,
57 const len_b = ctx.extra[b];
58 const slice_a = ctx.extra[a + 1 ..][0..len_a];
59 const slice_b = ctx.extra[b + 1 ..][0..len_b];
60 return std.mem.eql(u32, slice_a, slice_b);
61 }
6257
63 pub fn hash(ctx: @This(), key: u32) u64 {58 pub fn eql(ctx: @This(), a: ExtraSlice, b: ExtraSlice) bool {
64 const len = ctx.extra[key];59 const slice_a = ctx.extra[a.index..][0..a.len];
65 const slice = ctx.extra[key + 1 ..][0..len];60 const slice_b = ctx.extra[b.index..][0..b.len];
66 return std.hash_map.hashString(@ptrCast(slice));61 return std.mem.eql(u32, slice_a, slice_b);
67 }62 }
63
64 pub fn hash(ctx: @This(), key: ExtraSlice) u64 {
65 const slice = ctx.extra[key.index..][0..key.len];
66 return std.hash_map.hashString(@ptrCast(slice));
67 }
68 };
68 };69 };
6970
70 const TargetsTableContext = struct {71 const TargetsTableContext = struct {
...@@ -323,34 +324,33 @@ pub const Wip = struct {...@@ -323,34 +324,33 @@ pub const Wip = struct {
323 }324 }
324 }325 }
325326
326 pub fn reserveLengthPrefixed(wip: *Wip, n: usize) Allocator.Error![]u32 {327 pub fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 {
327 const slice = try wip.extra.addManyAsSlice(wip.gpa, n + 1);328 const extra_len = Storage.extraLen(extra);
328 slice[0] = @intCast(n);329 try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len);
329 return slice[1..];330 return addExtraAssumeCapacity(wip, extra);
330 }331 }
331332
332 pub fn dedupeLengthPrefixed(wip: *Wip, index: u32) Allocator.Error!u32 {333 /// Same as `addExtra` but uses a hash map to possibly return an already
333 assert(wip.extra.items.len == index + wip.extra.items[index] + 1);334 /// existing index instead of appending to `extra`.
335 pub fn addDeduped(wip: *Wip, extra: anytype) Allocator.Error!u32 {
334 const gpa = wip.gpa;336 const gpa = wip.gpa;
335 const gop = try wip.length_prefixed_table.getOrPutContext(gpa, index, @as(LengthPrefixedContext, .{337 const revert_index = wip.extra.items.len;
336 .extra = wip.extra.items,338 const extra_len = Storage.extraLen(extra);
337 }));339 try wip.extra.ensureUnusedCapacity(gpa, extra_len);
340 const new_index = addExtraAssumeCapacity(wip, extra);
341 const len: u32 = @intCast(wip.extra.items.len - new_index);
342
343 const gop = try wip.dedupe_table.getOrPutContext(gpa, .{
344 .index = new_index,
345 .len = len,
346 }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items }));
347
338 if (gop.found_existing) {348 if (gop.found_existing) {
339 wip.extra.items.len = index;349 wip.extra.items.len = revert_index;
340 return gop.key_ptr.*;350 return gop.key_ptr.index;
341 } else {
342 return index;
343 }351 }
344 }
345352
346 pub fn dedupeDeps(wip: *Wip, deps: Deps) Allocator.Error!Deps {353 return new_index;
347 return @enumFromInt(try dedupeLengthPrefixed(wip, @intFromEnum(deps)));
348 }
349
350 pub fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 {
351 const extra_len = Storage.extraLen(extra);
352 try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len);
353 return addExtraAssumeCapacity(wip, extra);
354 }354 }
355355
356 pub fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 {356 pub fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 {
...@@ -399,7 +399,7 @@ pub const AvailableOption = extern struct {...@@ -399,7 +399,7 @@ pub const AvailableOption = extern struct {
399pub const Step = extern struct {399pub const Step = extern struct {
400 name: String,400 name: String,
401 owner: Package.Index,401 owner: Package.Index,
402 deps: Deps,402 deps: Deps.Index,
403 max_rss: MaxRss,403 max_rss: MaxRss,
404 extended: Storage.Extended(Flags, union(Tag) {404 extended: Storage.Extended(Flags, union(Tag) {
405 check_file: CheckFile,405 check_file: CheckFile,
...@@ -1074,14 +1074,12 @@ pub const Package = struct {...@@ -1074,14 +1074,12 @@ pub const Package = struct {
1074 };1074 };
1075};1075};
10761076
1077/// Trailing:
1078/// * frameworks: FlagsPrefixedList(FrameworkFlags), // if flag is set
1079pub const Module = struct {1077pub const Module = struct {
1080 flags: Flags,1078 flags: Flags,
1081 flags2: Flags2,1079 flags2: Flags2,
1080 import_table: ImportTable.Index,
1082 owner: Package.Index,1081 owner: Package.Index,
1083 root_source_file: OptionalLazyPath,1082 root_source_file: OptionalLazyPath,
1084 import_table: ImportTable,
1085 resolved_target: ResolvedTarget.OptionalIndex,1083 resolved_target: ResolvedTarget.OptionalIndex,
1086 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),1084 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
1087 lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath),1085 lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath),
...@@ -1089,6 +1087,7 @@ pub const Module = struct {...@@ -1089,6 +1087,7 @@ pub const Module = struct {
1089 include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir),1087 include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir),
1090 rpaths: Storage.UnionList(.flags, .rpaths, RPath),1088 rpaths: Storage.UnionList(.flags, .rpaths, RPath),
1091 link_objects: Storage.UnionList(.flags, .link_objects, LinkObject),1089 link_objects: Storage.UnionList(.flags, .link_objects, LinkObject),
1090 frameworks: Storage.FlagLengthPrefixedList(.flags, .frameworks, Framework),
10921091
1093 pub const Optimize = enum(u3) {1092 pub const Optimize = enum(u3) {
1094 debug,1093 debug,
...@@ -1220,28 +1219,47 @@ pub const Module = struct {...@@ -1220,28 +1219,47 @@ pub const Module = struct {
1220 win32_resource_file: RcSourceFile.Index,1219 win32_resource_file: RcSourceFile.Index,
1221 };1220 };
12221221
1223 pub const FrameworkFlags = packed struct(u2) {1222 pub const Framework = struct {
1224 needed: bool,1223 flags: @This().Flags,
1225 weak: bool,1224 name: String,
1225
1226 pub const Flags = packed struct(u32) {
1227 needed: bool,
1228 weak: bool,
1229 _: u30 = 0,
1230 };
1226 };1231 };
1227};1232};
12281233
1229/// Points into `extra`, first element is len, then:1234pub const ImportTable = struct {
1230/// * import_name: String, // for each len1235 imports: Storage.MultiList(Import),
1231/// * Module.Index, // for each len1236
1232pub const ImportTable = enum(u32) {1237 pub const Import = struct {
1233 _,1238 name: String,
1239 module: Module.Index,
1240 };
1241
1242 /// Points into `extra`.
1243 pub const Index = enum(u32) {
1244 invalid = maxInt(u32),
1245 _,
1246 };
1234};1247};
12351248
1236/// Points into `extra`, where the first element is count of deps, following1249pub const Deps = struct {
1237/// elements is `Step.Index` per count.1250 steps: Storage.LengthPrefixedList(Step.Index),
1238pub const Deps = enum(u32) {
1239 _,
12401251
1241 pub fn slice(deps: Deps, c: *const Configuration) []Step.Index {1252 pub const Index = enum(u32) {
1242 const len = c.extra[@intFromEnum(deps)];1253 _,
1243 return @ptrCast(c.extra[@intFromEnum(deps) + 1 ..][0..len]);1254
1244 }1255 pub fn get(this: @This(), c: *const Configuration) Deps {
1256 return extraData(c, Deps, @intFromEnum(this));
1257 }
1258
1259 pub fn slice(this: @This(), c: *const Configuration) []const Step.Index {
1260 return get(this, c).steps.slice;
1261 }
1262 };
1245};1263};
12461264
1247/// Points into `extra`, where the first element is count of strings, following1265/// Points into `extra`, where the first element is count of strings, following
...@@ -1760,6 +1778,7 @@ pub const Storage = enum {...@@ -1760,6 +1778,7 @@ pub const Storage = enum {
1760 flag_length_prefixed_list,1778 flag_length_prefixed_list,
1761 union_list,1779 union_list,
1762 flag_union,1780 flag_union,
1781 multi_list,
17631782
1764 /// The presence of the field is determined by a boolean within a packed1783 /// The presence of the field is determined by a boolean within a packed
1765 /// struct.1784 /// struct.
...@@ -1853,7 +1872,8 @@ pub const Storage = enum {...@@ -1853,7 +1872,8 @@ pub const Storage = enum {
1853 };1872 };
1854 }1873 }
18551874
1856 /// The field contains a u32 length followed by that many items.1875 /// The field contains a u32 length followed by that many items, each
1876 /// element bitcastable to u32.
1857 pub fn LengthPrefixedList(comptime ElemArg: type) type {1877 pub fn LengthPrefixedList(comptime ElemArg: type) type {
1858 return struct {1878 return struct {
1859 slice: []const Elem,1879 slice: []const Elem,
...@@ -1867,6 +1887,17 @@ pub const Storage = enum {...@@ -1867,6 +1887,17 @@ pub const Storage = enum {
1867 };1887 };
1868 }1888 }
18691889
1890 /// The field contains a u32 length followed by that many items for the
1891 /// first field, that many items for the second field, etc.
1892 pub fn MultiList(comptime ElemArg: type) type {
1893 return struct {
1894 mal: std.MultiArrayList(Elem),
1895
1896 pub const storage: Storage = .multi_list;
1897 pub const Elem = ElemArg;
1898 };
1899 }
1900
1870 /// `UnionArg` is a tagged union with a small integer for the enum tag.1901 /// `UnionArg` is a tagged union with a small integer for the enum tag.
1871 ///1902 ///
1872 /// A field in flags determines whether the metadata is present.1903 /// A field in flags determines whether the metadata is present.
...@@ -2028,6 +2059,16 @@ pub const Storage = enum {...@@ -2028,6 +2059,16 @@ pub const Storage = enum {
2028 defer i.* = data_start + len;2059 defer i.* = data_start + len;
2029 return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };2060 return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };
2030 },2061 },
2062 .multi_list => {
2063 const data_start = i.* + 1;
2064 const len = buffer[data_start - 1];
2065 defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".fields.len;
2066 return .{ .mal = .{
2067 .bytes = @ptrCast(buffer[data_start..][0..len]),
2068 .len = len,
2069 .capacity = len,
2070 } };
2071 },
2031 .union_list => {2072 .union_list => {
2032 const flags = @field(container, @tagName(Field.flags));2073 const flags = @field(container, @tagName(Field.flags));
2033 const flag = @field(flags, @tagName(Field.flag));2074 const flag = @field(flags, @tagName(Field.flag));
...@@ -2082,6 +2123,7 @@ pub const Storage = enum {...@@ -2082,6 +2123,7 @@ pub const Storage = enum {
2082 .auto => switch (Field.storage) {2123 .auto => switch (Field.storage) {
2083 .flag_optional, .enum_optional, .extended => 1,2124 .flag_optional, .enum_optional, .extended => 1,
2084 .length_prefixed_list, .flag_length_prefixed_list => field.slice.len + 1,2125 .length_prefixed_list, .flag_length_prefixed_list => field.slice.len + 1,
2126 .multi_list => 1 + field.mal.len * @typeInfo(Field.Elem).@"struct".fields.len,
2085 .union_list => Field.extraLen(field.len),2127 .union_list => Field.extraLen(field.len),
2086 .flag_union => switch (field.u) {2128 .flag_union => switch (field.u) {
2087 inline else => |v| extraFieldLen(v),2129 inline else => |v| extraFieldLen(v),
...@@ -2153,6 +2195,17 @@ pub const Storage = enum {...@@ -2153,6 +2195,17 @@ pub const Storage = enum {
2153 @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice)));2195 @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice)));
2154 return len + 1;2196 return len + 1;
2155 },2197 },
2198 .multi_list => {
2199 const len: u32 = @intCast(value.mal.len);
2200 if (len == 0) return 0;
2201 buffer[i] = len;
2202 const fields = @typeInfo(Field.Elem).@"struct".fields;
2203 inline for (0..fields.len) |field_i| @memcpy(
2204 buffer[i + 1 + field_i * len ..][0..len],
2205 @as([]const u32, @ptrCast(value.mal.items(@enumFromInt(field_i)))),
2206 );
2207 return 1 + fields.len * len;
2208 },
2156 .union_list => {2209 .union_list => {
2157 if (value.len == 0) return 0;2210 if (value.len == 0) return 0;
2158 const Tag = @typeInfo(Field.Union).@"union".tag_type.?;2211 const Tag = @typeInfo(Field.Union).@"union".tag_type.?;