use crate::ion_hash_encoder::encode_value;
use crate::IonValue;
use digest::Digest;
use sha2::Sha256;
use std::cmp::{Ordering, PartialEq};
use std::marker::PhantomData;
#[derive(Debug)]
pub struct IonHash<D: Digest = Sha256> {
buffer: Vec<u8>,
hasher_type: PhantomData<D>,
}
impl<D: Digest> IonHash<D> {
pub fn add_bytes(&mut self, value: &[u8]) {
let value = IonHash::from_bytes::<D>(value);
self.dot(value);
}
pub fn add_hashed_bytes(&mut self, value: &[u8]) {
let value = IonHash::from_hashes_bytes::<D>(value);
self.dot(value);
}
pub fn add_ion_value(&mut self, value: &IonValue) {
let buffer = encode_value::<D>(value);
let value = IonHash::from_bytes::<D>(&buffer);
self.dot(value);
}
pub fn dot(&mut self, value: IonHash<D>) -> &mut Self {
if value.buffer.is_empty() {
return self;
}
if self.buffer.is_empty() {
self.buffer = value.buffer;
return self;
}
let mut buffer: Vec<u8> = vec![];
if *self < value {
buffer.extend(self.get());
buffer.extend(value.get());
} else {
buffer.extend(value.get());
buffer.extend(self.get());
}
self.buffer = D::digest(&buffer).to_vec();
self
}
pub fn get(&self) -> &[u8] {
&self.buffer
}
}
impl IonHash {
pub fn new() -> IonHash {
IonHash {
buffer: vec![],
hasher_type: PhantomData,
}
}
pub fn from_bytes<D: Digest>(buf: &[u8]) -> IonHash<D> {
let hased_bytes = D::digest(buf);
IonHash::from_hashes_bytes(&hased_bytes)
}
pub fn from_hashes_bytes<D: Digest>(buf: &[u8]) -> IonHash<D> {
IonHash {
buffer: buf.to_vec(),
hasher_type: PhantomData,
}
}
pub fn from_ion_value<D: Digest>(value: &IonValue) -> IonHash<D> {
let mut hash = IonHash::with_hasher::<D>();
hash.add_ion_value(value);
hash
}
pub fn with_hasher<D: Digest>() -> IonHash<D> {
IonHash {
buffer: vec![],
hasher_type: PhantomData,
}
}
pub fn digest<D: Digest>(value: &IonValue) -> Vec<u8> {
IonHash::from_ion_value::<D>(value).get().to_vec()
}
pub fn default_digest(value: &IonValue) -> Vec<u8> {
IonHash::from_ion_value::<Sha256>(value).get().to_vec()
}
}
impl Default for IonHash {
fn default() -> IonHash<Sha256> {
IonHash::with_hasher::<Sha256>()
}
}
impl<D: Digest> PartialEq for IonHash<D> {
fn eq(&self, _: &IonHash<D>) -> bool {
self.buffer == self.get()
}
}
impl<D: Digest> PartialOrd for IonHash<D> {
fn partial_cmp(&self, value: &IonHash<D>) -> Option<Ordering> {
self.buffer
.iter()
.rev()
.map(|byte| *byte as i8)
.partial_cmp(value.get().iter().rev().map(|byte| *byte as i8))
}
}