authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-19 02:24:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
log81ee4ab32c8617af3ce9690d562131a6a0924f1c
treebaaac2ab23998b5a8244fc6555f0e47e8f5b1f01
parent8bc09132121d751760b332a30a82c3dfa343a48a

configurer: serialize all data from run steps


3 files changed, 355 insertions(+), 54 deletions(-)

BRANCH_TODO+2
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1* make more stuff use IndexType
2* make addExtra return Index using reflection
1* remove Cache from configurer3* remove Cache from configurer
2* implement the build options4* implement the build options
3* don't forget to add -listen arg back5* don't forget to add -listen arg back
lib/compiler/configurer.zig+226-18
...@@ -351,6 +351,169 @@ const Serialize = struct {...@@ -351,6 +351,169 @@ const Serialize = struct {
351 })));351 })));
352 }352 }
353353
354 fn addEnvironMap(s: *Serialize, opt_map: ?*std.process.Environ.Map) !?Configuration.EnvironMap.Index {
355 const wc = s.wc;
356 const map = opt_map orelse return null;
357 return @enumFromInt(try wc.addDeduped(@as(Configuration.EnvironMap, .{
358 .keys = try wc.addStringList(map.array_hash_map.keys()),
359 .values = try wc.addStringList(map.array_hash_map.values()),
360 })));
361 }
362
363 fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuration.Step.Run.Arg.Index {
364 const wc = s.wc;
365 const result = try s.arena.alloc(Configuration.Step.Run.Arg.Index, args.len);
366 for (result, args) |*dest, src| {
367 dest.* = @enumFromInt(try wc.addExtra(@as(Configuration.Step.Run.Arg, switch (src) {
368 .artifact => |a| .{
369 .flags = .{
370 .tag = .artifact,
371 .prefix = a.prefix.len != 0,
372 .suffix = false,
373 .basename = false,
374 .path = false,
375 .producer = true,
376 .generated = false,
377 .dep_file = false,
378 },
379 .prefix = .{ .value = try s.addOptionalString(a.prefix) },
380 .suffix = .{ .value = null },
381 .basename = .{ .value = null },
382 .path = .{ .value = null },
383 .producer = .{ .value = stepIndex(s, &a.artifact.step) },
384 .generated = .{ .value = null },
385 },
386 .lazy_path => |a| .{
387 .flags = .{
388 .tag = .path_file,
389 .prefix = a.prefix.len != 0,
390 .suffix = false,
391 .basename = false,
392 .path = true,
393 .producer = false,
394 .generated = false,
395 .dep_file = false,
396 },
397 .prefix = .{ .value = try s.addOptionalString(a.prefix) },
398 .suffix = .{ .value = null },
399 .basename = .{ .value = null },
400 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
401 .producer = .{ .value = null },
402 .generated = .{ .value = null },
403 },
404 .decorated_directory => |a| .{
405 .flags = .{
406 .tag = .path_directory,
407 .prefix = a.prefix.len != 0,
408 .suffix = a.suffix.len != 0,
409 .basename = false,
410 .path = true,
411 .producer = false,
412 .generated = false,
413 .dep_file = false,
414 },
415 .prefix = .{ .value = try addOptionalString(s, a.prefix) },
416 .suffix = .{ .value = try addOptionalString(s, a.suffix) },
417 .basename = .{ .value = null },
418 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
419 .producer = .{ .value = null },
420 .generated = .{ .value = null },
421 },
422 .file_content => |a| .{
423 .flags = .{
424 .tag = .file_content,
425 .prefix = a.prefix.len != 0,
426 .suffix = false,
427 .basename = false,
428 .path = true,
429 .producer = false,
430 .generated = false,
431 .dep_file = false,
432 },
433 .prefix = .{ .value = try addOptionalString(s, a.prefix) },
434 .suffix = .{ .value = null },
435 .basename = .{ .value = null },
436 .path = .{ .value = try addLazyPath(s, a.lazy_path) },
437 .producer = .{ .value = null },
438 .generated = .{ .value = null },
439 },
440 .bytes => |a| .{
441 .flags = .{
442 .tag = .string,
443 .prefix = true,
444 .suffix = false,
445 .basename = false,
446 .path = false,
447 .producer = false,
448 .generated = false,
449 .dep_file = false,
450 },
451 .prefix = .{ .value = try addOptionalString(s, a) },
452 .suffix = .{ .value = null },
453 .basename = .{ .value = null },
454 .path = .{ .value = null },
455 .producer = .{ .value = null },
456 .generated = .{ .value = null },
457 },
458 .output_file => |a| .{
459 .flags = .{
460 .tag = .output_file,
461 .prefix = a.prefix.len != 0,
462 .suffix = false,
463 .basename = a.basename.len != 0,
464 .path = false,
465 .producer = false,
466 .generated = true,
467 .dep_file = false,
468 },
469 .prefix = .{ .value = try addOptionalString(s, a.prefix) },
470 .suffix = .{ .value = null },
471 .basename = .{ .value = try addOptionalString(s, a.basename) },
472 .path = .{ .value = null },
473 .producer = .{ .value = null },
474 .generated = .{ .value = a.generated_file },
475 },
476 .output_directory => |a| .{
477 .flags = .{
478 .tag = .output_directory,
479 .prefix = a.prefix.len != 0,
480 .suffix = false,
481 .basename = a.basename.len != 0,
482 .path = false,
483 .producer = false,
484 .generated = true,
485 .dep_file = false,
486 },
487 .prefix = .{ .value = try addOptionalString(s, a.prefix) },
488 .suffix = .{ .value = null },
489 .basename = .{ .value = try addOptionalString(s, a.basename) },
490 .path = .{ .value = null },
491 .producer = .{ .value = null },
492 .generated = .{ .value = a.generated_file },
493 },
494 .cli_rest_positionals => .{
495 .flags = .{
496 .tag = .cli_rest_positionals,
497 .prefix = false,
498 .suffix = false,
499 .basename = false,
500 .path = false,
501 .producer = false,
502 .generated = false,
503 .dep_file = false,
504 },
505 .prefix = .{ .value = null },
506 .suffix = .{ .value = null },
507 .basename = .{ .value = null },
508 .path = .{ .value = null },
509 .producer = .{ .value = null },
510 .generated = .{ .value = null },
511 },
512 })));
513 }
514 return result;
515 }
516
354 fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index {517 fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index {
355 const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len);518 const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len);
356 for (result, list) |*dest, src| dest.* = try addLazyPath(s, src);519 for (result, list) |*dest, src| dest.* = try addLazyPath(s, src);
...@@ -768,16 +931,33 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -768,16 +931,33 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
768 .update_source_files => @panic("TODO"),931 .update_source_files => @panic("TODO"),
769 .run => e: {932 .run => e: {
770 const run: *Step.Run = @fieldParentPtr("step", step);933 const run: *Step.Run = @fieldParentPtr("step", step);
771934 var expect_stderr_exact: ?Configuration.Bytes = null;
772 const captured_stdout: Configuration.OptionalString = if (run.captured_stdout) |cs|935 var expect_stdout_exact: ?Configuration.Bytes = null;
773 .init(try wc.addString(cs.output.basename))936 var expect_stderr_match: std.ArrayList(Configuration.Bytes) = .empty;
774 else937 var expect_stdout_match: std.ArrayList(Configuration.Bytes) = .empty;
775 .none;938 var expect_term: ?struct {
776939 status: Configuration.Step.Run.ExpectTermStatus,
777 const captured_stderr: Configuration.OptionalString = if (run.captured_stderr) |cs|940 value: u32,
778 .init(try wc.addString(cs.output.basename))941 } = null;
779 else942 switch (run.stdio) {
780 .none;943 .check => |checks| for (checks.items) |check| switch (check) {
944 .expect_stderr_exact => |bytes| expect_stderr_exact = try wc.addBytes(bytes),
945 .expect_stdout_exact => |bytes| expect_stdout_exact = try wc.addBytes(bytes),
946 .expect_stderr_match => |bytes| {
947 try expect_stderr_match.append(arena, try wc.addBytes(bytes));
948 },
949 .expect_stdout_match => |bytes| {
950 try expect_stdout_match.append(arena, try wc.addBytes(bytes));
951 },
952 .expect_term => |t| expect_term = switch (t) {
953 .exited => |x| .{ .status = .exited, .value = x },
954 .signal => |x| .{ .status = .signal, .value = @intFromEnum(x) },
955 .stopped => |x| .{ .status = .stopped, .value = x },
956 .unknown => |x| .{ .status = .unknown, .value = x },
957 },
958 },
959 else => {},
960 }
781961
782 const extra_index = try wc.addExtra(@as(Configuration.Step.Run, .{962 const extra_index = try wc.addExtra(@as(Configuration.Step.Run, .{
783 .flags = .{963 .flags = .{
...@@ -802,16 +982,44 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -802,16 +982,44 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
802 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none,982 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none,
803 .stdio_limit = run.stdio_limit != .unlimited,983 .stdio_limit = run.stdio_limit != .unlimited,
804 .producer = run.producer != null,984 .producer = run.producer != null,
985 .cwd = run.cwd != null,
986 .captured_stdout = run.captured_stdout != null,
987 .captured_stderr = run.captured_stderr != null,
988 .environ_map = run.environ_map != null,
805 },989 },
806 .file_inputs_len = @intCast(run.file_inputs.items.len),990 .flags2 = .{
807 .args_len = @intCast(run.argv.items.len),991 .expect_stderr_exact = expect_stderr_exact != null,
808 .cwd = try s.addOptionalLazyPathEnum(run.cwd),992 .expect_stdout_exact = expect_stdout_exact != null,
809 .captured_stdout = captured_stdout,993 .expect_stderr_match = expect_stderr_match.items.len != 0,
810 .captured_stderr = captured_stderr,994 .expect_stdout_match = expect_stdout_match.items.len != 0,
995 .expect_term = expect_term != null,
996 .expect_term_status = if (expect_term) |t| t.status else .exited,
997 },
998 .file_inputs = .{ .slice = try s.initLazyPathList(run.file_inputs.items) },
999 .args = .{ .slice = try s.initArgsList(run.argv.items) },
1000 .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
1001 .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
1002 .basename = try wc.addString(cs.output.basename),
1003 .generated_file = cs.output.generated_file,
1004 } else null },
1005 .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
1006 .basename = try wc.addString(cs.output.basename),
1007 .generated_file = cs.output.generated_file,
1008 } else null },
1009 .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
1010 .expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
1011 .stdio_limit = .{ .value = run.stdio_limit.toInt() },
1012 .producer = .{ .value = if (run.producer) |cs| s.stepIndex(&cs.step) else null },
1013 .expect_stderr_exact = .{ .value = if (expect_stderr_exact) |bytes| bytes else null },
1014 .expect_stdout_exact = .{ .value = if (expect_stdout_exact) |bytes| bytes else null },
1015 .expect_stderr_match = .{ .slice = expect_stderr_match.items },
1016 .expect_stdout_match = .{ .slice = expect_stdout_match.items },
1017 .stdin = .{ .u = switch (run.stdin) {
1018 .none => .none,
1019 .bytes => |bytes| .{ .bytes = try wc.addBytes(bytes) },
1020 .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) },
1021 } },
811 }));1022 }));
812
813 log.err("TODO serialize the trailing Run step data", .{});
814
815 break :e @enumFromInt(extra_index);1023 break :e @enumFromInt(extra_index);
816 },1024 },
817 .check_file => @panic("TODO"),1025 .check_file => @panic("TODO"),
lib/std/Build/Configuration.zig+127-36
...@@ -188,6 +188,18 @@ pub const Wip = struct {...@@ -188,6 +188,18 @@ pub const Wip = struct {
188 return .init(try addString(wip, bytes orelse return .none));188 return .init(try addString(wip, bytes orelse return .none));
189 }189 }
190190
191 pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList {
192 _ = wip;
193 _ = list;
194 @panic("TODO");
195 }
196
197 pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes {
198 _ = wip;
199 _ = bytes;
200 @panic("TODO");
201 }
202
191 pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String {203 pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String {
192 var buffer: [256]u8 = undefined;204 var buffer: [256]u8 = undefined;
193 var writer: std.Io.Writer = .fixed(&buffer);205 var writer: std.Io.Writer = .fixed(&buffer);
...@@ -500,52 +512,65 @@ pub const Step = extern struct {...@@ -500,52 +512,65 @@ pub const Step = extern struct {
500 };512 };
501 };513 };
502514
503 /// Trailing:
504 /// * LazyPath.Index for each file_inputs_len
505 /// * Arg for each args_len
506 /// * environ_map if corresponding flag is set
507 /// * stdin: Bytes, // if StdIn.bytes is chosen
508 /// * stdin: LazyPath.Index, // if StdIn.lazy_path is chosen
509 /// * checks: Checks, // if StdIo.check is chosen
510 /// * stdio_limit: u64, // if stdio_limit is set
511 /// * producer: Step.Index, // if producer is set. always compile step
512 pub const Run = struct {515 pub const Run = struct {
513 flags: @This().Flags,516 flags: @This().Flags,
514 file_inputs_len: u32,517 flags2: Flags2,
515 args_len: u32,518 args: Storage.LengthPrefixedList(Arg.Index),
516 cwd: LazyPath.OptionalIndex,519 cwd: Storage.FlagOptional(.flags, .cwd, LazyPath.Index),
517 captured_stdout: OptionalString, // basename520 captured_stdout: Storage.FlagOptional(.flags, .captured_stdout, CapturedStream),
518 captured_stderr: OptionalString, // basename521 captured_stderr: Storage.FlagOptional(.flags, .captured_stderr, CapturedStream),
519522 file_inputs: Storage.LengthPrefixedList(LazyPath.Index),
520 /// Trailing:523 stdio_limit: Storage.FlagOptional(.flags, .stdio_limit, u64),
521 /// * String if prefix set524 /// Always a compile step.
522 /// * String if suffix set525 producer: Storage.FlagOptional(.flags, .producer, Step.Index),
523 /// * String if basename set526 /// First half is keys, second half is values.
524 /// * Step.Index which is always a compile step if tag is artifact527 environ_map: Storage.FlagOptional(.flags, .environ_map, EnvironMap.Index),
525 /// * LazyPath.Index if tag is path_file, path_directory, or file_content528 stdin: Storage.FlagUnion(.flags, .stdin, StdIn),
529 expect_stderr_exact: Storage.FlagOptional(.flags2, .expect_stderr_exact, Bytes),
530 expect_stdout_exact: Storage.FlagOptional(.flags2, .expect_stdout_exact, Bytes),
531 expect_stderr_match: Storage.FlagLengthPrefixedList(.flags2, .expect_stderr_match, Bytes),
532 expect_stdout_match: Storage.FlagLengthPrefixedList(.flags2, .expect_stdout_match, Bytes),
533 expect_term_value: Storage.FlagOptional(.flags2, .expect_term, u32),
534
535 pub const CapturedStream = extern struct {
536 generated_file: GeneratedFileIndex,
537 basename: String,
538 };
539
526 pub const Arg = struct {540 pub const Arg = struct {
527 flags: Arg.Flags,541 flags: @This().Flags,
542 prefix: Storage.FlagOptional(.flags, .prefix, String),
543 suffix: Storage.FlagOptional(.flags, .suffix, String),
544 basename: Storage.FlagOptional(.flags, .basename, String),
545 path: Storage.FlagOptional(.flags, .path, LazyPath.Index),
546 /// Always a compile step.
547 producer: Storage.FlagOptional(.flags, .producer, Step.Index),
548 generated: Storage.FlagOptional(.flags, .generated, GeneratedFileIndex),
528549
529 pub const Flags = packed struct(u32) {550 pub const Flags = packed struct(u32) {
530 tag: Arg.Tag,551 tag: Arg.Tag,
531 prefix: bool,552 prefix: bool,
532 suffix: bool,553 suffix: bool,
533 basename: bool,554 basename: bool,
534 /// Implies Tag is output_file555 path: bool,
556 producer: bool,
557 generated: bool,
535 dep_file: bool,558 dep_file: bool,
536 _: u20 = 0,559 _: u22 = 0,
537 };560 };
538561
539 pub const Tag = enum(u8) {562 pub const Tag = enum(u3) {
540 artifact,563 artifact,
541 path_file,564 path_file,
542 path_directory,565 path_directory,
566 string,
543 file_content,567 file_content,
544 bytes,
545 output_file,568 output_file,
546 output_directory,569 output_directory,
547 cli_rest_positionals,570 cli_rest_positionals,
548 };571 };
572
573 pub const Index = IndexType(@This());
549 };574 };
550575
551 pub const Color = enum(u4) {576 pub const Color = enum(u4) {
...@@ -562,26 +587,47 @@ pub const Step = extern struct {...@@ -562,26 +587,47 @@ pub const Step = extern struct {
562 manual,587 manual,
563 };588 };
564589
565 pub const StdIn = enum(u2) { none, bytes, lazy_path };590 pub const StdIn = union(@This().Tag) {
591 none: void,
592 bytes: Bytes,
593 lazy_path: LazyPath.Index,
594
595 pub const Tag = enum(u2) { none, bytes, lazy_path };
596 };
566 pub const TrimWhitespace = enum(u2) { none, all, leading, trailing };597 pub const TrimWhitespace = enum(u2) { none, all, leading, trailing };
567 pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test };598 pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test };
568599
600 pub const ExpectTermStatus = enum(u2) { exited, signal, stopped, unknown };
601
569 pub const Flags = packed struct(u32) {602 pub const Flags = packed struct(u32) {
570 tag: Tag = .run,603 tag: Tag = .run,
571
572 disable_zig_progress: bool,604 disable_zig_progress: bool,
573 skip_foreign_checks: bool,605 skip_foreign_checks: bool,
574 failing_to_execute_foreign_is_an_error: bool,606 failing_to_execute_foreign_is_an_error: bool,
575 has_side_effects: bool,607 has_side_effects: bool,
576 test_runner_mode: bool,608 test_runner_mode: bool,
577 color: Color,609 color: Color,
578 stdin: StdIn,610 stdin: StdIn.Tag,
579 stdio: StdIo,611 stdio: StdIo,
580 stdout_trim_whitespace: TrimWhitespace,612 stdout_trim_whitespace: TrimWhitespace,
581 stderr_trim_whitespace: TrimWhitespace,613 stderr_trim_whitespace: TrimWhitespace,
582 stdio_limit: bool,614 stdio_limit: bool,
583 producer: bool,615 producer: bool,
584 _: u8 = 0,616 cwd: bool,
617 captured_stdout: bool,
618 captured_stderr: bool,
619 environ_map: bool,
620 _: u4 = 0,
621 };
622
623 pub const Flags2 = packed struct(u32) {
624 expect_stderr_exact: bool,
625 expect_stdout_exact: bool,
626 expect_stderr_match: bool,
627 expect_stdout_match: bool,
628 expect_term: bool,
629 expect_term_status: ExpectTermStatus,
630 _: u25 = 0,
585 };631 };
586 };632 };
587633
...@@ -1395,17 +1441,37 @@ pub const Deps = struct {...@@ -1395,17 +1441,37 @@ pub const Deps = struct {
1395 };1441 };
1396};1442};
13971443
1444pub const EnvironMap = struct {
1445 keys: StringList,
1446 values: StringList,
1447
1448 pub const Index = IndexType(@This());
1449};
1450
1398/// Points into `extra`, where the first element is count of strings, following1451/// Points into `extra`, where the first element is count of strings, following
1399/// elements is `String` per count.1452/// elements is `String` per count.
1400///1453///
1401/// Stored identically to `Deps`.1454/// Stored identically to `Deps`.
1455pub const StringList = enum(u32) {
1456 _,
1457
1458 pub fn slice(this: @This(), c: *const Configuration) []const String {
1459 const len = c.extra[@intFromEnum(this)];
1460 return @ptrCast(c.extra[@intFromEnum(this) + 1 ..][0..len]);
1461 }
1462};
1463
1402pub const OptionalStringList = enum(u32) {1464pub const OptionalStringList = enum(u32) {
1403 none = max_u32,1465 none = max_u32,
1404 _,1466 _,
14051467
1406 pub fn slice(osl: OptionalStringList, c: *const Configuration) ?[]const String {1468 pub fn unwrap(this: @This()) ?StringList {
1407 const len = c.extra[@intFromEnum(osl)];1469 if (this == .none) return null;
1408 return @ptrCast(c.extra[@intFromEnum(osl) + 1 ..][0..len]);1470 return @enumFromInt(@intFromEnum(this));
1471 }
1472
1473 pub fn slice(this: @This(), c: *const Configuration) ?[]const String {
1474 return (unwrap(this) orelse return null).slice(c);
1409 }1475 }
1410};1476};
14111477
...@@ -1499,6 +1565,13 @@ pub const String = enum(u32) {...@@ -1499,6 +1565,13 @@ pub const String = enum(u32) {
1499 }1565 }
1500};1566};
15011567
1568/// Arbitrary sequence of bytes that may contain null bytes.
1569pub const Bytes = extern struct {
1570 /// Points into `string_bytes`.
1571 index: u32,
1572 len: u32,
1573};
1574
1502pub const DefaultingBool = enum(u2) {1575pub const DefaultingBool = enum(u2) {
1503 false,1576 false,
1504 true,1577 true,
...@@ -2359,7 +2432,11 @@ pub const Storage = enum {...@@ -2359,7 +2432,11 @@ pub const Storage = enum {
2359 },2432 },
2360 },2433 },
2361 },2434 },
2362 .@"extern" => comptime unreachable,2435 .@"extern" => {
2436 const n = @divExact(@sizeOf(Field), @sizeOf(u32));
2437 defer i.* += n;
2438 return @bitCast(buffer[i.*..][0..n].*);
2439 },
2363 },2440 },
2364 else => comptime unreachable,2441 else => comptime unreachable,
2365 }2442 }
...@@ -2404,7 +2481,7 @@ pub const Storage = enum {...@@ -2404,7 +2481,7 @@ pub const Storage = enum {
2404 inline else => |v| extraFieldLen(v),2481 inline else => |v| extraFieldLen(v),
2405 },2482 },
2406 },2483 },
2407 .@"extern" => comptime unreachable,2484 .@"extern" => @divExact(@sizeOf(Field), @sizeOf(u32)),
2408 },2485 },
2409 else => @compileError("bad type: " ++ @typeName(Field)),2486 else => @compileError("bad type: " ++ @typeName(Field)),
2410 };2487 };
...@@ -2520,13 +2597,27 @@ pub const Storage = enum {...@@ -2520,13 +2597,27 @@ pub const Storage = enum {
2520 },2597 },
2521 },2598 },
2522 },2599 },
2523 .@"extern" => comptime unreachable,2600 .@"extern" => {
2601 const n = @divExact(@sizeOf(Field), @sizeOf(u32));
2602 buffer[i..][0..n].* = @bitCast(value);
2603 return n;
2604 },
2524 },2605 },
2525 else => @compileError("bad field type: " ++ @typeName(Field)),2606 else => @compileError("bad field type: " ++ @typeName(Field)),
2526 }2607 }
2527 }2608 }
2528};2609};
25292610
2611fn IndexType(comptime T: type) type {
2612 return enum(u32) {
2613 _,
2614
2615 pub fn get(this: @This(), c: *const Configuration) T {
2616 return extraData(c, T, @intFromEnum(this));
2617 }
2618 };
2619}
2620
2530pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T {2621pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T {
2531 var i: usize = index;2622 var i: usize = index;
2532 return Storage.data(c.extra, &i, T);2623 return Storage.data(c.extra, &i, T);