Skip to content

Commit cad1d68

Browse files
committed
Improve base-32 hash decoding performance with reverse map
The changes include: * Defining nix32Chars as a constexpr char[]. * Adding a constexpr std::array<unsigned char, 256> (reverseNix32Map) to map characters to their base-32 digit values at compile time. * Replacing the slow character search loop with a direct lookup using reverseNix32Map. * Removing std::once_flag/isBase32 logic in references.cc in favor of reverseNix32Map Signed-off-by: Alexander V. Nikolaev <avn@avnik.info>
1 parent 51a32e4 commit cad1d68

File tree

3 files changed

+23
-18
lines changed

3 files changed

+23
-18
lines changed

src/libutil/hash.cc

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,22 @@ static std::string printHash16(const Hash & hash)
7272
}
7373

7474
// omitted: E O U T
75-
const std::string nix32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
75+
constexpr char nix32Chars[] = "0123456789abcdfghijklmnpqrsvwxyz";
76+
77+
constexpr std::array<unsigned char, 256> makeReverseNix32Map()
78+
{
79+
std::array<unsigned char, 256> map{};
80+
81+
for (size_t i = 0; i < map.size(); ++i)
82+
map[i] = 0xFF; // invalid
83+
84+
for (unsigned char i = 0; i < 32; ++i)
85+
map[static_cast<unsigned char>(nix32Chars[i])] = i;
86+
87+
return map;
88+
}
89+
90+
constexpr const std::array<unsigned char, 256> reverseNix32Map = makeReverseNix32Map();
7691

7792
static std::string printHash32(const Hash & hash)
7893
{
@@ -217,12 +232,11 @@ Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
217232

218233
for (unsigned int n = 0; n < rest.size(); ++n) {
219234
char c = rest[rest.size() - n - 1];
220-
unsigned char digit;
221-
for (digit = 0; digit < nix32Chars.size(); ++digit) /* !!! slow */
222-
if (nix32Chars[digit] == c)
223-
break;
224-
if (digit >= 32)
225-
throw BadHash("invalid base-32 hash '%s'", rest);
235+
unsigned char digit = reverseNix32Map[static_cast<unsigned char>(c)];
236+
237+
if (digit == 0xFF)
238+
throw BadHash("invalid base-32 hash: '%s'", rest);
239+
226240
unsigned int b = n * 5;
227241
unsigned int i = b / 8;
228242
unsigned int j = b % 8;

src/libutil/include/nix/util/hash.hh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ constexpr inline size_t regularHashSize(HashAlgorithm type)
3535

3636
extern const StringSet hashAlgorithms;
3737

38-
extern const std::string nix32Chars;
38+
extern const std::array<unsigned char, 256> reverseNix32Map;
3939

4040
/**
4141
* @brief Enumeration representing the hash formats.

src/libutil/references.cc

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,11 @@ static size_t refLength = 32; /* characters */
1313

1414
static void search(std::string_view s, StringSet & hashes, StringSet & seen)
1515
{
16-
static std::once_flag initialised;
17-
static bool isBase32[256];
18-
std::call_once(initialised, []() {
19-
for (unsigned int i = 0; i < 256; ++i)
20-
isBase32[i] = false;
21-
for (unsigned int i = 0; i < nix32Chars.size(); ++i)
22-
isBase32[(unsigned char) nix32Chars[i]] = true;
23-
});
24-
2516
for (size_t i = 0; i + refLength <= s.size();) {
2617
int j;
2718
bool match = true;
2819
for (j = refLength - 1; j >= 0; --j)
29-
if (!isBase32[(unsigned char) s[i + j]]) {
20+
if (reverseNix32Map[(unsigned char) s[i + j]] == 0xFF) {
3021
i += j + 1;
3122
match = false;
3223
break;

0 commit comments

Comments
 (0)