| ... | @@ -33,7 +33,9 @@ pub const Header = extern struct { | ... | @@ -33,7 +33,9 @@ pub const Header = extern struct { |
| 33 | pub const Wip = struct { | 33 | pub const Wip = struct { |
| 34 | gpa: Allocator, | 34 | gpa: Allocator, |
| 35 | string_table: StringTable = .empty, | 35 | string_table: StringTable = .empty, |
| 36 | deps_table: DepsTable = .empty, | 36 | /// De-duplicates an array inside `extra` that has first element length |
| | 37 | /// followed by length elements. |
| | 38 | length_prefixed_table: LengthPrefixedTable = .empty, |
| 37 | targets_table: TargetsTable = .empty, | 39 | targets_table: TargetsTable = .empty, |
| 38 | | 40 | |
| 39 | string_bytes: std.ArrayList(u8) = .empty, | 41 | string_bytes: std.ArrayList(u8) = .empty, |
| ... | @@ -44,23 +46,23 @@ pub const Wip = struct { | ... | @@ -44,23 +46,23 @@ pub const Wip = struct { |
| 44 | path_deps: std.MultiArrayList(Path) = .empty, | 46 | path_deps: std.MultiArrayList(Path) = .empty, |
| 45 | extra: std.ArrayList(u32) = .empty, | 47 | extra: std.ArrayList(u32) = .empty, |
| 46 | | 48 | |
| 47 | const DepsTable = std.HashMapUnmanaged(Deps, void, DepsTableContext, std.hash_map.default_max_load_percentage); | 49 | const LengthPrefixedTable = std.HashMapUnmanaged(u32, void, LengthPrefixedContext, std.hash_map.default_max_load_percentage); |
| 48 | const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage); | 50 | const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage); |
| 49 | | 51 | |
| 50 | const DepsTableContext = struct { | 52 | const LengthPrefixedContext = struct { |
| 51 | extra: []const u32, | 53 | extra: []const u32, |
| 52 | | 54 | |
| 53 | pub fn eql(ctx: @This(), a: Deps, b: Deps) bool { | 55 | pub fn eql(ctx: @This(), a: u32, b: u32) bool { |
| 54 | const len_a = ctx.extra[@intFromEnum(a)]; | 56 | const len_a = ctx.extra[a]; |
| 55 | const len_b = ctx.extra[@intFromEnum(b)]; | 57 | const len_b = ctx.extra[b]; |
| 56 | const slice_a = ctx.extra[@intFromEnum(a) + 1 ..][0..len_a]; | 58 | const slice_a = ctx.extra[a + 1 ..][0..len_a]; |
| 57 | const slice_b = ctx.extra[@intFromEnum(b) + 1 ..][0..len_b]; | 59 | const slice_b = ctx.extra[b + 1 ..][0..len_b]; |
| 58 | return std.mem.eql(u32, slice_a, slice_b); | 60 | return std.mem.eql(u32, slice_a, slice_b); |
| 59 | } | 61 | } |
| 60 | | 62 | |
| 61 | pub fn hash(ctx: @This(), key: Deps) u64 { | 63 | pub fn hash(ctx: @This(), key: u32) u64 { |
| 62 | const len = ctx.extra[@intFromEnum(key)]; | 64 | const len = ctx.extra[key]; |
| 63 | const slice = ctx.extra[@intFromEnum(key) + 1 ..][0..len]; | 65 | const slice = ctx.extra[key + 1 ..][0..len]; |
| 64 | return std.hash_map.hashString(@ptrCast(slice)); | 66 | return std.hash_map.hashString(@ptrCast(slice)); |
| 65 | } | 67 | } |
| 66 | }; | 68 | }; |
| ... | @@ -174,6 +176,10 @@ pub const Wip = struct { | ... | @@ -174,6 +176,10 @@ pub const Wip = struct { |
| 174 | return new_off; | 176 | return new_off; |
| 175 | } | 177 | } |
| 176 | | 178 | |
| | 179 | pub fn addOptionalString(wip: *Wip, bytes: ?[]const u8) Allocator.Error!OptionalString { |
| | 180 | return .init(try addString(wip, bytes orelse return .none)); |
| | 181 | } |
| | 182 | |
| 177 | pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String { | 183 | pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String { |
| 178 | var buffer: [256]u8 = undefined; | 184 | var buffer: [256]u8 = undefined; |
| 179 | var writer: std.Io.Writer = .fixed(&buffer); | 185 | var writer: std.Io.Writer = .fixed(&buffer); |
| ... | @@ -324,27 +330,32 @@ pub const Wip = struct { | ... | @@ -324,27 +330,32 @@ pub const Wip = struct { |
| 324 | } | 330 | } |
| 325 | } | 331 | } |
| 326 | | 332 | |
| 327 | pub fn prepareDeps(wip: *Wip, n: usize) Allocator.Error![]u32 { | 333 | pub fn reserveLengthPrefixed(wip: *Wip, n: usize) Allocator.Error![]u32 { |
| 328 | const slice = try wip.extra.addManyAsSlice(wip.gpa, n + 1); | 334 | const slice = try wip.extra.addManyAsSlice(wip.gpa, n + 1); |
| 329 | slice[0] = @intCast(n); | 335 | slice[0] = @intCast(n); |
| 330 | return slice[1..]; | 336 | return slice[1..]; |
| 331 | } | 337 | } |
| 332 | | 338 | |
| 333 | pub fn dedupeDeps(wip: *Wip, deps: Deps) Allocator.Error!Deps { | 339 | pub fn dedupeLengthPrefixed(wip: *Wip, index: u32) Allocator.Error!u32 { |
| | 340 | assert(wip.extra.items.len == index + wip.extra.items[index] + 1); |
| 334 | const gpa = wip.gpa; | 341 | const gpa = wip.gpa; |
| 335 | const gop = try wip.deps_table.getOrPutContext(gpa, deps, @as(DepsTableContext, .{ | 342 | const gop = try wip.length_prefixed_table.getOrPutContext(gpa, index, @as(LengthPrefixedContext, .{ |
| 336 | .extra = wip.extra.items, | 343 | .extra = wip.extra.items, |
| 337 | })); | 344 | })); |
| 338 | if (gop.found_existing) { | 345 | if (gop.found_existing) { |
| 339 | wip.extra.items.len = @intFromEnum(deps); | 346 | wip.extra.items.len = index; |
| 340 | return gop.key_ptr.*; | 347 | return gop.key_ptr.*; |
| 341 | } else { | 348 | } else { |
| 342 | return deps; | 349 | return index; |
| 343 | } | 350 | } |
| 344 | } | 351 | } |
| 345 | | 352 | |
| | 353 | pub fn dedupeDeps(wip: *Wip, deps: Deps) Allocator.Error!Deps { |
| | 354 | return @enumFromInt(try dedupeLengthPrefixed(wip, @intFromEnum(deps))); |
| | 355 | } |
| | 356 | |
| 346 | pub fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { | 357 | pub fn addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { |
| 347 | const extra_len = Storage.calculateExtraLenUpperBound(@TypeOf(extra)); | 358 | const extra_len = Storage.extraLen(extra); |
| 348 | try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len); | 359 | try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len); |
| 349 | return addExtraAssumeCapacity(wip, extra); | 360 | return addExtraAssumeCapacity(wip, extra); |
| 350 | } | 361 | } |
| ... | @@ -397,7 +408,7 @@ pub const Step = extern struct { | ... | @@ -397,7 +408,7 @@ pub const Step = extern struct { |
| 397 | owner: Package.Index, | 408 | owner: Package.Index, |
| 398 | deps: Deps, | 409 | deps: Deps, |
| 399 | max_rss: MaxRss, | 410 | max_rss: MaxRss, |
| 400 | extended: Storage.ExtendedIndex(Flags, union(Tag) { | 411 | extended: Storage.Extended(Flags, union(Tag) { |
| 401 | check_file: CheckFile, | 412 | check_file: CheckFile, |
| 402 | check_object: CheckObject, | 413 | check_object: CheckObject, |
| 403 | compile: Compile, | 414 | compile: Compile, |
| ... | @@ -585,10 +596,10 @@ pub const Step = extern struct { | ... | @@ -585,10 +596,10 @@ pub const Step = extern struct { |
| 585 | root_module: Module.Index, | 596 | root_module: Module.Index, |
| 586 | root_name: String, | 597 | root_name: String, |
| 587 | | 598 | |
| 588 | //filters: FlagLengthPrefixedList(.flags, .filters_len, String), | 599 | filters: Storage.FlagLengthPrefixedList(.flags, .filters_len, String), |
| 589 | //exec_cmd_args: FlagLengthPrefixedList(.flags, .exec_cmd_args_len, u32), | 600 | exec_cmd_args: Storage.FlagLengthPrefixedList(.flags, .exec_cmd_args_len, OptionalString), |
| 590 | //installed_headers: FlagLengthPrefixedList(.flags, .installed_headers_len, InstalledHeader), | 601 | installed_headers: Storage.FlagLengthPrefixedList(.flags, .installed_headers_len, Storage.Extended(InstalledHeader.Flags, InstalledHeader)), |
| 591 | //force_undefined_symbols: FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String), | 602 | force_undefined_symbols: Storage.FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String), |
| 592 | //exacts: EnumConditionalPrefixedList(.flags4, .expect_errors, .exact, String), | 603 | //exacts: EnumConditionalPrefixedList(.flags4, .expect_errors, .exact, String), |
| 593 | linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath), | 604 | linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath), |
| 594 | version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath), | 605 | version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath), |
| ... | @@ -612,6 +623,46 @@ pub const Step = extern struct { | ... | @@ -612,6 +623,46 @@ pub const Step = extern struct { |
| 612 | error_limit: Storage.FlagOptional(.flags4, .error_limit, u32), | 623 | error_limit: Storage.FlagOptional(.flags4, .error_limit, u32), |
| 613 | build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String), | 624 | build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String), |
| 614 | | 625 | |
| | 626 | pub const InstalledHeader = union(@This().Tag) { |
| | 627 | file: File, |
| | 628 | directory: Directory, |
| | 629 | |
| | 630 | pub const Flags = packed struct(u32) { |
| | 631 | tag: InstalledHeader.Tag, |
| | 632 | _: u24 = 0, |
| | 633 | }; |
| | 634 | |
| | 635 | pub const Tag = enum(u8) { |
| | 636 | file, |
| | 637 | directory, |
| | 638 | }; |
| | 639 | |
| | 640 | pub const File = struct { |
| | 641 | flags: @This().Flags = .{}, |
| | 642 | source: LazyPath, |
| | 643 | dest_sub_path: String, |
| | 644 | |
| | 645 | pub const Flags = packed struct(u32) { |
| | 646 | tag: InstalledHeader.Tag = .file, |
| | 647 | _: u24 = 0, |
| | 648 | }; |
| | 649 | }; |
| | 650 | |
| | 651 | pub const Directory = struct { |
| | 652 | flags: @This().Flags, |
| | 653 | source: LazyPath, |
| | 654 | dest_sub_path: String, |
| | 655 | exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String), |
| | 656 | include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String), |
| | 657 | |
| | 658 | pub const Flags = packed struct(u32) { |
| | 659 | tag: InstalledHeader.Tag = .directory, |
| | 660 | exclude_extensions: bool, |
| | 661 | include_extensions: bool, |
| | 662 | _: u22 = 0, |
| | 663 | }; |
| | 664 | }; |
| | 665 | }; |
| 615 | pub const ExpectErrors = enum(u3) { contains, exact, starts_with, stderr_contains, none }; | 666 | pub const ExpectErrors = enum(u3) { contains, exact, starts_with, stderr_contains, none }; |
| 616 | pub const TestRunnerMode = enum(u2) { default, simple, server }; | 667 | pub const TestRunnerMode = enum(u2) { default, simple, server }; |
| 617 | pub const Entry = enum(u2) { default, disabled, enabled, symbol_name }; | 668 | pub const Entry = enum(u2) { default, disabled, enabled, symbol_name }; |
| ... | @@ -1636,6 +1687,7 @@ pub const Storage = enum { | ... | @@ -1636,6 +1687,7 @@ pub const Storage = enum { |
| 1636 | flag_optional, | 1687 | flag_optional, |
| 1637 | enum_optional, | 1688 | enum_optional, |
| 1638 | extended, | 1689 | extended, |
| | 1690 | flag_length_prefixed_list, |
| 1639 | | 1691 | |
| 1640 | /// The presence of the field is determined by a boolean within a packed | 1692 | /// The presence of the field is determined by a boolean within a packed |
| 1641 | /// struct. | 1693 | /// struct. |
| ... | @@ -1674,16 +1726,7 @@ pub const Storage = enum { | ... | @@ -1674,16 +1726,7 @@ pub const Storage = enum { |
| 1674 | | 1726 | |
| 1675 | /// The field indexes into an auxilary buffer, with the first element being | 1727 | /// The field indexes into an auxilary buffer, with the first element being |
| 1676 | /// a packed struct that contains the tag. | 1728 | /// a packed struct that contains the tag. |
| 1677 | pub fn Extended(comptime U: type) type { | 1729 | pub fn Extended(comptime BaseFlags: type, comptime U: type) type { |
| 1678 | return struct { | | |
| 1679 | value: U, | | |
| 1680 | | | |
| 1681 | pub const storage: Storage = .extended; | | |
| 1682 | }; | | |
| 1683 | } | | |
| 1684 | | | |
| 1685 | /// Equivalent to `Extended` but works in an `extern struct`. | | |
| 1686 | pub fn ExtendedIndex(comptime BaseFlags: type, comptime U: type) type { | | |
| 1687 | return enum(u32) { | 1730 | return enum(u32) { |
| 1688 | _, | 1731 | _, |
| 1689 | | 1732 | |
| ... | @@ -1699,6 +1742,30 @@ pub const Storage = enum { | ... | @@ -1699,6 +1742,30 @@ pub const Storage = enum { |
| 1699 | }; | 1742 | }; |
| 1700 | } | 1743 | } |
| 1701 | | 1744 | |
| | 1745 | /// A field in flags determines whether the length is zero or nonzero. If the length is |
| | 1746 | /// nonzero, then there is a length field followed by the list. |
| | 1747 | /// |
| | 1748 | /// When deserializing, the slice field is set. When serializing, the index |
| | 1749 | /// field must be set. |
| | 1750 | pub fn FlagLengthPrefixedList( |
| | 1751 | comptime flags_arg: @EnumLiteral(), |
| | 1752 | comptime flag_arg: @EnumLiteral(), |
| | 1753 | comptime ValueArg: type, |
| | 1754 | ) type { |
| | 1755 | return struct { |
| | 1756 | slice: []const Value, |
| | 1757 | |
| | 1758 | pub const storage: Storage = .flag_length_prefixed_list; |
| | 1759 | pub const flags = flags_arg; |
| | 1760 | pub const flag = flag_arg; |
| | 1761 | pub const Value = ValueArg; |
| | 1762 | |
| | 1763 | pub fn initErased(s: []const u32) @This() { |
| | 1764 | return .{ .slice = @ptrCast(s) }; |
| | 1765 | } |
| | 1766 | }; |
| | 1767 | } |
| | 1768 | |
| 1702 | pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize { | 1769 | pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize { |
| 1703 | var end = i; | 1770 | var end = i; |
| 1704 | _ = data(buffer, &end, S); | 1771 | _ = data(buffer, &end, S); |
| ... | @@ -1769,6 +1836,15 @@ pub const Storage = enum { | ... | @@ -1769,6 +1836,15 @@ pub const Storage = enum { |
| 1769 | }; | 1836 | }; |
| 1770 | }, | 1837 | }, |
| 1771 | .extended => @compileError("TODO"), | 1838 | .extended => @compileError("TODO"), |
| | 1839 | .flag_length_prefixed_list => { |
| | 1840 | const flags = @field(container, @tagName(Field.flags)); |
| | 1841 | const flag = @field(flags, @tagName(Field.flag)); |
| | 1842 | if (!flag) return .{ .slice = &.{} }; |
| | 1843 | const data_start = i.* + 1; |
| | 1844 | const len = buffer[data_start - 1]; |
| | 1845 | defer i.* = data_start + len; |
| | 1846 | return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; |
| | 1847 | }, |
| 1772 | }, | 1848 | }, |
| 1773 | }, | 1849 | }, |
| 1774 | .@"extern" => comptime unreachable, | 1850 | .@"extern" => comptime unreachable, |
| ... | @@ -1787,11 +1863,36 @@ pub const Storage = enum { | ... | @@ -1787,11 +1863,36 @@ pub const Storage = enum { |
| 1787 | return i; | 1863 | return i; |
| 1788 | } | 1864 | } |
| 1789 | | 1865 | |
| 1790 | fn calculateExtraLenUpperBound(comptime Extra: type) comptime_int { | 1866 | fn extraFieldLen(field: anytype) usize { |
| 1791 | var i = 0; | 1867 | const Field = @TypeOf(field); |
| 1792 | const fields = @typeInfo(Extra).@"struct".fields; | 1868 | return switch (@typeInfo(Field)) { |
| | 1869 | .int => |info| switch (info.bits) { |
| | 1870 | 32 => 1, |
| | 1871 | 64 => 2, |
| | 1872 | else => comptime unreachable, |
| | 1873 | }, |
| | 1874 | .@"enum" => 1, |
| | 1875 | .@"struct" => |info| switch (info.layout) { |
| | 1876 | .@"packed" => switch (info.backing_integer.?) { |
| | 1877 | u32 => 1, |
| | 1878 | u64 => 2, |
| | 1879 | else => comptime unreachable, |
| | 1880 | }, |
| | 1881 | .auto => switch (Field.storage) { |
| | 1882 | .flag_optional, .enum_optional, .extended => 1, |
| | 1883 | .flag_length_prefixed_list => field.slice.len + 1, |
| | 1884 | }, |
| | 1885 | .@"extern" => comptime unreachable, |
| | 1886 | }, |
| | 1887 | else => @compileError("bad type: " ++ @typeName(Field)), |
| | 1888 | }; |
| | 1889 | } |
| | 1890 | |
| | 1891 | fn extraLen(extra: anytype) usize { |
| | 1892 | const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; |
| | 1893 | var i: usize = 0; |
| 1793 | inline for (fields) |field| { | 1894 | inline for (fields) |field| { |
| 1794 | i += calculateExtraFieldLenUpperBound(field.type); | 1895 | i += Storage.extraFieldLen(@field(extra, field.name)); |
| 1795 | } | 1896 | } |
| 1796 | return i; | 1897 | return i; |
| 1797 | } | 1898 | } |
| ... | @@ -1836,6 +1937,13 @@ pub const Storage = enum { | ... | @@ -1836,6 +1937,13 @@ pub const Storage = enum { |
| 1836 | return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0; | 1937 | return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0; |
| 1837 | }, | 1938 | }, |
| 1838 | .extended => @compileError("TODO"), | 1939 | .extended => @compileError("TODO"), |
| | 1940 | .flag_length_prefixed_list => { |
| | 1941 | const len: u32 = @intCast(value.slice.len); |
| | 1942 | if (len == 0) return 0; |
| | 1943 | buffer[i] = len; |
| | 1944 | @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice))); |
| | 1945 | return len + 1; |
| | 1946 | }, |
| 1839 | }, | 1947 | }, |
| 1840 | }, | 1948 | }, |
| 1841 | .@"extern" => comptime unreachable, | 1949 | .@"extern" => comptime unreachable, |
| ... | @@ -1843,29 +1951,6 @@ pub const Storage = enum { | ... | @@ -1843,29 +1951,6 @@ pub const Storage = enum { |
| 1843 | else => @compileError("bad field type: " ++ @typeName(Field)), | 1951 | else => @compileError("bad field type: " ++ @typeName(Field)), |
| 1844 | } | 1952 | } |
| 1845 | } | 1953 | } |
| 1846 | | | |
| 1847 | fn calculateExtraFieldLenUpperBound(comptime Field: type) comptime_int { | | |
| 1848 | return switch (@typeInfo(Field)) { | | |
| 1849 | .int => |info| switch (info.bits) { | | |
| 1850 | 32 => 1, | | |
| 1851 | 64 => 2, | | |
| 1852 | else => comptime unreachable, | | |
| 1853 | }, | | |
| 1854 | .@"enum" => 1, | | |
| 1855 | .@"struct" => |info| switch (info.layout) { | | |
| 1856 | .@"packed" => switch (info.backing_integer.?) { | | |
| 1857 | u32 => 1, | | |
| 1858 | u64 => 2, | | |
| 1859 | else => comptime unreachable, | | |
| 1860 | }, | | |
| 1861 | .auto => switch (Field.storage) { | | |
| 1862 | .flag_optional, .enum_optional, .extended => 1, | | |
| 1863 | }, | | |
| 1864 | .@"extern" => comptime unreachable, | | |
| 1865 | }, | | |
| 1866 | else => comptime unreachable, | | |
| 1867 | }; | | |
| 1868 | } | | |
| 1869 | }; | 1954 | }; |
| 1870 | | 1955 | |
| 1871 | pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T { | 1956 | pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T { |