Skip to content

Commit 1710fd0

Browse files
authored
Merge pull request #13975 from obsidiansystems/prep-json-0
Misc prep changes for #13942
2 parents f78062d + 095ac66 commit 1710fd0

File tree

7 files changed

+154
-59
lines changed

7 files changed

+154
-59
lines changed

src/libutil-tests/hash.cc

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
#include <regex>
22

33
#include <gtest/gtest.h>
4+
#include <nlohmann/json.hpp>
45

56
#include "nix/util/hash.hh"
7+
#include "nix/util/tests/characterization.hh"
68

79
namespace nix {
810

9-
class BLAKE3HashTest : public virtual ::testing::Test
11+
class HashTest : public CharacterizationTest
1012
{
13+
std::filesystem::path unitTestData = getUnitTestData() / "hash";
14+
1115
public:
1216

1317
/**
@@ -16,8 +20,14 @@ class BLAKE3HashTest : public virtual ::testing::Test
1620
*/
1721
ExperimentalFeatureSettings mockXpSettings;
1822

19-
private:
23+
std::filesystem::path goldenMaster(std::string_view testStem) const override
24+
{
25+
return unitTestData / testStem;
26+
}
27+
};
2028

29+
class BLAKE3HashTest : public HashTest
30+
{
2131
void SetUp() override
2232
{
2333
mockXpSettings.set("experimental-features", "blake3-hashes");
@@ -137,6 +147,46 @@ TEST(hashString, testKnownSHA512Hashes2)
137147
"c7d329eeb6dd26545e96e55b874be909");
138148
}
139149

150+
/* ----------------------------------------------------------------------------
151+
* parsing hashes
152+
* --------------------------------------------------------------------------*/
153+
154+
TEST(hashParseExplicitFormatUnprefixed, testKnownSHA256Hashes1_correct)
155+
{
156+
// values taken from: https://tools.ietf.org/html/rfc4634
157+
auto s = "abc";
158+
159+
auto hash = hashString(HashAlgorithm::SHA256, s);
160+
ASSERT_EQ(
161+
hash,
162+
Hash::parseExplicitFormatUnprefixed(
163+
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
164+
HashAlgorithm::SHA256,
165+
HashFormat::Base16));
166+
}
167+
168+
TEST(hashParseExplicitFormatUnprefixed, testKnownSHA256Hashes1_wrongAlgo)
169+
{
170+
// values taken from: https://tools.ietf.org/html/rfc4634
171+
ASSERT_THROW(
172+
Hash::parseExplicitFormatUnprefixed(
173+
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
174+
HashAlgorithm::SHA1,
175+
HashFormat::Base16),
176+
BadHash);
177+
}
178+
179+
TEST(hashParseExplicitFormatUnprefixed, testKnownSHA256Hashes1_wrongBase)
180+
{
181+
// values taken from: https://tools.ietf.org/html/rfc4634
182+
ASSERT_THROW(
183+
Hash::parseExplicitFormatUnprefixed(
184+
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
185+
HashAlgorithm::SHA256,
186+
HashFormat::Nix32),
187+
BadHash);
188+
}
189+
140190
/* ----------------------------------------------------------------------------
141191
* parseHashFormat, parseHashFormatOpt, printHashFormat
142192
* --------------------------------------------------------------------------*/

src/libutil/hash.cc

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,37 @@ struct DecodeNamePair
9999

100100
} // namespace
101101

