Skip to content

Commit 8f17103

Browse files
committed
cthash::encode(encoding) and cthash::decode(encoding)
1 parent cada813 commit 8f17103

8 files changed

Lines changed: 195 additions & 107 deletions

File tree

include/cthash/encoding/base.hpp

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ template <typename T> concept has_conversion = requires {
8383

8484
template <typename> struct identify;
8585

86-
template <typename Encoding, typename R, typename ByteT = a_byte_type> struct decode_view {
87-
using properties = encoding_properties<Encoding>;
86+
template <auto Encoding, typename R, typename ByteT = a_byte_type> struct decode_view {
87+
using properties = encoding_properties<decltype(Encoding)>;
8888
// output is 8
8989
// input depends on the encoding
9090
using chunk_view = cthash::chunk_of_bits_view<8, properties::has_padding, R, properties::bits, properties>;
@@ -143,7 +143,7 @@ template <typename Encoding, typename R, typename ByteT = a_byte_type> struct de
143143
}
144144
};
145145

146-
template <typename Encoding, typename ByteT = a_byte_type> struct decode_action {
146+
template <auto Encoding, typename ByteT = a_byte_type> struct decode_action {
147147
template <std::ranges::input_range R> constexpr friend auto operator|(R && input, decode_action action) requires(character<std::ranges::range_value_t<R>>) {
148148
return action.operator()<R>(std::forward<R>(input));
149149
}
@@ -235,7 +235,7 @@ template <typename Encoding, typename R, typename CharT = a_character_type> stru
235235
}
236236
};
237237

238-
template <typename Encoding, typename CharT = a_character_type>
238+
template <typename Encoding = void, typename CharT = a_character_type>
239239
struct encode_action {
240240
template <std::ranges::input_range R> constexpr friend auto operator|(R && input, encode_action action) {
241241
return action.operator()<R>(std::forward<R>(input));
@@ -246,49 +246,57 @@ struct encode_action {
246246
};
247247

248248
// just aliases to existing encodings
249-
using base2 = encoding::base2;
250-
using binary = encoding::base2;
251-
using base4 = encoding::base4;
252-
using base8 = encoding::base8;
253-
using octal = encoding::base8;
254-
using base8_no_padding = encoding::base8_no_padding;
255-
using octal_no_padding = encoding::base8_no_padding;
256-
using base16 = encoding::base16;
257-
using base16_uppercase = encoding::base16_uppercase;
258-
using hexdec = encoding::base16;
259-
using hexdec_uppercase = encoding::base16_uppercase;
260-
using base32 = encoding::base32;
261-
using base32_no_padding = encoding::base32_no_padding;
262-
using z_base32 = encoding::z_base32;
263-
using base64 = encoding::base64;
264-
using base64url = encoding::base64url;
265-
using base64_no_padding = encoding::base64_no_padding;
249+
static constexpr auto base2 = encoding::base2{};
250+
static constexpr auto binary = encoding::base2{};
251+
static constexpr auto base4 = encoding::base4{};
252+
static constexpr auto base8 = encoding::base8{};
253+
static constexpr auto octal = encoding::base8{};
254+
static constexpr auto base8_no_padding = encoding::base8_no_padding{};
255+
static constexpr auto octal_no_padding = encoding::base8_no_padding{};
256+
static constexpr auto base16 = encoding::base16{};
257+
static constexpr auto base16_uppercase = encoding::base16_uppercase{};
258+
static constexpr auto hexdec = encoding::base16{};
259+
static constexpr auto hexdec_uppercase = encoding::base16_uppercase{};
260+
static constexpr auto base32 = encoding::base32{};
261+
static constexpr auto base32_no_padding = encoding::base32_no_padding{};
262+
static constexpr auto z_base32 = encoding::z_base32{};
263+
static constexpr auto base64 = encoding::base64{};
264+
static constexpr auto base64url = encoding::base64url{};
265+
static constexpr auto base64_no_padding = encoding::base64_no_padding{};
266+
267+
static constexpr auto unknown = encoding::unknown{};
266268

267269
// encoding / decoding interface
268-
template <typename Encoding, typename CharT = a_character_type> constexpr auto encode = encode_action<Encoding, CharT>{};
269-
template <typename Encoding, typename ByteT = a_byte_type> constexpr auto decode = decode_action<Encoding, ByteT>{};
270+
271+
template <typename CharT = a_character_type, encoding_type T> consteval auto encode(T) {
272+
return encode_action<T, CharT>{};
273+
}
274+
275+
template <typename CharT = a_byte_type, encoding_type T> consteval auto decode(T) {
276+
return decode_action<T{}, CharT>{};
277+
}
270278

271279
// for compatibility with the old API
272-
constexpr auto binary_encode = encode<base2, char>;
273-
constexpr auto base2_encode = encode<base2, char>;
274-
constexpr auto base4_encode = encode<base4, char>;
275-
constexpr auto base8_encode = encode<base8, char>;
276-
constexpr auto octal_encode = encode<base8, char>;
277-
constexpr auto base8_no_padding_encode = encode<base8_no_padding, char>;
278-
constexpr auto octal_no_padding_encode = encode<base8_no_padding, char>;
279-
constexpr auto hexdec_encode = encode<base16, char>;
280-
constexpr auto hexdec_uppercase_encode = encode<base16_uppercase, char>;
281-
constexpr auto base16_encode = encode<base16, char>;
282-
constexpr auto base32_encode = encode<base32, char>;
283-
constexpr auto base32_no_padding_encode = encode<base32_no_padding, char>;
284-
constexpr auto z_base32_encode = encode<z_base32, char>;
285-
constexpr auto base64_encode = encode<base64, char>;
286-
constexpr auto base64url_encode = encode<base64url, char>;
287-
constexpr auto base64_no_padding_encode = encode<base64_no_padding, char>;
280+
constexpr auto binary_encode = encode_action<encoding::base2, char>{};
281+
constexpr auto base2_encode = encode_action<encoding::base2, char>{};
282+
constexpr auto base4_encode = encode_action<encoding::base4, char>{};
283+
constexpr auto base8_encode = encode_action<encoding::base8, char>{};
284+
constexpr auto octal_encode = encode_action<encoding::base8, char>{};
285+
constexpr auto base8_no_padding_encode = encode_action<encoding::base8_no_padding, char>{};
286+
constexpr auto octal_no_padding_encode = encode_action<encoding::base8_no_padding, char>{};
287+
constexpr auto hexdec_encode = encode_action<encoding::base16, char>{};
288+
constexpr auto hexdec_uppercase_encode = encode_action<encoding::base16_uppercase, char>{};
289+
constexpr auto base16_encode = encode_action<encoding::base16, char>{};
290+
constexpr auto base32_encode = encode_action<encoding::base32, char>{};
291+
constexpr auto base32_no_padding_encode = encode_action<encoding::base32_no_padding, char>{};
292+
constexpr auto z_base32_encode = encode_action<encoding::z_base32, char>{};
293+
constexpr auto base64_encode = encode_action<encoding::base64, char>{};
294+
constexpr auto base64url_encode = encode_action<encoding::base64url, char>{};
295+
constexpr auto base64_no_padding_encode = encode_action<encoding::base64_no_padding, char>{};
288296

289297
// same here
290298
template <typename Encoding, typename CharT = char> constexpr auto encode_to = encode_action<Encoding, CharT>{};
291-
template <typename Encoding, typename ByteT = std::byte> constexpr auto decode_from = decode_action<Encoding, ByteT>{};
299+
template <typename Encoding, typename ByteT = std::byte> constexpr auto decode_from = decode_action<Encoding{}, ByteT>{};
292300

293301
} // namespace cthash
294302

include/cthash/encoding/encodings.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,18 @@ namespace encoding {
112112

113113
using known_encodings = list<base64, base64_no_padding, base64url, base32, base32_no_padding, z_base32, base16, base16_uppercase, base8, base4, base2>;
114114

115+
struct unknown {
116+
static constexpr std::string_view name = "unknown";
117+
static constexpr char alphabet[] = "";
118+
};
119+
115120
} // namespace encoding
116121

122+
template <typename T> concept encoding_type = requires {
123+
std::string_view{T::name};
124+
std::span{T::alphabet};
125+
};
126+
117127
struct translation_table {
118128
static constexpr size_t table_size = 256;
119129
uint8_t data[table_size];

include/cthash/hasher.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ template <typename Config> struct hasher: private internal_hasher<Config> {
208208
constexpr hasher(hasher &&) noexcept = default;
209209
constexpr ~hasher() noexcept = default;
210210

211+
template <typename T> explicit constexpr hasher(T && in) noexcept requires requires(hasher & h, T && in) { h.update(in); }: hasher{} {
212+
update(in);
213+
}
214+
211215
// support for various input types
212216
constexpr hasher & update(std::span<const std::byte> input) noexcept {
213217
super::update_to_buffer_and_process(input);
@@ -249,7 +253,7 @@ template <typename Config> struct hasher: private internal_hasher<Config> {
249253
}
250254

251255
// parsing hash from string
252-
template <typename Encoding = encoding::hexdec> static constexpr std::optional<tagged_hash_value<Config>> parse(const convertible_to_strview auto & str) {
256+
template <auto Encoding = hexdec> static constexpr tagged_hash_value<Config> parse(const convertible_to_strview auto & str) {
253257
return parse_into_hash<Encoding, tagged_hash_value<Config>>(std::basic_string_view(str));
254258
}
255259
};

include/cthash/sha3/common.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ template <typename Config> struct keccak_hasher: basic_keccak_hasher<Config> {
282282
constexpr keccak_hasher(keccak_hasher &&) noexcept = default;
283283
constexpr ~keccak_hasher() noexcept = default;
284284

285+
template <typename T> explicit constexpr keccak_hasher(T && in) noexcept requires requires(keccak_hasher & h, T && in) { h.update(in); }: keccak_hasher{} {
286+
update(in);
287+
}
288+
285289
constexpr keccak_hasher & update(std::span<const std::byte> input) noexcept {
286290
super::update(input);
287291
return *this;

include/cthash/value.hpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@ template <typename T> concept convertible_to_strview = requires(const T & str) {
3434

3535
// hash_value
3636

37-
template <typename Encoding = cthash::encoding::hexdec, typename HashType, typename CharT = char, typename Traits = std::char_traits<CharT>> static constexpr std::optional<HashType> parse_into_hash(std::basic_string_view<CharT, Traits> view) {
37+
struct invalid_text_input { }; // TODO maybe do better?
38+
39+
template <auto Encoding = cthash::hexdec, typename HashType, typename CharT = char, typename Traits = std::char_traits<CharT>> static constexpr HashType parse_into_hash(std::basic_string_view<CharT, Traits> view) {
3840
// TODO check size
39-
auto decoded_view = view | cthash::decode<Encoding>;
41+
auto decoded_view = view | cthash::decode(Encoding);
42+
// identify<decltype(decoded_view)> i;
4043
const size_t needed_size = decoded_view.size();
4144
if constexpr (requires { {HashType::size()} -> std::same_as<size_t>; }) {
4245
if (needed_size != HashType::size()) {
4346
std::cout << "needed_size (" << needed_size << ") != hash_type::size(" << HashType::size() << ")\n";
44-
return std::nullopt; // or exception?
47+
throw invalid_text_input{};
4548
}
4649
}
4750
// TODO variable length hashes?
@@ -63,7 +66,10 @@ template <size_t N> struct hash_value: std::array<std::byte, N> {
6366
template <typename CharT> explicit constexpr hash_value(const CharT (&in)[N * 2u + 1u]) noexcept: super{internal::hexdec_to_binary<N>(std::span<const CharT, N * 2u>(in, N * 2u))} { }
6467
template <typename CharT> explicit constexpr hash_value(const fixed_string<CharT, N * 2u> & in) noexcept: super{internal::hexdec_to_binary<N>(std::span<const CharT, N * 2u>(in.data(), in.size()))} { }
6568

66-
template <typename Encoding = encoding::hexdec> static constexpr auto parse(const convertible_to_strview auto & str) {
69+
template <encoding_type Encoding> explicit constexpr hash_value(Encoding, const convertible_to_strview auto & str): hash_value{parse<Encoding{}>(str)} {
70+
}
71+
72+
template <auto Encoding = hexdec> static constexpr auto parse(const convertible_to_strview auto & str) {
6773
return parse_into_hash<Encoding, hash_value>(std::basic_string_view{str});
6874
}
6975

@@ -164,9 +170,10 @@ template <typename Tag, size_t = internal::digest_bytes_length_of<Tag>> struct t
164170

165171
using super = hash_value<N>;
166172
using super::super;
173+
167174
template <typename CharT> explicit constexpr tagged_hash_value(const fixed_string<CharT, N * 2u> & in) noexcept: super{in} { }
168175

169-
template <typename Encoding = encoding::hexdec> static constexpr auto parse(const convertible_to_strview auto & str) {
176+
template <auto Encoding = hexdec> static constexpr auto parse(const convertible_to_strview auto & str) {
170177
return parse_into_hash<Encoding, tagged_hash_value>(std::basic_string_view{str});
171178
}
172179

@@ -187,6 +194,18 @@ template <typename Tag, size_t = internal::digest_bytes_length_of<Tag>> struct t
187194
template <typename Encoding = typename cthash::default_encoding<Tag>::encoding, typename CharT = char> constexpr friend auto to_fixed_string(const tagged_hash_value & value) {
188195
return to_fixed_string<Encoding, CharT>(static_cast<const super &>(value));
189196
}
197+
198+
friend constexpr bool operator==(const tagged_hash_value & lhs, const tagged_hash_value & rhs) noexcept {
199+
return static_cast<const super &>(lhs) == static_cast<const super &>(rhs);
200+
}
201+
202+
friend constexpr auto operator<=>(const tagged_hash_value & lhs, const tagged_hash_value & rhs) noexcept {
203+
return static_cast<const super &>(lhs) <=> static_cast<const super &>(rhs);
204+
}
205+
206+
template <typename Other> friend constexpr bool operator==(const tagged_hash_value & lhs, const tagged_hash_value<Other> & rhs) noexcept = delete;
207+
208+
template <typename Other> friend constexpr auto operator<=>(const tagged_hash_value & lhs, const tagged_hash_value<Other> & rhs) noexcept = delete;
190209
};
191210

192211
template <typename T> concept variable_digest_length = T::digest_length_bit == 0u;

tests/decoding/basic.cpp

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,61 +12,61 @@ static auto materialize(const auto & range, std::optional<size_t> expected_size
1212
}
1313

1414
TEST_CASE("decode hexdec") {
15-
const auto view0 = ""sv | cthash::decode<cthash::base16>;
15+
const auto view0 = ""sv | cthash::decode(cthash::base16);
1616
REQUIRE(view0.size() == 0);
1717
REQUIRE(materialize(view0) == "");
1818

19-
const auto view1 = "00000000"sv | cthash::decode<cthash::base16>;
19+
const auto view1 = "00000000"sv | cthash::decode(cthash::base16);
2020
REQUIRE(view1.size() == 4);
2121
REQUIRE(materialize(view1) == "\0\0\0\0"sv);
2222

23-
const auto view2 = "3432"sv | cthash::decode<cthash::base16>;
23+
const auto view2 = "3432"sv | cthash::decode(cthash::base16);
2424
REQUIRE(view2.size() == 2);
2525
REQUIRE(materialize(view2) == "42");
2626

27-
const auto view3 = "48616E61"sv | cthash::decode<cthash::base16>;
27+
const auto view3 = "48616E61"sv | cthash::decode(cthash::base16);
2828
REQUIRE(view3.size() == 4);
2929
REQUIRE(materialize(view3) == "Hana");
3030

31-
const auto view4 = "48616e61"sv | cthash::decode<cthash::base16>;
31+
const auto view4 = "48616e61"sv | cthash::decode(cthash::base16);
3232
REQUIRE(view4.size() == 4);
3333
REQUIRE(materialize(view4) == "Hana");
3434
}
3535

3636
TEST_CASE("decode base64") {
37-
const auto view0 = ""sv | cthash::decode<cthash::base64>;
37+
const auto view0 = ""sv | cthash::decode(cthash::base64);
3838
REQUIRE(view0.size() == 0);
3939
REQUIRE(materialize(view0) == "");
4040

41-
const auto view1 = "aGVsbG8gdGhlcmU="sv | cthash::decode<cthash::base64>;
41+
const auto view1 = "aGVsbG8gdGhlcmU="sv | cthash::decode(cthash::base64);
4242
REQUIRE(view1.size() == 11u);
4343
REQUIRE(materialize(view1) == "hello there"sv);
4444
REQUIRE(materialize(view1).size() == 11u);
4545

46-
const auto view2 = "YmFuYW5h"sv | cthash::decode<cthash::base64>;
46+
const auto view2 = "YmFuYW5h"sv | cthash::decode(cthash::base64);
4747
REQUIRE(view2.size() == 6u);
4848
REQUIRE(materialize(view2) == "banana"sv);
4949
}
5050

5151
TEST_CASE("decode binary") {
52-
const auto view0 = ""sv | cthash::decode<cthash::binary>;
52+
const auto view0 = ""sv | cthash::decode(cthash::binary);
5353
REQUIRE(view0.size() == 0);
5454
REQUIRE(materialize(view0) == "");
5555

56-
const auto view1 = "00000000"sv | cthash::decode<cthash::binary>;
56+
const auto view1 = "00000000"sv | cthash::decode(cthash::binary);
5757
REQUIRE(view1.size() == 1u);
5858
REQUIRE(materialize(view1) == "\0"sv);
5959
REQUIRE(materialize(view1).size() == 1u);
6060
}
6161

62-
template <typename Encoding> auto roundtrip(const std::vector<uint8_t> & provided, std::optional<std::string_view> expected_encoded = std::nullopt) {
63-
const auto encoded = provided | cthash::encode<Encoding> | std::ranges::to<std::string>();
62+
template <auto Encoding> auto roundtrip(const std::vector<uint8_t> & provided, std::optional<std::string_view> expected_encoded = std::nullopt) {
63+
const auto encoded = provided | cthash::encode(Encoding) | std::ranges::to<std::string>();
6464
if (expected_encoded.has_value()) {
6565
// check fi we provided how it should encoded
6666
REQUIRE(encoded == *expected_encoded);
6767
}
6868

69-
const auto decode_view = encoded | cthash::decode<Encoding>;
69+
const auto decode_view = encoded | cthash::decode(Encoding);
7070

7171
SECTION("should give same size as provided.size()") {
7272
// should give same size as provided size
@@ -80,36 +80,38 @@ template <typename Encoding> auto roundtrip(const std::vector<uint8_t> & provide
8080
REQUIRE(provided == decoded);
8181
}
8282

83-
const auto encoded2 = decoded | cthash::encode<Encoding> | std::ranges::to<std::string>();
83+
const auto encoded2 = decoded | cthash::encode(Encoding) | std::ranges::to<std::string>();
8484

8585
SECTION("subsequent encoding should be same as first encoding") {
8686
REQUIRE(encoded2 == encoded);
8787
}
8888
}
8989

90-
TEMPLATE_TEST_CASE("decode roundtrip", "[roundtrip]", cthash::binary, cthash::octal, cthash::octal_no_padding, cthash::hexdec, cthash::base32, cthash::base64) {
90+
TEMPLATE_TEST_CASE("decode roundtrip", "[roundtrip]", cthash::encoding::binary, cthash::encoding::octal, cthash::encoding::octal_no_padding, cthash::encoding::hexdec, cthash::encoding::base32, cthash::encoding::base64) {
91+
static constexpr auto encoding = TestType{};
92+
9193
SECTION("empty") {
9294
const auto provided = std::vector<uint8_t>{};
93-
roundtrip<TestType>(provided);
95+
roundtrip<encoding>(provided);
9496
}
9597
SECTION("one byte") {
9698
const auto provided = std::vector<uint8_t>{0x1u};
97-
roundtrip<TestType>(provided);
99+
roundtrip<encoding>(provided);
98100
}
99101
SECTION("two bytes") {
100102
const auto provided = std::vector<uint8_t>{0x1u, 0xFFu};
101-
roundtrip<TestType>(provided);
103+
roundtrip<encoding>(provided);
102104
}
103105
SECTION("three bytes") {
104106
const auto provided = std::vector<uint8_t>{0x1u, 0xFFu, 0x42u};
105-
roundtrip<TestType>(provided);
107+
roundtrip<encoding>(provided);
106108
}
107109
SECTION("four bytes") {
108110
const auto provided = std::vector<uint8_t>{0x1u, 0xFFu, 0x42u, 0x16u};
109-
roundtrip<TestType>(provided);
111+
roundtrip<encoding>(provided);
110112
}
111113
SECTION("five bytes") {
112114
const auto provided = std::vector<uint8_t>{0x1u, 0xFFu, 0x42u, 0x16u, 011u};
113-
roundtrip<TestType>(provided);
115+
roundtrip<encoding>(provided);
114116
}
115117
}

0 commit comments

Comments
 (0)