1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::fmt::{self, Display, Formatter};

use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use sha2::Digest;

/// Hashing algorithm to be used for the module info
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HashAlgorithm {
    /// Sha256
    Sha256,
    /// XXHash
    XXHash,
}

/// The hash of a WebAssembly module.
#[derive(
    Debug,
    Copy,
    Clone,
    PartialEq,
    Eq,
    Hash,
    PartialOrd,
    Ord,
    RkyvSerialize,
    RkyvDeserialize,
    Archive,
)]
#[archive_attr(derive(CheckBytes, Debug))]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ModuleHash {
    /// xxhash
    XXHash([u8; 8]),

    /// sha256
    Sha256([u8; 32]),
}

impl ModuleHash {
    /// Create a new [`ModuleHash`] from the raw xxhash hash.
    pub fn xxhash_from_bytes(key: [u8; 8]) -> Self {
        Self::XXHash(key)
    }

    /// Create a new [`ModuleHash`] from the raw sha256 hash.
    pub fn sha256_from_bytes(key: [u8; 32]) -> Self {
        Self::Sha256(key)
    }

    /// Parse a XXHash hash from a hex-encoded string.
    pub fn xxhash_parse_hex(hex_str: &str) -> Result<Self, hex::FromHexError> {
        let mut hash = [0_u8; 8];
        hex::decode_to_slice(hex_str, &mut hash)?;
        Ok(Self::xxhash_from_bytes(hash))
    }

    /// Parse a Sha256 hash from a hex-encoded string.
    pub fn sha256_parse_hex(hex_str: &str) -> Result<Self, hex::FromHexError> {
        let mut hash = [0_u8; 32];
        hex::decode_to_slice(hex_str, &mut hash)?;
        Ok(Self::sha256_from_bytes(hash))
    }

    /// Generate a new [`ModuleCache`] based on the XXHash hash of some bytes.
    pub fn xxhash(wasm: impl AsRef<[u8]>) -> Self {
        let wasm = wasm.as_ref();

        let hash = xxhash_rust::xxh64::xxh64(wasm, 0);

        Self::XXHash(hash.to_ne_bytes())
    }

    /// Generate a new [`ModuleCache`] based on the Sha256 hash of some bytes.
    pub fn sha256(wasm: impl AsRef<[u8]>) -> Self {
        let wasm = wasm.as_ref();

        let hash: [u8; 32] = sha2::Sha256::digest(wasm).into();

        Self::Sha256(hash)
    }

    /// Get the raw hash.
    pub fn as_bytes(&self) -> &[u8] {
        match self {
            Self::XXHash(bytes) => bytes.as_slice(),
            Self::Sha256(bytes) => bytes.as_slice(),
        }
    }
}

impl From<webc::metadata::AtomSignature> for ModuleHash {
    fn from(value: webc::metadata::AtomSignature) -> Self {
        match value {
            webc::metadata::AtomSignature::Sha256(bytes) => Self::Sha256(bytes),
        }
    }
}

impl Display for ModuleHash {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        fn format<const N: usize>(f: &mut Formatter<'_>, bytes: &[u8; N]) -> fmt::Result {
            for byte in bytes {
                write!(f, "{byte:02X}")?;
            }

            Ok(())
        }

        match self {
            Self::XXHash(bytes) => format(f, bytes)?,
            Self::Sha256(bytes) => format(f, bytes)?,
        }

        Ok(())
    }
}