Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/libstore/binary-cache-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,18 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(

auto info = mkInfo(narHashSink.finish());
auto narInfo = make_ref<NarInfo>(info);
narInfo->compression = config.compression;
narInfo->compression = config.compression.to_string(); // FIXME: Make NarInfo use CompressionAlgo

@Ericson2314 Ericson2314 Jan 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto [fileHash, fileSize] = fileHashSink.finish();
narInfo->fileHash = fileHash;
narInfo->fileSize = fileSize;
narInfo->url = "nar/" + narInfo->fileHash->to_string(HashFormat::Nix32, false) + ".nar"
+ (config.compression == "xz" ? ".xz"
: config.compression == "bzip2" ? ".bz2"
: config.compression == "zstd" ? ".zst"
: config.compression == "lzip" ? ".lzip"
: config.compression == "lz4" ? ".lz4"
: config.compression == "br" ? ".br"
: "");
+ (config.compression == CompressionAlgo::xz ? ".xz"
: config.compression == CompressionAlgo::bzip2 ? ".bz2"
: config.compression == CompressionAlgo::zstd ? ".zst"
: config.compression == CompressionAlgo::lzip ? ".lzip"
: config.compression == CompressionAlgo::lz4 ? ".lz4"
: config.compression == CompressionAlgo::brotli ? ".br"
: "");

auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();
printMsg(
Expand Down
12 changes: 7 additions & 5 deletions src/libstore/http-binary-cache-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ void HttpBinaryCacheStore::init()
}
}