102+
static DecodeNamePair baseExplicit(HashFormat format)
103+
{
104+
switch (format) {
105+
case HashFormat::Base16:
106+
return {base16::decode, "base16"};
107+
case HashFormat::Nix32:
108+
return {BaseNix32::decode, "nix32"};
109+
case HashFormat::Base64:
110+
return {base64::decode, "Base64"};
111+
case HashFormat::SRI:
112+
assert(false);
113+
}
114+
}
115+
102116
/**
103117
* Given the expected size of the message once decoded it, figure out
104118
* which encoding we are using by looking at the size of the encoded
105119
* message.
106120
*/
107-
static DecodeNamePair baseFromSize(std::string_view rest, HashAlgorithm algo)
121+
static HashFormat baseFromSize(std::string_view rest, HashAlgorithm algo)
108122
{
109123
auto hashSize = regularHashSize(algo);
124+
110125
if (rest.size() == base16::encodedLength(hashSize))
111-
return {base16::decode, "base16"};
126+
return HashFormat::Base16;
112127

113128
if (rest.size() == BaseNix32::encodedLength(hashSize))
114-
return {BaseNix32::decode, "nix32"};
129+
return HashFormat::Nix32;
115130

116131
if (rest.size() == base64::encodedLength(hashSize))
117-
return {base64::decode, "Base64"};
132+
return HashFormat::Base64;
118133

119134
throw BadHash("hash '%s' has wrong length for hash algorithm '%s'", rest, printHashAlgo(algo));
120135
}
@@ -135,7 +150,8 @@ static Hash parseLowLevel(std::string_view rest, HashAlgorithm algo, DecodeNameP
135150
e.addTrace({}, "While decoding hash '%s'", rest);
136151
}
137152
if (d.size() != res.hashSize)
138-
throw BadHash("invalid %s hash '%s' %d %d", pair.encodingName, rest);
153+
throw BadHash(
154+
"invalid %s hash '%s', length %d != expected length %d", pair.encodingName, rest, d.size(), res.hashSize);
139155
assert(res.hashSize);
140156
memcpy(res.hash, d.data(), res.hashSize);
141157

@@ -189,7 +205,7 @@ static Hash parseAnyHelper(std::string_view rest, auto resolveAlgo)
189205
} else {
190206
/* Otherwise, decide via the length of the hash (for the
191207
given algorithm) what base encoding it is. */
192-
return baseFromSize(rest, algo);
208+
return baseExplicit(baseFromSize(rest, algo));
193209
}
194210
}();
195211

@@ -224,7 +240,12 @@ Hash Hash::parseAny(std::string_view original, std::optional<HashAlgorithm> optA
224240

225241
Hash Hash::parseNonSRIUnprefixed(std::string_view s, HashAlgorithm algo)
226242
{
227-
return parseLowLevel(s, algo, baseFromSize(s, algo));
243+
return parseExplicitFormatUnprefixed(s, algo, baseFromSize(s, algo));
244+
}
245+
246+
Hash Hash::parseExplicitFormatUnprefixed(std::string_view s, HashAlgorithm algo, HashFormat format)
247+
{
248+
return parseLowLevel(s, algo, baseExplicit(format));
228249
}
229250

230251
Hash Hash::random(HashAlgorithm algo)

src/libutil/include/nix/util/experimental-features.hh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "nix/util/error.hh"
55
#include "nix/util/types.hh"
6+
#include "nix/util/json-non-null.hh"
67

78
#include <nlohmann/json_fwd.hpp>
89

@@ -89,6 +90,13 @@ public:
8990
MissingExperimentalFeature(ExperimentalFeature missingFeature);
9091
};
9192

93+
/**
94+
* `ExperimentalFeature` is always rendered as a string.
95+
*/
96+
template<>
97+
struct json_avoids_null<ExperimentalFeature> : std::true_type
98+
{};
99+
92100
/**
93101
* Semi-magic conversion to and from json.
94102
* See the nlohmann/json readme for more details.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ struct Hash
9090
*/
9191
static Hash parseNonSRIUnprefixed(std::string_view s, HashAlgorithm algo);
9292

93+
/**
94+
* Like `parseNonSRIUnprefixed`, but the hash format has been
95+
* explicitly given.
96+
*
97+
* @param explicitFormat cannot be SRI, but must be one of the
98+
* "bases".
99+
*/
100+
static Hash parseExplicitFormatUnprefixed(std::string_view s, HashAlgorithm algo, HashFormat explicitFormat);
101+
93102
static Hash parseSRI(std::string_view original);
94103

