use std::{
cmp::Ordering,
hash::{Hash, Hasher},
path::Path,
};
use ahash::AHasher;
use crate::{
delta,
internal::{
metadata::{EntryMeta, MetaAttached},
pack::entry::Entry,
},
};
#[inline]
fn content_sort_key(data: &[u8]) -> u64 {
let len = data.len().min(8);
let mut key: u64 = 0;
for &byte in &data[..len] {
key = (key << 8) | byte as u64;
}
key <<= (8 - len) * 8;
key
}
pub(crate) fn magic_sort(
a: &MetaAttached<Entry, EntryMeta>,
b: &MetaAttached<Entry, EntryMeta>,
) -> Ordering {
let path_a = a.meta.file_path.as_ref();
let path_b = b.meta.file_path.as_ref();
match (path_a, path_b) {
(Some(pa), Some(pb)) => {
let pa = Path::new(pa);
let pb = Path::new(pb);
let dir_ord = pa.parent().cmp(&pb.parent());
if dir_ord != Ordering::Equal {
return dir_ord;
}
let hash_a = delta::pack_name_hash(pa.as_os_str().as_encoded_bytes());
let hash_b = delta::pack_name_hash(pb.as_os_str().as_encoded_bytes());
if hash_a != hash_b {
return hash_b.cmp(&hash_a); }
}
(Some(_), None) => return Ordering::Less,
(None, Some(_)) => return Ordering::Greater,
(None, None) => {
let key_a = content_sort_key(&a.inner.data);
let key_b = content_sort_key(&b.inner.data);
let ord = key_a.cmp(&key_b);
if ord != Ordering::Equal {
return ord;
}
}
}
let ord = b.inner.data.len().cmp(&a.inner.data.len());
if ord != Ordering::Equal {
return ord;
}
(a as *const MetaAttached<Entry, EntryMeta>).cmp(&(b as *const MetaAttached<Entry, EntryMeta>))
}
pub(crate) fn calc_hash(data: &[u8]) -> u64 {
let mut hasher = AHasher::default();
data.hash(&mut hasher);
hasher.finish()
}
#[cfg(feature = "diff_rabin")]
pub(crate) fn multi_point_similar(a: &[u8], b: &[u8]) -> bool {
let min_len = a.len().min(b.len());
if min_len < 16 {
return false;
}
let head_len = 128.min(min_len);
if calc_hash(&a[..head_len]) == calc_hash(&b[..head_len]) {
return true;
}
let tail_start = min_len.saturating_sub(128);
if calc_hash(&a[tail_start..min_len]) == calc_hash(&b[tail_start..min_len]) {
return true;
}
false
}
#[cfg(not(feature = "diff_rabin"))]
pub(crate) fn cheap_similar(a: &[u8], b: &[u8]) -> bool {
let k = a.len().min(b.len()).min(128);
if k == 0 {
return false;
}
calc_hash(&a[..k]) == calc_hash(&b[..k])
}