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