Extend internal API docs, part 2

Picking up from #8111.

Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
This commit is contained in:
John Ericson 2023-03-26 21:12:25 -04:00
parent 8ae9d66940
commit abd5e7dec0
32 changed files with 640 additions and 359 deletions

View file

@ -3,8 +3,15 @@
namespace nix {
/**
* An InstallableCommand where the single positional argument must be an
* InstallableValue in particular.
*/
struct InstallableValueCommand : InstallableCommand
{
/**
* Entry point to this command
*/
virtual void run(ref<Store> store, ref<InstallableValue> installable) = 0;
void run(ref<Store> store, ref<Installable> installable) override;

View file

@ -87,39 +87,56 @@ struct FileTransfer
{
virtual ~FileTransfer() { }
/* Enqueue a data transfer request, returning a future to the result of
the download. The future may throw a FileTransferError
exception. */
/**
* Enqueue a data transfer request, returning a future to the result of
* the download. The future may throw a FileTransferError
* exception.
*/
virtual void enqueueFileTransfer(const FileTransferRequest & request,
Callback<FileTransferResult> callback) = 0;
std::future<FileTransferResult> enqueueFileTransfer(const FileTransferRequest & request);
/* Synchronously download a file. */
/**
* Synchronously download a file.
*/
FileTransferResult download(const FileTransferRequest & request);
/* Synchronously upload a file. */
/**
* Synchronously upload a file.
*/
FileTransferResult upload(const FileTransferRequest & request);
/* Download a file, writing its data to a sink. The sink will be
invoked on the thread of the caller. */
/**
* Download a file, writing its data to a sink. The sink will be
* invoked on the thread of the caller.
*/
void download(FileTransferRequest && request, Sink & sink);
enum Error { NotFound, Forbidden, Misc, Transient, Interrupted };
};
/* Return a shared FileTransfer object. Using this object is preferred
because it enables connection reuse and HTTP/2 multiplexing. */
/**
* @return a shared FileTransfer object.
*
* Using this object is preferred because it enables connection reuse
* and HTTP/2 multiplexing.
*/
ref<FileTransfer> getFileTransfer();
/* Return a new FileTransfer object. */
/**
* @return a new FileTransfer object
*
* Prefer getFileTransfer() to this; see its docs for why.
*/
ref<FileTransfer> makeFileTransfer();
class FileTransferError : public Error
{
public:
FileTransfer::Error error;
std::optional<std::string> response; // intentionally optional
/// intentionally optional
std::optional<std::string> response;
template<typename... Args>
FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args);

View file

@ -4,6 +4,13 @@
namespace nix {
/**
* Helper to try downcasting a Store with a nice method if it fails.
*
* This is basically an alternative to the user-facing part of
* Store::unsupported that allows us to still have a nice message but
* better interface design.
*/
template<typename T>
T & require(Store & store)
{

View file

@ -14,6 +14,10 @@ namespace nix {
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
/**
* Enumeration of all the request types for the "worker protocol", used
* by unix:// and ssh-ng:// stores.
*/
typedef enum {
wopIsValidPath = 1,
wopHasSubstitutes = 3,
@ -74,7 +78,12 @@ typedef enum {
class Store;
struct Source;
/* To guide overloading */
/**
* Used to guide overloading
*
* See https://en.cppreference.com/w/cpp/language/adl for the broader
* concept of what is going on here.
*/
template<typename T>
struct Phantom {};
@ -103,18 +112,19 @@ MAKE_WORKER_PROTO(X_, Y_);
#undef X_
#undef Y_
/* These use the empty string for the null case, relying on the fact
that the underlying types never serialize to the empty string.
We do this instead of a generic std::optional<T> instance because
ordinal tags (0 or 1, here) are a bit of a compatability hazard. For
the same reason, we don't have a std::variant<T..> instances (ordinal
tags 0...n).
We could the generic instances and then these as specializations for
compatability, but that's proven a bit finnicky, and also makes the
worker protocol harder to implement in other languages where such
specializations may not be allowed.
/**
* These use the empty string for the null case, relying on the fact
* that the underlying types never serialize to the empty string.
*
* We do this instead of a generic std::optional<T> instance because
* ordinal tags (0 or 1, here) are a bit of a compatability hazard. For
* the same reason, we don't have a std::variant<T..> instances (ordinal
* tags 0...n).
*
* We could the generic instances and then these as specializations for
* compatability, but that's proven a bit finnicky, and also makes the
* worker protocol harder to implement in other languages where such
* specializations may not be allowed.
*/
MAKE_WORKER_PROTO(, std::optional<StorePath>);
MAKE_WORKER_PROTO(, std::optional<ContentAddress>);

View file

@ -7,54 +7,71 @@
namespace nix {
/* dumpPath creates a Nix archive of the specified path. The format
is as follows:
IF path points to a REGULAR FILE:
dump(path) = attrs(
[ ("type", "regular")
, ("contents", contents(path))
])
IF path points to a DIRECTORY:
dump(path) = attrs(
[ ("type", "directory")
, ("entries", concat(map(f, sort(entries(path)))))
])
where f(fn) = attrs(
[ ("name", fn)
, ("file", dump(path + "/" + fn))
])
where:
attrs(as) = concat(map(attr, as)) + encN(0)
attrs((a, b)) = encS(a) + encS(b)
encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
encN(n) = 64-bit little-endian encoding of n.
contents(path) = the contents of a regular file.
sort(strings) = lexicographic sort by 8-bit value (strcmp).
entries(path) = the entries of a directory, without `.' and
`..'.
`+' denotes string concatenation. */
/**
* dumpPath creates a Nix archive of the specified path.
*
* @param path the file system data to dump. Dumping is recursive so if
* this is a directory we dump it and all its children.
*
* @param [out] sink The serialised archive is fed into this sink.
*
* @param filter Can be used to skip certain files.
*
* The format is as follows:
*
* IF path points to a REGULAR FILE:
* dump(path) = attrs(
* [ ("type", "regular")
* , ("contents", contents(path))
* ])
*
* IF path points to a DIRECTORY:
* dump(path) = attrs(
* [ ("type", "directory")
* , ("entries", concat(map(f, sort(entries(path)))))
* ])
* where f(fn) = attrs(
* [ ("name", fn)
* , ("file", dump(path + "/" + fn))
* ])
*
* where:
*
* attrs(as) = concat(map(attr, as)) + encN(0)
* attrs((a, b)) = encS(a) + encS(b)
*
* encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
*
* encN(n) = 64-bit little-endian encoding of n.
*
* contents(path) = the contents of a regular file.
*
* sort(strings) = lexicographic sort by 8-bit value (strcmp).
*
* entries(path) = the entries of a directory, without `.' and
* `..'.
*
* `+' denotes string concatenation.
*/
void dumpPath(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
/* Same as `void dumpPath()`, but returns the last modified date of the path */
/**
* Same as dumpPath(), but returns the last modified date of the path.
*/
time_t dumpPathAndGetMtime(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
/**
* Dump an archive with a single file with these contents.
*
* @param s Contents of the file.
*/
void dumpString(std::string_view s, Sink & sink);
/* FIXME: fix this API, it sucks. */
/**
* \todo Fix this API, it sucks.
*/
struct ParseSink
{
virtual void createDirectory(const Path & path) { };
@ -68,8 +85,10 @@ struct ParseSink
virtual void createSymlink(const Path & path, const std::string & target) { };
};
/* If the NAR archive contains a single file at top-level, then save
the contents of the file to `s'. Otherwise barf. */
/**
* If the NAR archive contains a single file at top-level, then save
* the contents of the file to `s'. Otherwise barf.
*/
struct RetrieveRegularNARSink : ParseSink
{
bool regular = true;
@ -97,7 +116,9 @@ void parseDump(ParseSink & sink, Source & source);
void restorePath(const Path & path, Source & source);
/* Read a NAR from 'source' and write it to 'sink'. */
/**
* Read a NAR from 'source' and write it to 'sink'.
*/
void copyNAR(Source & source, Sink & sink);
void copyPath(const Path & from, const Path & to);

View file

@ -18,16 +18,22 @@ class Args
{
public:
/* Parse the command line, throwing a UsageError if something goes
wrong. */
/**
* Parse the command line, throwing a UsageError if something goes
* wrong.
*/
void parseCmdline(const Strings & cmdline);
/* Return a short one-line description of the command. */
/**
* Return a short one-line description of the command.
*/
virtual std::string description() { return ""; }
virtual bool forceImpureByDefault() { return false; }
/* Return documentation about this command, in Markdown format. */
/**
* Return documentation about this command, in Markdown format.
*/
virtual std::string doc() { return ""; }
protected:
@ -146,13 +152,17 @@ protected:
std::set<std::string> hiddenCategories;
/* Called after all command line flags before the first non-flag
argument (if any) have been processed. */
/**
* Called after all command line flags before the first non-flag
* argument (if any) have been processed.
*/
virtual void initialFlagsProcessed() {}
/* Called after the command line has been processed if we need to generate
completions. Useful for commands that need to know the whole command line
in order to know what completions to generate. */
/**
* Called after the command line has been processed if we need to generate
* completions. Useful for commands that need to know the whole command line
* in order to know what completions to generate.
*/
virtual void completionHook() { }
public:
@ -166,7 +176,9 @@ public:
expectedArgs.emplace_back(std::move(arg));
}
/* Expect a string argument. */
/**
* Expect a string argument.
*/
void expectArg(const std::string & label, std::string * dest, bool optional = false)
{
expectArgs({
@ -176,7 +188,9 @@ public:
});
}
/* Expect 0 or more arguments. */
/**
* Expect 0 or more arguments.
*/
void expectArgs(const std::string & label, std::vector<std::string> * dest)
{
expectArgs({
@ -202,14 +216,19 @@ private:
std::set<ExperimentalFeature> flagExperimentalFeatures;
};
/* A command is an argument parser that can be executed by calling its
run() method. */
/**
* A command is an argument parser that can be executed by calling its
* run() method.
*/
struct Command : virtual public Args
{
friend class MultiCommand;
virtual ~Command() { }
/**
* Entry point to the command
*/
virtual void run() = 0;
typedef int Category;
@ -221,8 +240,10 @@ struct Command : virtual public Args
typedef std::map<std::string, std::function<ref<Command>()>> Commands;
/* An argument parser that supports multiple subcommands,
i.e. <command> <subcommand>. */
/**
* An argument parser that supports multiple subcommands,
* i.e. <command> <subcommand>.
*/
class MultiCommand : virtual public Args
{
public:
@ -230,7 +251,9 @@ public:
std::map<Command::Category, std::string> categories;
// Selected command, if any.
/**
* Selected command, if any.
*/
std::optional<std::pair<std::string, ref<Command>>> command;
MultiCommand(const Commands & commands);

View file

@ -5,9 +5,11 @@
namespace nix {
/* A callback is a wrapper around a lambda that accepts a valid of
type T or an exception. (We abuse std::future<T> to pass the value or
exception.) */
/**
* A callback is a wrapper around a lambda that accepts a valid of
* type T or an exception. (We abuse std::future<T> to pass the value or
* exception.)
*/
template<typename T>
class Callback
{

View file

@ -8,19 +8,20 @@
namespace nix {
/* A canonical representation of a path. It ensures the following:
- It always starts with a slash.
- It never ends with a slash, except if the path is "/".
- A slash is never followed by a slash (i.e. no empty components).
- There are no components equal to '.' or '..'.
Note that the path does not need to correspond to an actually
existing path, and there is no guarantee that symlinks are
resolved.
/**
* A canonical representation of a path. It ensures the following:
*
* - It always starts with a slash.
*
* - It never ends with a slash, except if the path is "/".
*
* - A slash is never followed by a slash (i.e. no empty components).
*
* - There are no components equal to '.' or '..'.
*
* Note that the path does not need to correspond to an actually
* existing path, and there is no guarantee that symlinks are
* resolved.
*/
class CanonPath
{
@ -28,8 +29,10 @@ class CanonPath
public:
/* Construct a canon path from a non-canonical path. Any '.', '..'
or empty components are removed. */
/**
* Construct a canon path from a non-canonical path. Any '.', '..'
* or empty components are removed.
*/
CanonPath(std::string_view raw);
explicit CanonPath(const char * raw)
@ -44,9 +47,11 @@ public:
static CanonPath root;
/* If `raw` starts with a slash, return
`CanonPath(raw)`. Otherwise return a `CanonPath` representing
`root + "/" + raw`. */
/**
* If `raw` starts with a slash, return
* `CanonPath(raw)`. Otherwise return a `CanonPath` representing
* `root + "/" + raw`.
*/
CanonPath(std::string_view raw, const CanonPath & root);
bool isRoot() const
@ -58,8 +63,10 @@ public:
const std::string & abs() const
{ return path; }
/* Like abs(), but return an empty string if this path is
'/'. Thus the returned string never ends in a slash. */
/**
* Like abs(), but return an empty string if this path is
* '/'. Thus the returned string never ends in a slash.
*/
const std::string & absOrEmpty() const
{
const static std::string epsilon;
@ -107,7 +114,9 @@ public:
std::optional<CanonPath> parent() const;
/* Remove the last component. Panics if this path is the root. */
/**
* Remove the last component. Panics if this path is the root.
*/
void pop();
std::optional<std::string_view> dirOf() const
@ -128,10 +137,12 @@ public:
bool operator != (const CanonPath & x) const
{ return path != x.path; }
/* Compare paths lexicographically except that path separators
are sorted before any other character. That is, in the sorted order
a directory is always followed directly by its children. For
instance, 'foo' < 'foo/bar' < 'foo!'. */
/**
* Compare paths lexicographically except that path separators
* are sorted before any other character. That is, in the sorted order
* a directory is always followed directly by its children. For
* instance, 'foo' < 'foo/bar' < 'foo!'.
*/
bool operator < (const CanonPath & x) const
{
auto i = path.begin();
@ -147,27 +158,37 @@ public:
return i == path.end() && j != x.path.end();
}
/* Return true if `this` is equal to `parent` or a child of
`parent`. */
/**
* Return true if `this` is equal to `parent` or a child of
* `parent`.
*/
bool isWithin(const CanonPath & parent) const;
CanonPath removePrefix(const CanonPath & prefix) const;
/* Append another path to this one. */
/**
* Append another path to this one.
*/
void extend(const CanonPath & x);
/* Concatenate two paths. */
/**
* Concatenate two paths.
*/
CanonPath operator + (const CanonPath & x) const;
/* Add a path component to this one. It must not contain any slashes. */
/**
* Add a path component to this one. It must not contain any slashes.
*/
void push(std::string_view c);
CanonPath operator + (std::string_view c) const;
/* Check whether access to this path is allowed, which is the case
if 1) `this` is within any of the `allowed` paths; or 2) any of
the `allowed` paths are within `this`. (The latter condition
ensures access to the parents of allowed paths.) */
/**
* Check whether access to this path is allowed, which is the case
* if 1) `this` is within any of the `allowed` paths; or 2) any of
* the `allowed` paths are within `this`. (The latter condition
* ensures access to the parents of allowed paths.)
*/
bool isAllowed(const std::set<CanonPath> & allowed) const;
/* Return a representation `x` of `path` relative to `this`, i.e.

View file

@ -18,10 +18,12 @@ struct CgroupStats
std::optional<std::chrono::microseconds> cpuUser, cpuSystem;
};
/* Destroy the cgroup denoted by 'path'. The postcondition is that
'path' does not exist, and thus any processes in the cgroup have
been killed. Also return statistics from the cgroup just before
destruction. */
/**
* Destroy the cgroup denoted by 'path'. The postcondition is that
* 'path' does not exist, and thus any processes in the cgroup have
* been killed. Also return statistics from the cgroup just before
* destruction.
*/
CgroupStats destroyCgroup(const Path & cgroup);
}

View file

@ -7,20 +7,24 @@
namespace nix {
/* Provides an indexable container like vector<> with memory overhead
guarantees like list<> by allocating storage in chunks of ChunkSize
elements instead of using a contiguous memory allocation like vector<>
does. Not using a single vector that is resized reduces memory overhead
on large data sets by on average (growth factor)/2, mostly
eliminates copies within the vector during resizing, and provides stable
references to its elements. */
/**
* Provides an indexable container like vector<> with memory overhead
* guarantees like list<> by allocating storage in chunks of ChunkSize
* elements instead of using a contiguous memory allocation like vector<>
* does. Not using a single vector that is resized reduces memory overhead
* on large data sets by on average (growth factor)/2, mostly
* eliminates copies within the vector during resizing, and provides stable
* references to its elements.
*/
template<typename T, size_t ChunkSize>
class ChunkedVector {
private:
uint32_t size_ = 0;
std::vector<std::vector<T>> chunks;
/* keep this out of the ::add hot path */
/**
* Keep this out of the ::add hot path
*/
[[gnu::noinline]]
auto & addChunk()
{

View file

@ -1,6 +1,7 @@
#pragma once
/* Awfull hacky generation of the comparison operators by doing a lexicographic
/**
* Awful hacky generation of the comparison operators by doing a lexicographic
* comparison between the choosen fields.
*
* ```

View file

@ -124,21 +124,21 @@ public:
void reapplyUnknownSettings();
};
/* A class to simplify providing configuration settings. The typical
use is to inherit Config and add Setting<T> members:
class MyClass : private Config
{
Setting<int> foo{this, 123, "foo", "the number of foos to use"};
Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
MyClass() : Config(readConfigFile("/etc/my-app.conf"))
{
std::cout << foo << "\n"; // will print 123 unless overridden
}
};
/**
* A class to simplify providing configuration settings. The typical
* use is to inherit Config and add Setting<T> members:
*
* class MyClass : private Config
* {
* Setting<int> foo{this, 123, "foo", "the number of foos to use"};
* Setting<std::string> bar{this, "blabla", "bar", "the name of the bar"};
*
* MyClass() : Config(readConfigFile("/etc/my-app.conf"))
* {
* std::cout << foo << "\n"; // will print 123 unless overridden
* }
* };
*/
class Config : public AbstractConfig
{
friend class AbstractSetting;
@ -228,7 +228,9 @@ protected:
bool isOverridden() const { return overridden; }
};
/* A setting of type T. */
/**
* A setting of type T.
*/
template<typename T>
class BaseSetting : public AbstractSetting
{
@ -311,8 +313,10 @@ public:
void operator =(const T & v) { this->assign(v); }
};
/* A special setting for Paths. These are automatically canonicalised
(e.g. "/foo//bar/" becomes "/foo/bar"). */
/**
* A special setting for Paths. These are automatically canonicalised
* (e.g. "/foo//bar/" becomes "/foo/bar").
*/
class PathSetting : public BaseSetting<Path>
{
bool allowEmpty;

View file

@ -54,20 +54,26 @@ typedef enum {
lvlVomit
} Verbosity;
// the lines of code surrounding an error.
/**
* The lines of code surrounding an error.
*/
struct LinesOfCode {
std::optional<std::string> prevLineOfCode;
std::optional<std::string> errLineOfCode;
std::optional<std::string> nextLineOfCode;
};
/* An abstract type that represents a location in a source file. */
/**
* An abstract type that represents a location in a source file.
*/
struct AbstractPos
{
uint32_t line = 0;
uint32_t column = 0;
/* Return the contents of the source file. */
/**
* Return the contents of the source file.
*/
virtual std::optional<std::string> getSource() const
{ return std::nullopt; };
@ -104,8 +110,10 @@ struct ErrorInfo {
std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace);
/* BaseError should generally not be caught, as it has Interrupted as
a subclass. Catch Error instead. */
/**
* BaseError should generally not be caught, as it has Interrupted as
* a subclass. Catch Error instead.
*/
class BaseError : public std::exception
{
protected:

View file

@ -12,7 +12,7 @@ namespace nix {
*
* If you update this, dont forget to also change the map defining their
* string representation in the corresponding `.cc` file.
**/
*/
enum struct ExperimentalFeature
{
CaDerivations,

View file

@ -1,6 +1,8 @@
#pragma once
/* A trivial class to run a function at the end of a scope. */
/**
* A trivial class to run a function at the end of a scope.
*/
template<typename Fn>
class Finally
{

View file

@ -8,20 +8,25 @@
namespace nix {
/* Inherit some names from other namespaces for convenience. */
/**
* Inherit some names from other namespaces for convenience.
*/
using boost::format;
/* A variadic template that does nothing. Useful to call a function
for all variadic arguments but ignoring the result. */
/**
* A variadic template that does nothing. Useful to call a function
* for all variadic arguments but ignoring the result.
*/
struct nop { template<typename... T> nop(T...) {} };
/* A helper for formatting strings. fmt(format, a_0, ..., a_n) is
equivalent to boost::format(format) % a_0 % ... %
... a_n. However, fmt(s) is equivalent to s (so no %-expansion
takes place). */
/**
* A helper for formatting strings. fmt(format, a_0, ..., a_n) is
* equivalent to boost::format(format) % a_0 % ... %
* ... a_n. However, fmt(s) is equivalent to s (so no %-expansion
* takes place).
*/
template<class F>
inline void formatHelper(F & f)
{

View file

@ -8,21 +8,23 @@ namespace nix {
namespace git {
// A line from the output of `git ls-remote --symref`.
//
// These can be of two kinds:
//
// - Symbolic references of the form
//
// ref: {target} {reference}
//
// where {target} is itself a reference and {reference} is optional
//
// - Object references of the form
//
// {target} {reference}
//
// where {target} is a commit id and {reference} is mandatory
/**
* A line from the output of `git ls-remote --symref`.
*
* These can be of two kinds:
*
* - Symbolic references of the form
*
* ref: {target} {reference}
*
* where {target} is itself a reference and {reference} is optional
*
* - Object references of the form
*
* {target} {reference}
*
* where {target} is a commit id and {reference} is mandatory
*/
struct LsRemoteRefLine {
enum struct Kind {
Symbolic,

View file

@ -33,62 +33,86 @@ struct Hash
HashType type;
/* Create a zero-filled hash object. */
/**
* Create a zero-filled hash object.
*/
Hash(HashType type);
/* Parse the hash from a string representation in the format
"[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
Subresource Integrity hash expression). If the 'type' argument
is not present, then the hash type must be specified in the
string. */
/**
* Parse the hash from a string representation in the format
* "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
* Subresource Integrity hash expression). If the 'type' argument
* is not present, then the hash type must be specified in the
* string.
*/
static Hash parseAny(std::string_view s, std::optional<HashType> type);
/* Parse a hash from a string representation like the above, except the
type prefix is mandatory is there is no separate arguement. */
/**
* Parse a hash from a string representation like the above, except the
* type prefix is mandatory is there is no separate arguement.
*/
static Hash parseAnyPrefixed(std::string_view s);
/* Parse a plain hash that musst not have any prefix indicating the type.
The type is passed in to disambiguate. */
/**
* Parse a plain hash that musst not have any prefix indicating the type.
* The type is passed in to disambiguate.
*/
static Hash parseNonSRIUnprefixed(std::string_view s, HashType type);
static Hash parseSRI(std::string_view original);
private:
/* The type must be provided, the string view must not include <type>
prefix. `isSRI` helps disambigate the various base-* encodings. */
/**
* The type must be provided, the string view must not include <type>
* prefix. `isSRI` helps disambigate the various base-* encodings.
*/
Hash(std::string_view s, HashType type, bool isSRI);
public:
/* Check whether two hash are equal. */
/**
* Check whether two hash are equal.
*/
bool operator == (const Hash & h2) const;
/* Check whether two hash are not equal. */
/**
* Check whether two hash are not equal.
*/
bool operator != (const Hash & h2) const;
/* For sorting. */
/**
* For sorting.
*/
bool operator < (const Hash & h) const;
/* Returns the length of a base-16 representation of this hash. */
/**
* Returns the length of a base-16 representation of this hash.
*/
size_t base16Len() const
{
return hashSize * 2;
}
/* Returns the length of a base-32 representation of this hash. */
/**
* Returns the length of a base-32 representation of this hash.
*/
size_t base32Len() const
{
return (hashSize * 8 - 1) / 5 + 1;
}
/* Returns the length of a base-64 representation of this hash. */
/**
* Returns the length of a base-64 representation of this hash.
*/
size_t base64Len() const
{
return ((4 * hashSize / 3) + 3) & ~3;
}
/* Return a string representation of the hash, in base-16, base-32
or base-64. By default, this is prefixed by the hash type
(e.g. "sha256:"). */
/**
* Return a string representation of the hash, in base-16, base-32
* or base-64. By default, this is prefixed by the hash type
* (e.g. "sha256:").
*/
std::string to_string(Base base, bool includeType) const;
std::string gitRev() const
@ -104,35 +128,53 @@ public:
static Hash dummy;
};
/* Helper that defaults empty hashes to the 0 hash. */
/**
* Helper that defaults empty hashes to the 0 hash.
*/
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
/**
* Print a hash in base-16 if it's MD5, or base-32 otherwise.
*/
std::string printHash16or32(const Hash & hash);
/* Compute the hash of the given string. */
/**
* Compute the hash of the given string.
*/
Hash hashString(HashType ht, std::string_view s);
/* Compute the hash of the given file. */
/**
* Compute the hash of the given file.
*/
Hash hashFile(HashType ht, const Path & path);
/* Compute the hash of the given path. The hash is defined as
(essentially) hashString(ht, dumpPath(path)). */
/**
* Compute the hash of the given path. The hash is defined as
* (essentially) hashString(ht, dumpPath(path)).
*/
typedef std::pair<Hash, uint64_t> HashResult;
HashResult hashPath(HashType ht, const Path & path,
PathFilter & filter = defaultPathFilter);
/* Compress a hash to the specified number of bytes by cyclically
XORing bytes together. */
/**
* Compress a hash to the specified number of bytes by cyclically
* XORing bytes together.
*/
Hash compressHash(const Hash & hash, unsigned int newSize);
/* Parse a string representing a hash type. */
/**
* Parse a string representing a hash type.
*/
HashType parseHashType(std::string_view s);
/* Will return nothing on parse error */
/**
* Will return nothing on parse error
*/
std::optional<HashType> parseHashTypeOpt(std::string_view s);
/* And the reverse. */
/**
* And the reverse.
*/
std::string_view printHashType(HashType ht);

View file

@ -6,11 +6,13 @@
namespace nix {
/* Highlight all the given matches in the given string `s` by wrapping
them between `prefix` and `postfix`.
If some matches overlap, then their union will be wrapped rather
than the individual matches. */
/**
* Highlight all the given matches in the given string `s` by wrapping
* them between `prefix` and `postfix`.
*
* If some matches overlap, then their union will be wrapped rather
* than the individual matches.
*/
std::string hiliteMatches(
std::string_view s,
std::vector<std::smatch> matches,

View file

@ -217,7 +217,9 @@ extern Verbosity verbosity; /* suppress msgs > this */
#define debug(args...) printMsg(lvlDebug, args)
#define vomit(args...) printMsg(lvlVomit, args)
/* if verbosity >= lvlWarn, print a message with a yellow 'warning:' prefix. */
/**
* if verbosity >= lvlWarn, print a message with a yellow 'warning:' prefix.
*/
template<typename... Args>
inline void warn(const std::string & fs, const Args & ... args)
{

View file

@ -7,7 +7,9 @@
namespace nix {
/* A simple least-recently used cache. Not thread-safe. */
/**
* A simple least-recently used cache. Not thread-safe.
*/
template<typename Key, typename Value>
class LRUCache
{
@ -31,7 +33,9 @@ public:
LRUCache(size_t capacity) : capacity(capacity) { }
/* Insert or upsert an item in the cache. */
/**
* Insert or upsert an item in the cache.
*/
void upsert(const Key & key, const Value & value)
{
if (capacity == 0) return;
@ -39,7 +43,9 @@ public:
erase(key);
if (data.size() >= capacity) {
/* Retire the oldest item. */
/**
* Retire the oldest item.
*/
auto oldest = lru.begin();
data.erase(*oldest);
lru.erase(oldest);
@ -63,14 +69,18 @@ public:
return true;
}
/* Look up an item in the cache. If it exists, it becomes the most
recently used item. */
/**
* Look up an item in the cache. If it exists, it becomes the most
* recently used item.
* */
std::optional<Value> get(const Key & key)
{
auto i = data.find(key);
if (i == data.end()) return {};
/* Move this item to the back of the LRU list. */
/**
* Move this item to the back of the LRU list.
*/
lru.erase(i->second.first.it);
auto j = lru.insert(lru.end(), i);
i->second.first.it = j;

View file

@ -11,33 +11,37 @@
namespace nix {
/* This template class implements a simple pool manager of resources
of some type R, such as database connections. It is used as
follows:
class Connection { ... };
Pool<Connection> pool;
{
auto conn(pool.get());
conn->exec("select ...");
}
Here, the Connection object referenced by conn is automatically
returned to the pool when conn goes out of scope.
/**
* This template class implements a simple pool manager of resources
* of some type R, such as database connections. It is used as
* follows:
*
* class Connection { ... };
*
* Pool<Connection> pool;
*
* {
* auto conn(pool.get());
* conn->exec("select ...");
* }
*
* Here, the Connection object referenced by conn is automatically
* returned to the pool when conn goes out of scope.
*/
template <class R>
class Pool
{
public:
/* A function that produces new instances of R on demand. */
/**
* A function that produces new instances of R on demand.
*/
typedef std::function<ref<R>()> Factory;
/* A function that checks whether an instance of R is still
usable. Unusable instances are removed from the pool. */
/**
* A function that checks whether an instance of R is still
* usable. Unusable instances are removed from the pool.
*/
typedef std::function<bool(const ref<R> &)> Validator;
private:

View file

@ -6,8 +6,10 @@
namespace nix {
/* A simple non-nullable reference-counted pointer. Actually a wrapper
around std::shared_ptr that prevents null constructions. */
/**
* A simple non-nullable reference-counted pointer. Actually a wrapper
* around std::shared_ptr that prevents null constructions.
*/
template<typename T>
class ref
{

View file

@ -10,7 +10,9 @@ namespace boost::context { struct stack_context; }
namespace nix {
/* Abstract destination of binary data. */
/**
* Abstract destination of binary data.
*/
struct Sink
{
virtual ~Sink() { }
@ -18,7 +20,9 @@ struct Sink
virtual bool good() { return true; }
};
/* Just throws away data. */
/**
* Just throws away data.
*/
struct NullSink : Sink
{
void operator () (std::string_view data) override
@ -32,8 +36,10 @@ struct FinishSink : virtual Sink
};
/* A buffered abstract sink. Warning: a BufferedSink should not be
used from multiple threads concurrently. */
/**
* A buffered abstract sink. Warning: a BufferedSink should not be
* used from multiple threads concurrently.
*/
struct BufferedSink : virtual Sink
{
size_t bufSize, bufPos;
@ -50,19 +56,25 @@ struct BufferedSink : virtual Sink
};
/* Abstract source of binary data. */
/**
* Abstract source of binary data.
*/
struct Source
{
virtual ~Source() { }
/* Store exactly len bytes in the buffer pointed to by data.
It blocks until all the requested data is available, or throws
an error if it is not going to be available. */
/**
* Store exactly len bytes in the buffer pointed to by data.
* It blocks until all the requested data is available, or throws
* an error if it is not going to be available.
*/
void operator () (char * data, size_t len);
/* Store up to len in the buffer pointed to by data, and
return the number of bytes stored. It blocks until at least
one byte is available. */
/**
* Store up to len in the buffer pointed to by data, and
* return the number of bytes stored. It blocks until at least
* one byte is available.
*/
virtual size_t read(char * data, size_t len) = 0;
virtual bool good() { return true; }
@ -73,8 +85,10 @@ struct Source
};
/* A buffered abstract source. Warning: a BufferedSource should not be
used from multiple threads concurrently. */
/**
* A buffered abstract source. Warning: a BufferedSource should not be
* used from multiple threads concurrently.
*/
struct BufferedSource : Source
{
size_t bufSize, bufPosIn, bufPosOut;
@ -88,12 +102,16 @@ struct BufferedSource : Source
bool hasData();
protected:
/* Underlying read call, to be overridden. */
/**
* Underlying read call, to be overridden.
*/
virtual size_t readUnbuffered(char * data, size_t len) = 0;
};
/* A sink that writes data to a file descriptor. */
/**
* A sink that writes data to a file descriptor.
*/
struct FdSink : BufferedSink
{
int fd;
@ -123,7 +141,9 @@ private:
};
/* A source that reads data from a file descriptor. */
/**
* A source that reads data from a file descriptor.
*/
struct FdSource : BufferedSource
{
int fd;
@ -149,7 +169,9 @@ private:
};
/* A sink that writes data to a string. */
/**
* A sink that writes data to a string.
*/
struct StringSink : Sink
{
std::string s;
@ -163,7 +185,9 @@ struct StringSink : Sink
};
/* A source that reads data from a string. */
/**
* A source that reads data from a string.
*/
struct StringSource : Source
{
std::string_view s;
@ -173,7 +197,9 @@ struct StringSource : Source
};
/* A sink that writes all incoming data to two other sinks. */
/**
* A sink that writes all incoming data to two other sinks.
*/
struct TeeSink : Sink
{
Sink & sink1, & sink2;
@ -186,7 +212,9 @@ struct TeeSink : Sink
};
/* Adapter class of a Source that saves all data read to a sink. */
/**
* Adapter class of a Source that saves all data read to a sink.
*/
struct TeeSource : Source
{
Source & orig;
@ -201,7 +229,9 @@ struct TeeSource : Source
}
};
/* A reader that consumes the original Source until 'size'. */
/**
* A reader that consumes the original Source until 'size'.
*/
struct SizedSource : Source
{
Source & orig;
@ -219,7 +249,9 @@ struct SizedSource : Source
return n;
}
/* Consume the original source until no remain data is left to consume. */
/**
* Consume the original source until no remain data is left to consume.
*/
size_t drainAll()
{
std::vector<char> buf(8192);
@ -232,7 +264,9 @@ struct SizedSource : Source
}
};
/* A sink that that just counts the number of bytes given to it */
/**
* A sink that that just counts the number of bytes given to it
*/
struct LengthSink : Sink
{
uint64_t length = 0;
@ -243,7 +277,9 @@ struct LengthSink : Sink
}
};
/* Convert a function into a sink. */
/**
* Convert a function into a sink.
*/
struct LambdaSink : Sink
{
typedef std::function<void(std::string_view data)> lambda_t;
@ -259,7 +295,9 @@ struct LambdaSink : Sink
};
/* Convert a function into a source. */
/**
* Convert a function into a source.
*/
struct LambdaSource : Source
{
typedef std::function<size_t(char *, size_t)> lambda_t;
@ -274,8 +312,10 @@ struct LambdaSource : Source
}
};
/* Chain two sources together so after the first is exhausted, the second is
used */
/**
* Chain two sources together so after the first is exhausted, the second is
* used
*/
struct ChainSource : Source
{
Source & source1, & source2;
@ -289,8 +329,10 @@ struct ChainSource : Source
std::unique_ptr<FinishSink> sourceToSink(std::function<void(Source &)> fun);
/* Convert a function that feeds data into a Sink into a Source. The
Source executes the function as a coroutine. */
/**
* Convert a function that feeds data into a Sink into a Source. The
* Source executes the function as a coroutine.
*/
std::unique_ptr<Source> sinkToSource(
std::function<void(Sink &)> fun,
std::function<void()> eof = []() {
@ -376,7 +418,9 @@ Source & operator >> (Source & in, bool & b)
Error readError(Source & source);
/* An adapter that converts a std::basic_istream into a source. */
/**
* An adapter that converts a std::basic_istream into a source.
*/
struct StreamToSourceAdapter : Source
{
std::shared_ptr<std::basic_istream<char>> istream;
@ -399,12 +443,13 @@ struct StreamToSourceAdapter : Source
};
/* A source that reads a distinct format of concatenated chunks back into its
logical form, in order to guarantee a known state to the original stream,
even in the event of errors.
Use with FramedSink, which also allows the logical stream to be terminated
in the event of an exception.
/**
* A source that reads a distinct format of concatenated chunks back into its
* logical form, in order to guarantee a known state to the original stream,
* even in the event of errors.
*
* Use with FramedSink, which also allows the logical stream to be terminated
* in the event of an exception.
*/
struct FramedSource : Source
{
@ -450,10 +495,11 @@ struct FramedSource : Source
}
};
/* Write as chunks in the format expected by FramedSource.
The exception_ptr reference can be used to terminate the stream when you
detect that an error has occurred on the remote end.
/**
* Write as chunks in the format expected by FramedSource.
*
* The exception_ptr reference can be used to terminate the stream when you
* detect that an error has occurred on the remote end.
*/
struct FramedSink : nix::BufferedSink
{
@ -487,17 +533,20 @@ struct FramedSink : nix::BufferedSink
};
};
/* Stack allocation strategy for sinkToSource.
Mutable to avoid a boehm gc dependency in libutil.
boost::context doesn't provide a virtual class, so we define our own.
/**
* Stack allocation strategy for sinkToSource.
* Mutable to avoid a boehm gc dependency in libutil.
*
* boost::context doesn't provide a virtual class, so we define our own.
*/
struct StackAllocator {
virtual boost::context::stack_context allocate() = 0;
virtual void deallocate(boost::context::stack_context sctx) = 0;
/* The stack allocator to use in sinkToSource and potentially elsewhere.
It is reassigned by the initGC() method in libexpr. */
/**
* The stack allocator to use in sinkToSource and potentially elsewhere.
* It is reassigned by the initGC() method in libexpr.
*/
static StackAllocator *defaultAllocator;
};

View file

@ -7,10 +7,12 @@
namespace nix {
// If `separator` is found, we return the portion of the string before the
// separator, and modify the string argument to contain only the part after the
// separator. Otherwise, we return `std::nullopt`, and we leave the argument
// string alone.
/**
* If `separator` is found, we return the portion of the string before the
* separator, and modify the string argument to contain only the part after the
* separator. Otherwise, we return `std::nullopt`, and we leave the argument
* string alone.
*/
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator) {
auto sepInstance = string.find(separator);

View file

@ -13,7 +13,8 @@ int levenshteinDistance(std::string_view first, std::string_view second);
*/
class Suggestion {
public:
int distance; // The smaller the better
/// The smaller the better
int distance;
std::string suggestion;
std::string to_string() const;
@ -43,7 +44,9 @@ public:
std::ostream & operator<<(std::ostream & str, const Suggestion &);
std::ostream & operator<<(std::ostream & str, const Suggestions &);
// Either a value of type `T`, or some suggestions
/**
* Either a value of type `T`, or some suggestions
*/
template<typename T>
class OrSuggestions {
public:

View file

@ -7,22 +7,22 @@
namespace nix {
/* This template class ensures synchronized access to a value of type
T. It is used as follows:
struct Data { int x; ... };
Sync<Data> data;
{
auto data_(data.lock());
data_->x = 123;
}
Here, "data" is automatically unlocked when "data_" goes out of
scope.
/**
* This template class ensures synchronized access to a value of type
* T. It is used as follows:
*
* struct Data { int x; ... };
*
* Sync<Data> data;
*
* {
* auto data_(data.lock());
* data_->x = 123;
* }
*
* Here, "data" is automatically unlocked when "data_" goes out of
* scope.
*/
template<class T, class M = std::mutex>
class Sync
{

View file

@ -14,7 +14,7 @@ struct TarArchive {
TarArchive(const Path & path);
// disable copy constructor
/// disable copy constructor
TarArchive(const TarArchive &) = delete;
void close();

View file

@ -13,8 +13,10 @@ namespace nix {
MakeError(ThreadPoolShutDown, Error);
/* A simple thread pool that executes a queue of work items
(lambdas). */
/**
* A simple thread pool that executes a queue of work items
* (lambdas).
*/
class ThreadPool
{
public:
@ -23,19 +25,30 @@ public:
~ThreadPool();
// FIXME: use std::packaged_task?
/**
* An individual work item.
*
* \todo use std::packaged_task?
*/
typedef std::function<void()> work_t;
/* Enqueue a function to be executed by the thread pool. */
/**
* Enqueue a function to be executed by the thread pool.
*/
void enqueue(const work_t & t);
/* Execute work items until the queue is empty. Note that work
items are allowed to add new items to the queue; this is
handled correctly. Queue processing stops prematurely if any
work item throws an exception. This exception is propagated to
the calling thread. If multiple work items throw an exception
concurrently, only one item is propagated; the others are
printed on stderr and otherwise ignored. */
/**
* Execute work items until the queue is empty.
*
* \note Note that work items are allowed to add new items to the
* queue; this is handled correctly.
*
* Queue processing stops prematurely if any work item throws an
* exception. This exception is propagated to the calling thread. If
* multiple work items throw an exception concurrently, only one
* item is propagated; the others are printed on stderr and
* otherwise ignored.
*/
void process();
private:
@ -62,9 +75,11 @@ private:
void shutdown();
};
/* Process in parallel a set of items of type T that have a partial
ordering between them. Thus, any item is only processed after all
its dependencies have been processed. */
/**
* Process in parallel a set of items of type T that have a partial
* ordering between them. Thus, any item is only processed after all
* its dependencies have been processed.
*/
template<typename T>
void processGraph(
ThreadPool & pool,

View file

@ -17,7 +17,9 @@ typedef std::set<std::string> StringSet;
typedef std::map<std::string, std::string> StringMap;
typedef std::map<std::string, std::string> StringPairs;
/* Paths are just strings. */
/**
* Paths are just strings.
*/
typedef std::string Path;
typedef std::string_view PathView;
typedef std::list<Path> Paths;
@ -25,15 +27,19 @@ typedef std::set<Path> PathSet;
typedef std::vector<std::pair<std::string, std::string>> Headers;
/* Helper class to run code at startup. */
/**
* Helper class to run code at startup.
*/
template<typename T>
struct OnStartup
{
OnStartup(T && t) { t(); }
};
/* Wrap bools to prevent string literals (i.e. 'char *') from being
cast to a bool in Attr. */
/**
* Wrap bools to prevent string literals (i.e. 'char *') from being
* cast to a bool in Attr.
*/
template<typename T>
struct Explicit {
T t;
@ -45,21 +51,25 @@ struct Explicit {
};
/* This wants to be a little bit like rust's Cow type.
Some parts of the evaluator benefit greatly from being able to reuse
existing allocations for strings, but have to be able to also use
newly allocated storage for values.
We do not define implicit conversions, even with ref qualifiers,
since those can easily become ambiguous to the reader and can degrade
into copying behaviour we want to avoid. */
/**
* This wants to be a little bit like rust's Cow type.
* Some parts of the evaluator benefit greatly from being able to reuse
* existing allocations for strings, but have to be able to also use
* newly allocated storage for values.
*
* We do not define implicit conversions, even with ref qualifiers,
* since those can easily become ambiguous to the reader and can degrade
* into copying behaviour we want to avoid.
*/
class BackedStringView {
private:
std::variant<std::string, std::string_view> data;
/* Needed to introduce a temporary since operator-> must return
a pointer. Without this we'd need to store the view object
even when we already own a string. */
/**
* Needed to introduce a temporary since operator-> must return
* a pointer. Without this we'd need to store the view object
* even when we already own a string.
*/
class Ptr {
private:
std::string_view view;
@ -77,8 +87,10 @@ public:
BackedStringView(const BackedStringView &) = delete;
BackedStringView & operator=(const BackedStringView &) = delete;
/* We only want move operations defined since the sole purpose of
this type is to avoid copies. */
/**
* We only want move operations defined since the sole purpose of
* this type is to avoid copies.
*/
BackedStringView(BackedStringView && other) = default;
BackedStringView & operator=(BackedStringView && other) = default;

View file

@ -22,21 +22,22 @@ const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
// A Git ref (i.e. branch or tag name).
const static std::string refRegexS = "[a-zA-Z0-9@][a-zA-Z0-9_.\\/@-]*"; // FIXME: check
/// A Git ref (i.e. branch or tag name).
/// \todo check that this is correct.
const static std::string refRegexS = "[a-zA-Z0-9@][a-zA-Z0-9_.\\/@-]*";
extern std::regex refRegex;
// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
// This is because of the definition of a ref in refs.c in https://github.com/git/git
// See tests/fetchGitRefs.sh for the full definition
/// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
/// This is because of the definition of a ref in refs.c in https://github.com/git/git
/// See tests/fetchGitRefs.sh for the full definition
const static std::string badGitRefRegexS = "//|^[./]|/\\.|\\.\\.|[[:cntrl:][:space:]:?^~\[]|\\\\|\\*|\\.lock$|\\.lock/|@\\{|[/.]$|^@$|^$";
extern std::regex badGitRefRegex;
// A Git revision (a SHA-1 commit hash).
/// A Git revision (a SHA-1 commit hash).
const static std::string revRegexS = "[0-9a-fA-F]{40}";
extern std::regex revRegex;
// A ref or revision, or a ref followed by a revision.
/// A ref or revision, or a ref followed by a revision.
const static std::string refAndOrRevRegex = "(?:(" + revRegexS + ")|(?:(" + refRegexS + ")(?:/(" + revRegexS + "))?))";
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";

View file

@ -7,7 +7,8 @@ namespace nix {
struct ParsedURL
{
std::string url;
std::string base; // URL without query/fragment
/// URL without query/fragment
std::string base;
std::string scheme;
std::optional<std::string> authority;
std::string path;
@ -28,7 +29,7 @@ std::map<std::string, std::string> decodeQuery(const std::string & query);
ParsedURL parseURL(const std::string & url);
/*
/**
* Although thats not really standardized anywhere, an number of tools
* use a scheme of the form 'x+y' in urls, where y is the transport layer
* scheme, and x is the application layer scheme.