1use core::marker::PhantomData;
7use core::ops::Index;
8use core::slice::SliceIndex;
9use core::{cmp, str};
10
11use crate::{sha256, FromSliceError};
12
13type HashEngine = sha256::HashEngine;
14
15pub trait Tag {
17 fn engine() -> sha256::HashEngine;
19}
20
21#[repr(transparent)]
23pub struct Hash<T: Tag>([u8; 32], PhantomData<T>);
24
25#[cfg(feature = "schemars")]
26impl<T: Tag> schemars::JsonSchema for Hash<T> {
27 fn schema_name() -> String { "Hash".to_owned() }
28
29 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
30 let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
31 schema.string = Some(Box::new(schemars::schema::StringValidation {
32 max_length: Some(32 * 2),
33 min_length: Some(32 * 2),
34 pattern: Some("[0-9a-fA-F]+".to_owned()),
35 }));
36 schema.into()
37 }
38}
39
40impl<T: Tag> Hash<T> {
41 fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) }
42
43 fn internal_engine() -> HashEngine { T::engine() }
44}
45
46impl<T: Tag> Copy for Hash<T> {}
47impl<T: Tag> Clone for Hash<T> {
48 fn clone(&self) -> Self { *self }
49}
50impl<T: Tag> PartialEq for Hash<T> {
51 fn eq(&self, other: &Hash<T>) -> bool { self.0 == other.0 }
52}
53impl<T: Tag> Eq for Hash<T> {}
54impl<T: Tag> Default for Hash<T> {
55 fn default() -> Self { Hash([0; 32], PhantomData) }
56}
57impl<T: Tag> PartialOrd for Hash<T> {
58 fn partial_cmp(&self, other: &Hash<T>) -> Option<cmp::Ordering> {
59 Some(cmp::Ord::cmp(self, other))
60 }
61}
62impl<T: Tag> Ord for Hash<T> {
63 fn cmp(&self, other: &Hash<T>) -> cmp::Ordering { cmp::Ord::cmp(&self.0, &other.0) }
64}
65impl<T: Tag> core::hash::Hash for Hash<T> {
66 fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.0.hash(h) }
67}
68
69crate::internal_macros::hash_trait_impls!(256, true, T: Tag);
70
71fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
72 use crate::Hash as _;
73
74 Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array())
75}
76
77#[macro_export]
115macro_rules! sha256t_hash_newtype {
116 ($($(#[$($tag_attr:tt)*])* $tag_vis:vis struct $tag:ident = $constructor:tt($($tag_value:tt)+); $(#[$($hash_attr:tt)*])* $hash_vis:vis struct $hash_name:ident($(#[$($field_attr:tt)*])* _);)+) => {
117 $(
118 $crate::sha256t_hash_newtype_tag!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*);
119
120 impl $crate::sha256t::Tag for $tag {
121 #[inline]
122 fn engine() -> $crate::sha256::HashEngine {
123 const MIDSTATE: ($crate::sha256::Midstate, usize) = $crate::sha256t_hash_newtype_tag_constructor!($constructor, $($tag_value)+);
124 #[allow(unused)]
125 const _LENGTH_CHECK: () = [(); 1][MIDSTATE.1 % 64];
126 $crate::sha256::HashEngine::from_midstate(MIDSTATE.0, MIDSTATE.1)
127 }
128 }
129
130 $crate::hash_newtype! {
131 $(#[$($hash_attr)*])*
132 $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>);
133 }
134 )+
135 }
136}
137
138#[doc(hidden)]
140#[macro_export]
141macro_rules! sha256t_hash_newtype_tag {
142 ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => {
143 #[doc = "The tag used for [`"]
144 #[doc = $name]
145 #[doc = "`]\n\n"]
146 $(#[$($attr)*])*
147 #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
148 $vis struct $tag;
149 };
150}
151
152#[doc(hidden)]
153#[macro_export]
154macro_rules! sha256t_hash_newtype_tag_constructor {
155 (hash_str, $value:expr) => {
156 ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64)
157 };
158 (hash_bytes, $value:expr) => {
159 ($crate::sha256::Midstate::hash_tag($value), 64)
160 };
161 (raw, $bytes:expr, $len:expr) => {
162 ($crate::sha256::Midstate::from_byte_array($bytes), $len)
163 };
164}
165
166#[cfg(test)]
167mod tests {
168 #[cfg(feature = "alloc")]
169 use crate::Hash;
170 use crate::{sha256, sha256t};
171
172 const TEST_MIDSTATE: [u8; 32] = [
173 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147,
174 108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201,
175 ];
176
177 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
178 pub struct TestHashTag;
179
180 impl sha256t::Tag for TestHashTag {
181 fn engine() -> sha256::HashEngine {
182 let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
184 sha256::HashEngine::from_midstate(midstate, 64)
185 }
186 }
187
188 #[cfg(feature = "schemars")]
189 impl schemars::JsonSchema for TestHashTag {
190 fn schema_name() -> String { "Hash".to_owned() }
191
192 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
193 let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
194 schema.string = Some(Box::new(schemars::schema::StringValidation {
195 max_length: Some(64 * 2),
196 min_length: Some(64 * 2),
197 pattern: Some("[0-9a-fA-F]+".to_owned()),
198 }));
199 schema.into()
200 }
201 }
202
203 #[cfg(feature = "alloc")]
205 pub type TestHash = sha256t::Hash<TestHashTag>;
206
207 sha256t_hash_newtype! {
208 struct NewTypeTag = raw(TEST_MIDSTATE, 64);
210
211 #[hash_newtype(backward)]
213 struct NewTypeHash(_);
214 }
215
216 #[test]
217 #[cfg(feature = "alloc")]
218 fn test_sha256t() {
219 assert_eq!(
220 TestHash::hash(&[0]).to_string(),
221 "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
222 );
223 assert_eq!(
224 NewTypeHash::hash(&[0]).to_string(),
225 "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
226 );
227 }
228}