oxedyne/fe2o3/fe2o3_iop_hash/src/api.rs
4.4 KiB, 9 runs
created by r1870400018:413, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use oxedyne_fe2o3_core::{ |
| 2 | prelude::*, |
| 3 | alt::Gnomon, |
| 4 | }; |
| 5 | use oxedyne_fe2o3_namex::id::InNamex; |
| 6 | |
| 7 | use std::fmt; |
| 8 | |
| 9 | |
| 10 | /// Most hash implementations seem to be multi-step. Hashing in one go allows us to include the |
| 11 | /// identity transformation as a no-operation option. They are also self-consuming, so we have to |
| 12 | /// respect that. |
| 13 | pub trait Hasher: |
| 14 | Clone |
| 15 | + std::fmt::Debug |
| 16 | + InNamex |
| 17 | + Send |
| 18 | + Sync |
| 19 | { |
| 20 | fn hash<const S: usize>(self, input: &[&[u8]], salt: [u8; S]) -> Hash<S>; |
| 21 | /// The a priori length. The identity hash length is the input length, but we don't know |
| 22 | /// that before seeing the input. |
| 23 | fn hash_length(&self) -> Gnomon<usize>; |
| 24 | fn is_identity(&self) -> bool; |
| 25 | } |
| 26 | |
| 27 | /// The empty hasher should just return the input. |
| 28 | impl Hasher for () { |
| 29 | fn hash<const S: usize>(self, input: &[&[u8]], _salt: [u8; S]) -> Hash<S> { |
| 30 | let len = input.iter().map(|slice| slice.len()).sum(); |
| 31 | let mut result = Vec::with_capacity(len); |
| 32 | |
| 33 | for slice in input { |
| 34 | result.extend_from_slice(slice); |
| 35 | } |
| 36 | |
| 37 | Hash::new(HashForm::Identity(result), [0u8; S]) |
| 38 | } |
| 39 | fn hash_length(&self) -> Gnomon<usize> { Gnomon::Unknown } |
| 40 | fn is_identity(&self) -> bool { true } |
| 41 | } |
| 42 | |
| 43 | #[derive(Clone)] |
| 44 | pub struct Hash< |
| 45 | const S: usize, |
| 46 | > { |
| 47 | form: HashForm, |
| 48 | salt: [u8; S], |
| 49 | } |
| 50 | |
| 51 | impl< |
| 52 | const S: usize, |
| 53 | > |
| 54 | fmt::Debug for Hash<S> |
| 55 | { |
| 56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 57 | write!(f, "Hash{{form: {:?}, salt: {:02x?}}}", self.form, self.salt) |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | impl< |
| 62 | const S: usize, |
| 63 | > |
| 64 | Hash<S> |
| 65 | { |
| 66 | pub fn new(form: HashForm, salt: [u8; S]) -> Self { |
| 67 | Self { |
| 68 | form, |
| 69 | salt, |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | pub fn as_hashform(self) -> HashForm { self.form } |
| 74 | |
| 75 | pub fn as_vec(self) -> Vec<u8> { |
| 76 | let salt_clone = self.salt.clone(); |
| 77 | let mut result = self.form.as_vec(); |
| 78 | result.extend_from_slice(&salt_clone); |
| 79 | result |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// A way to represent a hash result using more efficient primitives, when possible. |
| 84 | #[derive(Clone, Eq, Ord, PartialEq, PartialOrd)] |
| 85 | pub enum HashForm { |
| 86 | Identity(Vec<u8>), // Output = Input |
| 87 | U32(u32), |
| 88 | U64(u64), |
| 89 | U128(u128), |
| 90 | Bytes32([u8; 32]), |
| 91 | Bytes(Vec<u8>), |
| 92 | } |
| 93 | |
| 94 | impl fmt::Debug for HashForm { |
| 95 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 96 | match self { |
| 97 | Self::Identity(v) | |
| 98 | Self::Bytes(v) => write!(f, "{:02x?}", v), |
| 99 | Self::Bytes32(a32) => write!(f, "{:02x?}", a32), |
| 100 | Self::U128(nu128) => write!(f, "0x{:032x?}", nu128), |
| 101 | Self::U64(nu64) => write!(f, "0x{:016x?}", nu64), |
| 102 | Self::U32(nu32) => write!(f, "0x{:08x?}", nu32), |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | impl HashForm { |
| 108 | /// A priori length of hash when encoded to bytes. |
| 109 | pub fn len(&self) -> Gnomon<usize> { |
| 110 | match self { |
| 111 | Self::Identity(_) => Gnomon::Unknown, |
| 112 | Self::Bytes(v) => Gnomon::Known(v.len()), |
| 113 | Self::Bytes32(_) => Gnomon::Known(32), |
| 114 | Self::U128(_) => Gnomon::Known(16), |
| 115 | Self::U64(_) => Gnomon::Known(8), |
| 116 | Self::U32(_) => Gnomon::Known(4), |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /// Consuming conversion to byte vector. |
| 121 | pub fn as_vec(self) -> Vec<u8> { |
| 122 | match self { |
| 123 | Self::Identity(v) | |
| 124 | Self::Bytes(v) => v, |
| 125 | Self::Bytes32(a32) => a32.to_vec(), |
| 126 | Self::U128(n128) => n128.to_be_bytes().to_vec(), |
| 127 | Self::U64(n64) => n64.to_be_bytes().to_vec(), |
| 128 | Self::U32(n32) => n32.to_be_bytes().to_vec(), |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /// Truncates the byte encoded form of itself to a `u32`, except when there are not enough |
| 133 | /// bytes. |
| 134 | pub fn to_u32(&self) -> Outcome<u32> { |
| 135 | match self { |
| 136 | Self::Identity(v) | Self::Bytes(v) => { |
| 137 | if v.len() < 4 { |
| 138 | return Err(err!( |
| 139 | "With only {} bytes, the HashForm has too few to create \ |
| 140 | a u32 (4).", v.len(); |
| 141 | TooSmall, Conversion)); |
| 142 | } |
| 143 | Ok(u32::from_be_bytes(res!(<[u8; 4]>::try_from(&v[..4]), Decode, Bytes))) |
| 144 | }, |
| 145 | Self::Bytes32(a32) => Ok(u32::from_be_bytes(res!(<[u8; 4]>::try_from(&a32[..4]), Decode, Bytes))), |
| 146 | Self::U128(nu128) => Ok(*nu128 as u32), |
| 147 | Self::U64(nu64) => Ok(*nu64 as u32), |
| 148 | Self::U32(nu32) => Ok(*nu32), |
| 149 | } |
| 150 | } |
| 151 | } |