RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
md5.h
Go to the documentation of this file.
1/*
2 * MD5 hash in C and x86 assembly
3 *
4 * Copyright (c) 2016 Project Nayuki
5 * https://www.nayuki.io/page/fast-md5-hash-implementation-in-x86-assembly
6 *
7 * (MIT License)
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of
10 * this software and associated documentation files (the "Software"), to deal in
11 * the Software without restriction, including without limitation the rights to
12 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13 * of
14 * the Software, and to permit persons to whom the Software is furnished to do
15 * so,
16 * subject to the following conditions:
17 * - The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 * - The Software is provided "as is", without warranty of any kind, express or
20 * implied, including but not limited to the warranties of merchantability,
21 * fitness for a particular purpose and noninfringement. In no event shall the
22 * authors or copyright holders be liable for any claim, damages or other
23 * liability, whether in an action of contract, tort or otherwise, arising
24 * from,
25 * out of or in connection with the Software or the use or other dealings in
26 * the
27 * Software.
28 */
29
30#pragma once
31
32#include "rawspeedconfig.h"
33#include "adt/Array1DRef.h"
34#include "adt/Casts.h"
35#include "adt/CroppedArray1DRef.h" // IWYU pragma: keep
36#include "adt/Invariant.h"
37#include <array>
38#include <concepts>
39#include <cstddef>
40#include <cstdint>
41#include <cstdio>
42#include <cstring>
43#include <stdint.h>
44#include <string>
45#include <type_traits>
46#include <variant>
47
48namespace rawspeed::md5 {
49
50class MD5Hasher final {
51public:
52 using state_type = std::array<uint32_t, 4>;
53
54 static constexpr int block_size = 64;
55 using block_type = std::array<uint8_t, block_size>;
56
57 MD5Hasher() noexcept = delete;
58 ~MD5Hasher() noexcept = delete;
59
60 MD5Hasher(const MD5Hasher&) = delete;
61 MD5Hasher(MD5Hasher&&) noexcept = delete;
62 MD5Hasher& operator=(const MD5Hasher&) = delete;
63 MD5Hasher& operator=(MD5Hasher&&) noexcept = delete;
64
66 Array1DRef<const uint8_t> block) noexcept;
67};
68
69template <class> inline constexpr bool always_false_v = false;
70
71template <int N> class BufferCoalescer final {
72 struct NoBuffer final {
73 static constexpr int block_length = 0;
74 };
75 struct FullBufferRef final {
76 // NOLINTNEXTLINE(google-explicit-constructor)
77 FullBufferRef(Array1DRef<const uint8_t> block_) : block(block_.begin()) {
78 invariant(block_.size() == block_length);
79 }
80 const uint8_t* block;
81 static constexpr int block_length = N;
82 };
87
88 std::variant<NoBuffer, FullBufferRef, CoalescingBuffer> state = NoBuffer();
89
90public:
91 template <typename ArgTy>
92 void take_block_impl(ArgTy& arg,
93 Array1DRef<const uint8_t> message) const = delete;
94
95private:
96 template <typename ArgTy>
97 requires std::same_as<ArgTy, NoBuffer>
98 void take_block_impl(NoBuffer& /*unused*/,
100 invariant(message.size() != 0);
101
102 if (message.size() == N) {
103 state = FullBufferRef(message);
104 return;
105 }
106
109 state = buf;
110 }
111
112 template <typename ArgTy>
113 requires std::same_as<ArgTy, CoalescingBuffer>
115 Array1DRef<const uint8_t> message) const {
116 invariant(message.size() != 0);
117 invariant(message.size() < N);
118
119 invariant(arg.block_length + message.size() <= N);
120
121 auto out =
122 Array1DRef(arg.block.data(), implicit_cast<int>(arg.block.size()))
123 .getCrop(/*offset=*/arg.block_length, /*size=*/message.size());
124 std::copy(message.begin(), message.end(), out.begin());
125 arg.block_length += message.size();
126 }
127
128 [[nodiscard]] __attribute__((always_inline)) int length() const noexcept {
129 return std::visit([](const auto& arg) { return arg.block_length; }, state);
130 }
131
132public:
133 [[nodiscard]] __attribute__((always_inline)) int
134 bytesAvaliable() const noexcept {
135 return N - length();
136 }
137
138 [[nodiscard]] bool blockIsEmpty() const noexcept {
139 return bytesAvaliable() == N;
140 }
141
142 [[nodiscard]] bool blockIsFull() const noexcept {
143 return bytesAvaliable() == 0;
144 }
145
146 void reset() noexcept { state = NoBuffer(); }
147
148 __attribute__((always_inline)) void
149 take_block(Array1DRef<const uint8_t> message) noexcept {
150 invariant(message.size() != 0);
151 invariant(message.size() <= N);
152
153 std::visit(
154 [this, message]<typename T>(T& arg) {
155 if constexpr (std::is_same_v<T, FullBufferRef>)
156 __builtin_unreachable();
157 else
158 this->take_block_impl<T>(arg, message);
159 },
160 state);
161 }
162
163 [[nodiscard]] FullBufferRef getAsFullBufferRef() const {
164 return std::visit(
165 []<typename T>(const T& arg) -> FullBufferRef {
166 if constexpr (std::is_same_v<T, FullBufferRef>)
167 return arg;
168 else if constexpr (std::is_same_v<T, CoalescingBuffer>) {
169 invariant(arg.block_length == N);
170 return {{arg.block.data(), arg.block_length}};
171 } else if constexpr (std::is_same_v<T, NoBuffer>) {
172 __builtin_unreachable();
173 } else {
174 static_assert(always_false_v<T>, "non-exhaustive visitor!");
175 }
176 },
177 state);
178 }
179};
180
181class MD5 final {
184
186
187 static constexpr const MD5Hasher::state_type md5_init = {
188 {UINT32_C(0x67452301), UINT32_C(0xEFCDAB89), UINT32_C(0x98BADCFE),
189 UINT32_C(0x10325476)}};
190
191 void reset() noexcept {
192 state = md5_init;
193 buffer.reset();
194 bytes_total = 0;
195 }
196
197 [[nodiscard]] int bytesAvaliableInBlock() const noexcept RAWSPEED_READONLY {
198 return buffer.bytesAvaliable();
199 }
200
201 [[nodiscard]] bool blockIsEmpty() const noexcept RAWSPEED_READONLY {
202 return buffer.blockIsEmpty();
203 }
204
205 [[nodiscard]] bool blockIsFull() const noexcept RAWSPEED_READONLY {
206 return buffer.blockIsFull();
207 }
208
209 __attribute__((always_inline)) inline void compressFullBlock() noexcept;
210
211 template <typename T>
212 requires std::is_same_v<T, uint8_t>
213 MD5& operator<<(const T& v) noexcept;
214
215 template <typename T>
216 requires std::is_same_v<T, uint8_t>
217 MD5& take_full_block(Array1DRef<const T> message) noexcept;
218
219 template <typename T>
220 requires std::is_same_v<T, uint8_t>
221 MD5& take_block(Array1DRef<const T> message) noexcept;
222
223public:
224 MD5() noexcept { reset(); }
225 ~MD5() noexcept { invariant(bytes_total == 0); }
226
227 MD5(const MD5&) = delete;
228 MD5(MD5&&) noexcept = delete;
229 MD5& operator=(const MD5&) = delete;
230 MD5& operator=(MD5&&) noexcept = delete;
231
232 template <typename T>
233 requires std::is_same_v<T, uint8_t>
234 MD5& take(const T* message, size_t len) noexcept;
235
236 MD5Hasher::state_type flush() noexcept;
237};
238
239__attribute__((always_inline)) inline void MD5::compressFullBlock() noexcept {
240 invariant(blockIsFull() && "Bad block size.");
241
242 const auto fullBlock = buffer.getAsFullBufferRef();
244 state, {fullBlock.block, decltype(fullBlock)::block_length});
245 buffer.reset();
246}
247
248template <typename T>
249 requires std::is_same_v<T, uint8_t>
250__attribute__((always_inline)) inline MD5&
251MD5::take_block(Array1DRef<const T> message) noexcept {
252 invariant(!blockIsFull());
253 invariant(message.size() != 0);
254 invariant(message.size() <= MD5Hasher::block_size);
255 invariant(message.size() <= bytesAvaliableInBlock());
256
257 buffer.take_block(message);
258 bytes_total += message.size();
259
260 return *this;
261}
262
263template <typename T>
264 requires std::is_same_v<T, uint8_t>
265__attribute__((always_inline)) inline MD5&
266MD5::take_full_block(Array1DRef<const T> message) noexcept {
267 invariant(blockIsEmpty());
268
269 invariant(message.size() == MD5Hasher::block_size);
270
271 take_block(message);
272 invariant(blockIsFull());
273
274 return *this;
275}
276
277template <typename T>
278 requires std::is_same_v<T, uint8_t>
279__attribute__((always_inline)) inline MD5& MD5::take(const T* message,
280 size_t len) noexcept {
281 invariant(message != nullptr);
282 invariant(!blockIsFull());
283
284 if (len == 0)
285 return *this;
286
287 auto msg = Array1DRef(message, implicit_cast<int>(len));
288
289 if (!blockIsEmpty()) {
290 auto prefix_size = implicit_cast<int>(
291 std::min<size_t>(msg.size(), bytesAvaliableInBlock()));
292 auto prefixMsg =
293 msg.getCrop(/*offset=*/0, /*size=*/prefix_size).getAsArray1DRef();
294 msg = msg.getCrop(prefixMsg.size(), /*size=*/msg.size() - prefixMsg.size())
295 .getAsArray1DRef();
296 take_block(prefixMsg);
297 if (blockIsFull())
298 compressFullBlock();
299 }
300
301 if (msg.size() == 0)
302 return *this;
303
304 const auto numFullBlocks = implicit_cast<int>(
305 msg.size() / MD5Hasher::block_size); // Truncating division!
306 for (int blockIdx = 0; blockIdx != numFullBlocks; ++blockIdx) {
307 auto innerMsg = msg.getCrop(/*offset=*/0, /*size=*/MD5Hasher::block_size)
308 .getAsArray1DRef();
309 msg = msg.getCrop(innerMsg.size(), /*size=*/msg.size() - innerMsg.size())
310 .getAsArray1DRef();
311 take_full_block(innerMsg);
312 compressFullBlock();
313 }
314
315 if (msg.size() > 0) {
316 invariant(blockIsEmpty());
317 invariant(msg.size() < MD5Hasher::block_size);
318 take_block(msg);
319 invariant(!blockIsFull());
320 }
321
322 return *this;
323}
324
325__attribute__((always_inline)) inline MD5Hasher::state_type
326MD5::flush() noexcept {
327 invariant(!blockIsFull());
328
329 static constexpr std::array<uint8_t, 1> magic0 = {0x80};
330 buffer.take_block({magic0.data(), magic0.size()});
331
332 static constexpr std::array<uint8_t, MD5Hasher::block_size> zeropadding = {};
333 if (bytesAvaliableInBlock() < 8) {
334 if (bytesAvaliableInBlock() > 0)
335 buffer.take_block({zeropadding.data(), bytesAvaliableInBlock()});
336 compressFullBlock();
337 }
338
339 if (bytesAvaliableInBlock() > 8)
340 buffer.take_block({zeropadding.data(), bytesAvaliableInBlock() - 8});
341
342 std::array<uint8_t, 8> magic1;
343 magic1[0] = static_cast<uint8_t>((bytes_total & 0x1FU) << 3);
344 bytes_total >>= 5;
345 for (size_t i = 1; i < 8; i++) {
346 magic1[i] = static_cast<uint8_t>(bytes_total);
347 bytes_total >>= 8;
348 }
349 buffer.take_block({magic1.data(), magic1.size()});
350 compressFullBlock();
351
352 MD5Hasher::state_type tmp = state;
353 reset();
354 return tmp;
355}
356
357// computes hash of the buffer message with length len
358[[nodiscard]] MD5Hasher::state_type md5_hash(const uint8_t* message,
359 size_t len) noexcept;
360
361// returns hash as string
362[[nodiscard]] std::string
364
365} // namespace rawspeed::md5
#define invariant(expr)
Definition Invariant.h:27
bool RAWSPEED_READNONE __attribute__((visibility("default"))) benchmarkDryRun()
Definition Common.cpp:35
int RAWSPEED_READONLY size() const
void take_block_impl(NoBuffer &, Array1DRef< const uint8_t > message)
Definition md5.h:98
__attribute__((always_inline)) int bytesAvaliable() const noexcept
Definition md5.h:133
bool blockIsFull() const noexcept
Definition md5.h:142
void take_block_impl(CoalescingBuffer &arg, Array1DRef< const uint8_t > message) const
Definition md5.h:114
bool blockIsEmpty() const noexcept
Definition md5.h:138
FullBufferRef getAsFullBufferRef() const
Definition md5.h:163
std::variant< NoBuffer, FullBufferRef, CoalescingBuffer > state
Definition md5.h:88
void reset() noexcept
Definition md5.h:146
void take_block_impl(ArgTy &arg, Array1DRef< const uint8_t > message) const =delete
__attribute__((always_inline)) void take_block(Array1DRef< const uint8_t > message) noexcept
Definition md5.h:148
__attribute__((always_inline)) int length() const noexcept
Definition md5.h:128
MD5Hasher() noexcept=delete
std::array< uint8_t, block_size > block_type
Definition md5.h:55
static state_type compress(state_type state, Array1DRef< const uint8_t > block) noexcept
std::array< uint32_t, 4 > state_type
Definition md5.h:52
static constexpr int block_size
Definition md5.h:54
bool blockIsEmpty() const noexcept RAWSPEED_READONLY
Definition md5.h:201
MD5Hasher::state_type state
Definition md5.h:185
~MD5() noexcept
Definition md5.h:225
MD5Hasher::state_type flush() noexcept
MD5(MD5 &&) noexcept=delete
void reset() noexcept
Definition md5.h:191
static constexpr const MD5Hasher::state_type md5_init
Definition md5.h:187
BufferCoalescer< MD5Hasher::block_size > buffer
Definition md5.h:182
MD5 & take_full_block(Array1DRef< const T > message) noexcept
MD5 & take(const T *message, size_t len) noexcept
bool blockIsFull() const noexcept RAWSPEED_READONLY
Definition md5.h:205
MD5 & take_block(Array1DRef< const T > message) noexcept
__attribute__((always_inline)) inline void compressFullBlock() noexcept
MD5(const MD5 &)=delete
int bytesAvaliableInBlock() const noexcept RAWSPEED_READONLY
Definition md5.h:197
MD5() noexcept
Definition md5.h:224
__attribute__((always_inline)) inline void MD5 std::string hash_to_string(const MD5Hasher::state_type &hash) noexcept
constexpr bool always_false_v
Definition md5.h:69
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
throw T(buf.data())
void RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE static char buf[bufSize]
Array1DRef(T *data_, int numElts_) -> Array1DRef< T >
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
FullBufferRef(Array1DRef< const uint8_t > block_)
Definition md5.h:77
static constexpr int block_length
Definition md5.h:73