| ... | @@ -275,10 +275,9 @@ pub const Manifest = struct { | ... | @@ -275,10 +275,9 @@ pub const Manifest = struct { |
| 275 | /// When this is null, `Manifest` is in "pre-check" phase. Otherwise it is in "post-check" phase. | 275 | /// When this is null, `Manifest` is in "pre-check" phase. Otherwise it is in "post-check" phase. |
| 276 | manifest_file: ?Io.File, | 276 | manifest_file: ?Io.File, |
| 277 | manifest_dirty: bool, | 277 | manifest_dirty: bool, |
| 278 | /// Set this flag to true before calling hit() in order to indicate that | 278 | /// Set this flag to true before calling `check` in order to indicate that upon a cache hit, the code |
| 279 | /// upon a cache hit, the code using the cache will not modify the files | 279 | /// using the cache will not modify the files within the cache directory. This allows multiple processes |
| 280 | /// within the cache directory. This allows multiple processes to utilize | 280 | /// to utilize the same cache directory at the same time. |
| 281 | /// the same cache directory at the same time. | | |
| 282 | want_shared_lock: bool = true, | 281 | want_shared_lock: bool = true, |
| 283 | have_exclusive_lock: bool = false, | 282 | have_exclusive_lock: bool = false, |
| 284 | // Indicate that we want isProblematicTimestamp to perform a filesystem write in | 283 | // Indicate that we want isProblematicTimestamp to perform a filesystem write in |
| ... | @@ -347,12 +346,23 @@ pub const Manifest = struct { | ... | @@ -347,12 +346,23 @@ pub const Manifest = struct { |
| 347 | have_stat: bool, | 346 | have_stat: bool, |
| 348 | /// Determines whether `File.digest` is populated. | 347 | /// Determines whether `File.digest` is populated. |
| 349 | have_digest: bool, | 348 | have_digest: bool, |
| 350 | contents: enum(usize) { | 349 | contents: packed union(u64) { |
| 351 | requested = std.math.maxInt(u32) - 1, | 350 | status: enum(u64) { |
| 352 | not_requested = std.math.maxInt(u32), | 351 | unrequested = std.math.maxInt(u64) - 1, |
| 353 | /// Byte offset index into `Manifest.all_input_content`. | 352 | requested = std.math.maxInt(u64), |
| 354 | _, | 353 | _, |
| 355 | }, | 354 | pub fn populated(this: @This()) bool { |
| | 355 | return switch (this) { |
| | 356 | .unrequested, .requested => false, |
| | 357 | _ => true, |
| | 358 | }; |
| | 359 | } |
| | 360 | }, |
| | 361 | populated: packed struct(u64) { |
| | 362 | off: u32, |
| | 363 | len: u32, |
| | 364 | }, |
| | 365 | } align(@alignOf(u32)), |
| 356 | /// `have_handle` determines whether this is populated. | 366 | /// `have_handle` determines whether this is populated. |
| 357 | handle: union { | 367 | handle: union { |
| 358 | file: Io.File, | 368 | file: Io.File, |
| ... | @@ -362,6 +372,20 @@ pub const Manifest = struct { | ... | @@ -362,6 +372,20 @@ pub const Manifest = struct { |
| 362 | /// Index into `Manifest.input_paths`. | 372 | /// Index into `Manifest.input_paths`. |
| 363 | pub const Index = enum(u32) { | 373 | pub const Index = enum(u32) { |
| 364 | _, | 374 | _, |
| | 375 | |
| | 376 | pub fn get(i: @This(), manifest: *const Manifest) InputPath { |
| | 377 | return manifest.input_paths.items[@backingInt(i)]; |
| | 378 | } |
| | 379 | |
| | 380 | pub fn contents(i: @This(), manifest: *const Manifest) []const u8 { |
| | 381 | const input_path = get(i, manifest); |
| | 382 | const populated = switch (input_path.contents.status) { |
| | 383 | .unrequested => unreachable, // Must request contents before calling `check`. |
| | 384 | .requested => unreachable, // Must call `check` first. |
| | 385 | _ => input_path.contents.populated, |
| | 386 | }; |
| | 387 | return manifest.all_input_content.items[populated.off..][0..populated.len]; |
| | 388 | } |
| 365 | }; | 389 | }; |
| 366 | }; | 390 | }; |
| 367 | | 391 | |
| ... | @@ -611,8 +635,8 @@ pub const Manifest = struct { | ... | @@ -611,8 +635,8 @@ pub const Manifest = struct { |
| 611 | existing_input_file.have_handle = true; | 635 | existing_input_file.have_handle = true; |
| 612 | }, | 636 | }, |
| 613 | } | 637 | } |
| 614 | if (options.request_contents) switch (existing_input_file.contents) { | 638 | if (options.request_contents) switch (existing_input_file.contents.status) { |
| 615 | .requested, .not_requested => existing_input_file.contents = .requested, | 639 | .requested, .unrequested => existing_input_file.contents = .{ .status = .requested }, |
| 616 | _ => {}, | 640 | _ => {}, |
| 617 | }; | 641 | }; |
| 618 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); | 642 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); |
| ... | @@ -636,7 +660,7 @@ pub const Manifest = struct { | ... | @@ -636,7 +660,7 @@ pub const Manifest = struct { |
| 636 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, | 660 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, |
| 637 | .dir => |opt_dir| if (opt_dir) |dir| .{ .dir = dir } else undefined, | 661 | .dir => |opt_dir| if (opt_dir) |dir| .{ .dir = dir } else undefined, |
| 638 | }, | 662 | }, |
| 639 | .contents = if (options.request_contents) .requested else .not_requested, | 663 | .contents = .{ .status = if (options.request_contents) .requested else .unrequested }, |
| 640 | .have_digest = false, | 664 | .have_digest = false, |
| 641 | .have_stat = options.stat != null, | 665 | .have_stat = options.stat != null, |
| 642 | }); | 666 | }); |
| ... | @@ -931,7 +955,7 @@ pub const Manifest = struct { | ... | @@ -931,7 +955,7 @@ pub const Manifest = struct { |
| 931 | ) CheckFileError!void { | 955 | ) CheckFileError!void { |
| 932 | if (input_path.have_handle) @panic("TODO"); | 956 | if (input_path.have_handle) @panic("TODO"); |
| 933 | if (input_path.have_stat) @panic("TODO"); | 957 | if (input_path.have_stat) @panic("TODO"); |
| 934 | if (input_path.contents != .not_requested) @panic("TODO"); | 958 | if (input_path.contents.status != .unrequested) @panic("TODO"); |
| 935 | if (input_path.request_handle) @panic("TODO"); | 959 | if (input_path.request_handle) @panic("TODO"); |
| 936 | switch (try checkFile(m, c, file_off, file_path)) { | 960 | switch (try checkFile(m, c, file_off, file_path)) { |
| 937 | .hit => {}, | 961 | .hit => {}, |
| ... | @@ -1727,7 +1751,7 @@ test "cache file and then recall it" { | ... | @@ -1727,7 +1751,7 @@ test "cache file and then recall it" { |
| 1727 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); | 1751 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1728 | | 1752 | |
| 1729 | // There should be nothing in the cache | 1753 | // There should be nothing in the cache |
| 1730 | try testing.expectEqual(false, try ch.hit(.none)); | 1754 | try testing.expectEqual(.miss, try ch.check(.none)); |
| 1731 | | 1755 | |
| 1732 | digest1 = ch.final(); | 1756 | digest1 = ch.final(); |
| 1733 | try ch.writeManifest(); | 1757 | try ch.writeManifest(); |
| ... | @@ -1742,7 +1766,7 @@ test "cache file and then recall it" { | ... | @@ -1742,7 +1766,7 @@ test "cache file and then recall it" { |
| 1742 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); | 1766 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1743 | | 1767 | |
| 1744 | // Cache hit! We just "built" the same file | 1768 | // Cache hit! We just "built" the same file |
| 1745 | try testing.expect(try ch.hit(.none)); | 1769 | try testing.expectEqual(.hit, try ch.check(.none)); |
| 1746 | digest2 = ch.final(); | 1770 | digest2 = ch.final(); |
| 1747 | | 1771 | |
| 1748 | try testing.expectEqual(false, ch.have_exclusive_lock); | 1772 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | @@ -1788,40 +1812,40 @@ test "check that changing a file makes cache fail" { | ... | @@ -1788,40 +1812,40 @@ test "check that changing a file makes cache fail" { |
| 1788 | defer cache.manifest_dir.close(io); | 1812 | defer cache.manifest_dir.close(io); |
| 1789 | | 1813 | |
| 1790 | { | 1814 | { |
| 1791 | var ch = cache.obtain(); | 1815 | var man = cache.obtain(); |
| 1792 | defer ch.deinit(); | 1816 | defer man.deinit(); |
| 1793 | | 1817 | |
| 1794 | ch.hash.addBytes("1234"); | 1818 | man.hash.addBytes("1234"); |
| 1795 | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); | 1819 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1796 | | 1820 | |
| 1797 | // There should be nothing in the cache | 1821 | // There should be nothing in the cache |
| 1798 | try testing.expectEqual(false, try ch.hit(.none)); | 1822 | try testing.expectEqual(.miss, try man.check(.none)); |
| 1799 | | 1823 | |
| 1800 | try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.keys()[temp_file_idx].contents.?)); | 1824 | try testing.expectEqualStrings(original_temp_file_contents, temp_file_idx.contents(&man)); |
| 1801 | | 1825 | |
| 1802 | digest1 = ch.final(); | 1826 | digest1 = man.final(); |
| 1803 | | 1827 | |
| 1804 | try ch.writeManifest(); | 1828 | try man.writeManifest(); |
| 1805 | } | 1829 | } |
| 1806 | | 1830 | |
| 1807 | try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = updated_temp_file_contents }); | 1831 | try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = updated_temp_file_contents }); |
| 1808 | | 1832 | |
| 1809 | { | 1833 | { |
| 1810 | var ch = cache.obtain(); | 1834 | var man = cache.obtain(); |
| 1811 | defer ch.deinit(); | 1835 | defer man.deinit(); |
| 1812 | | 1836 | |
| 1813 | ch.hash.addBytes("1234"); | 1837 | man.hash.addBytes("1234"); |
| 1814 | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); | 1838 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1815 | | 1839 | |
| 1816 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 1840 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1817 | try testing.expectEqual(false, try ch.hit(.none)); | 1841 | try testing.expectEqual(.miss, try man.check(.none)); |
| 1818 | | 1842 | |
| 1819 | // The cache system does not keep the contents of re-hashed input files. | 1843 | // The cache system does not keep the contents of re-hashed input files. |
| 1820 | try testing.expect(ch.files.keys()[temp_file_idx].contents == null); | 1844 | try testing.expectEqual(false, temp_file_idx.get(&man).contents.status.populated()); |
| 1821 | | 1845 | |
| 1822 | digest2 = ch.final(); | 1846 | digest2 = man.final(); |
| 1823 | | 1847 | |
| 1824 | try ch.writeManifest(); | 1848 | try man.writeManifest(); |
| 1825 | } | 1849 | } |
| 1826 | | 1850 | |
| 1827 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); | 1851 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); |
| ... | @@ -1858,7 +1882,7 @@ test "no file inputs" { | ... | @@ -1858,7 +1882,7 @@ test "no file inputs" { |
| 1858 | man.hash.addBytes("1234"); | 1882 | man.hash.addBytes("1234"); |
| 1859 | | 1883 | |
| 1860 | // There should be nothing in the cache | 1884 | // There should be nothing in the cache |
| 1861 | try testing.expectEqual(false, try man.check(.none)); | 1885 | try testing.expectEqual(.miss, try man.check(.none)); |
| 1862 | | 1886 | |
| 1863 | digest1 = man.final(); | 1887 | digest1 = man.final(); |
| 1864 | | 1888 | |
| ... | @@ -1870,7 +1894,7 @@ test "no file inputs" { | ... | @@ -1870,7 +1894,7 @@ test "no file inputs" { |
| 1870 | | 1894 | |
| 1871 | man.hash.addBytes("1234"); | 1895 | man.hash.addBytes("1234"); |
| 1872 | | 1896 | |
| 1873 | try testing.expect(try man.check(.none)); | 1897 | try testing.expectEqual(.hit, try man.check(.none)); |
| 1874 | digest2 = man.final(); | 1898 | digest2 = man.final(); |
| 1875 | try testing.expectEqual(false, man.have_exclusive_lock); | 1899 | try testing.expectEqual(false, man.have_exclusive_lock); |
| 1876 | } | 1900 | } |
| ... | @@ -1922,9 +1946,9 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1922,9 +1946,9 @@ test "Manifest with files added after initial hash work" { |
| 1922 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 1946 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1923 | | 1947 | |
| 1924 | // There should be nothing in the cache | 1948 | // There should be nothing in the cache |
| 1925 | try testing.expectEqual(false, try ch.hit(.none)); | 1949 | try testing.expectEqual(.miss, try ch.check(.none)); |
| 1926 | | 1950 | |
| 1927 | _ = try ch.addDiscoveredPath(temp_file2); | 1951 | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 1928 | | 1952 | |
| 1929 | digest1 = ch.final(); | 1953 | digest1 = ch.final(); |
| 1930 | try ch.writeManifest(); | 1954 | try ch.writeManifest(); |
| ... | @@ -1936,7 +1960,7 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1936,7 +1960,7 @@ test "Manifest with files added after initial hash work" { |
| 1936 | ch.hash.addBytes("1234"); | 1960 | ch.hash.addBytes("1234"); |
| 1937 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 1961 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1938 | | 1962 | |
| 1939 | try testing.expect(try ch.hit(.none)); | 1963 | try testing.expect(.hit == try ch.check(.none)); |
| 1940 | digest2 = ch.final(); | 1964 | digest2 = ch.final(); |
| 1941 | | 1965 | |
| 1942 | try testing.expectEqual(false, ch.have_exclusive_lock); | 1966 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | @@ -1960,9 +1984,9 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1960,9 +1984,9 @@ test "Manifest with files added after initial hash work" { |
| 1960 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); | 1984 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1961 | | 1985 | |
| 1962 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 1986 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| 1963 | try testing.expectEqual(false, try ch.hit(.none)); | 1987 | try testing.expectEqual(.miss, try ch.check(.none)); |
| 1964 | | 1988 | |
| 1965 | _ = try ch.addDiscoveredPath(temp_file2); | 1989 | _ = try ch.addDiscoveredPath(.{ .discovered_path = .{ .unresolved = .initCwd(temp_file2) } }); |
| 1966 | | 1990 | |
| 1967 | digest3 = ch.final(); | 1991 | digest3 = ch.final(); |
| 1968 | | 1992 | |