Warning removal.

This commit is contained in:
Adam Ierymenko 2021-12-22 21:15:39 -05:00
parent e55d3e4d4b
commit b0f7cc1238
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3

View file

@ -6,12 +6,13 @@
* https://www.zerotier.com/ * https://www.zerotier.com/
*/ */
use std::mem::zeroed; use std::mem::size_of;
use std::ptr::{slice_from_raw_parts, write_bytes, copy_nonoverlapping};
use crate::IDENTITY_HASH_SIZE; use crate::IDENTITY_HASH_SIZE;
// The number of indexing sub-hashes to use, must be <= IDENTITY_HASH_SIZE / 8 // The number of indexing sub-hashes to use, must be <= IDENTITY_HASH_SIZE / 8
const KEY_MAPPING_ITERATIONS: usize = IDENTITY_HASH_SIZE / 8; const KEY_MAPPING_ITERATIONS: usize = 3;
#[inline(always)] #[inline(always)]
fn xorshift64(mut x: u64) -> u64 { fn xorshift64(mut x: u64) -> u64 {
@ -28,15 +29,11 @@ struct IBLTEntry {
count: i64, count: i64,
} }
impl Default for IBLTEntry {
fn default() -> Self { unsafe { zeroed() } }
}
/// An IBLT (invertible bloom lookup table) specialized for reconciling sets of identity hashes. /// An IBLT (invertible bloom lookup table) specialized for reconciling sets of identity hashes.
/// This skips some extra hashing that would be necessary in a universal implementation since identity /// This skips some extra hashing that would be necessary in a universal implementation since identity
/// hashes are already randomly distributed strong hashes. /// hashes are already randomly distributed strong hashes.
pub struct IBLT { pub struct IBLT {
map: Box<[IBLTEntry]>, map: Vec<IBLTEntry>,
} }
impl IBLTEntry { impl IBLTEntry {
@ -53,21 +50,49 @@ impl IBLTEntry {
impl IBLT { impl IBLT {
/// Construct a new IBLT with a given capacity. /// Construct a new IBLT with a given capacity.
pub fn new(buckets: usize) -> Self { pub fn new(buckets: usize) -> Self {
assert!(KEY_MAPPING_ITERATIONS <= (IDENTITY_HASH_SIZE / 8) && (IDENTITY_HASH_SIZE % 8) == 0);
assert!(buckets > 0); assert!(buckets > 0);
Self { Self {
map: { map: {
let mut tmp = Vec::new(); let mut tmp: Vec<IBLTEntry> = Vec::with_capacity(buckets);
tmp.resize_with(buckets, IBLTEntry::default); unsafe {
tmp.into_boxed_slice() tmp.set_len(buckets);
write_bytes(tmp.as_mut_ptr().cast::<u8>(), 0, buckets * size_of::<IBLTEntry>());
}
tmp
} }
} }
} }
/// Obtain IBLT from a byte array.
/// This returns None if the supplied bytes are not a valid IBLT.
pub fn from_bytes(b: &[u8]) -> Option<Self> {
if b.len() >= size_of::<IBLTEntry>() && (b.len() % size_of::<IBLTEntry>()) == 0 {
let buckets = b.len() / size_of::<IBLTEntry>();
Some(Self {
map: {
let mut tmp: Vec<IBLTEntry> = Vec::with_capacity(buckets);
unsafe {
tmp.set_len(buckets);
copy_nonoverlapping(b.as_ptr(), tmp.as_mut_ptr().cast::<u8>(), buckets * size_of::<IBLTEntry>());
}
tmp
}
})
} else {
None
}
}
/// Get this IBLT as a byte array that is ready to be sent over the wire.
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*slice_from_raw_parts(self.map.as_ptr().cast(), size_of::<IBLTEntry>() * self.map.len()) }
}
fn ins_rem(&mut self, key: &[u64; IDENTITY_HASH_SIZE / 8], delta: i64) { fn ins_rem(&mut self, key: &[u64; IDENTITY_HASH_SIZE / 8], delta: i64) {
let check_hash = u64::from_le(key[0]).wrapping_add(xorshift64(u64::from_le(key[1]))).to_le(); let check_hash = u64::from_le(key[0]).wrapping_add(xorshift64(u64::from_le(key[1]))).to_le();
let buckets = self.map.len();
for mapping_sub_hash in 0..KEY_MAPPING_ITERATIONS { for mapping_sub_hash in 0..KEY_MAPPING_ITERATIONS {
let b = unsafe { self.map.get_unchecked_mut((u64::from_le(key[mapping_sub_hash]) as usize) % self.map.len()) }; let b = unsafe { self.map.get_unchecked_mut((u64::from_le(key[mapping_sub_hash]) as usize) % buckets) };
for j in 0..(IDENTITY_HASH_SIZE / 8) { for j in 0..(IDENTITY_HASH_SIZE / 8) {
b.key_sum[j] ^= key[j]; b.key_sum[j] ^= key[j];
} }
@ -130,18 +155,18 @@ impl IBLT {
/// bucket with only one entry (1 or -1). It can be obtained from the return /// bucket with only one entry (1 or -1). It can be obtained from the return
/// values of either subtract() or singular_bucket(). /// values of either subtract() or singular_bucket().
pub fn list<F: FnMut(&[u8; IDENTITY_HASH_SIZE], bool) -> bool>(&mut self, mut f: F) { pub fn list<F: FnMut(&[u8; IDENTITY_HASH_SIZE], bool) -> bool>(&mut self, mut f: F) {
let mut singular_buckets: Vec<usize> = Vec::with_capacity(1024);
let buckets = self.map.len(); let buckets = self.map.len();
let mut singular_buckets: Vec<u32> = Vec::with_capacity(buckets);
for i in 0..buckets { for i in 0..buckets {
if unsafe { self.map.get_unchecked(i) }.is_singular() { if unsafe { self.map.get_unchecked(i) }.is_singular() {
singular_buckets.push(i); singular_buckets.push(i as u32);
}; };
} }
let mut key = [0_u64; IDENTITY_HASH_SIZE / 8]; let mut key = [0_u64; IDENTITY_HASH_SIZE / 8];
while !singular_buckets.is_empty() { while !singular_buckets.is_empty() {
let b = unsafe { self.map.get_unchecked_mut(singular_buckets.pop().unwrap()) }; let b = unsafe { self.map.get_unchecked_mut(singular_buckets.pop().unwrap() as usize) };
if b.is_singular() { if b.is_singular() {
for j in 0..(IDENTITY_HASH_SIZE / 8) { for j in 0..(IDENTITY_HASH_SIZE / 8) {
key[j] = b.key_sum[j]; key[j] = b.key_sum[j];
@ -149,7 +174,7 @@ impl IBLT {
if f(unsafe { &*key.as_ptr().cast::<[u8; IDENTITY_HASH_SIZE]>() }, b.count == 1) { if f(unsafe { &*key.as_ptr().cast::<[u8; IDENTITY_HASH_SIZE]>() }, b.count == 1) {
let check_hash = u64::from_le(key[0]).wrapping_add(xorshift64(u64::from_le(key[1]))).to_le(); let check_hash = u64::from_le(key[0]).wrapping_add(xorshift64(u64::from_le(key[1]))).to_le();
for mapping_sub_hash in 0..KEY_MAPPING_ITERATIONS { for mapping_sub_hash in 0..KEY_MAPPING_ITERATIONS {
let bi = (u64::from_le(key[mapping_sub_hash]) as usize) % buckets; let bi = (u64::from_le(unsafe { *key.get_unchecked(mapping_sub_hash) }) as usize) % buckets;
let b = unsafe { self.map.get_unchecked_mut(bi) }; let b = unsafe { self.map.get_unchecked_mut(bi) };
for j in 0..(IDENTITY_HASH_SIZE / 8) { for j in 0..(IDENTITY_HASH_SIZE / 8) {
b.key_sum[j] ^= key[j]; b.key_sum[j] ^= key[j];
@ -157,7 +182,7 @@ impl IBLT {
b.check_hash_sum ^= check_hash; b.check_hash_sum ^= check_hash;
b.count = i64::from_le(b.count).wrapping_sub(1).to_le(); b.count = i64::from_le(b.count).wrapping_sub(1).to_le();
if b.is_singular() { if b.is_singular() {
singular_buckets.push(bi); singular_buckets.push(bi as u32);
} }
} }
} else { } else {
@ -171,25 +196,29 @@ impl IBLT {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use zerotier_core_crypto::hash::SHA384; use zerotier_core_crypto::hash::SHA384;
use crate::iblt::IBLT; use crate::iblt::*;
#[allow(unused_variables)] #[allow(unused_variables)]
#[test] #[test]
fn insert_and_list() { fn insert_and_list() {
let mut t = IBLT::new(1024); assert_eq!(size_of::<IBLTEntry>(), IDENTITY_HASH_SIZE + 8 + 8);
let expected_cnt = 512; assert!(KEY_MAPPING_ITERATIONS <= (IDENTITY_HASH_SIZE / 8) && (IDENTITY_HASH_SIZE % 8) == 0);
for i in 0..expected_cnt {
let k = SHA384::hash(&(i as u64).to_le_bytes()); for expected_cnt in 0..800 {
t.insert(&k); let mut t = IBLT::new(1000);
for i in 0..expected_cnt {
let k = SHA384::hash(&((i + expected_cnt) as u32).to_le_bytes());
t.insert(&k);
}
let mut cnt = 0;
t.list(|k, d| {
cnt += 1;
//println!("{} {}", zerotier_core_crypto::hex::to_string(k), d);
true
});
//println!("retrieved {} keys", cnt);
assert_eq!(cnt, expected_cnt);
} }
let mut cnt = 0;
t.list(|k, d| {
cnt += 1;
//println!("{} {}", zerotier_core_crypto::hex::to_string(k), d);
true
});
println!("retrieved {} keys", cnt);
assert_eq!(cnt, expected_cnt);
} }
#[test] #[test]