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
5 changes: 3 additions & 2 deletions src/libstore-tests/ref-scan-bench.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "nix/util/references.hh"
#include "nix/store/path.hh"
#include "nix/util/base-nix-32.hh"

#include <benchmark/benchmark.h>

Expand All @@ -10,9 +11,9 @@ using namespace nix;
template<typename OIt>
static void randomReference(std::mt19937 & urng, OIt outIter)
{
auto dist = std::uniform_int_distribution<std::size_t>(0, nix32Chars.size() - 1);
auto dist = std::uniform_int_distribution<std::size_t>(0, BaseNix32::characters.size() - 1);
dist(urng);
std::generate_n(outIter, StorePath::HashLen, [&]() { return nix32Chars[dist(urng)]; });
std::generate_n(outIter, StorePath::HashLen, [&]() { return BaseNix32::characters[dist(urng)]; });
}

/**
Expand Down
42 changes: 42 additions & 0 deletions src/libutil/base-nix-32.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <cassert>

#include "nix/util/base-nix-32.hh"

namespace nix {

constexpr const std::array<unsigned char, 256> BaseNix32::reverseMap = [] {
std::array<unsigned char, 256> map{};

for (size_t i = 0; i < map.size(); ++i)
map[i] = invalid; // invalid

for (unsigned char i = 0; i < 32; ++i)
map[static_cast<unsigned char>(characters[i])] = i;

return map;
}();

std::string BaseNix32::encode(std::span<const uint8_t> originalData)
{
if (originalData.size() == 0)
return {};

size_t len = encodedLength(originalData.size());
assert(len);

std::string s;
s.reserve(len);

for (int n = (int) len - 1; n >= 0; n--) {
unsigned int b = n * 5;
unsigned int i = b / 8;
unsigned int j = b % 8;
unsigned char c =
(originalData.data()[i] >> j) | (i >= originalData.size() - 1 ? 0 : originalData.data()[i + 1] << (8 - j));
s.push_back(characters[c & 0x1f]);
}

return s;
}

} // namespace nix
38 changes: 6 additions & 32 deletions src/libutil/hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "nix/util/archive.hh"
#include "nix/util/configuration.hh"
#include "nix/util/split.hh"
#include "nix/util/base-nix-32.hh"

#include <sys/types.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -71,39 +72,10 @@ static std::string printHash16(const Hash & hash)
return buf;
}

// omitted: E O U T
constexpr char nix32Chars[] = "0123456789abcdfghijklmnpqrsvwxyz";

constexpr const std::array<unsigned char, 256> reverseNix32Map = [] {
std::array<unsigned char, 256> map{};

for (size_t i = 0; i < map.size(); ++i)
map[i] = 0xFF; // invalid

for (unsigned char i = 0; i < 32; ++i)
map[static_cast<unsigned char>(nix32Chars[i])] = i;

return map;
}();

static std::string printHash32(const Hash & hash)
{
assert(hash.hashSize);
size_t len = hash.base32Len();
assert(len);

std::string s;
s.reserve(len);

for (int n = (int) len - 1; n >= 0; n--) {
unsigned int b = n * 5;
unsigned int i = b / 8;
unsigned int j = b % 8;
unsigned char c = (hash.hash[i] >> j) | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
s.push_back(nix32Chars[c & 0x1f]);
}

return s;
return BaseNix32::encode({&hash.hash[0], hash.hashSize});
}

std::string printHash16or32(const Hash & hash)
Expand Down Expand Up @@ -229,11 +201,13 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)

for (unsigned int n = 0; n < rest.size(); ++n) {
char c = rest[rest.size() - n - 1];
unsigned char digit = reverseNix32Map[static_cast<unsigned char>(c)];
auto digit_opt = BaseNix32::lookupReverse(c);

if (digit == 0xFF)
if (!digit_opt)
throw BadHash("invalid base-32 hash: '%s'", rest);

uint8_t digit = std::move(*digit_opt);

unsigned int b = n * 5;
unsigned int i = b / 8;
unsigned int j = b % 8;
Expand Down
45 changes: 45 additions & 0 deletions src/libutil/include/nix/util/base-nix-32.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once
///@file

#include <array>
#include <cstdint>
#include <optional>
#include <string>
#include <span>

namespace nix {

struct BaseNix32
{
/// omitted: E O U T
constexpr static std::array<char, 32> characters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
'b', 'c', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'p', 'q', 'r', 's', 'v', 'w', 'x', 'y', 'z'};

private:
static const std::array<uint8_t, 256> reverseMap;

const static constexpr uint8_t invalid = 0xFF;

public:
static inline std::optional<uint8_t> lookupReverse(char base32)
{
uint8_t digit = reverseMap[static_cast<unsigned char>(base32)];
if (digit == invalid)
return std::nullopt;
else
return digit;
Comment on lines +30 to +31
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
else
return digit;
return digit;

}

/**
* Returns the length of a base-32 representation of this hash.
*/
static size_t encodedLength(size_t originalLength)
{
return (originalLength * 8 - 1) / 5 + 1;
}

static std::string encode(std::span<const uint8_t> originalData);
};

} // namespace nix
4 changes: 1 addition & 3 deletions src/libutil/include/nix/util/hash.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,14 @@ constexpr inline size_t regularHashSize(HashAlgorithm type)

extern const StringSet hashAlgorithms;

extern const std::array<unsigned char, 256> reverseNix32Map;

/**
* @brief Enumeration representing the hash formats.
*/
enum struct HashFormat : int {
/// @brief Base 64 encoding.
/// @see [IETF RFC 4648, section 4](https://datatracker.ietf.org/doc/html/rfc4648#section-4).
Base64,
/// @brief Nix-specific base-32 encoding. @see nix32Chars
/// @brief Nix-specific base-32 encoding. @see BaseNix32
Nix32,
/// @brief Lowercase hexadecimal encoding. @see base16Chars
Base16,
Expand Down
1 change: 1 addition & 0 deletions src/libutil/include/nix/util/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ headers = files(
'archive.hh',
'args.hh',
'args/root.hh',
'base-nix-32.hh',
'callback.hh',
'canon-path.hh',
'checked-arithmetic.hh',
Expand Down
1 change: 1 addition & 0 deletions src/libutil/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ subdir('nix-meson-build-support/common')
sources = [config_priv_h] + files(
'archive.cc',
'args.cc',
'base-nix-32.cc',
'canon-path.cc',
'compression.cc',
'compute-levels.cc',
Expand Down
3 changes: 2 additions & 1 deletion src/libutil/references.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "nix/util/references.hh"
#include "nix/util/hash.hh"
#include "nix/util/archive.hh"
#include "nix/util/base-nix-32.hh"

#include <map>
#include <cstdlib>
Expand All @@ -17,7 +18,7 @@ static void search(std::string_view s, StringSet & hashes, StringSet & seen)
int j;
bool match = true;
for (j = refLength - 1; j >= 0; --j)
if (reverseNix32Map[(unsigned char) s[i + j]] == 0xFF) {
if (!BaseNix32::lookupReverse(s[i + j])) {
i += j + 1;
match = false;
break;
Expand Down
Loading