Skip to content

Commit c490674

Browse files
committed
Deduplicate listNar and MemorySourceAccessor::File
`listNar` did the not-so-pretty thing of going straight to JSON. Now it uses `MemorySourceAccessor::File`, or rather variations of it, to go to a C++ data type first, and only JSON second. To accomplish this we add some type parameters to the `File` data type. Actually, we need to do two rounds of this, because shallow NAR listings. There is `FileT` and `DirectoryT` accordingly.
1 parent ac36d74 commit c490674

File tree

8 files changed

+236
-91
lines changed

8 files changed

+236
-91
lines changed

src/libstore/binary-cache-store.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
208208
if (config.writeNARListing) {
209209
nlohmann::json j = {
210210
{"version", 1},
211-
{"root", listNar(*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/remote-fs-accessor.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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/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<>> entries;
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 entries < other.entries;
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/nar-accessor.hh

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22
///@file
33

4-
#include "nix/util/source-accessor.hh"
4+
#include "nix/util/memory-source-accessor.hh"
55

66
#include <functional>
77

@@ -34,10 +34,55 @@ GetNarBytes seekableGetNarBytes(const Path & path);
3434

3535
ref<SourceAccessor> makeLazyNarAccessor(const nlohmann::json & listing, GetNarBytes getNarBytes);
3636

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+
3783
/**
38-
* Write a JSON representation of the contents of a NAR (except file
39-
* contents).
84+
* Serialize a ShallowNarListing to JSON.
4085
*/
41-
nlohmann::json listNar(SourceAccessor & accessor, const CanonPath & path, bool recurse);
86+
void to_json(nlohmann::json & j, const ShallowNarListing & listing);
4287

4388
} // namespace nix

src/libutil/memory-source-accessor.cc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,26 @@ bool MemorySourceAccessor::pathExists(const CanonPath & path)
6868
return open(path, std::nullopt);
6969
}
7070

71-
MemorySourceAccessor::Stat MemorySourceAccessor::File::lstat() const
71+
template<>
72+
SourceAccessor::Stat MemorySourceAccessor::File::lstat() const
7273
{
7374
return std::visit(
7475
overloaded{
7576
[](const Regular & r) {
76-
return Stat{
77-
.type = tRegular,
77+
return SourceAccessor::Stat{
78+
.type = SourceAccessor::tRegular,
7879
.fileSize = r.contents.size(),
7980
.isExecutable = r.executable,
8081
};
8182
},
8283
[](const Directory &) {
83-
return Stat{
84-
.type = tDirectory,
84+
return SourceAccessor::Stat{
85+
.type = SourceAccessor::tDirectory,
8586
};
8687
},
8788
[](const Symlink &) {
88-
return Stat{
89-
.type = tSymlink,
89+
return SourceAccessor::Stat{
90+
.type = SourceAccessor::tSymlink,
9091
};
9192
},
9293
},

0 commit comments

Comments
 (0)