1use std::{borrow::Borrow, fmt, str::FromStr};
4
5use postcard::experimental::max_size::MaxSize;
6use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
7
8#[derive(PartialEq, Eq, Copy, Clone, Hash)]
10pub struct Hash(blake3::Hash);
11
12impl fmt::Debug for Hash {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 f.debug_tuple("Hash").field(&DD(self.to_hex())).finish()
15 }
16}
17
18struct DD<T: fmt::Display>(T);
19
20impl<T: fmt::Display> fmt::Debug for DD<T> {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 fmt::Display::fmt(&self.0, f)
23 }
24}
25
26impl Hash {
27 pub const EMPTY: Hash = Hash::from_bytes([
29 175, 19, 73, 185, 245, 249, 161, 166, 160, 64, 77, 234, 54, 220, 201, 73, 155, 203, 37,
30 201, 173, 193, 18, 183, 204, 154, 147, 202, 228, 31, 50, 98,
31 ]);
32
33 pub fn new(buf: impl AsRef<[u8]>) -> Self {
35 let val = blake3::hash(buf.as_ref());
36 Hash(val)
37 }
38
39 pub fn as_bytes(&self) -> &[u8; 32] {
41 self.0.as_bytes()
42 }
43
44 pub const fn from_bytes(bytes: [u8; 32]) -> Self {
46 Self(blake3::Hash::from_bytes(bytes))
47 }
48
49 pub fn to_hex(&self) -> String {
51 self.0.to_hex().to_string()
52 }
53
54 pub fn fmt_short(&self) -> String {
57 data_encoding::HEXLOWER.encode(&self.as_bytes()[..5])
58 }
59}
60
61impl AsRef<[u8]> for Hash {
62 fn as_ref(&self) -> &[u8] {
63 self.0.as_bytes()
64 }
65}
66
67impl Borrow<[u8]> for Hash {
68 fn borrow(&self) -> &[u8] {
69 self.0.as_bytes()
70 }
71}
72
73impl Borrow<[u8; 32]> for Hash {
74 fn borrow(&self) -> &[u8; 32] {
75 self.0.as_bytes()
76 }
77}
78
79impl From<Hash> for blake3::Hash {
80 fn from(value: Hash) -> Self {
81 value.0
82 }
83}
84
85impl From<blake3::Hash> for Hash {
86 fn from(value: blake3::Hash) -> Self {
87 Hash(value)
88 }
89}
90
91impl From<[u8; 32]> for Hash {
92 fn from(value: [u8; 32]) -> Self {
93 Hash(blake3::Hash::from(value))
94 }
95}
96
97impl From<Hash> for [u8; 32] {
98 fn from(value: Hash) -> Self {
99 *value.as_bytes()
100 }
101}
102
103impl From<&[u8; 32]> for Hash {
104 fn from(value: &[u8; 32]) -> Self {
105 Hash(blake3::Hash::from(*value))
106 }
107}
108
109impl PartialOrd for Hash {
110 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
111 Some(self.0.as_bytes().cmp(other.0.as_bytes()))
112 }
113}
114
115impl Ord for Hash {
116 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
117 self.0.as_bytes().cmp(other.0.as_bytes())
118 }
119}
120
121impl fmt::Display for Hash {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 let mut res = [b'b'; 52];
125 data_encoding::BASE32_NOPAD.encode_mut(self.as_bytes(), &mut res);
127 let t = std::str::from_utf8_mut(res.as_mut()).unwrap();
129 t.make_ascii_lowercase();
131 f.write_str(t)
133 }
134}
135
136#[derive(Debug, thiserror::Error)]
137pub enum HexOrBase32ParseError {
138 #[error("Invalid length")]
139 DecodeInvalidLength,
140 #[error("Failed to decode {0}")]
141 Decode(#[from] data_encoding::DecodeError),
142}
143
144impl FromStr for Hash {
145 type Err = HexOrBase32ParseError;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 let mut bytes = [0u8; 32];
149
150 let res = if s.len() == 64 {
151 data_encoding::HEXLOWER.decode_mut(s.as_bytes(), &mut bytes)
153 } else {
154 let input = s.to_ascii_uppercase();
155 let input = input.as_bytes();
156 if data_encoding::BASE32_NOPAD.decode_len(input.len())? != bytes.len() {
157 return Err(HexOrBase32ParseError::DecodeInvalidLength);
158 }
159 data_encoding::BASE32_NOPAD.decode_mut(input, &mut bytes)
160 };
161 match res {
162 Ok(len) => {
163 if len != 32 {
164 return Err(HexOrBase32ParseError::DecodeInvalidLength);
165 }
166 }
167 Err(partial) => return Err(partial.error.into()),
168 }
169 Ok(Self(blake3::Hash::from_bytes(bytes)))
170 }
171}
172
173impl Serialize for Hash {
174 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175 where
176 S: Serializer,
177 {
178 if serializer.is_human_readable() {
179 serializer.serialize_str(self.to_string().as_str())
180 } else {
181 self.0.as_bytes().serialize(serializer)
182 }
183 }
184}
185
186impl<'de> Deserialize<'de> for Hash {
187 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188 where
189 D: Deserializer<'de>,
190 {
191 if deserializer.is_human_readable() {
192 let s = String::deserialize(deserializer)?;
193 s.parse().map_err(de::Error::custom)
194 } else {
195 let data: [u8; 32] = Deserialize::deserialize(deserializer)?;
196 Ok(Self(blake3::Hash::from(data)))
197 }
198 }
199}
200
201impl MaxSize for Hash {
202 const POSTCARD_MAX_SIZE: usize = 32;
203}
204
205#[derive(
207 Clone,
208 Copy,
209 PartialEq,
210 Eq,
211 PartialOrd,
212 Ord,
213 Serialize,
214 Deserialize,
215 Default,
216 Debug,
217 MaxSize,
218 Hash,
219 derive_more::Display,
220)]
221pub enum BlobFormat {
222 #[default]
224 Raw,
225 HashSeq,
227}
228
229impl From<BlobFormat> for u64 {
230 fn from(value: BlobFormat) -> Self {
231 match value {
232 BlobFormat::Raw => 0,
233 BlobFormat::HashSeq => 1,
234 }
235 }
236}
237
238impl BlobFormat {
239 pub const fn is_raw(&self) -> bool {
241 matches!(self, BlobFormat::Raw)
242 }
243
244 pub const fn is_hash_seq(&self) -> bool {
246 matches!(self, BlobFormat::HashSeq)
247 }
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, MaxSize, Hash)]
252pub struct HashAndFormat {
253 pub hash: Hash,
255 pub format: BlobFormat,
257}
258
259impl From<Hash> for HashAndFormat {
260 fn from(hash: Hash) -> Self {
261 Self::raw(hash)
262 }
263}
264
265#[cfg(feature = "redb")]
266mod redb_support {
267 use postcard::experimental::max_size::MaxSize;
268 use redb::{Key as RedbKey, Value as RedbValue};
269
270 use super::{Hash, HashAndFormat};
271
272 impl RedbValue for Hash {
273 type SelfType<'a> = Self;
274
275 type AsBytes<'a> = &'a [u8; 32];
276
277 fn fixed_width() -> Option<usize> {
278 Some(32)
279 }
280
281 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
282 where
283 Self: 'a,
284 {
285 let contents: &'a [u8; 32] = data.try_into().unwrap();
286 (*contents).into()
287 }
288
289 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
290 where
291 Self: 'a,
292 Self: 'b,
293 {
294 value.as_bytes()
295 }
296
297 fn type_name() -> redb::TypeName {
298 redb::TypeName::new("iroh_blobs::Hash")
299 }
300 }
301
302 impl RedbKey for Hash {
303 fn compare(data1: &[u8], data2: &[u8]) -> std::cmp::Ordering {
304 data1.cmp(data2)
305 }
306 }
307
308 impl RedbValue for HashAndFormat {
309 type SelfType<'a> = Self;
310
311 type AsBytes<'a> = [u8; Self::POSTCARD_MAX_SIZE];
312
313 fn fixed_width() -> Option<usize> {
314 Some(Self::POSTCARD_MAX_SIZE)
315 }
316
317 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
318 where
319 Self: 'a,
320 {
321 let t: &'a [u8; Self::POSTCARD_MAX_SIZE] = data.try_into().unwrap();
322 postcard::from_bytes(t.as_slice()).unwrap()
323 }
324
325 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
326 where
327 Self: 'a,
328 Self: 'b,
329 {
330 let mut res = [0u8; 33];
331 postcard::to_slice(&value, &mut res).unwrap();
332 res
333 }
334
335 fn type_name() -> redb::TypeName {
336 redb::TypeName::new("iroh_blobs::HashAndFormat")
337 }
338 }
339}
340
341impl HashAndFormat {
342 pub fn new(hash: Hash, format: BlobFormat) -> Self {
344 Self { hash, format }
345 }
346
347 pub fn raw(hash: Hash) -> Self {
349 Self {
350 hash,
351 format: BlobFormat::Raw,
352 }
353 }
354
355 pub fn hash_seq(hash: Hash) -> Self {
357 Self {
358 hash,
359 format: BlobFormat::HashSeq,
360 }
361 }
362}
363
364impl fmt::Display for HashAndFormat {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 let mut slice = [0u8; 65];
367 hex::encode_to_slice(self.hash.as_bytes(), &mut slice[1..]).unwrap();
368 match self.format {
369 BlobFormat::Raw => {
370 write!(f, "{}", std::str::from_utf8(&slice[1..]).unwrap())
371 }
372 BlobFormat::HashSeq => {
373 slice[0] = b's';
374 write!(f, "{}", std::str::from_utf8(&slice).unwrap())
375 }
376 }
377 }
378}
379
380impl FromStr for HashAndFormat {
381 type Err = anyhow::Error;
382
383 fn from_str(s: &str) -> Result<Self, Self::Err> {
384 let s = s.as_bytes();
385 let mut hash = [0u8; 32];
386 match s.len() {
387 64 => {
388 hex::decode_to_slice(s, &mut hash)?;
389 Ok(Self::raw(hash.into()))
390 }
391 65 if s[0].eq_ignore_ascii_case(&b's') => {
392 hex::decode_to_slice(&s[1..], &mut hash)?;
393 Ok(Self::hash_seq(hash.into()))
394 }
395 _ => anyhow::bail!("invalid hash and format"),
396 }
397 }
398}
399
400impl Serialize for HashAndFormat {
401 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
402 where
403 S: Serializer,
404 {
405 if serializer.is_human_readable() {
406 serializer.serialize_str(self.to_string().as_str())
407 } else {
408 (self.hash, self.format).serialize(serializer)
409 }
410 }
411}
412
413impl<'de> Deserialize<'de> for HashAndFormat {
414 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
415 where
416 D: Deserializer<'de>,
417 {
418 if deserializer.is_human_readable() {
419 let s = String::deserialize(deserializer)?;
420 s.parse().map_err(de::Error::custom)
421 } else {
422 let (hash, format) = <(Hash, BlobFormat)>::deserialize(deserializer)?;
423 Ok(Self { hash, format })
424 }
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use serde_test::{assert_tokens, Configure, Token};
431
432 use super::*;
433 use crate::{assert_eq_hex, util::hexdump::parse_hexdump};
434
435 #[test]
436 fn test_display_parse_roundtrip() {
437 for i in 0..100 {
438 let hash: Hash = blake3::hash(&[i]).into();
439 let text = hash.to_string();
440 let hash1 = text.parse::<Hash>().unwrap();
441 assert_eq!(hash, hash1);
442
443 let text = hash.to_hex();
444 let hash1 = Hash::from_str(&text).unwrap();
445 assert_eq!(hash, hash1);
446 }
447 }
448
449 #[test]
450 fn test_hash() {
451 let data = b"hello world";
452 let hash = Hash::new(data);
453
454 let encoded = hash.to_string();
455 assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
456 }
457
458 #[test]
459 fn test_empty_hash() {
460 let hash = Hash::new(b"");
461 assert_eq!(hash, Hash::EMPTY);
462 }
463
464 #[test]
465 fn hash_wire_format() {
466 let hash = Hash::from([0xab; 32]);
467 let serialized = postcard::to_stdvec(&hash).unwrap();
468 let expected = parse_hexdump(r"
469 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab # hash
470 ").unwrap();
471 assert_eq_hex!(serialized, expected);
472 }
473
474 #[cfg(feature = "redb")]
475 #[test]
476 fn hash_redb() {
477 use redb::Value as RedbValue;
478 let bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
479 let hash = Hash::from(bytes);
480 assert_eq!(<Hash as RedbValue>::fixed_width(), Some(32));
481 assert_eq!(
482 <Hash as RedbValue>::type_name(),
483 redb::TypeName::new("iroh_blobs::Hash")
484 );
485 let serialized = <Hash as RedbValue>::as_bytes(&hash);
486 assert_eq!(serialized, &bytes);
487 let deserialized = <Hash as RedbValue>::from_bytes(serialized.as_slice());
488 assert_eq!(deserialized, hash);
489 let expected = parse_hexdump(
490 r"
491 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
492 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
493 ",
494 )
495 .unwrap();
496 assert_eq_hex!(serialized, expected);
497 }
498
499 #[cfg(feature = "redb")]
500 #[test]
501 fn hash_and_format_redb() {
502 use redb::Value as RedbValue;
503 let hash_bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
504 let hash = Hash::from(hash_bytes);
505 let haf = HashAndFormat::raw(hash);
506 assert_eq!(<HashAndFormat as RedbValue>::fixed_width(), Some(33));
507 assert_eq!(
508 <HashAndFormat as RedbValue>::type_name(),
509 redb::TypeName::new("iroh_blobs::HashAndFormat")
510 );
511 let serialized = <HashAndFormat as RedbValue>::as_bytes(&haf);
512 let mut bytes = [0u8; 33];
513 bytes[0..32].copy_from_slice(&hash_bytes);
514 assert_eq!(serialized, bytes);
515 let deserialized = <HashAndFormat as RedbValue>::from_bytes(serialized.as_slice());
516 assert_eq!(deserialized, haf);
517 let expected = parse_hexdump(
518 r"
519 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
520 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
521 00 # format (raw)
522 ",
523 )
524 .unwrap();
525 assert_eq_hex!(serialized, expected);
526 }
527
528 #[test]
529 fn test_hash_serde() {
530 let hash = Hash::new("hello");
531
532 let mut tokens = Vec::new();
534 tokens.push(Token::Tuple { len: 32 });
535 for byte in hash.as_bytes() {
536 tokens.push(Token::U8(*byte));
537 }
538 tokens.push(Token::TupleEnd);
539 assert_eq!(tokens.len(), 34);
540
541 assert_tokens(&hash.compact(), &tokens);
542
543 let tokens = vec![Token::String(
544 "5khrmpntq2bjexseshc6ldklwnig56gbj23yvbxjbdcwestheahq",
545 )];
546 assert_tokens(&hash.readable(), &tokens);
547 }
548
549 #[test]
550 fn test_hash_postcard() {
551 let hash = Hash::new("hello");
552 let ser = postcard::to_stdvec(&hash).unwrap();
553 let de = postcard::from_bytes(&ser).unwrap();
554 assert_eq!(hash, de);
555
556 assert_eq!(ser.len(), 32);
557 }
558
559 #[test]
560 fn test_hash_json() {
561 let hash = Hash::new("hello");
562 let ser = serde_json::to_string(&hash).unwrap();
563 let de = serde_json::from_str(&ser).unwrap();
564 assert_eq!(hash, de);
565 assert_eq!(ser.len(), 54);
567 }
568
569 #[test]
570 fn test_hash_and_format_parse() {
571 let hash = Hash::new("hello");
572
573 let expected = HashAndFormat::raw(hash);
574 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
575 assert_eq!(expected, actual);
576
577 let expected = HashAndFormat::hash_seq(hash);
578 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
579 assert_eq!(expected, actual);
580 }
581
582 #[test]
583 fn test_hash_and_format_postcard() {
584 let haf = HashAndFormat::raw(Hash::new("hello"));
585 let ser = postcard::to_stdvec(&haf).unwrap();
586 let de = postcard::from_bytes(&ser).unwrap();
587 assert_eq!(haf, de);
588 }
589
590 #[test]
591 fn test_hash_and_format_json() {
592 let haf = HashAndFormat::raw(Hash::new("hello"));
593 let ser = serde_json::to_string(&haf).unwrap();
594 let de = serde_json::from_str(&ser).unwrap();
595 assert_eq!(haf, de);
596 }
597
598 #[test]
599 fn test_hash_invalid() {
600 let _ = Hash::from_str("invalid").unwrap_err();
601 }
602}