use core::{
fmt,
ops::{
Add,
AddAssign,
},
};
#[derive(Copy, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Key([u64; 4]);
impl Key {
fn write_bytes(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x")?;
for limb in &self.0 {
write!(f, "_")?;
for byte in &limb.to_le_bytes() {
write!(f, "{:02X}", byte)?;
}
}
Ok(())
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Key(")?;
self.write_bytes(f)?;
write!(f, ")")?;
Ok(())
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.write_bytes(f)
}
}
impl From<[u8; 32]> for Key {
#[inline]
fn from(bytes: [u8; 32]) -> Self {
if cfg!(target_endian = "little") {
Self(unsafe { ::core::mem::transmute::<[u8; 32], [u64; 4]>(bytes) })
} else {
Self::from_bytes_be_fallback(bytes)
}
}
}
impl Key {
#[inline]
fn from_bytes_be_fallback(bytes: [u8; 32]) -> Self {
#[inline]
fn carve_out_u64_bytes(bytes: &[u8; 32], offset: u8) -> [u8; 8] {
let o = (offset * 8) as usize;
[
bytes[o],
bytes[o + 1],
bytes[o + 2],
bytes[o + 3],
bytes[o + 4],
bytes[o + 5],
bytes[o + 6],
bytes[o + 7],
]
}
Self([
u64::from_le_bytes(carve_out_u64_bytes(&bytes, 0)),
u64::from_le_bytes(carve_out_u64_bytes(&bytes, 1)),
u64::from_le_bytes(carve_out_u64_bytes(&bytes, 2)),
u64::from_le_bytes(carve_out_u64_bytes(&bytes, 3)),
])
}
pub fn try_as_bytes(&self) -> Option<&[u8; 32]> {
if cfg!(target_endian = "little") {
return Some(self.as_bytes())
}
None
}
#[cfg(target_endian = "little")]
pub fn as_bytes(&self) -> &[u8; 32] {
unsafe { &*(&self.0 as *const [u64; 4] as *const [u8; 32]) }
}
}
impl scale::Encode for Key {
#[inline(always)]
fn size_hint(&self) -> usize {
32
}
#[inline]
fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
if cfg!(target_endian = "little") {
dest.write(self.try_as_bytes().expect("little endian is asserted"))
} else {
dest.write(&self.to_bytes())
}
}
}
impl scale::Decode for Key {
#[inline]
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
Ok(Self::from(<[u8; 32] as scale::Decode>::decode(input)?))
}
}
#[cfg(target_endian = "little")]
impl Key {
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
if cfg!(target_endian = "little") {
unsafe { core::mem::transmute::<[u64; 4], [u8; 32]>(self.0) }
} else {
self.to_bytes_be_fallback()
}
}
fn to_bytes_be_fallback(&self) -> [u8; 32] {
let mut result = [0x00; 32];
for i in 0..4 {
let o = i * 8;
result[o..(o + 8)].copy_from_slice(&self.0[i].to_le_bytes());
}
result
}
}
impl Add<u64> for Key {
type Output = Key;
fn add(mut self, rhs: u64) -> Self::Output {
self += rhs;
self
}
}
impl<'a> Add<u64> for &'a Key {
type Output = Key;
fn add(self, rhs: u64) -> Self::Output {
<Key as Add<u64>>::add(*self, rhs)
}
}
impl<'a> Add<&'a u64> for Key {
type Output = Key;
fn add(self, rhs: &'a u64) -> Self::Output {
<Key as Add<u64>>::add(self, *rhs)
}
}
impl<'a, 'b> Add<&'b u64> for &'a Key {
type Output = Key;
fn add(self, rhs: &'b u64) -> Self::Output {
<&'a Key as Add<u64>>::add(self, *rhs)
}
}
#[cfg(feature = "std")]
const _: () = {
use tetsy_scale_info::{
build::Fields,
Path,
Type,
TypeInfo,
};
impl TypeInfo for Key {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("Key", "pro_primitives"))
.composite(Fields::unnamed().field_of::<[u8; 32]>("[u8; 32]"))
}
}
};
impl AddAssign<u64> for Key {
#[inline]
#[rustfmt::skip]
fn add_assign(&mut self, rhs: u64) {
let (res_0, ovfl_0) = self.0[0].overflowing_add(rhs);
let (res_1, ovfl_1) = self.0[1].overflowing_add(ovfl_0 as u64);
let (res_2, ovfl_2) = self.0[2].overflowing_add(ovfl_1 as u64);
let (res_3, _ovfl_3) = self.0[3].overflowing_add(ovfl_2 as u64);
self.0[0] = res_0;
self.0[1] = res_1;
self.0[2] = res_2;
self.0[3] = res_3;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_bytes() -> [u8; 32] {
*b"\
\x00\x01\x02\x03\x04\x05\x06\x07\
\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\
\x10\x11\x12\x13\x14\x15\x16\x17\
\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\
"
}
#[test]
fn default_works() {
assert_eq!(<Key as Default>::default().to_bytes(), [0x00; 32]);
}
#[test]
fn debug_works() {
let key = Key::from(test_bytes());
assert_eq!(
format!("{:?}", key),
String::from(
"Key(0x\
_0001020304050607\
_08090A0B0C0D0E0F\
_1011121314151617\
_18191A1B1C1D1E1F\
)"
),
);
}
#[test]
#[rustfmt::skip]
fn from_works() {
let test_bytes = test_bytes();
assert_eq!(Key::from(test_bytes).to_bytes(), test_bytes);
assert_eq!(Key::from_bytes_be_fallback(test_bytes).to_bytes(), test_bytes);
assert_eq!(Key::from(test_bytes).to_bytes_be_fallback(), test_bytes);
assert_eq!(Key::from_bytes_be_fallback(test_bytes).to_bytes_be_fallback(), test_bytes);
}
#[test]
fn add_one_to_zero() {
let bytes = [0x00; 32];
let expected = {
let mut bytes = [0x00; 32];
bytes[0] = 0x01;
bytes
};
let mut key = Key::from(bytes);
key.add_assign(1u64);
assert_eq!(key.to_bytes(), expected);
}
#[test]
fn add_with_ovfl() {
let bytes = *b"\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\
";
let expected = {
let mut expected = [0x00; 32];
expected[8] = 0x01;
expected
};
let mut key = Key::from(bytes);
key.add_assign(1u64);
assert_eq!(key.to_bytes(), expected);
}
#[test]
fn add_with_ovfl_2() {
let bytes = *b"\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\
";
let expected = {
let mut expected = [0x00; 32];
expected[16] = 0x01;
expected
};
let mut key = Key::from(bytes);
key.add_assign(1u64);
assert_eq!(key.to_bytes(), expected);
}
#[test]
fn add_with_ovfl_3() {
let bytes = *b"\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\
\x00\x00\x00\x00\x00\x00\x00\x00\
";
let expected = {
let mut expected = [0x00; 32];
expected[24] = 0x01;
expected
};
let mut key = Key::from(bytes);
key.add_assign(1u64);
assert_eq!(key.to_bytes(), expected);
}
#[test]
fn add_with_wrap() {
let bytes = [0xFF; 32];
let expected = [0x00; 32];
let mut key = Key::from(bytes);
key.add_assign(1u64);
assert_eq!(key.to_bytes(), expected);
}
#[test]
fn add_assign_to_zero() {
for test_value in &[0_u64, 1, 42, 10_000, u32::MAX as u64, u64::MAX] {
let mut key = <Key as Default>::default();
let expected = {
let mut expected = [0x00; 32];
expected[0..8].copy_from_slice(&test_value.to_le_bytes());
expected
};
key.add_assign(*test_value);
assert_eq!(key.to_bytes(), expected);
}
}
}