/include/cthash/sha3/common.hpp
Line | Count | Source |
1 | | #ifndef CTHASH_SHA3_COMMON_HPP |
2 | | #define CTHASH_SHA3_COMMON_HPP |
3 | | |
4 | | #include "keccak.hpp" |
5 | | #include "../hasher.hpp" |
6 | | #include "../internal/bit.hpp" |
7 | | #include "../internal/convert.hpp" |
8 | | #include "../simple.hpp" |
9 | | #include "../value.hpp" |
10 | | #include <cstdint> |
11 | | |
12 | | namespace cthash { |
13 | | |
14 | | template <typename T, typename Y> concept castable_to = requires(T val) { {static_cast<Y>(val)} -> std::same_as<Y>; }; |
15 | | |
16 | | template <size_t N> struct keccak_suffix { |
17 | | unsigned bits; |
18 | | std::array<std::byte, N> values; |
19 | | |
20 | | constexpr keccak_suffix(unsigned b, castable_to<std::byte> auto... v) noexcept: bits{b}, values{static_cast<std::byte>(v)...} { } |
21 | | }; |
22 | | |
23 | | template <castable_to<std::byte>... Ts> keccak_suffix(unsigned, Ts...) -> keccak_suffix<sizeof...(Ts)>; |
24 | | |
25 | | template <typename T> struct identify; |
26 | | |
27 | 0 | template <typename T, byte_like Byte> constexpr auto convert_prefix_into_aligned(std::span<const Byte> input, unsigned pos) noexcept -> std::array<std::byte, sizeof(T)> { |
28 | 0 | assert(input.size() <= sizeof(T)); |
29 | 0 | assert(pos <= sizeof(T)); |
30 | 0 | assert((input.size() + pos) <= sizeof(T)); |
31 | 0 |
|
32 | 0 | std::array<std::byte, sizeof(T)> buffer{}; |
33 | 0 |
|
34 | 0 | std::fill(buffer.begin(), buffer.end(), std::byte{0}); |
35 | 0 | std::transform(input.data(), input.data() + input.size(), buffer.data() + pos, [](auto v) { return static_cast<std::byte>(v); }); |
36 | 0 |
|
37 | 0 | return buffer; |
38 | 0 | } |
39 | | |
40 | 0 | template <typename T, byte_like Byte> constexpr auto convert_prefix_into_value(std::span<const Byte> input, unsigned pos) noexcept -> uint64_t { |
41 | 0 | const auto tmp = convert_prefix_into_aligned<T, Byte>(input, pos); |
42 | 0 | return cast_from_le_bytes<T>(std::span<const std::byte, 8>(tmp)); |
43 | 0 | } |
44 | | |
45 | | template <typename Config> struct basic_keccak_hasher { |
46 | | static_assert(Config::digest_length_bit % 8u == 0u); |
47 | | static_assert(Config::rate_bit % 8u == 0u); |
48 | | static_assert(Config::capacity_bit % 8u == 0u); |
49 | | |
50 | | static_assert((Config::rate_bit + Config::capacity_bit) == 1600u, "Only Keccak 1600 is implemented"); |
51 | | |
52 | | static constexpr size_t digest_length = Config::digest_length_bit / 8u; |
53 | | static constexpr size_t rate = Config::rate_bit / 8u; |
54 | | static constexpr size_t capacity = Config::capacity_bit / 8u; |
55 | | |
56 | | using result_t = cthash::tagged_hash_value<Config>; |
57 | | using digest_span_t = std::span<std::byte, digest_length>; |
58 | | |
59 | | keccak::state_1600 internal_state{}; |
60 | | uint8_t position{0u}; |
61 | | |
62 | 73 | constexpr basic_keccak_hasher() noexcept { |
63 | 73 | std::fill(internal_state.begin(), internal_state.end(), uint64_t{0}); |
64 | 73 | } |
65 | | |
66 | 11.9k | template <byte_like T> constexpr size_t xor_overwrite_block(std::span<const T> input) noexcept { |
67 | 11.9k | using value_t = keccak::state_1600::value_type; |
68 | | |
69 | 11.9k | if ((std::is_constant_evaluated() | (std::endian::native != std::endian::little))) { Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
Branch (69:7): [Folded - Ignored]
|
70 | 0 | assert((size_t(position) + input.size()) <= rate); Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
Branch (70:4): [True: 0.00%, False: 0.00%]
|
71 | | |
72 | | // unaligned prefix (by copying from left to right it should be little endian) |
73 | 0 | if (position % sizeof(value_t) != 0u) { Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
Branch (73:8): [True: 0.00%, False: 0.00%]
|
74 | | // xor unaligned value and move to aligned if possible |
75 | 0 | const size_t prefix_size = std::min(input.size(), sizeof(value_t) - (position % sizeof(value_t))); |
76 | 0 | internal_state[position / sizeof(uint64_t)] ^= convert_prefix_into_value<value_t>(input.first(prefix_size), static_cast<unsigned>(position % sizeof(value_t))); |
77 | 0 | position += static_cast<uint8_t>(prefix_size); |
78 | 0 | input = input.subspan(prefix_size); |
79 | 0 | } |
80 | | |
81 | | // aligned blocks |
82 | 0 | while (input.size() >= sizeof(value_t)) { Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
Branch (82:11): [True: 0.00%, False: 0.00%]
|
83 | | // xor aligned value and move to next |
84 | 0 | internal_state[position / sizeof(value_t)] ^= cast_from_le_bytes<value_t>(input.template first<sizeof(value_t)>()); |
85 | 0 | position += static_cast<uint8_t>(sizeof(value_t)); |
86 | 0 | input = input.subspan(sizeof(value_t)); |
87 | 0 | } |
88 | | |
89 | | // unaligned suffix |
90 | 0 | if (not input.empty()) { Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
Branch (90:8): [True: 0.00%, False: 0.00%]
|
91 | | // xor and finish |
92 | 0 | internal_state[position / sizeof(value_t)] ^= convert_prefix_into_value<value_t>(input, 0u); |
93 | 0 | position += static_cast<uint8_t>(input.size()); |
94 | 0 | } |
95 | |
|
96 | 0 | return position; |
97 | 11.9k | } else { |
98 | 11.9k | const auto buffer = std::as_writable_bytes(std::span<uint64_t>(internal_state)); |
99 | 11.9k | const auto remaining = buffer.subspan(position); |
100 | 11.9k | const auto place = remaining.first(std::min(input.size(), remaining.size())); |
101 | | |
102 | 132k | std::transform(place.data(), place.data() + place.size(), input.data(), place.data(), [](std::byte lhs, auto rhs) { return lhs ^ static_cast<std::byte>(rhs); }); |
103 | | |
104 | 11.9k | position += static_cast<uint8_t>(place.size()); |
105 | 11.9k | return position; |
106 | 11.9k | } |
107 | 11.9k | } |
108 | | |
109 | 10.9k | template <byte_like T> constexpr auto update(std::span<const T> input) noexcept { |
110 | 10.9k | assert(position < rate); Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
Branch (110:3): [True: 0.00%, False: 100.00%]
|
111 | 10.9k | const size_t remaining_in_buffer = rate - position; |
112 | | |
113 | 10.9k | if (remaining_in_buffer > input.size()) { Branch (113:7): [True: 100.00%, False: 0.00%]
Branch (113:7): [True: 93.29%, False: 6.71%]
Branch (113:7): [True: 100.00%, False: 0.00%]
Branch (113:7): [True: 91.66%, False: 8.34%]
Branch (113:7): [True: 100.00%, False: 0.00%]
Branch (113:7): [True: 91.97%, False: 8.03%]
Branch (113:7): [True: 100.00%, False: 0.00%]
Branch (113:7): [True: 100.00%, False: 0.00%]
Branch (113:7): [True: 94.57%, False: 5.43%]
Branch (113:7): [True: 100.00%, False: 0.00%]
|
114 | | // xor overwrite as much as we can, and that's all |
115 | 10.2k | xor_overwrite_block(input); |
116 | 10.2k | assert(position < rate); Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
Branch (116:4): [True: 0.00%, False: 100.00%]
|
117 | 10.2k | return; |
118 | 10.2k | } |
119 | | |
120 | | // finish block and call keccak :) |
121 | 783 | const auto first_part = input.first(remaining_in_buffer); |
122 | 783 | input = input.subspan(remaining_in_buffer); |
123 | 783 | xor_overwrite_block(first_part); |
124 | 783 | assert(position == rate); Branch (124:3): [True: 0.00%, False: 0.00%]
Branch (124:3): [True: 0.00%, False: 100.00%]
Branch (124:3): [True: 0.00%, False: 0.00%]
Branch (124:3): [True: 0.00%, False: 100.00%]
Branch (124:3): [True: 0.00%, False: 0.00%]
Branch (124:3): [True: 0.00%, False: 100.00%]
Branch (124:3): [True: 0.00%, False: 0.00%]
Branch (124:3): [True: 0.00%, False: 0.00%]
Branch (124:3): [True: 0.00%, False: 100.00%]
Branch (124:3): [True: 0.00%, False: 0.00%]
|
125 | 783 | keccak_f(internal_state); |
126 | 783 | position = 0u; |
127 | | |
128 | | // for each full block we can absorb directly |
129 | 1.10k | while (input.size() >= rate) { Branch (129:10): [True: 0.00%, False: 0.00%]
Branch (129:10): [True: 29.18%, False: 70.82%]
Branch (129:10): [True: 0.00%, False: 0.00%]
Branch (129:10): [True: 27.41%, False: 72.59%]
Branch (129:10): [True: 0.00%, False: 0.00%]
Branch (129:10): [True: 27.51%, False: 72.49%]
Branch (129:10): [True: 0.00%, False: 0.00%]
Branch (129:10): [True: 0.00%, False: 0.00%]
Branch (129:10): [True: 32.39%, False: 67.61%]
Branch (129:10): [True: 0.00%, False: 0.00%]
|
130 | 317 | const auto block = input.template first<rate>(); |
131 | 317 | input = input.subspan(rate); |
132 | 317 | assert(position == 0u); Branch (132:4): [True: 0.00%, False: 0.00%]
Branch (132:4): [True: 0.00%, False: 100.00%]
Branch (132:4): [True: 0.00%, False: 0.00%]
Branch (132:4): [True: 0.00%, False: 100.00%]
Branch (132:4): [True: 0.00%, False: 0.00%]
Branch (132:4): [True: 0.00%, False: 100.00%]
Branch (132:4): [True: 0.00%, False: 0.00%]
Branch (132:4): [True: 0.00%, False: 0.00%]
Branch (132:4): [True: 0.00%, False: 100.00%]
Branch (132:4): [True: 0.00%, False: 0.00%]
|
133 | 317 | xor_overwrite_block<T>(block); |
134 | 317 | keccak_f(internal_state); |
135 | 317 | position = 0u; |
136 | 317 | } |
137 | | |
138 | | // xor overwrite internal state with current remainder, and set position to end of it |
139 | 783 | if (not input.empty()) { Branch (139:7): [True: 0.00%, False: 0.00%]
Branch (139:7): [True: 85.16%, False: 14.84%]
Branch (139:7): [True: 0.00%, False: 0.00%]
Branch (139:7): [True: 91.42%, False: 8.58%]
Branch (139:7): [True: 0.00%, False: 0.00%]
Branch (139:7): [True: 90.18%, False: 9.82%]
Branch (139:7): [True: 0.00%, False: 0.00%]
Branch (139:7): [True: 0.00%, False: 0.00%]
Branch (139:7): [True: 74.31%, False: 25.69%]
Branch (139:7): [True: 0.00%, False: 0.00%]
|
140 | 677 | assert(position == 0u); Branch (140:4): [True: 0.00%, False: 0.00%]
Branch (140:4): [True: 0.00%, False: 100.00%]
Branch (140:4): [True: 0.00%, False: 0.00%]
Branch (140:4): [True: 0.00%, False: 100.00%]
Branch (140:4): [True: 0.00%, False: 0.00%]
Branch (140:4): [True: 0.00%, False: 100.00%]
Branch (140:4): [True: 0.00%, False: 0.00%]
Branch (140:4): [True: 0.00%, False: 0.00%]
Branch (140:4): [True: 0.00%, False: 100.00%]
Branch (140:4): [True: 0.00%, False: 0.00%]
|
141 | 677 | xor_overwrite_block(input); |
142 | 677 | assert(position < rate); Branch (142:4): [True: 0.00%, False: 0.00%]
Branch (142:4): [True: 0.00%, False: 100.00%]
Branch (142:4): [True: 0.00%, False: 0.00%]
Branch (142:4): [True: 0.00%, False: 100.00%]
Branch (142:4): [True: 0.00%, False: 0.00%]
Branch (142:4): [True: 0.00%, False: 100.00%]
Branch (142:4): [True: 0.00%, False: 0.00%]
Branch (142:4): [True: 0.00%, False: 0.00%]
Branch (142:4): [True: 0.00%, False: 100.00%]
Branch (142:4): [True: 0.00%, False: 0.00%]
|
143 | 677 | } |
144 | 783 | } |
145 | | |
146 | | // pad the message |
147 | 69 | constexpr void xor_padding_block() noexcept { |
148 | 69 | assert(position < rate); Branch (148:3): [True: 0.00%, False: 100.00%]
Branch (148:3): [True: 0.00%, False: 100.00%]
Branch (148:3): [True: 0.00%, False: 100.00%]
Branch (148:3): [True: 0.00%, False: 100.00%]
Branch (148:3): [True: 0.00%, False: 100.00%]
Branch (148:3): [True: 0.00%, False: 100.00%]
|
149 | | |
150 | 69 | constexpr const auto & suffix = Config::suffix; |
151 | 69 | constexpr std::byte suffix_and_start_of_padding = (suffix.values[0] | (std::byte{0b0000'0001u} << suffix.bits)); |
152 | | |
153 | 69 | internal_state[position / sizeof(uint64_t)] ^= uint64_t(suffix_and_start_of_padding) << ((position % sizeof(uint64_t)) * 8u); |
154 | 69 | internal_state[(rate - 1u) / sizeof(uint64_t)] ^= 0x8000000000000000ull; // last bit |
155 | 69 | } |
156 | | |
157 | 68 | constexpr void final_absorb() noexcept { |
158 | 68 | xor_padding_block(); |
159 | 68 | keccak_f(internal_state); |
160 | 68 | } |
161 | | |
162 | | // get resulting hash |
163 | 24 | constexpr void squeeze(std::span<std::byte> output) noexcept { |
164 | 24 | using value_t = keccak::state_1600::value_type; |
165 | | |
166 | 24 | static_assert((rate % sizeof(value_t)) == 0u); |
167 | 24 | auto r = std::span<const value_t>(internal_state).first(rate / sizeof(value_t)); |
168 | | |
169 | | // aligned results will be processed here... |
170 | 180 | while ((output.size() >= sizeof(value_t))) { Branch (170:10): [True: 86.67%, False: 13.33%]
Branch (170:10): [True: 86.67%, False: 13.33%]
|
171 | | // if we ran out of `rate` part, we need to squeeze another block |
172 | 156 | if (r.empty()) { Branch (172:8): [True: 2.56%, False: 97.44%]
Branch (172:8): [True: 2.56%, False: 97.44%]
|
173 | 4 | keccak_f(internal_state); |
174 | 4 | r = std::span<const value_t>(internal_state).first(rate / sizeof(value_t)); |
175 | 4 | } |
176 | | |
177 | | // look at current to process |
178 | 156 | const value_t current = r.front(); |
179 | 156 | const auto part = output.first<sizeof(value_t)>(); |
180 | | |
181 | | // convert |
182 | 156 | unwrap_littleendian_number<value_t>{part} = current; |
183 | | |
184 | | // move to next |
185 | 156 | r = r.subspan(1u); |
186 | 156 | output = output.subspan(sizeof(value_t)); |
187 | 156 | } |
188 | | |
189 | | // unaligned result is here |
190 | 24 | if (!output.empty()) { Branch (190:7): [True: 33.33%, False: 66.67%]
Branch (190:7): [True: 33.33%, False: 66.67%]
|
191 | | // if we ran out of `rate` part, we need to squeeze another block |
192 | 8 | if (r.empty()) { Branch (192:8): [True: 0.00%, False: 100.00%]
Branch (192:8): [True: 0.00%, False: 100.00%]
|
193 | 0 | keccak_f(internal_state); |
194 | 0 | r = std::span<const value_t>(internal_state).first(rate / sizeof(value_t)); |
195 | 0 | } |
196 | | |
197 | 8 | const value_t current = r.front(); |
198 | | |
199 | | // convert |
200 | 8 | std::array<std::byte, sizeof(value_t)> tmp; |
201 | 8 | unwrap_littleendian_number<value_t>{tmp} = current; |
202 | 8 | assert(tmp.size() > output.size()); Branch (202:4): [True: 0.00%, False: 100.00%]
Branch (202:4): [True: 0.00%, False: 100.00%]
|
203 | 8 | std::copy_n(tmp.data(), output.size(), output.data()); |
204 | 8 | } |
205 | 24 | } |
206 | | |
207 | | constexpr void squeeze(digest_span_t output_fixed) noexcept |
208 | | requires((digest_length < rate) && digest_length != 0u) |
209 | 44 | { |
210 | 44 | auto output = std::span<std::byte>(output_fixed); |
211 | | |
212 | | // we don't need to squeeze anything |
213 | 44 | using value_t = keccak::state_1600::value_type; |
214 | | |
215 | 44 | static_assert((rate % sizeof(value_t)) == 0u); |
216 | 44 | auto r = std::span<const value_t>(internal_state).first(rate / sizeof(value_t)); |
217 | | |
218 | | // aligned results will be processed here... |
219 | 265 | while ((output.size() >= sizeof(value_t))) { Branch (219:10): [True: 80.00%, False: 20.00%]
Branch (219:10): [True: 85.71%, False: 14.29%]
Branch (219:10): [True: 75.00%, False: 25.00%]
Branch (219:10): [True: 88.89%, False: 11.11%]
|
220 | 221 | assert(!r.empty()); Branch (220:4): [True: 0.00%, False: 100.00%]
Branch (220:4): [True: 0.00%, False: 100.00%]
Branch (220:4): [True: 0.00%, False: 100.00%]
Branch (220:4): [True: 0.00%, False: 100.00%]
|
221 | | // look at current to process |
222 | 221 | const value_t current = r.front(); |
223 | 221 | const auto part = output.template first<sizeof(value_t)>(); |
224 | | |
225 | | // convert |
226 | 221 | unwrap_littleendian_number<value_t>{part} = current; |
227 | | |
228 | | // move to next |
229 | 221 | r = r.subspan(1u); |
230 | 221 | output = output.subspan(sizeof(value_t)); |
231 | 221 | } |
232 | | |
233 | 44 | if constexpr ((output_fixed.size() % sizeof(value_t)) != 0u) { |
234 | | // unaligned result is here |
235 | 9 | assert(!output.empty()); Branch (235:4): [True: 0.00%, False: 100.00%]
|
236 | 9 | assert(!r.empty()); Branch (236:4): [True: 0.00%, False: 100.00%]
|
237 | | |
238 | 9 | const value_t current = r.front(); |
239 | | |
240 | | // convert |
241 | 9 | std::array<std::byte, sizeof(value_t)> tmp; |
242 | 9 | unwrap_littleendian_number<value_t>{tmp} = current; |
243 | 9 | assert(tmp.size() > output.size()); Branch (243:4): [True: 0.00%, False: 100.00%]
|
244 | 9 | std::copy_n(tmp.data(), output.size(), output.data()); |
245 | 9 | } |
246 | 44 | } |
247 | | |
248 | | constexpr void final(digest_span_t digest) noexcept |
249 | | requires(digest_length != 0u) |
250 | 44 | { |
251 | 44 | final_absorb(); |
252 | 44 | squeeze(digest); |
253 | 44 | } |
254 | | |
255 | | constexpr result_t final() noexcept |
256 | | requires(digest_length != 0u) |
257 | 44 | { |
258 | 44 | result_t output; |
259 | 44 | final(output); |
260 | 44 | return output; |
261 | 44 | } |
262 | | |
263 | | template <size_t N> constexpr auto final() noexcept |
264 | | requires(digest_length == 0u) |
265 | 24 | { |
266 | 24 | static_assert(N % 8u == 0u, "Only whole bytes are supported!"); |
267 | 24 | using result_type = typename Config::template variable_digest<N>; |
268 | 24 | result_type output; |
269 | 24 | final_absorb(); |
270 | 24 | squeeze(output); |
271 | 24 | return output; |
272 | 24 | } |
273 | | }; |
274 | | |
275 | | template <typename Config> struct keccak_hasher: basic_keccak_hasher<Config> { |
276 | | using super = basic_keccak_hasher<Config>; |
277 | | using result_t = typename super::result_t; |
278 | | using digest_span_t = typename super::digest_span_t; |
279 | | |
280 | 73 | constexpr keccak_hasher() noexcept: super() { } |
281 | | constexpr keccak_hasher(const keccak_hasher &) noexcept = default; |
282 | | constexpr keccak_hasher(keccak_hasher &&) noexcept = default; |
283 | | constexpr ~keccak_hasher() noexcept = default; |
284 | | |
285 | 4 | constexpr keccak_hasher & update(std::span<const std::byte> input) noexcept { |
286 | 4 | super::update(input); |
287 | 4 | return *this; |
288 | 4 | } |
289 | | |
290 | 942 | template <convertible_to_byte_span T> constexpr keccak_hasher & update(const T & something) noexcept { |
291 | 942 | using value_type = typename decltype(std::span(something))::value_type; |
292 | 942 | super::update(std::span<const value_type>(something)); |
293 | 942 | return *this; |
294 | 942 | } |
295 | | |
296 | 2.50k | template <one_byte_char CharT> constexpr keccak_hasher & update(std::basic_string_view<CharT> in) noexcept { |
297 | 2.50k | super::update(std::span(in.data(), in.size())); |
298 | 2.50k | return *this; |
299 | 2.50k | } |
300 | | |
301 | 7.53k | template <string_literal T> constexpr keccak_hasher & update(const T & lit) noexcept { |
302 | 7.53k | super::update(std::span(lit, std::size(lit) - 1u)); |
303 | 7.53k | return *this; |
304 | 7.53k | } |
305 | | |
306 | | // TODO: any range with value convertible to byte |
307 | | |
308 | | using super::final; |
309 | | }; |
310 | | |
311 | | } // namespace cthash |
312 | | |
313 | | #endif |