use crate::prelude::*;
use crate::{EntryError, IndexType, MrkleNode};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
#[allow(non_camel_case_types)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct entry {
bytes: [u8],
}
impl entry {
#[inline]
pub fn try_from_bytes(digest: &[u8]) -> Result<&Self, EntryError> {
match digest.len() {
16 | 20 | 28 | 32 | 48 | 64 => Ok(
#[allow(unsafe_code)]
unsafe {
&*(digest as *const [u8] as *const entry)
},
),
len => Err(EntryError::InvalidByteSliceLength(len)),
}
}
#[inline]
pub fn from_bytes_unchecked(value: &[u8]) -> &Self {
Self::from_bytes(value)
}
#[inline]
pub(crate) fn from_bytes(value: &[u8]) -> &Self {
#[allow(unsafe_code)]
unsafe {
&*(value as *const [u8] as *const entry)
}
}
}
impl entry {
#[inline]
pub fn first_byte(&self) -> u8 {
self.bytes[0]
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[inline]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl AsRef<entry> for &entry {
fn as_ref(&self) -> &entry {
self
}
}
impl<'a> TryFrom<&'a [u8]> for &'a entry {
type Error = EntryError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
entry::try_from_bytes(value)
}
}
impl<T, D: Digest, Ix: IndexType> PartialEq<MrkleNode<T, D, Ix>> for &entry {
fn eq(&self, other: &MrkleNode<T, D, Ix>) -> bool {
*self == other.as_ref()
}
}
impl entry {
#[inline]
#[must_use]
pub fn hex_to_buffer<'a>(&self, buffer: &'a mut [u8]) -> &'a mut str {
let num_hex_bytes = self.bytes.len() * 2;
for (i, &byte) in self.bytes.iter().enumerate() {
let hex_chars = format!("{:02x}", byte);
buffer[i * 2] = hex_chars.as_bytes()[0];
buffer[i * 2 + 1] = hex_chars.as_bytes()[1];
}
str::from_utf8_mut(&mut buffer[..num_hex_bytes]).expect("hex digits are valid UTF-8")
}
#[inline]
#[cfg(feature = "std")]
pub fn hex_to_writer<W: std::io::Write>(&self, out: &mut W) -> std::io::Result<()> {
let mut hex_buf = vec![0u8; self.bytes.len() * 2];
let hex_str = self.hex_to_buffer(&mut hex_buf);
out.write_all(hex_str.as_bytes())
}
#[inline]
pub fn to_hex_with_len(&self, len: usize) -> HexDisplay<'_> {
HexDisplay {
inner: self,
size: len.min(self.bytes.len() * 2), }
}
#[inline]
pub fn to_hex(&self) -> HexDisplay<'_> {
HexDisplay {
inner: self,
size: self.bytes.len() * 2,
}
}
}
#[must_use]
pub struct HexDisplay<'id> {
inner: &'id entry,
size: usize,
}
impl core::fmt::Debug for HexDisplay<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"HexDisplay {{ len : {:?} slice : {:?} }}",
self.size, self.inner
)
}
}
impl core::fmt::Display for HexDisplay<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut buf = vec![0u8; self.size];
let hex_str = if self.size <= self.inner.bytes.len() * 2 {
let truncated_bytes = self.size / 2;
let temp_entry = entry::from_bytes(&self.inner.bytes[..truncated_bytes]);
temp_entry.hex_to_buffer(&mut buf)
} else {
self.inner.hex_to_buffer(&mut buf)
};
f.write_str(&hex_str[..self.size.min(hex_str.len())])
}
}
impl core::fmt::Display for entry {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "{}", self.to_hex())
}
}
#[cfg(feature = "serde")]
impl<'de: 'a, 'a> serde::Deserialize<'de> for &'a entry {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
{
struct __Visitor<'de: 'a, 'a> {
marker: core::marker::PhantomData<&'a entry>,
lifetime: core::marker::PhantomData<&'de ()>,
}
impl<'de: 'a, 'a> serde::de::Visitor<'de> for __Visitor<'de, 'a> {
type Value = &'a entry;
fn expecting(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Formatter::write_str(__formatter, "tuple struct Digest")
}
#[inline]
fn visit_newtype_struct<__E>(
self,
__e: __E,
) -> core::result::Result<Self::Value, __E::Error>
where
__E: serde::Deserializer<'de>,
{
let __field0: &'a [u8] = match <&'a [u8] as serde::Deserialize>::deserialize(__e) {
Ok(__val) => __val,
Err(__err) => {
return Err(__err);
}
};
Ok(entry::try_from_bytes(__field0).expect("hash of known length"))
}
#[inline]
fn visit_seq<__A>(self, mut __seq: __A) -> core::result::Result<Self::Value, __A::Error>
where
__A: serde::de::SeqAccess<'de>,
{
let __field0 =
match match serde::de::SeqAccess::next_element::<&'a [u8]>(&mut __seq) {
Ok(__val) => __val,
Err(__err) => {
return Err(__err);
}
} {
Some(__value) => __value,
None => {
return Err(serde::de::Error::invalid_length(
0usize,
&"tuple struct Digest with 1 element",
));
}
};
Ok(entry::try_from_bytes(__field0).expect("hash of known length"))
}
}
serde::Deserializer::deserialize_newtype_struct(
deserializer,
"Digest",
__Visitor {
marker: core::marker::PhantomData::<&'a entry>,
lifetime: core::marker::PhantomData,
},
)
}
}
#[cfg(test)]
mod test {
use super::*;
use crypto::digest::Digest;
#[test]
fn test_entry_creation() {
let digest20 = vec![0u8; 20]; let e20 = entry::try_from_bytes(&digest20).unwrap();
assert_eq!(e20.len(), 20);
let digest32 = vec![0u8; 32]; let e32 = entry::try_from_bytes(&digest32).unwrap();
assert_eq!(e32.len(), 32);
let invalid = vec![0u8; 15];
assert!(entry::try_from_bytes(&invalid).is_err());
}
#[cfg(feature = "std")]
#[test]
fn test_hex_display() {
let digest = vec![0xde, 0xad, 0xbe, 0xef];
let e = entry::from_bytes_unchecked(&digest);
let hex_full = format!("{}", e.to_hex());
assert_eq!(hex_full, "deadbeef");
let hex_partial = format!("{}", e.to_hex_with_len(4)); assert_eq!(hex_partial, "dead");
}
#[test]
fn test_actual_hash() {
use sha1::Sha1;
let src = Sha1::digest(b"hello world");
let mut result = [0u8; 20];
result.copy_from_slice(&src);
let e = entry::try_from_bytes(&result).unwrap();
assert_eq!(e.len(), 20);
assert_eq!(e.first_byte(), result[0]);
}
#[test]
fn test_with_sha256() {
use sha2::Sha256;
let src = Sha256::digest(b"test data");
let mut result = [0u8; 32];
result.copy_from_slice(&src);
let e = entry::try_from_bytes(&result).unwrap();
assert_eq!(e.len(), 32);
}
}