#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(trivial_numeric_casts)]
#![warn(unreachable_pub)]
#![warn(unused_qualifications)]
mod hash;
mod tests;
#[cfg(feature = "image")]
mod img;
use core::fmt::{self, Display, Formatter};
use core::str::FromStr;
use hash::blockhash;
fn distance<const SIZE: usize>(left: &[u8; SIZE], right: &[u8; SIZE]) -> u32 {
let mut dist = 0;
for i in 0..SIZE {
dist += (left[i] ^ right[i]).count_ones();
}
dist
}
fn parse_char(c: u8) -> Result<u8, BlockhashParseError> {
let val = match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - (b'a' - 10),
b'A'..=b'F' => c - (b'A' - 10),
_ => return Err(BlockhashParseError),
};
Ok(val)
}
fn parse_hash<const SIZE: usize>(s: &str) -> Result<[u8; SIZE], BlockhashParseError> {
let s = s.as_bytes();
if s.len() != SIZE * 2 {
return Err(BlockhashParseError);
}
let mut bytes = [0; SIZE];
for i in 0..SIZE {
bytes[i] = (parse_char(s[2 * i])? << 4) | parse_char(s[2 * i + 1])?;
}
Ok(bytes)
}
fn fmt_hash<const SIZE: usize>(f: &mut Formatter, hash: [u8; SIZE]) -> fmt::Result {
for byte in hash {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BlockhashParseError;
impl Display for BlockhashParseError {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("invalid hash string")
}
}
#[cfg(feature = "std")]
impl std::error::Error for BlockhashParseError {}
#[inline]
#[must_use]
pub fn blockhash16<I: Image>(img: &I) -> Blockhash16 {
Blockhash16(blockhash::<I, 4, 16, 2>(img))
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Blockhash16([u8; 2]);
impl Blockhash16 {
#[inline]
#[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn distance(&self, other: &Self) -> u32 {
distance(&self.0, &other.0)
}
}
impl FromStr for Blockhash16 {
type Err = BlockhashParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_hash(s).map(Self)
}
}
impl Display for Blockhash16 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_hash(f, self.0)
}
}
impl From<[u8; 2]> for Blockhash16 {
#[inline]
fn from(bytes: [u8; 2]) -> Self {
Blockhash16(bytes)
}
}
impl From<Blockhash16> for [u8; 2] {
#[inline]
fn from(hash: Blockhash16) -> Self {
hash.0
}
}
impl From<u16> for Blockhash16 {
#[inline]
fn from(int: u16) -> Self {
Blockhash16(int.to_be_bytes())
}
}
impl From<Blockhash16> for u16 {
#[inline]
fn from(hash: Blockhash16) -> Self {
u16::from_be_bytes(hash.0)
}
}
#[inline]
#[must_use]
pub fn blockhash64<I: Image>(img: &I) -> Blockhash64 {
Blockhash64(blockhash::<I, 8, 64, 8>(img))
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Blockhash64([u8; 8]);
impl Blockhash64 {
#[inline]
#[must_use]
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn distance(&self, other: &Self) -> u32 {
distance(&self.0, &other.0)
}
}
impl FromStr for Blockhash64 {
type Err = BlockhashParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_hash(s).map(Self)
}
}
impl Display for Blockhash64 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_hash(f, self.0)
}
}
impl From<[u8; 8]> for Blockhash64 {
#[inline]
fn from(bytes: [u8; 8]) -> Self {
Blockhash64(bytes)
}
}
impl From<Blockhash64> for [u8; 8] {
#[inline]
fn from(hash: Blockhash64) -> Self {
hash.0
}
}
impl From<u64> for Blockhash64 {
#[inline]
fn from(int: u64) -> Self {
Blockhash64(int.to_be_bytes())
}
}
impl From<Blockhash64> for u64 {
#[inline]
fn from(hash: Blockhash64) -> Self {
u64::from_be_bytes(hash.0)
}
}
#[inline]
#[must_use]
pub fn blockhash144<I: Image>(img: &I) -> Blockhash144 {
Blockhash144(blockhash::<I, 12, 144, 18>(img))
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Blockhash144([u8; 18]);
impl Blockhash144 {
#[inline]
#[must_use]
pub fn distance(&self, other: &Self) -> u32 {
distance(&self.0, &other.0)
}
}
impl FromStr for Blockhash144 {
type Err = BlockhashParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_hash(s).map(Self)
}
}
impl Display for Blockhash144 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_hash(f, self.0)
}
}
impl From<[u8; 18]> for Blockhash144 {
#[inline]
fn from(bytes: [u8; 18]) -> Self {
Blockhash144(bytes)
}
}
impl From<Blockhash144> for [u8; 18] {
#[inline]
fn from(hash: Blockhash144) -> Self {
hash.0
}
}
#[inline]
#[must_use]
pub fn blockhash256<I: Image>(img: &I) -> Blockhash256 {
Blockhash256(blockhash::<I, 16, 256, 32>(img))
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Blockhash256([u8; 32]);
impl Blockhash256 {
#[inline]
#[must_use]
pub fn distance(&self, other: &Self) -> u32 {
distance(&self.0, &other.0)
}
}
impl FromStr for Blockhash256 {
type Err = BlockhashParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_hash(s).map(Self)
}
}
impl Display for Blockhash256 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt_hash(f, self.0)
}
}
impl From<[u8; 32]> for Blockhash256 {
#[inline]
fn from(bytes: [u8; 32]) -> Self {
Blockhash256(bytes)
}
}
impl From<Blockhash256> for [u8; 32] {
#[inline]
fn from(hash: Blockhash256) -> Self {
hash.0
}
}
pub trait Image {
const MAX_BRIGHTNESS: u32;
fn dimensions(&self) -> (u32, u32);
fn brightness(&self, x: u32, y: u32) -> u32;
}