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
259#[cfg(feature = "redb")]
260mod redb_support {
261 use postcard::experimental::max_size::MaxSize;
262 use redb::{Key as RedbKey, Value as RedbValue};
263
264 use super::{Hash, HashAndFormat};
265
266 impl RedbValue for Hash {
267 type SelfType<'a> = Self;
268
269 type AsBytes<'a> = &'a [u8; 32];
270
271 fn fixed_width() -> Option<usize> {
272 Some(32)
273 }
274
275 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
276 where
277 Self: 'a,
278 {
279 let contents: &'a [u8; 32] = data.try_into().unwrap();
280 (*contents).into()
281 }
282
283 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
284 where
285 Self: 'a,
286 Self: 'b,
287 {
288 value.as_bytes()
289 }
290
291 fn type_name() -> redb::TypeName {
292 redb::TypeName::new("iroh_blobs::Hash")
293 }
294 }
295
296 impl RedbKey for Hash {
297 fn compare(data1: &[u8], data2: &[u8]) -> std::cmp::Ordering {
298 data1.cmp(data2)
299 }
300 }
301
302 impl RedbValue for HashAndFormat {
303 type SelfType<'a> = Self;
304
305 type AsBytes<'a> = [u8; Self::POSTCARD_MAX_SIZE];
306
307 fn fixed_width() -> Option<usize> {
308 Some(Self::POSTCARD_MAX_SIZE)
309 }
310
311 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
312 where
313 Self: 'a,
314 {
315 let t: &'a [u8; Self::POSTCARD_MAX_SIZE] = data.try_into().unwrap();
316 postcard::from_bytes(t.as_slice()).unwrap()
317 }
318
319 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
320 where
321 Self: 'a,
322 Self: 'b,
323 {
324 let mut res = [0u8; 33];
325 postcard::to_slice(&value, &mut res).unwrap();
326 res
327 }
328
329 fn type_name() -> redb::TypeName {
330 redb::TypeName::new("iroh_blobs::HashAndFormat")
331 }
332 }
333}
334
335impl HashAndFormat {
336 pub fn new(hash: Hash, format: BlobFormat) -> Self {
338 Self { hash, format }
339 }
340
341 pub fn raw(hash: Hash) -> Self {
343 Self {
344 hash,
345 format: BlobFormat::Raw,
346 }
347 }
348
349 pub fn hash_seq(hash: Hash) -> Self {
351 Self {
352 hash,
353 format: BlobFormat::HashSeq,
354 }
355 }
356}
357
358impl fmt::Display for HashAndFormat {
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 let mut slice = [0u8; 65];
361 hex::encode_to_slice(self.hash.as_bytes(), &mut slice[1..]).unwrap();
362 match self.format {
363 BlobFormat::Raw => {
364 write!(f, "{}", std::str::from_utf8(&slice[1..]).unwrap())
365 }
366 BlobFormat::HashSeq => {
367 slice[0] = b's';
368 write!(f, "{}", std::str::from_utf8(&slice).unwrap())
369 }
370 }
371 }
372}
373
374impl FromStr for HashAndFormat {
375 type Err = anyhow::Error;
376
377 fn from_str(s: &str) -> Result<Self, Self::Err> {
378 let s = s.as_bytes();
379 let mut hash = [0u8; 32];
380 match s.len() {
381 64 => {
382 hex::decode_to_slice(s, &mut hash)?;
383 Ok(Self::raw(hash.into()))
384 }
385 65 if s[0].eq_ignore_ascii_case(&b's') => {
386 hex::decode_to_slice(&s[1..], &mut hash)?;
387 Ok(Self::hash_seq(hash.into()))
388 }
389 _ => anyhow::bail!("invalid hash and format"),
390 }
391 }
392}
393
394impl Serialize for HashAndFormat {
395 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
396 where
397 S: Serializer,
398 {
399 if serializer.is_human_readable() {
400 serializer.serialize_str(self.to_string().as_str())
401 } else {
402 (self.hash, self.format).serialize(serializer)
403 }
404 }
405}
406
407impl<'de> Deserialize<'de> for HashAndFormat {
408 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
409 where
410 D: Deserializer<'de>,
411 {
412 if deserializer.is_human_readable() {
413 let s = String::deserialize(deserializer)?;
414 s.parse().map_err(de::Error::custom)
415 } else {
416 let (hash, format) = <(Hash, BlobFormat)>::deserialize(deserializer)?;
417 Ok(Self { hash, format })
418 }
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use serde_test::{assert_tokens, Configure, Token};
425
426 use super::*;
427 use crate::{assert_eq_hex, util::hexdump::parse_hexdump};
428
429 #[test]
430 fn test_display_parse_roundtrip() {
431 for i in 0..100 {
432 let hash: Hash = blake3::hash(&[i]).into();
433 let text = hash.to_string();
434 let hash1 = text.parse::<Hash>().unwrap();
435 assert_eq!(hash, hash1);
436
437 let text = hash.to_hex();
438 let hash1 = Hash::from_str(&text).unwrap();
439 assert_eq!(hash, hash1);
440 }
441 }
442
443 #[test]
444 fn test_hash() {
445 let data = b"hello world";
446 let hash = Hash::new(data);
447
448 let encoded = hash.to_string();
449 assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
450 }
451
452 #[test]
453 fn test_empty_hash() {
454 let hash = Hash::new(b"");
455 assert_eq!(hash, Hash::EMPTY);
456 }
457
458 #[test]
459 fn hash_wire_format() {
460 let hash = Hash::from([0xab; 32]);
461 let serialized = postcard::to_stdvec(&hash).unwrap();
462 let expected = parse_hexdump(r"
463 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
464 ").unwrap();
465 assert_eq_hex!(serialized, expected);
466 }
467
468 #[cfg(feature = "redb")]
469 #[test]
470 fn hash_redb() {
471 use redb::Value as RedbValue;
472 let bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
473 let hash = Hash::from(bytes);
474 assert_eq!(<Hash as RedbValue>::fixed_width(), Some(32));
475 assert_eq!(
476 <Hash as RedbValue>::type_name(),
477 redb::TypeName::new("iroh_blobs::Hash")
478 );
479 let serialized = <Hash as RedbValue>::as_bytes(&hash);
480 assert_eq!(serialized, &bytes);
481 let deserialized = <Hash as RedbValue>::from_bytes(serialized.as_slice());
482 assert_eq!(deserialized, hash);
483 let expected = parse_hexdump(
484 r"
485 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
486 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
487 ",
488 )
489 .unwrap();
490 assert_eq_hex!(serialized, expected);
491 }
492
493 #[cfg(feature = "redb")]
494 #[test]
495 fn hash_and_format_redb() {
496 use redb::Value as RedbValue;
497 let hash_bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
498 let hash = Hash::from(hash_bytes);
499 let haf = HashAndFormat::raw(hash);
500 assert_eq!(<HashAndFormat as RedbValue>::fixed_width(), Some(33));
501 assert_eq!(
502 <HashAndFormat as RedbValue>::type_name(),
503 redb::TypeName::new("iroh_blobs::HashAndFormat")
504 );
505 let serialized = <HashAndFormat as RedbValue>::as_bytes(&haf);
506 let mut bytes = [0u8; 33];
507 bytes[0..32].copy_from_slice(&hash_bytes);
508 assert_eq!(serialized, bytes);
509 let deserialized = <HashAndFormat as RedbValue>::from_bytes(serialized.as_slice());
510 assert_eq!(deserialized, haf);
511 let expected = parse_hexdump(
512 r"
513 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
514 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
515 00 # format (raw)
516 ",
517 )
518 .unwrap();
519 assert_eq_hex!(serialized, expected);
520 }
521
522 #[test]
523 fn test_hash_serde() {
524 let hash = Hash::new("hello");
525
526 let mut tokens = Vec::new();
528 tokens.push(Token::Tuple { len: 32 });
529 for byte in hash.as_bytes() {
530 tokens.push(Token::U8(*byte));
531 }
532 tokens.push(Token::TupleEnd);
533 assert_eq!(tokens.len(), 34);
534
535 assert_tokens(&hash.compact(), &tokens);
536
537 let tokens = vec![Token::String(
538 "5khrmpntq2bjexseshc6ldklwnig56gbj23yvbxjbdcwestheahq",
539 )];
540 assert_tokens(&hash.readable(), &tokens);
541 }
542
543 #[test]
544 fn test_hash_postcard() {
545 let hash = Hash::new("hello");
546 let ser = postcard::to_stdvec(&hash).unwrap();
547 let de = postcard::from_bytes(&ser).unwrap();
548 assert_eq!(hash, de);
549
550 assert_eq!(ser.len(), 32);
551 }
552
553 #[test]
554 fn test_hash_json() {
555 let hash = Hash::new("hello");
556 let ser = serde_json::to_string(&hash).unwrap();
557 let de = serde_json::from_str(&ser).unwrap();
558 assert_eq!(hash, de);
559 assert_eq!(ser.len(), 54);
561 }
562
563 #[test]
564 fn test_hash_and_format_parse() {
565 let hash = Hash::new("hello");
566
567 let expected = HashAndFormat::raw(hash);
568 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
569 assert_eq!(expected, actual);
570
571 let expected = HashAndFormat::hash_seq(hash);
572 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
573 assert_eq!(expected, actual);
574 }
575
576 #[test]
577 fn test_hash_and_format_postcard() {
578 let haf = HashAndFormat::raw(Hash::new("hello"));
579 let ser = postcard::to_stdvec(&haf).unwrap();
580 let de = postcard::from_bytes(&ser).unwrap();
581 assert_eq!(haf, de);
582 }
583
584 #[test]
585 fn test_hash_and_format_json() {
586 let haf = HashAndFormat::raw(Hash::new("hello"));
587 let ser = serde_json::to_string(&haf).unwrap();
588 let de = serde_json::from_str(&ser).unwrap();
589 assert_eq!(haf, de);
590 }
591
592 #[test]
593 fn test_hash_invalid() {
594 let _ = Hash::from_str("invalid").unwrap_err();
595 }
596}