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 data_encoding::BASE32_NOPAD.decode_mut(s.to_ascii_uppercase().as_bytes(), &mut bytes)
155 };
156 match res {
157 Ok(len) => {
158 if len != 32 {
159 return Err(HexOrBase32ParseError::DecodeInvalidLength);
160 }
161 }
162 Err(partial) => return Err(partial.error.into()),
163 }
164 Ok(Self(blake3::Hash::from_bytes(bytes)))
165 }
166}
167
168impl Serialize for Hash {
169 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170 where
171 S: Serializer,
172 {
173 if serializer.is_human_readable() {
174 serializer.serialize_str(self.to_string().as_str())
175 } else {
176 self.0.as_bytes().serialize(serializer)
177 }
178 }
179}
180
181impl<'de> Deserialize<'de> for Hash {
182 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183 where
184 D: Deserializer<'de>,
185 {
186 if deserializer.is_human_readable() {
187 let s = String::deserialize(deserializer)?;
188 s.parse().map_err(de::Error::custom)
189 } else {
190 let data: [u8; 32] = Deserialize::deserialize(deserializer)?;
191 Ok(Self(blake3::Hash::from(data)))
192 }
193 }
194}
195
196impl MaxSize for Hash {
197 const POSTCARD_MAX_SIZE: usize = 32;
198}
199
200#[derive(
202 Clone,
203 Copy,
204 PartialEq,
205 Eq,
206 PartialOrd,
207 Ord,
208 Serialize,
209 Deserialize,
210 Default,
211 Debug,
212 MaxSize,
213 Hash,
214 derive_more::Display,
215)]
216pub enum BlobFormat {
217 #[default]
219 Raw,
220 HashSeq,
222}
223
224impl From<BlobFormat> for u64 {
225 fn from(value: BlobFormat) -> Self {
226 match value {
227 BlobFormat::Raw => 0,
228 BlobFormat::HashSeq => 1,
229 }
230 }
231}
232
233impl BlobFormat {
234 pub const fn is_raw(&self) -> bool {
236 matches!(self, BlobFormat::Raw)
237 }
238
239 pub const fn is_hash_seq(&self) -> bool {
241 matches!(self, BlobFormat::HashSeq)
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, MaxSize, Hash)]
247pub struct HashAndFormat {
248 pub hash: Hash,
250 pub format: BlobFormat,
252}
253
254#[cfg(feature = "redb")]
255mod redb_support {
256 use postcard::experimental::max_size::MaxSize;
257 use redb::{Key as RedbKey, Value as RedbValue};
258
259 use super::{Hash, HashAndFormat};
260
261 impl RedbValue for Hash {
262 type SelfType<'a> = Self;
263
264 type AsBytes<'a> = &'a [u8; 32];
265
266 fn fixed_width() -> Option<usize> {
267 Some(32)
268 }
269
270 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
271 where
272 Self: 'a,
273 {
274 let contents: &'a [u8; 32] = data.try_into().unwrap();
275 (*contents).into()
276 }
277
278 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
279 where
280 Self: 'a,
281 Self: 'b,
282 {
283 value.as_bytes()
284 }
285
286 fn type_name() -> redb::TypeName {
287 redb::TypeName::new("iroh_blobs::Hash")
288 }
289 }
290
291 impl RedbKey for Hash {
292 fn compare(data1: &[u8], data2: &[u8]) -> std::cmp::Ordering {
293 data1.cmp(data2)
294 }
295 }
296
297 impl RedbValue for HashAndFormat {
298 type SelfType<'a> = Self;
299
300 type AsBytes<'a> = [u8; Self::POSTCARD_MAX_SIZE];
301
302 fn fixed_width() -> Option<usize> {
303 Some(Self::POSTCARD_MAX_SIZE)
304 }
305
306 fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
307 where
308 Self: 'a,
309 {
310 let t: &'a [u8; Self::POSTCARD_MAX_SIZE] = data.try_into().unwrap();
311 postcard::from_bytes(t.as_slice()).unwrap()
312 }
313
314 fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
315 where
316 Self: 'a,
317 Self: 'b,
318 {
319 let mut res = [0u8; 33];
320 postcard::to_slice(&value, &mut res).unwrap();
321 res
322 }
323
324 fn type_name() -> redb::TypeName {
325 redb::TypeName::new("iroh_blobs::HashAndFormat")
326 }
327 }
328}
329
330impl HashAndFormat {
331 pub fn new(hash: Hash, format: BlobFormat) -> Self {
333 Self { hash, format }
334 }
335
336 pub fn raw(hash: Hash) -> Self {
338 Self {
339 hash,
340 format: BlobFormat::Raw,
341 }
342 }
343
344 pub fn hash_seq(hash: Hash) -> Self {
346 Self {
347 hash,
348 format: BlobFormat::HashSeq,
349 }
350 }
351}
352
353impl fmt::Display for HashAndFormat {
354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355 let mut slice = [0u8; 65];
356 hex::encode_to_slice(self.hash.as_bytes(), &mut slice[1..]).unwrap();
357 match self.format {
358 BlobFormat::Raw => {
359 write!(f, "{}", std::str::from_utf8(&slice[1..]).unwrap())
360 }
361 BlobFormat::HashSeq => {
362 slice[0] = b's';
363 write!(f, "{}", std::str::from_utf8(&slice).unwrap())
364 }
365 }
366 }
367}
368
369impl FromStr for HashAndFormat {
370 type Err = anyhow::Error;
371
372 fn from_str(s: &str) -> Result<Self, Self::Err> {
373 let s = s.as_bytes();
374 let mut hash = [0u8; 32];
375 match s.len() {
376 64 => {
377 hex::decode_to_slice(s, &mut hash)?;
378 Ok(Self::raw(hash.into()))
379 }
380 65 if s[0].eq_ignore_ascii_case(&b's') => {
381 hex::decode_to_slice(&s[1..], &mut hash)?;
382 Ok(Self::hash_seq(hash.into()))
383 }
384 _ => anyhow::bail!("invalid hash and format"),
385 }
386 }
387}
388
389impl Serialize for HashAndFormat {
390 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
391 where
392 S: Serializer,
393 {
394 if serializer.is_human_readable() {
395 serializer.serialize_str(self.to_string().as_str())
396 } else {
397 (self.hash, self.format).serialize(serializer)
398 }
399 }
400}
401
402impl<'de> Deserialize<'de> for HashAndFormat {
403 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
404 where
405 D: Deserializer<'de>,
406 {
407 if deserializer.is_human_readable() {
408 let s = String::deserialize(deserializer)?;
409 s.parse().map_err(de::Error::custom)
410 } else {
411 let (hash, format) = <(Hash, BlobFormat)>::deserialize(deserializer)?;
412 Ok(Self { hash, format })
413 }
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use serde_test::{assert_tokens, Configure, Token};
420
421 use super::*;
422 use crate::{assert_eq_hex, util::hexdump::parse_hexdump};
423
424 #[test]
425 fn test_display_parse_roundtrip() {
426 for i in 0..100 {
427 let hash: Hash = blake3::hash(&[i]).into();
428 let text = hash.to_string();
429 let hash1 = text.parse::<Hash>().unwrap();
430 assert_eq!(hash, hash1);
431
432 let text = hash.to_hex();
433 let hash1 = Hash::from_str(&text).unwrap();
434 assert_eq!(hash, hash1);
435 }
436 }
437
438 #[test]
439 fn test_hash() {
440 let data = b"hello world";
441 let hash = Hash::new(data);
442
443 let encoded = hash.to_string();
444 assert_eq!(encoded.parse::<Hash>().unwrap(), hash);
445 }
446
447 #[test]
448 fn test_empty_hash() {
449 let hash = Hash::new(b"");
450 assert_eq!(hash, Hash::EMPTY);
451 }
452
453 #[test]
454 fn hash_wire_format() {
455 let hash = Hash::from([0xab; 32]);
456 let serialized = postcard::to_stdvec(&hash).unwrap();
457 let expected = parse_hexdump(r"
458 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
459 ").unwrap();
460 assert_eq_hex!(serialized, expected);
461 }
462
463 #[cfg(feature = "redb")]
464 #[test]
465 fn hash_redb() {
466 use redb::Value as RedbValue;
467 let bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
468 let hash = Hash::from(bytes);
469 assert_eq!(<Hash as RedbValue>::fixed_width(), Some(32));
470 assert_eq!(
471 <Hash as RedbValue>::type_name(),
472 redb::TypeName::new("iroh_blobs::Hash")
473 );
474 let serialized = <Hash as RedbValue>::as_bytes(&hash);
475 assert_eq!(serialized, &bytes);
476 let deserialized = <Hash as RedbValue>::from_bytes(serialized.as_slice());
477 assert_eq!(deserialized, hash);
478 let expected = parse_hexdump(
479 r"
480 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
481 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
482 ",
483 )
484 .unwrap();
485 assert_eq_hex!(serialized, expected);
486 }
487
488 #[cfg(feature = "redb")]
489 #[test]
490 fn hash_and_format_redb() {
491 use redb::Value as RedbValue;
492 let hash_bytes: [u8; 32] = (0..32).collect::<Vec<_>>().as_slice().try_into().unwrap();
493 let hash = Hash::from(hash_bytes);
494 let haf = HashAndFormat::raw(hash);
495 assert_eq!(<HashAndFormat as RedbValue>::fixed_width(), Some(33));
496 assert_eq!(
497 <HashAndFormat as RedbValue>::type_name(),
498 redb::TypeName::new("iroh_blobs::HashAndFormat")
499 );
500 let serialized = <HashAndFormat as RedbValue>::as_bytes(&haf);
501 let mut bytes = [0u8; 33];
502 bytes[0..32].copy_from_slice(&hash_bytes);
503 assert_eq!(serialized, bytes);
504 let deserialized = <HashAndFormat as RedbValue>::from_bytes(serialized.as_slice());
505 assert_eq!(deserialized, haf);
506 let expected = parse_hexdump(
507 r"
508 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
509 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f # hash
510 00 # format (raw)
511 ",
512 )
513 .unwrap();
514 assert_eq_hex!(serialized, expected);
515 }
516
517 #[test]
518 fn test_hash_serde() {
519 let hash = Hash::new("hello");
520
521 let mut tokens = Vec::new();
523 tokens.push(Token::Tuple { len: 32 });
524 for byte in hash.as_bytes() {
525 tokens.push(Token::U8(*byte));
526 }
527 tokens.push(Token::TupleEnd);
528 assert_eq!(tokens.len(), 34);
529
530 assert_tokens(&hash.compact(), &tokens);
531
532 let tokens = vec![Token::String(
533 "5khrmpntq2bjexseshc6ldklwnig56gbj23yvbxjbdcwestheahq",
534 )];
535 assert_tokens(&hash.readable(), &tokens);
536 }
537
538 #[test]
539 fn test_hash_postcard() {
540 let hash = Hash::new("hello");
541 let ser = postcard::to_stdvec(&hash).unwrap();
542 let de = postcard::from_bytes(&ser).unwrap();
543 assert_eq!(hash, de);
544
545 assert_eq!(ser.len(), 32);
546 }
547
548 #[test]
549 fn test_hash_json() {
550 let hash = Hash::new("hello");
551 let ser = serde_json::to_string(&hash).unwrap();
552 let de = serde_json::from_str(&ser).unwrap();
553 assert_eq!(hash, de);
554 assert_eq!(ser.len(), 54);
556 }
557
558 #[test]
559 fn test_hash_and_format_parse() {
560 let hash = Hash::new("hello");
561
562 let expected = HashAndFormat::raw(hash);
563 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
564 assert_eq!(expected, actual);
565
566 let expected = HashAndFormat::hash_seq(hash);
567 let actual = expected.to_string().parse::<HashAndFormat>().unwrap();
568 assert_eq!(expected, actual);
569 }
570
571 #[test]
572 fn test_hash_and_format_postcard() {
573 let haf = HashAndFormat::raw(Hash::new("hello"));
574 let ser = postcard::to_stdvec(&haf).unwrap();
575 let de = postcard::from_bytes(&ser).unwrap();
576 assert_eq!(haf, de);
577 }
578
579 #[test]
580 fn test_hash_and_format_json() {
581 let haf = HashAndFormat::raw(Hash::new("hello"));
582 let ser = serde_json::to_string(&haf).unwrap();
583 let de = serde_json::from_str(&ser).unwrap();
584 assert_eq!(haf, de);
585 }
586}