std::optional<std::string> HttpBinaryCacheStore::getCompressionMethod(const std::string & path)
std::optional<CompressionAlgo> HttpBinaryCacheStore::getCompressionMethod(const std::string & path)
{
if (hasSuffix(path, ".narinfo") && !config->narinfoCompression.get().empty())
if (hasSuffix(path, ".narinfo") && config->narinfoCompression.get())
return config->narinfoCompression;
else if (hasSuffix(path, ".ls") && !config->lsCompression.get().empty())
else if (hasSuffix(path, ".ls") && config->lsCompression.get())
return config->lsCompression;
else if (hasPrefix(path, "log/") && !config->logCompression.get().empty())
else if (hasPrefix(path, "log/") && config->logCompression.get())
return config->logCompression;
else
return std::nullopt;
Expand Down Expand Up @@ -160,7 +160,9 @@ void HttpBinaryCacheStore::upsertFile(
try {
if (auto compressionMethod = getCompressionMethod(path)) {
CompressedSource compressed(source, *compressionMethod);
Headers headers = {{"Content-Encoding", *compressionMethod}};
/* TODO: Validate that this is a valid content encoding. We probably shouldn't set non-standard values here.
*/
Headers headers = {{"Content-Encoding", showCompressionAlgo(*compressionMethod)}};
upload(path, compressed, compressed.size(), mimeType, std::move(headers));
} else {
upload(path, source, sizeHint, mimeType, std::nullopt);
Expand Down
12 changes: 9 additions & 3 deletions src/libstore/include/nix/store/binary-cache-store.hh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
///@file

#include "nix/util/signature/local-keys.hh"
#include "nix/util/compression-settings.hh"
#include "nix/store/store-api.hh"
#include "nix/store/log-store.hh"

Expand All @@ -18,8 +18,14 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
{
using StoreConfig::StoreConfig;

const Setting<std::string> compression{
this, "xz", "compression", "NAR compression method (`xz`, `bzip2`, `gzip`, `zstd`, or `none`)."};
const Setting<CompressionAlgo> compression{
this,
CompressionAlgo::xz,
"compression",
R"(
NAR compression method. One of: `xz`, `bzip2`, `gzip`, `zstd`, `none`, `br`, `compress`, `grzip`, `lrzip`, `lz4`, `lzip`, `lzma` or `lzop`.
To use a particular compression method Nix has to be built with a version of libarchive that natively supports that compression algorithm.
)"};

const Setting<bool> writeNARListing{
this, false, "write-nar-listing", "Whether to write a JSON file that lists the files in each NAR."};
Expand Down
13 changes: 7 additions & 6 deletions src/libstore/include/nix/store/http-binary-cache-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ struct HttpBinaryCacheStoreConfig : std::enable_shared_from_this<HttpBinaryCache

ParsedURL cacheUri;

const Setting<std::string> narinfoCompression{
this, "", "narinfo-compression", "Compression method for `.narinfo` files."};
const Setting<std::optional<CompressionAlgo>> narinfoCompression{
this, std::nullopt, "narinfo-compression", "Compression method for `.narinfo` files."};

const Setting<std::string> lsCompression{this, "", "ls-compression", "Compression method for `.ls` files."};
const Setting<std::optional<CompressionAlgo>> lsCompression{
this, std::nullopt, "ls-compression", "Compression method for `.ls` files."};

const Setting<std::string> logCompression{
const Setting<std::optional<CompressionAlgo>> logCompression{
this,
"",
std::nullopt,
"log-compression",
R"(
Compression method for `log/*` files. It is recommended to
Expand Down Expand Up @@ -72,7 +73,7 @@ public:

protected:

std::optional<std::string> getCompressionMethod(const std::string & path);
std::optional<CompressionAlgo> getCompressionMethod(const std::string & path);

void maybeDisable();

Expand Down
5 changes: 3 additions & 2 deletions src/libstore/include/nix/store/nar-info.hh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
///@file

#include "nix/util/compression-algo.hh"
#include "nix/util/types.hh"
#include "nix/util/hash.hh"
#include "nix/store/path-info.hh"
Expand All @@ -12,7 +13,7 @@ struct StoreDirConfig;
struct UnkeyedNarInfo : virtual UnkeyedValidPathInfo
{
std::string url;
std::string compression;
std::string compression; // FIXME: Use CompressionAlgo

@Ericson2314 Ericson2314 Jan 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::optional<Hash> fileHash;
uint64_t fileSize = 0;

Expand Down Expand Up @@ -42,7 +43,7 @@ struct NarInfo : ValidPathInfo, UnkeyedNarInfo
/* Later copies from `*this` are pointless. The argument is only
there so the constructors can also call
`UnkeyedValidPathInfo`, but this won't happen since the base
class is virtual. Only this counstructor (assuming it is most
class is virtual. Only this constructor (assuming it is most
derived) will initialize that virtual base class. */
, ValidPathInfo{info.path, static_cast<const UnkeyedValidPathInfo &>(*this)}
, UnkeyedNarInfo{static_cast<const UnkeyedValidPathInfo &>(*this)}
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/local-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,7 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)

auto tmpFile = fmt("%s.tmp.%d", logPath, getpid());

writeFile(tmpFile, compress("bzip2", log));
writeFile(tmpFile, compress(CompressionAlgo::bzip2, log));

std::filesystem::rename(tmpFile, logPath);
}
Expand Down
4 changes: 3 additions & 1 deletion src/libstore/s3-binary-cache-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ void S3BinaryCacheStore::upsertFile(
try {
if (auto compressionMethod = getCompressionMethod(path)) {
CompressedSource compressed(source, *compressionMethod);
Headers headers = {{"Content-Encoding", *compressionMethod}};
/* TODO: Validate that this is a valid content encoding. We probably shouldn't set non-standard values here.
*/
Headers headers = {{"Content-Encoding", showCompressionAlgo(*compressionMethod)}};
doUpload(compressed, compressed.size(), std::move(headers));
} else {
doUpload(source, sizeHint, std::nullopt);
Expand Down
17 changes: 6 additions & 11 deletions src/libutil-tests/compression.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ namespace nix {
* compress / decompress
* --------------------------------------------------------------------------*/

TEST(compress, compressWithUnknownMethod)
{
ASSERT_THROW(compress("invalid-method", "something-to-compress"), UnknownCompressionMethod);
}

TEST(compress, noneMethodDoesNothingToTheInput)
{
auto o = compress("none", "this-is-a-test");
auto o = compress(CompressionAlgo::none, "this-is-a-test");

ASSERT_EQ(o, "this-is-a-test");
}
Expand Down Expand Up @@ -43,7 +38,7 @@ TEST(decompress, decompressXzCompressed)
{
auto method = "xz";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
auto o = decompress(method, compress(method, str));
auto o = decompress(method, compress(CompressionAlgo::xz, str));

ASSERT_EQ(o, str);
}
Expand All @@ -52,7 +47,7 @@ TEST(decompress, decompressBzip2Compressed)
{
auto method = "bzip2";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
auto o = decompress(method, compress(method, str));
auto o = decompress(method, compress(CompressionAlgo::bzip2, str));

ASSERT_EQ(o, str);
}
Expand All @@ -61,7 +56,7 @@ TEST(decompress, decompressBrCompressed)
{
auto method = "br";
auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
auto o = decompress(method, compress(method, str));
auto o = decompress(method, compress(CompressionAlgo::brotli, str));

ASSERT_EQ(o, str);
}
Expand All @@ -82,7 +77,7 @@ TEST(makeCompressionSink, noneSinkDoesNothingToInput)
{
StringSink strSink;
auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
auto sink = makeCompressionSink("none", strSink);
auto sink = makeCompressionSink(CompressionAlgo::none, strSink);
(*sink)(inputString);
sink->finish();

Expand All @@ -94,7 +89,7 @@ TEST(makeCompressionSink, compressAndDecompress)
StringSink strSink;
auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
auto decompressionSink = makeDecompressionSink("bzip2", strSink);
auto sink = makeCompressionSink("bzip2", *decompressionSink);
auto sink = makeCompressionSink(CompressionAlgo::bzip2, *decompressionSink);

(*sink)(inputString);
sink->finish();
Expand Down
46 changes: 46 additions & 0 deletions src/libutil/compression-algo.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "nix/util/compression-algo.hh"
#include "nix/util/error.hh"
#include "nix/util/types.hh"

#include <unordered_map>

namespace nix {

CompressionAlgo parseCompressionAlgo(std::string_view method, bool suggestions)
{
#define NIX_COMPRESSION_ALGO_FROM_STRING(name, value) {name, CompressionAlgo::value},
static const std::unordered_map<std::string_view, CompressionAlgo> lookupTable = {
NIX_FOR_EACH_COMPRESSION_ALGO(NIX_COMPRESSION_ALGO_FROM_STRING)};
#undef NIX_COMPRESSION_ALGO_FROM_STRING

if (auto it = lookupTable.find(method); it != lookupTable.end())
return it->second;

ErrorInfo err = {.level = lvlError, .msg = HintFmt("unknown compression method '%s'", method)};

if (suggestions) {
static const StringSet allNames = [&]() {
StringSet res;
for (auto & [name, _] : lookupTable)
res.emplace(name);
return res;
}();
err.suggestions = Suggestions::bestMatches(allNames, method);
}

throw UnknownCompressionMethod(std::move(err));
}

std::string showCompressionAlgo(CompressionAlgo method)
{
switch (method) {
#define NIX_COMPRESSION_ALGO_TO_STRING(name, value) \
case CompressionAlgo::value: \
return name;
NIX_FOR_EACH_COMPRESSION_ALGO(NIX_COMPRESSION_ALGO_TO_STRING);
#undef NIX_COMPRESSION_ALGO_TO_STRING
}
unreachable();
}

} // namespace nix
68 changes: 68 additions & 0 deletions src/libutil/compression-settings.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "nix/util/configuration.hh"
#include "nix/util/compression-settings.hh"
#include "nix/util/json-impls.hh"
#include "nix/util/config-impl.hh"
#include "nix/util/abstract-setting-to-json.hh"

#include <nlohmann/json.hpp>

namespace nix {

template<>
CompressionAlgo BaseSetting<CompressionAlgo>::parse(const std::string & str) const
try {
return parseCompressionAlgo(str, /*suggestions=*/true);
} catch (UnknownCompressionMethod & e) {
throw UsageError(e.info().suggestions, "option '%s' has invalid value '%s'", name, str);
}

template<>
std::optional<CompressionAlgo> BaseSetting<std::optional<CompressionAlgo>>::parse(const std::string & str) const
try {
if (str.empty())
return std::nullopt;
return parseCompressionAlgo(str, /*suggestions=*/true);
} catch (UnknownCompressionMethod & e) {
throw UsageError(e.info().suggestions, "option '%s' has invalid value '%s'", name, str);
}

template<>
struct BaseSetting<CompressionAlgo>::trait
{
static constexpr bool appendable = false;
};

template<>
struct BaseSetting<std::optional<CompressionAlgo>>::trait
{
static constexpr bool appendable = false;
};

template<>
std::string BaseSetting<CompressionAlgo>::to_string() const
{
return std::string{showCompressionAlgo(value)};
}

template<>
std::string BaseSetting<std::optional<CompressionAlgo>>::to_string() const
{
if (value)
return std::string{showCompressionAlgo(*value)};
return "";
}

/* Same as with all settings - empty string means std::nullopt. */
template<>
struct json_avoids_null<CompressionAlgo> : std::true_type
{};

#define NIX_COMPRESSION_JSON(name, value) {CompressionAlgo::value, name},
NLOHMANN_JSON_SERIALIZE_ENUM(CompressionAlgo, {NIX_FOR_EACH_COMPRESSION_ALGO(NIX_COMPRESSION_JSON)});
#undef NIX_COMPRESSION_JSON

/* Explicit instantiation of templates */
template class BaseSetting<CompressionAlgo>;
template class BaseSetting<std::optional<CompressionAlgo>>;

} // namespace nix
37 changes: 2 additions & 35 deletions src/libutil/compression.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ struct ArchiveDecompressionSource : Source
}
};

/* These strings are a part of the public API in store parameters and such. Do not change!
Happens to match enum names. */
/* Happens to match enum names. */
#define NIX_FOR_EACH_LA_ALGO(MACRO) \
MACRO(bzip2) \
MACRO(compress) \
Expand Down Expand Up @@ -319,38 +318,6 @@ struct BrotliCompressionSink : ChunkedCompressionSink
}
};

/* Parses a *compression* method into the corresponding enum. This is only used
in the *compression* case and user interface. Content-Encoding should not use
these. */
static CompressionAlgo parseNixCompressionAlgoString(std::string_view method)
{
static const std::unordered_map<std::string_view, CompressionAlgo> lookupTable = {
{"none", CompressionAlgo::none},
{"br", CompressionAlgo::brotli},
#define NIX_DEF_LA_ALGO_NAME(algo) {#algo, CompressionAlgo::algo},
NIX_FOR_EACH_LA_ALGO(NIX_DEF_LA_ALGO_NAME)
#undef NIX_DEF_LA_ALGO_NAME
};

if (auto it = lookupTable.find(method); it != lookupTable.end())
return it->second;

static const StringSet allNames = [&]() {
StringSet res;
for (auto & [name, _] : lookupTable)
res.emplace(name);
return res;
}();

throw UnknownCompressionMethod(
Suggestions::bestMatches(allNames, method), "unknown compression method '%s'", method);
}

ref<CompressionSink> makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level)
{
return makeCompressionSink(parseNixCompressionAlgoString(method), nextSink, parallel, level);
}

ref<CompressionSink> makeCompressionSink(CompressionAlgo method, Sink & nextSink, const bool parallel, int level)
{
switch (method) {
Expand All @@ -367,7 +334,7 @@ ref<CompressionSink> makeCompressionSink(CompressionAlgo method, Sink & nextSink
unreachable();
}

std::string compress(const std::string & method, std::string_view in, const bool parallel, int level)
std::string compress(CompressionAlgo method, std::string_view in, const bool parallel, int level)
{
StringSink ssink;
auto sink = makeCompressionSink(method, ssink, parallel, level);
Expand Down
Loading
Loading