use alloc::string::ToString as _;
use core::fmt::Write as _;
#[derive(Clone, PartialEq, Eq, Hash, Copy)]
#[repr(C)]
pub struct JobId(pub [u64; 4]);
const _: () = {
assert!(
core::mem::size_of::<JobId>() == core::mem::size_of::<[u64; 4]>(),
"JobId size changed, update transmutes"
);
};
impl core::fmt::Debug for JobId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("JobId").field(&self.to_string()).finish()
}
}
impl core::fmt::Display for JobId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for limb in &self.0 {
write!(f, "{limb:016x}")?;
}
Ok(())
}
}
impl Default for JobId {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl JobId {
pub const ZERO: Self = Self([0; 4]);
pub const MIN: Self = Self::ZERO;
pub const MAX: Self = {
let mut limbs = [u64::MAX; 4];
limbs[4 - 1] &= u64::MAX;
Self(limbs)
};
}
macro_rules! impl_from_numbers {
(
$($ty:ty),*
) => {
$(
impl From<$ty> for JobId {
#[inline]
fn from(value: $ty) -> Self {
let mut id = [0; 4];
id[3] = value as u64;
Self(id)
}
}
impl From<&$ty> for JobId {
#[inline]
fn from(value: &$ty) -> Self {
Self::from(*value)
}
}
impl From<JobId> for $ty {
#[inline]
fn from(value: JobId) -> Self {
value.0[3] as $ty
}
}
)*
};
($(wide $ty:ty),*) => {
$(
impl From<$ty> for JobId {
#[inline]
fn from(value: $ty) -> Self {
let mut id = [0; 4];
id[3] = value as u64;
id[2] = (value >> 64) as u64;
Self(id)
}
}
impl From<&$ty> for JobId {
#[inline]
fn from(value: &$ty) -> Self {
Self::from(*value)
}
}
impl From<JobId> for $ty {
#[inline]
fn from(value: JobId) -> Self {
(value.0[2] as $ty) << 64 | value.0[3] as $ty
}
}
)*
};
}
impl_from_numbers!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);
impl_from_numbers!(wide u128, wide i128);
impl From<[u8; 32]> for JobId {
#[inline]
fn from(value: [u8; 32]) -> Self {
Self([
u64::from_le_bytes([
value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7],
]),
u64::from_le_bytes([
value[8], value[9], value[10], value[11], value[12], value[13], value[14],
value[15],
]),
u64::from_le_bytes([
value[16], value[17], value[18], value[19], value[20], value[21], value[22],
value[23],
]),
u64::from_le_bytes([
value[24], value[25], value[26], value[27], value[28], value[29], value[30],
value[31],
]),
])
}
}
impl From<JobId> for [u8; 32] {
#[inline]
fn from(value: JobId) -> Self {
let a = value.0[0].to_le_bytes();
let b = value.0[1].to_le_bytes();
let c = value.0[2].to_le_bytes();
let d = value.0[3].to_le_bytes();
[
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], b[0], b[1], b[2], b[3], b[4], b[5],
b[6], b[7], c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], d[0], d[1], d[2], d[3],
d[4], d[5], d[6], d[7],
]
}
}
impl From<&[u8]> for JobId {
#[inline]
fn from(value: &[u8]) -> Self {
use tiny_keccak::Hasher;
let mut hasher = tiny_keccak::Keccak::v256();
hasher.update(value);
let mut out = [0u8; 32];
hasher.finalize(&mut out);
Self::from(out)
}
}
impl From<&str> for JobId {
#[inline]
fn from(value: &str) -> Self {
Self::from(value.as_bytes())
}
}
impl From<alloc::string::String> for JobId {
#[inline]
fn from(value: alloc::string::String) -> Self {
Self::from(value.as_str())
}
}
impl From<JobId> for alloc::string::String {
#[inline]
fn from(value: JobId) -> Self {
let hash: [u8; 32] = value.into();
let mut result = alloc::string::String::with_capacity(64);
for byte in &hash {
let _ = write!(result, "{byte:02x}");
}
result
}
}
impl From<&alloc::string::String> for JobId {
#[inline]
fn from(value: &alloc::string::String) -> Self {
Self::from(value.as_str())
}
}
impl From<alloc::vec::Vec<u8>> for JobId {
#[inline]
fn from(value: alloc::vec::Vec<u8>) -> Self {
Self::from(&value)
}
}
impl From<JobId> for alloc::vec::Vec<u8> {
#[inline]
fn from(value: JobId) -> Self {
let hash: [u8; 32] = value.into();
hash.to_vec()
}
}
impl From<&alloc::vec::Vec<u8>> for JobId {
#[inline]
fn from(value: &alloc::vec::Vec<u8>) -> Self {
Self::from(value.as_slice())
}
}
impl From<()> for JobId {
#[inline]
fn from((): ()) -> Self {
Self::ZERO
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_from_into_numbers {
($($ty:ty),*) => {
$(
let num: $ty = 42;
let job_id = JobId::from(num);
assert_eq!(job_id, JobId::from(num));
assert_eq!(num, <$ty>::from(job_id));
let num: $ty = <$ty>::MAX;
let job_id = JobId::from(num);
assert_eq!(job_id, JobId::from(num));
assert_eq!(num, <$ty>::from(job_id));
let num: $ty = <$ty>::MIN;
let job_id = JobId::from(num);
assert_eq!(job_id, JobId::from(num));
assert_eq!(num, <$ty>::from(job_id));
)*
};
}
#[test]
fn job_id_works() {
let id = JobId::from("test");
assert_eq!(id, JobId::from("test"));
assert_ne!(id, JobId::from("test2"));
}
#[test]
fn it_works_with_numbers() {
test_from_into_numbers!(
u8, i8, u16, i16, u32, i32, u64, i64, usize, isize, u128, i128
);
}
}