1use std::fmt::{self, Display, Formatter, LowerHex, UpperHex};
50use std::num::ParseIntError;
51
52use chksum_hash_core as core;
53
54pub const LENGTH_BITS: usize = 512;
56pub const LENGTH_BYTES: usize = LENGTH_BITS / 8;
58pub const LENGTH_WORDS: usize = LENGTH_BYTES / 2;
60pub const LENGTH_DWORDS: usize = LENGTH_WORDS / 2;
62pub const LENGTH_QWORDS: usize = LENGTH_DWORDS / 2;
64pub const LENGTH_HEX: usize = LENGTH_BYTES * 2;
66
67#[must_use]
69pub fn new(digest: [u8; LENGTH_BYTES]) -> Digest {
70 Digest::new(digest)
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77pub struct Digest([u8; LENGTH_BYTES]);
78
79impl Digest {
80 #[must_use]
82 pub const fn new(digest: [u8; LENGTH_BYTES]) -> Self {
83 Self(digest)
84 }
85
86 #[must_use]
88 pub const fn as_bytes(&self) -> &[u8] {
89 &self.0
90 }
91
92 #[must_use]
94 pub fn into_inner(self) -> [u8; LENGTH_BYTES] {
95 let Self(inner) = self;
96 inner
97 }
98
99 #[must_use]
132 pub fn to_hex_lowercase(&self) -> String {
133 format!("{self:x}")
134 }
135
136 #[must_use]
169 pub fn to_hex_uppercase(&self) -> String {
170 format!("{self:X}")
171 }
172}
173
174impl core::Digest for Digest {}
175
176impl AsRef<[u8]> for Digest {
177 fn as_ref(&self) -> &[u8] {
178 self.as_bytes()
179 }
180}
181
182impl From<[u8; LENGTH_BYTES]> for Digest {
183 fn from(digest: [u8; LENGTH_BYTES]) -> Self {
184 Self::new(digest)
185 }
186}
187
188impl From<Digest> for [u8; LENGTH_BYTES] {
189 fn from(digest: Digest) -> Self {
190 digest.into_inner()
191 }
192}
193
194impl Display for Digest {
195 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
196 LowerHex::fmt(self, f)
197 }
198}
199
200impl LowerHex for Digest {
201 #[rustfmt::skip]
202 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
203 let digest = format!(
204 "{:02x}{:02x}{:02x}{:02x}\
205 {:02x}{:02x}{:02x}{:02x}\
206 {:02x}{:02x}{:02x}{:02x}\
207 {:02x}{:02x}{:02x}{:02x}\
208 {:02x}{:02x}{:02x}{:02x}\
209 {:02x}{:02x}{:02x}{:02x}\
210 {:02x}{:02x}{:02x}{:02x}\
211 {:02x}{:02x}{:02x}{:02x}\
212 {:02x}{:02x}{:02x}{:02x}\
213 {:02x}{:02x}{:02x}{:02x}\
214 {:02x}{:02x}{:02x}{:02x}\
215 {:02x}{:02x}{:02x}{:02x}\
216 {:02x}{:02x}{:02x}{:02x}\
217 {:02x}{:02x}{:02x}{:02x}\
218 {:02x}{:02x}{:02x}{:02x}\
219 {:02x}{:02x}{:02x}{:02x}",
220 self.0[0x00], self.0[0x01], self.0[0x02], self.0[0x03],
221 self.0[0x04], self.0[0x05], self.0[0x06], self.0[0x07],
222 self.0[0x08], self.0[0x09], self.0[0x0A], self.0[0x0B],
223 self.0[0x0C], self.0[0x0D], self.0[0x0E], self.0[0x0F],
224 self.0[0x10], self.0[0x11], self.0[0x12], self.0[0x13],
225 self.0[0x14], self.0[0x15], self.0[0x16], self.0[0x17],
226 self.0[0x18], self.0[0x19], self.0[0x1A], self.0[0x1B],
227 self.0[0x1C], self.0[0x1D], self.0[0x1E], self.0[0x1F],
228 self.0[0x20], self.0[0x21], self.0[0x22], self.0[0x23],
229 self.0[0x24], self.0[0x25], self.0[0x26], self.0[0x27],
230 self.0[0x28], self.0[0x29], self.0[0x2A], self.0[0x2B],
231 self.0[0x2C], self.0[0x2D], self.0[0x2E], self.0[0x2F],
232 self.0[0x30], self.0[0x31], self.0[0x32], self.0[0x33],
233 self.0[0x34], self.0[0x35], self.0[0x36], self.0[0x37],
234 self.0[0x38], self.0[0x39], self.0[0x3A], self.0[0x3B],
235 self.0[0x3C], self.0[0x3D], self.0[0x3E], self.0[0x3F],
236 );
237 if formatter.alternate() {
238 formatter.pad_integral(true, "0x", &digest)
239 } else {
240 formatter.pad(&digest)
241 }
242 }
243}
244
245impl UpperHex for Digest {
246 #[rustfmt::skip]
247 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
248 let digest = format!(
249 "{:02X}{:02X}{:02X}{:02X}\
250 {:02X}{:02X}{:02X}{:02X}\
251 {:02X}{:02X}{:02X}{:02X}\
252 {:02X}{:02X}{:02X}{:02X}\
253 {:02X}{:02X}{:02X}{:02X}\
254 {:02X}{:02X}{:02X}{:02X}\
255 {:02X}{:02X}{:02X}{:02X}\
256 {:02X}{:02X}{:02X}{:02X}\
257 {:02X}{:02X}{:02X}{:02X}\
258 {:02X}{:02X}{:02X}{:02X}\
259 {:02X}{:02X}{:02X}{:02X}\
260 {:02X}{:02X}{:02X}{:02X}\
261 {:02X}{:02X}{:02X}{:02X}\
262 {:02X}{:02X}{:02X}{:02X}\
263 {:02X}{:02X}{:02X}{:02X}\
264 {:02X}{:02X}{:02X}{:02X}",
265 self.0[0x00], self.0[0x01], self.0[0x02], self.0[0x03],
266 self.0[0x04], self.0[0x05], self.0[0x06], self.0[0x07],
267 self.0[0x08], self.0[0x09], self.0[0x0A], self.0[0x0B],
268 self.0[0x0C], self.0[0x0D], self.0[0x0E], self.0[0x0F],
269 self.0[0x10], self.0[0x11], self.0[0x12], self.0[0x13],
270 self.0[0x14], self.0[0x15], self.0[0x16], self.0[0x17],
271 self.0[0x18], self.0[0x19], self.0[0x1A], self.0[0x1B],
272 self.0[0x1C], self.0[0x1D], self.0[0x1E], self.0[0x1F],
273 self.0[0x20], self.0[0x21], self.0[0x22], self.0[0x23],
274 self.0[0x24], self.0[0x25], self.0[0x26], self.0[0x27],
275 self.0[0x28], self.0[0x29], self.0[0x2A], self.0[0x2B],
276 self.0[0x2C], self.0[0x2D], self.0[0x2E], self.0[0x2F],
277 self.0[0x30], self.0[0x31], self.0[0x32], self.0[0x33],
278 self.0[0x34], self.0[0x35], self.0[0x36], self.0[0x37],
279 self.0[0x38], self.0[0x39], self.0[0x3A], self.0[0x3B],
280 self.0[0x3C], self.0[0x3D], self.0[0x3E], self.0[0x3F],
281 );
282 if formatter.alternate() {
283 formatter.pad_integral(true, "0X", &digest)
284 } else {
285 formatter.pad(&digest)
286 }
287 }
288}
289
290impl TryFrom<&str> for Digest {
291 type Error = FormatError;
292
293 fn try_from(digest: &str) -> Result<Self, Self::Error> {
294 if digest.len() != LENGTH_HEX {
295 let error = Self::Error::InvalidLength {
296 value: digest.len(),
297 proper: LENGTH_HEX,
298 };
299 return Err(error);
300 }
301 let digest = [
302 u64::from_str_radix(&digest[0x00..0x10], 16)?.to_be_bytes(),
303 u64::from_str_radix(&digest[0x10..0x20], 16)?.to_be_bytes(),
304 u64::from_str_radix(&digest[0x20..0x30], 16)?.to_be_bytes(),
305 u64::from_str_radix(&digest[0x30..0x40], 16)?.to_be_bytes(),
306 u64::from_str_radix(&digest[0x40..0x50], 16)?.to_be_bytes(),
307 u64::from_str_radix(&digest[0x50..0x60], 16)?.to_be_bytes(),
308 u64::from_str_radix(&digest[0x60..0x70], 16)?.to_be_bytes(),
309 u64::from_str_radix(&digest[0x70..0x80], 16)?.to_be_bytes(),
310 ];
311 #[rustfmt::skip]
312 let digest = [
313 digest[0][0], digest[0][1], digest[0][2], digest[0][3],
314 digest[0][4], digest[0][5], digest[0][6], digest[0][7],
315 digest[1][0], digest[1][1], digest[1][2], digest[1][3],
316 digest[1][4], digest[1][5], digest[1][6], digest[1][7],
317 digest[2][0], digest[2][1], digest[2][2], digest[2][3],
318 digest[2][4], digest[2][5], digest[2][6], digest[2][7],
319 digest[3][0], digest[3][1], digest[3][2], digest[3][3],
320 digest[3][4], digest[3][5], digest[3][6], digest[3][7],
321 digest[4][0], digest[4][1], digest[4][2], digest[4][3],
322 digest[4][4], digest[4][5], digest[4][6], digest[4][7],
323 digest[5][0], digest[5][1], digest[5][2], digest[5][3],
324 digest[5][4], digest[5][5], digest[5][6], digest[5][7],
325 digest[6][0], digest[6][1], digest[6][2], digest[6][3],
326 digest[6][4], digest[6][5], digest[6][6], digest[6][7],
327 digest[7][0], digest[7][1], digest[7][2], digest[7][3],
328 digest[7][4], digest[7][5], digest[7][6], digest[7][7],
329 ];
330 let digest = Self::from(digest);
331 Ok(digest)
332 }
333}
334
335#[derive(Debug, Eq, PartialEq, thiserror::Error)]
337pub enum FormatError {
338 #[error("Invalid length `{value}`, proper value `{proper}`")]
340 InvalidLength { value: usize, proper: usize },
341 #[error(transparent)]
343 ParseError(#[from] ParseIntError),
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn as_bytes() {
352 #[rustfmt::skip]
353 let digest = [
354 0xCF, 0x83, 0xE1, 0x35,
355 0x7E, 0xEF, 0xB8, 0xBD,
356 0xF1, 0x54, 0x28, 0x50,
357 0xD6, 0x6D, 0x80, 0x07,
358 0xD6, 0x20, 0xE4, 0x05,
359 0x0B, 0x57, 0x15, 0xDC,
360 0x83, 0xF4, 0xA9, 0x21,
361 0xD3, 0x6C, 0xE9, 0xCE,
362 0x47, 0xD0, 0xD1, 0x3C,
363 0x5D, 0x85, 0xF2, 0xB0,
364 0xFF, 0x83, 0x18, 0xD2,
365 0x87, 0x7E, 0xEC, 0x2F,
366 0x63, 0xB9, 0x31, 0xBD,
367 0x47, 0x41, 0x7A, 0x81,
368 0xA5, 0x38, 0x32, 0x7A,
369 0xF9, 0x27, 0xDA, 0x3E,
370 ];
371 assert_eq!(Digest::new(digest).as_bytes(), &digest);
372 }
373
374 #[test]
375 fn as_ref() {
376 #[rustfmt::skip]
377 let digest = [
378 0xCF, 0x83, 0xE1, 0x35,
379 0x7E, 0xEF, 0xB8, 0xBD,
380 0xF1, 0x54, 0x28, 0x50,
381 0xD6, 0x6D, 0x80, 0x07,
382 0xD6, 0x20, 0xE4, 0x05,
383 0x0B, 0x57, 0x15, 0xDC,
384 0x83, 0xF4, 0xA9, 0x21,
385 0xD3, 0x6C, 0xE9, 0xCE,
386 0x47, 0xD0, 0xD1, 0x3C,
387 0x5D, 0x85, 0xF2, 0xB0,
388 0xFF, 0x83, 0x18, 0xD2,
389 0x87, 0x7E, 0xEC, 0x2F,
390 0x63, 0xB9, 0x31, 0xBD,
391 0x47, 0x41, 0x7A, 0x81,
392 0xA5, 0x38, 0x32, 0x7A,
393 0xF9, 0x27, 0xDA, 0x3E,
394 ];
395 assert_eq!(Digest::new(digest).as_ref(), &digest);
396 }
397
398 #[test]
399 fn format() {
400 #[rustfmt::skip]
401 let digest = Digest::new([
402 0xCF, 0x83, 0xE1, 0x35,
403 0x7E, 0xEF, 0xB8, 0xBD,
404 0xF1, 0x54, 0x28, 0x50,
405 0xD6, 0x6D, 0x80, 0x07,
406 0xD6, 0x20, 0xE4, 0x05,
407 0x0B, 0x57, 0x15, 0xDC,
408 0x83, 0xF4, 0xA9, 0x21,
409 0xD3, 0x6C, 0xE9, 0xCE,
410 0x47, 0xD0, 0xD1, 0x3C,
411 0x5D, 0x85, 0xF2, 0xB0,
412 0xFF, 0x83, 0x18, 0xD2,
413 0x87, 0x7E, 0xEC, 0x2F,
414 0x63, 0xB9, 0x31, 0xBD,
415 0x47, 0x41, 0x7A, 0x81,
416 0xA5, 0x38, 0x32, 0x7A,
417 0xF9, 0x27, 0xDA, 0x3E,
418 ]);
419 assert_eq!(
420 format!("{digest:x}"),
421 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
422 );
423 assert_eq!(
424 format!("{digest:#x}"),
425 "0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
426 );
427 assert_eq!(
428 format!("{digest:136x}"),
429 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e "
430 );
431 assert_eq!(
432 format!("{digest:>136x}"),
433 " cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
434 );
435 assert_eq!(
436 format!("{digest:^136x}"),
437 " cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e "
438 );
439 assert_eq!(
440 format!("{digest:<136x}"),
441 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e "
442 );
443 assert_eq!(
444 format!("{digest:.^136x}"),
445 "....cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e...."
446 );
447 assert_eq!(format!("{digest:.8x}"), "cf83e135");
448 assert_eq!(
449 format!("{digest:X}"),
450 "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E"
451 );
452 assert_eq!(
453 format!("{digest:#X}"),
454 "0XCF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E"
455 );
456 assert_eq!(
457 format!("{digest:136X}"),
458 "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E "
459 );
460 assert_eq!(
461 format!("{digest:>136X}"),
462 " CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E"
463 );
464 assert_eq!(
465 format!("{digest:^136X}"),
466 " CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E "
467 );
468 assert_eq!(
469 format!("{digest:<136X}"),
470 "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E "
471 );
472 assert_eq!(
473 format!("{digest:.^136X}"),
474 "....CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E...."
475 );
476 assert_eq!(format!("{digest:.8X}"), "CF83E135");
477 }
478
479 #[test]
480 fn from() {
481 #[rustfmt::skip]
482 let digest = [
483 0xCF, 0x83, 0xE1, 0x35,
484 0x7E, 0xEF, 0xB8, 0xBD,
485 0xF1, 0x54, 0x28, 0x50,
486 0xD6, 0x6D, 0x80, 0x07,
487 0xD6, 0x20, 0xE4, 0x05,
488 0x0B, 0x57, 0x15, 0xDC,
489 0x83, 0xF4, 0xA9, 0x21,
490 0xD3, 0x6C, 0xE9, 0xCE,
491 0x47, 0xD0, 0xD1, 0x3C,
492 0x5D, 0x85, 0xF2, 0xB0,
493 0xFF, 0x83, 0x18, 0xD2,
494 0x87, 0x7E, 0xEC, 0x2F,
495 0x63, 0xB9, 0x31, 0xBD,
496 0x47, 0x41, 0x7A, 0x81,
497 0xA5, 0x38, 0x32, 0x7A,
498 0xF9, 0x27, 0xDA, 0x3E,
499 ];
500 assert_eq!(Digest::from(digest), Digest::new(digest));
501 assert_eq!(<[u8; 64]>::from(Digest::new(digest)), digest);
502 }
503
504 #[test]
505 fn to_hex() {
506 #[rustfmt::skip]
507 let digest = Digest::new([
508 0xCF, 0x83, 0xE1, 0x35,
509 0x7E, 0xEF, 0xB8, 0xBD,
510 0xF1, 0x54, 0x28, 0x50,
511 0xD6, 0x6D, 0x80, 0x07,
512 0xD6, 0x20, 0xE4, 0x05,
513 0x0B, 0x57, 0x15, 0xDC,
514 0x83, 0xF4, 0xA9, 0x21,
515 0xD3, 0x6C, 0xE9, 0xCE,
516 0x47, 0xD0, 0xD1, 0x3C,
517 0x5D, 0x85, 0xF2, 0xB0,
518 0xFF, 0x83, 0x18, 0xD2,
519 0x87, 0x7E, 0xEC, 0x2F,
520 0x63, 0xB9, 0x31, 0xBD,
521 0x47, 0x41, 0x7A, 0x81,
522 0xA5, 0x38, 0x32, 0x7A,
523 0xF9, 0x27, 0xDA, 0x3E,
524 ]);
525 #[rustfmt::skip]
526 assert_eq!(digest.to_hex_lowercase(), "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
527 #[rustfmt::skip]
528 assert_eq!(digest.to_hex_uppercase(), "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E");
529 }
530
531 #[test]
532 fn try_from() {
533 assert_eq!(
534 Digest::try_from("cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"),
535 Digest::try_from("CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E")
536 );
537 #[rustfmt::skip]
538 assert_eq!(
539 Digest::try_from("CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E"),
540 Ok(Digest::new([
541 0xCF, 0x83, 0xE1, 0x35,
542 0x7E, 0xEF, 0xB8, 0xBD,
543 0xF1, 0x54, 0x28, 0x50,
544 0xD6, 0x6D, 0x80, 0x07,
545 0xD6, 0x20, 0xE4, 0x05,
546 0x0B, 0x57, 0x15, 0xDC,
547 0x83, 0xF4, 0xA9, 0x21,
548 0xD3, 0x6C, 0xE9, 0xCE,
549 0x47, 0xD0, 0xD1, 0x3C,
550 0x5D, 0x85, 0xF2, 0xB0,
551 0xFF, 0x83, 0x18, 0xD2,
552 0x87, 0x7E, 0xEC, 0x2F,
553 0x63, 0xB9, 0x31, 0xBD,
554 0x47, 0x41, 0x7A, 0x81,
555 0xA5, 0x38, 0x32, 0x7A,
556 0xF9, 0x27, 0xDA, 0x3E,
557 ]))
558 );
559 assert!(matches!(Digest::try_from("CF"), Err(FormatError::InvalidLength { .. })));
560 assert!(matches!(
561 Digest::try_from("CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3EXX"),
562 Err(FormatError::InvalidLength { .. })
563 ));
564 assert!(matches!(
565 Digest::try_from("CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DAXX"),
566 Err(FormatError::ParseError(_))
567 ));
568 }
569}