95104
public:
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#pragma once
2+
///@file
3+
4+
#include <string>
5+
#include <vector>
6+
#include <set>
7+
#include <map>
8+
#include <list>
9+
10+
namespace nix {
11+
12+
/**
13+
* For `adl_serializer<std::optional<T>>` below, we need to track what
14+
* types are not already using `null`. Only for them can we use `null`
15+
* to represent `std::nullopt`.
16+
*/
17+
template<typename T>
18+
struct json_avoids_null;
19+
20+
/**
21+
* Handle numbers in default impl
22+
*/
23+
template<typename T>
24+
struct json_avoids_null : std::bool_constant<std::is_integral<T>::value>
25+
{};
26+
27+
template<>
28+
struct json_avoids_null<std::nullptr_t> : std::false_type
29+
{};
30+
31+
template<>
32+
struct json_avoids_null<bool> : std::true_type
33+
{};
34+
35+
template<>
36+
struct json_avoids_null<std::string> : std::true_type
37+
{};
38+
39+
template<typename T>
40+
struct json_avoids_null<std::vector<T>> : std::true_type
41+
{};
42+
43+
template<typename T>
44+
struct json_avoids_null<std::list<T>> : std::true_type
45+
{};
46+
47+
template<typename T, typename Compare>
48+
struct json_avoids_null<std::set<T, Compare>> : std::true_type
49+
{};
50+
51+
template<typename K, typename V, typename Compare>
52+
struct json_avoids_null<std::map<K, V, Compare>> : std::true_type
53+
{};
54+
55+
} // namespace nix

src/libutil/include/nix/util/json-utils.hh

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "nix/util/error.hh"
88
#include "nix/util/types.hh"
9+
#include "nix/util/json-non-null.hh"
910

1011
namespace nix {
1112

@@ -59,56 +60,6 @@ Strings getStringList(const nlohmann::json & value);
5960
StringMap getStringMap(const nlohmann::json & value);
6061
StringSet getStringSet(const nlohmann::json & value);
6162

62-
/**
63-
* For `adl_serializer<std::optional<T>>` below, we need to track what
64-
* types are not already using `null`. Only for them can we use `null`
65-
* to represent `std::nullopt`.
66-
*/
67-
template<typename T>
68-
struct json_avoids_null;
69-
70-
/**
71-
* Handle numbers in default impl
72-
*/
73-
template<typename T>
74-
struct json_avoids_null : std::bool_constant<std::is_integral<T>::value>
75-
{};
76-
77-
template<>
78-
struct json_avoids_null<std::nullptr_t> : std::false_type
79-
{};
80-
81-
template<>
82-
struct json_avoids_null<bool> : std::true_type
83-
{};
84-
85-
template<>
86-
struct json_avoids_null<std::string> : std::true_type
87-
{};
88-
89-
template<typename T>
90-
struct json_avoids_null<std::vector<T>> : std::true_type
91-
{};
92-
93-
template<typename T>
94-
struct json_avoids_null<std::list<T>> : std::true_type
95-
{};
96-
97-
template<typename T, typename Compare>
98-
struct json_avoids_null<std::set<T, Compare>> : std::true_type
99-
{};
100-
101-
template<typename K, typename V>
102-
struct json_avoids_null<std::map<K, V>> : std::true_type
103-
{};
104-
105-
/**
106-
* `ExperimentalFeature` is always rendered as a string.
107-
*/
108-
template<>
109-
struct json_avoids_null<ExperimentalFeature> : std::true_type
110-
{};
111-
11263
} // namespace nix
11364

11465
namespace nlohmann {

src/libutil/include/nix/util/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ headers = files(
4242
'hash.hh',
4343
'hilite.hh',
4444
'json-impls.hh',
45+
'json-non-null.hh',
4546
'json-utils.hh',
4647
'logging.hh',
4748
'lru-cache.hh',

0 commit comments

Comments
 (0)