Skip to content

Commit 50407ab

Browse files
authored
Merge pull request #14598 from NixOS/nar-listing-dedup
Deduplicate `listNar` and `MemorySourceAccessor::File`
2 parents 5caebab + c490674 commit 50407ab

File tree

15 files changed

+291
-146
lines changed

15 files changed

+291
-146
lines changed

src/libstore-tests/references.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ TEST(references, scanForReferencesDeep)
9999
// Create an in-memory file system with various reference patterns
100100
auto accessor = make_ref<MemorySourceAccessor>();
101101
accessor->root = File::Directory{
102-
.contents{
102+
.entries{
103103
{
104104
// file1.txt: contains hash1
105105
"file1.txt",
@@ -125,7 +125,7 @@ TEST(references, scanForReferencesDeep)
125125
// subdir: a subdirectory
126126
"subdir",
127127
File::Directory{
128-
.contents{
128+
.entries{
129129
{
130130
// subdir/file4.txt: contains hash1 again
131131
"file4.txt",

src/libstore/binary-cache-store.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "nix/util/sync.hh"
99
#include "nix/store/remote-fs-accessor.hh"
1010
#include "nix/store/nar-info-disk-cache.hh"
11-
#include "nix/store/nar-accessor.hh"
11+
#include "nix/util/nar-accessor.hh"
1212
#include "nix/util/thread-pool.hh"
1313
#include "nix/util/callback.hh"
1414
#include "nix/util/signals.hh"
@@ -208,7 +208,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
208208
if (config.writeNARListing) {
209209
nlohmann::json j = {
210210
{"version", 1},
211-
{"root", listNar(ref<SourceAccessor>(narAccessor), CanonPath::root, true)},
211+
{"root", listNarDeep(*narAccessor, CanonPath::root)},
212212
};
213213

214214
upsertFile(std::string(info.path.hashPart()) + ".ls", j.dump(), "application/json");

src/libstore/include/nix/store/meson.build

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ headers = [ config_pub_h ] + files(
5555
'machines.hh',
5656
'make-content-addressed.hh',
5757
'names.hh',
58-
'nar-accessor.hh',
5958
'nar-info-disk-cache.hh',
6059
'nar-info.hh',
6160
'outputs-spec.hh',

src/libstore/include/nix/store/nar-accessor.hh

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/libstore/meson.build

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ sources = files(
300300
'make-content-addressed.cc',
301301
'misc.cc',
302302
'names.cc',
303-
'nar-accessor.cc',
304303
'nar-info-disk-cache.cc',
305304
'nar-info.cc',
306305
'optimise-store.cc',

src/libstore/remote-fs-accessor.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <nlohmann/json.hpp>
22
#include "nix/store/remote-fs-accessor.hh"
3-
#include "nix/store/nar-accessor.hh"
3+
#include "nix/util/nar-accessor.hh"
44

55
#include <sys/types.h>
66
#include <sys/stat.h>
@@ -39,7 +39,7 @@ ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std:
3939

4040
if (cacheDir != "") {
4141
try {
42-
nlohmann::json j = listNar(narAccessor, CanonPath::root, true);
42+
nlohmann::json j = listNarDeep(*narAccessor, CanonPath::root);
4343
writeFile(makeCacheFile(hashPart, "ls"), j.dump());
4444
} catch (...) {
4545
ignoreExceptionExceptInterrupt();

src/libutil-tests/git.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ TEST_F(GitTest, both_roundrip)
230230

231231
auto files = make_ref<MemorySourceAccessor>();
232232
files->root = File::Directory{
233-
.contents{
233+
.entries{
234234
{
235235
"foo",
236236
File::Regular{
@@ -240,7 +240,7 @@ TEST_F(GitTest, both_roundrip)
240240
{
241241
"bar",
242242
File::Directory{
243-
.contents =
243+
.entries =
244244
{
245245
{
246246
"baz",

src/libutil/include/nix/util/memory-source-accessor.hh

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,111 @@
44
#include "nix/util/source-path.hh"
55
#include "nix/util/fs-sink.hh"
66
#include "nix/util/variant-wrapper.hh"
7+
#include "nix/util/json-impls.hh"
78

89
namespace nix {
910

1011
/**
11-
* An source accessor for an in-memory file system.
12+
* File System Object definitions
13+
*
14+
* @see https://nix.dev/manual/nix/latest/store/file-system-object.html
1215
*/
13-
struct MemorySourceAccessor : virtual SourceAccessor
16+
namespace fso {
17+
18+
template<typename RegularContents>
19+
struct Regular
20+
{
21+
bool executable = false;
22+
RegularContents contents;
23+
24+
auto operator<=>(const Regular &) const = default;
25+
};
26+
27+
/**
28+
* Child parameter because sometimes we want "shallow" directories without
29+
* full file children.
30+
*/
31+
template<typename Child>
32+
struct DirectoryT
33+
{
34+
using Name = std::string;
35+
36+
std::map<Name, Child, std::less<>> entries;
37+
38+
inline bool operator==(const DirectoryT &) const noexcept;
39+
inline std::strong_ordering operator<=>(const DirectoryT &) const noexcept;
40+
};
41+
42+
struct Symlink
43+
{
44+
std::string target;
45+
46+
auto operator<=>(const Symlink &) const = default;
47+
};
48+
49+
/**
50+
* For when we know there is child, but don't know anything about it.
51+
*
52+
* This is not part of the core File System Object data model --- this
53+
* represents not knowing, not an additional type of file.
54+
*/
55+
struct Opaque
56+
{
57+
auto operator<=>(const Opaque &) const = default;
58+
};
59+
60+
/**
61+
* `File<std::string>` nicely defining what a "file system object"
62+
* is in Nix.
63+
*
64+
* With a different type arugment, it is also can be a "skeletal"
65+
* version is that abstract syntax for a "NAR listing".
66+
*/
67+
template<typename RegularContents, bool recur>
68+
struct VariantT
1469
{
70+
bool operator==(const VariantT &) const noexcept;
71+
std::strong_ordering operator<=>(const VariantT &) const noexcept;
72+
73+
using Regular = nix::fso::Regular<RegularContents>;
74+
1575
/**
16-
* In addition to being part of the implementation of
17-
* `MemorySourceAccessor`, this has a side benefit of nicely
18-
* defining what a "file system object" is in Nix.
76+
* In the default case, we do want full file children for our directory.
1977
*/
20-
struct File
21-
{
22-
bool operator==(const File &) const noexcept;
23-
std::strong_ordering operator<=>(const File &) const noexcept;
78+
using Directory = nix::fso::DirectoryT<std::conditional_t<recur, VariantT, Opaque>>;
2479

25-
struct Regular
26-
{
27-
bool executable = false;
28-
std::string contents;
80+
using Symlink = nix::fso::Symlink;
2981

30-
bool operator==(const Regular &) const = default;
31-
auto operator<=>(const Regular &) const = default;
32-
};
82+
using Raw = std::variant<Regular, Directory, Symlink>;
83+
Raw raw;
3384

34-
struct Directory
35-
{
36-
using Name = std::string;
85+
MAKE_WRAPPER_CONSTRUCTOR(VariantT);
3786

38-
std::map<Name, File, std::less<>> contents;
87+
SourceAccessor::Stat lstat() const;
88+
};
3989

40-
bool operator==(const Directory &) const noexcept;
41-
// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet.
42-
bool operator<(const Directory &) const noexcept;
43-
};
90+
template<typename Child>
91+
inline bool DirectoryT<Child>::operator==(const DirectoryT &) const noexcept = default;
4492

45-
struct Symlink
46-
{
47-
std::string target;
93+
template<typename Child>
94+
inline std::strong_ordering DirectoryT<Child>::operator<=>(const DirectoryT &) const noexcept = default;
4895

49-
bool operator==(const Symlink &) const = default;
50-
auto operator<=>(const Symlink &) const = default;
51-
};
96+
template<typename RegularContents, bool recur>
97+
inline bool
98+
VariantT<RegularContents, recur>::operator==(const VariantT<RegularContents, recur> &) const noexcept = default;
5299

53-
using Raw = std::variant<Regular, Directory, Symlink>;
54-
Raw raw;
100+
template<typename RegularContents, bool recur>
101+
inline std::strong_ordering
102+
VariantT<RegularContents, recur>::operator<=>(const VariantT<RegularContents, recur> &) const noexcept = default;
55103

56-
MAKE_WRAPPER_CONSTRUCTOR(File);
104+
} // namespace fso
57105

58-
Stat lstat() const;
59-
};
106+
/**
107+
* An source accessor for an in-memory file system.
108+
*/
109+
struct MemorySourceAccessor : virtual SourceAccessor
110+
{
111+
using File = fso::VariantT<std::string, true>;
60112

61113
std::optional<File> root;
62114

@@ -89,19 +141,6 @@ struct MemorySourceAccessor : virtual SourceAccessor
89141
SourcePath addFile(CanonPath path, std::string && contents);
90142
};
91143

92-
inline bool MemorySourceAccessor::File::Directory::operator==(
93-
const MemorySourceAccessor::File::Directory &) const noexcept = default;
94-
95-
inline bool
96-
MemorySourceAccessor::File::Directory::operator<(const MemorySourceAccessor::File::Directory & other) const noexcept
97-
{
98-
return contents < other.contents;
99-
}
100-
101-
inline bool MemorySourceAccessor::File::operator==(const MemorySourceAccessor::File &) const noexcept = default;
102-
inline std::strong_ordering
103-
MemorySourceAccessor::File::operator<=>(const MemorySourceAccessor::File &) const noexcept = default;
104-
105144
/**
106145
* Write to a `MemorySourceAccessor` at the given path
107146
*/

src/libutil/include/nix/util/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ headers = files(
5050
'memory-source-accessor.hh',
5151
'mounted-source-accessor.hh',
5252
'muxable-pipe.hh',
53+
'nar-accessor.hh',
5354
'os-string.hh',
5455
'pool.hh',
5556
'pos-idx.hh',
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#pragma once
2+
///@file
3+
4+
#include "nix/util/memory-source-accessor.hh"
5+
6+
#include <functional>
7+
8+
#include <nlohmann/json_fwd.hpp>
9+
10+
namespace nix {
11+
12+
struct Source;
13+
14+
/**
15+
* Return an object that provides access to the contents of a NAR
16+
* file.
17+
*/
18+
ref<SourceAccessor> makeNarAccessor(std::string && nar);
19+
20+
ref<SourceAccessor> makeNarAccessor(Source & source);
21+
22+
/**
23+
* Create a NAR accessor from a NAR listing (in the format produced by
24+
* listNar()). The callback getNarBytes(offset, length) is used by the
25+
* readFile() method of the accessor to get the contents of files
26+
* inside the NAR.
27+
*/
28+
using GetNarBytes = std::function<std::string(uint64_t, uint64_t)>;
29+
30+
/**
31+
* The canonical GetNarBytes function for a seekable Source.
32+
*/
33+
GetNarBytes seekableGetNarBytes(const Path & path);
34+
35+
ref<SourceAccessor> makeLazyNarAccessor(const nlohmann::json & listing, GetNarBytes getNarBytes);
36+
37+
struct NarListingRegularFile
38+
{
39+
/**
40+
* @see `SourceAccessor::Stat::fileSize`
41+
*/
42+
std::optional<uint64_t> fileSize;
43+
44+
/**
45+
* @see `SourceAccessor::Stat::narOffset`
46+
*
47+
* We only set to non-`std::nullopt` if it is also non-zero.
48+
*/
49+
std::optional<uint64_t> narOffset;
50+
51+
auto operator<=>(const NarListingRegularFile &) const = default;
52+
};
53+
54+
/**
55+
* Abstract syntax for a "NAR listing".
56+
*/
57+
using NarListing = fso::VariantT<NarListingRegularFile, true>;
58+
59+
/**
60+
* Shallow NAR listing where directory children are not recursively expanded.
61+
* Uses a variant that can hold Regular/Symlink fully, but Directory children
62+
* are just unit types indicating presence without content.
63+
*/
64+
using ShallowNarListing = fso::VariantT<NarListingRegularFile, false>;
65+
66+
/**
67+
* Return a deep structured representation of the contents of a NAR (except file
68+
* contents), recursively listing all children.
69+
*/
70+
NarListing listNarDeep(SourceAccessor & accessor, const CanonPath & path);
71+
72+
/**
73+
* Return a shallow structured representation of the contents of a NAR (except file
74+
* contents), only listing immediate children without recursing.
75+
*/
76+
ShallowNarListing listNarShallow(SourceAccessor & accessor, const CanonPath & path);
77+
78+
/**
79+
* Serialize a NarListing to JSON.
80+
*/
81+
void to_json(nlohmann::json & j, const NarListing & listing);
82+
83+
/**
84+
* Serialize a ShallowNarListing to JSON.
85+
*/
86+
void to_json(nlohmann::json & j, const ShallowNarListing & listing);
87+
88+
} // namespace nix

0 commit comments

Comments
 (0)