use alloc::vec::Vec;
use core::cmp::Ordering;
use core::convert::{TryFrom, TryInto};
use core::num::Wrapping;
use core::ops::Deref;
use crate::encoding::encode_var_int;
use crate::{sha256, Error, Id, FINGERPRINT_SIZE, ID_SIZE};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(crate) enum Mode {
Skip = 0,
Fingerprint = 1,
IdList = 2,
}
impl Mode {
pub fn as_u64(&self) -> u64 {
*self as u64
}
}
impl TryFrom<u64> for Mode {
type Error = Error;
fn try_from(mode: u64) -> Result<Self, Self::Error> {
match mode {
0 => Ok(Mode::Skip),
1 => Ok(Mode::Fingerprint),
2 => Ok(Mode::IdList),
m => Err(Error::UnexpectedMode(m)),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Item {
pub timestamp: u64,
pub id: Id,
}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Item {
fn cmp(&self, other: &Self) -> Ordering {
if self.timestamp != other.timestamp {
self.timestamp.cmp(&other.timestamp)
} else {
self.id.cmp(&other.id)
}
}
}
impl Item {
pub fn new() -> Self {
Self::default()
}
pub fn with_timestamp(timestamp: u64) -> Self {
let mut item = Self::new();
item.timestamp = timestamp;
item
}
#[inline]
pub fn with_timestamp_and_id(timestamp: u64, id: Id) -> Self {
Self { timestamp, id }
}
pub fn get_id(&self) -> &Id {
&self.id
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Bound {
pub item: Item,
pub id_len: usize,
}
impl Bound {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub fn from_item(item: &Item) -> Self {
let mut bound = Self::new();
bound.item = *item;
bound.id_len = ID_SIZE;
bound
}
pub fn with_timestamp(timestamp: u64) -> Self {
let mut bound = Self::new();
bound.item.timestamp = timestamp;
bound.id_len = 0;
bound
}
pub fn with_timestamp_and_id<T>(timestamp: u64, id: T) -> Result<Self, Error>
where
T: AsRef<[u8]>,
{
let id: &[u8] = id.as_ref();
let len: usize = id.len();
if len > ID_SIZE {
return Err(Error::IdTooBig);
}
let mut out = Bound::new();
out.item.timestamp = timestamp;
out.item.id[..len].copy_from_slice(id);
out.id_len = len;
Ok(out)
}
}
impl PartialOrd for Bound {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Bound {
fn cmp(&self, other: &Self) -> Ordering {
self.item.cmp(&other.item)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Fingerprint {
buf: [u8; FINGERPRINT_SIZE],
}
impl Deref for Fingerprint {
type Target = [u8; FINGERPRINT_SIZE];
fn deref(&self) -> &Self::Target {
&self.buf
}
}
impl Fingerprint {
#[inline]
pub fn to_bytes(self) -> [u8; FINGERPRINT_SIZE] {
self.buf
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Accumulator {
buf: [u8; ID_SIZE],
}
impl Accumulator {
pub fn new() -> Self {
Self { buf: [0; ID_SIZE] }
}
pub fn add(&mut self, buf: &[u8; ID_SIZE]) -> Result<(), Error> {
let mut curr_carry = Wrapping(0u64);
let mut next_carry = Wrapping(0u64);
let p = &self.buf[..];
let po = buf;
let mut wtr = Vec::with_capacity(ID_SIZE);
for i in 0..4 {
let orig = Wrapping(u64::from_le_bytes(p[(i * 8)..(i * 8 + 8)].try_into()?));
let other_v = Wrapping(u64::from_le_bytes(po[(i * 8)..(i * 8 + 8)].try_into()?));
let mut next = orig;
next += curr_carry;
if next < orig {
next_carry = Wrapping(1u64);
}
next += other_v;
if next < other_v {
next_carry = Wrapping(1u64);
}
wtr.extend_from_slice(&next.0.to_le_bytes());
curr_carry = next_carry;
next_carry = Wrapping(0u64);
}
self.buf.copy_from_slice(&wtr);
Ok(())
}
pub fn get_fingerprint(&self, n: u64) -> Result<Fingerprint, Error> {
let var_int: Vec<u8> = encode_var_int(n);
let mut input: Vec<u8> = Vec::with_capacity(ID_SIZE + var_int.len());
input.extend(&self.buf);
input.extend(var_int);
let hash: [u8; 32] = sha256::hash(input);
Ok(Fingerprint {
buf: hash[0..FINGERPRINT_SIZE].try_into()?,
})
}
}