| ... | ... | @@ -275,10 +275,9 @@ pub const Manifest = struct { |
| 275 | 275 | /// When this is null, `Manifest` is in "pre-check" phase. Otherwise it is in "post-check" phase. |
| 276 | 276 | manifest_file: ?Io.File, |
| 277 | 277 | manifest_dirty: bool, |
| 278 | | /// Set this flag to true before calling hit() in order to indicate that |
| 279 | | /// upon a cache hit, the code using the cache will not modify the files |
| 280 | | /// within the cache directory. This allows multiple processes to utilize |
| 281 | | /// the same cache directory at the same time. |
| 278 | /// Set this flag to true before calling `check` in order to indicate that upon a cache hit, the code |
| 279 | /// using the cache will not modify the files within the cache directory. This allows multiple processes |
| 280 | /// to utilize the same cache directory at the same time. |
| 282 | 281 | want_shared_lock: bool = true, |
| 283 | 282 | have_exclusive_lock: bool = false, |
| 284 | 283 | // Indicate that we want isProblematicTimestamp to perform a filesystem write in |
| ... | ... | @@ -347,12 +346,23 @@ pub const Manifest = struct { |
| 347 | 346 | have_stat: bool, |
| 348 | 347 | /// Determines whether `File.digest` is populated. |
| 349 | 348 | have_digest: bool, |
| 350 | | contents: enum(usize) { |
| 351 | | requested = std.math.maxInt(u32) - 1, |
| 352 | | not_requested = std.math.maxInt(u32), |
| 353 | | /// Byte offset index into `Manifest.all_input_content`. |
| 354 | | _, |
| 355 | | }, |
| 349 | contents: packed union(u64) { |
| 350 | status: enum(u64) { |
| 351 | unrequested = std.math.maxInt(u64) - 1, |
| 352 | requested = std.math.maxInt(u64), |
| 353 | _, |
| 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 | 366 | /// `have_handle` determines whether this is populated. |
| 357 | 367 | handle: union { |
| 358 | 368 | file: Io.File, |
| ... | ... | @@ -362,6 +372,20 @@ pub const Manifest = struct { |
| 362 | 372 | /// Index into `Manifest.input_paths`. |
| 363 | 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 | 635 | existing_input_file.have_handle = true; |
| 612 | 636 | }, |
| 613 | 637 | } |
| 614 | | if (options.request_contents) switch (existing_input_file.contents) { |
| 615 | | .requested, .not_requested => existing_input_file.contents = .requested, |
| 638 | if (options.request_contents) switch (existing_input_file.contents.status) { |
| 639 | .requested, .unrequested => existing_input_file.contents = .{ .status = .requested }, |
| 616 | 640 | _ => {}, |
| 617 | 641 | }; |
| 618 | 642 | const existing_header = m.files.keys()[gop.index].get(m.contents.items); |
| ... | ... | @@ -636,7 +660,7 @@ pub const Manifest = struct { |
| 636 | 660 | .file => |opt_file| if (opt_file) |file| .{ .file = file } else undefined, |
| 637 | 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 | 664 | .have_digest = false, |
| 641 | 665 | .have_stat = options.stat != null, |
| 642 | 666 | }); |
| ... | ... | @@ -931,7 +955,7 @@ pub const Manifest = struct { |
| 931 | 955 | ) CheckFileError!void { |
| 932 | 956 | if (input_path.have_handle) @panic("TODO"); |
| 933 | 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 | 959 | if (input_path.request_handle) @panic("TODO"); |
| 936 | 960 | switch (try checkFile(m, c, file_off, file_path)) { |
| 937 | 961 | .hit => {}, |
| ... | ... | @@ -1727,7 +1751,7 @@ test "cache file and then recall it" { |
| 1727 | 1751 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1728 | 1752 | |
| 1729 | 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 | 1756 | digest1 = ch.final(); |
| 1733 | 1757 | try ch.writeManifest(); |
| ... | ... | @@ -1742,7 +1766,7 @@ test "cache file and then recall it" { |
| 1742 | 1766 | _ = try ch.addInputPath(.initCwd(temp_file), .{}); |
| 1743 | 1767 | |
| 1744 | 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 | 1770 | digest2 = ch.final(); |
| 1747 | 1771 | |
| 1748 | 1772 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | ... | @@ -1788,40 +1812,40 @@ test "check that changing a file makes cache fail" { |
| 1788 | 1812 | defer cache.manifest_dir.close(io); |
| 1789 | 1813 | |
| 1790 | 1814 | { |
| 1791 | | var ch = cache.obtain(); |
| 1792 | | defer ch.deinit(); |
| 1815 | var man = cache.obtain(); |
| 1816 | defer man.deinit(); |
| 1793 | 1817 | |
| 1794 | | ch.hash.addBytes("1234"); |
| 1795 | | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1818 | man.hash.addBytes("1234"); |
| 1819 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1796 | 1820 | |
| 1797 | 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 | 1831 | try tmp.dir.writeFile(io, .{ .sub_path = temp_file, .data = updated_temp_file_contents }); |
| 1808 | 1832 | |
| 1809 | 1833 | { |
| 1810 | | var ch = cache.obtain(); |
| 1811 | | defer ch.deinit(); |
| 1834 | var man = cache.obtain(); |
| 1835 | defer man.deinit(); |
| 1812 | 1836 | |
| 1813 | | ch.hash.addBytes("1234"); |
| 1814 | | const temp_file_idx = try ch.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1837 | man.hash.addBytes("1234"); |
| 1838 | const temp_file_idx = try man.addInputPath(.initCwd(temp_file), .{ .request_contents = true }); |
| 1815 | 1839 | |
| 1816 | 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 | 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 | 1851 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); |
| ... | ... | @@ -1858,7 +1882,7 @@ test "no file inputs" { |
| 1858 | 1882 | man.hash.addBytes("1234"); |
| 1859 | 1883 | |
| 1860 | 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 | 1887 | digest1 = man.final(); |
| 1864 | 1888 | |
| ... | ... | @@ -1870,7 +1894,7 @@ test "no file inputs" { |
| 1870 | 1894 | |
| 1871 | 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 | 1898 | digest2 = man.final(); |
| 1875 | 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 | 1946 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1923 | 1947 | |
| 1924 | 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 | 1953 | digest1 = ch.final(); |
| 1930 | 1954 | try ch.writeManifest(); |
| ... | ... | @@ -1936,7 +1960,7 @@ test "Manifest with files added after initial hash work" { |
| 1936 | 1960 | ch.hash.addBytes("1234"); |
| 1937 | 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 | 1964 | digest2 = ch.final(); |
| 1941 | 1965 | |
| 1942 | 1966 | try testing.expectEqual(false, ch.have_exclusive_lock); |
| ... | ... | @@ -1960,9 +1984,9 @@ test "Manifest with files added after initial hash work" { |
| 1960 | 1984 | _ = try ch.addInputPath(.initCwd(temp_file1), .{}); |
| 1961 | 1985 | |
| 1962 | 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 | 1991 | digest3 = ch.final(); |
| 1968 | 1992 | |