diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index 1b81435..49a8a05 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/src/bridge_table.rs @@ -255,14 +255,20 @@ pub struct BridgeTable { /// In some instances a single bridge may need to be added to a bucket /// In that case, a spare bucket will be removed from the set of spare bridges. One pub unallocated_bridges: Vec, + // To prevent issues with a counter for the hashmap keys, we keep a list of keys that + // no longer match any buckets that can be used before increasing the counter + pub recycleable_keys: Vec, + // We maintain a list of keys that have been blocked, as well as the date of their blocking + // so that they can be repurposed with new buckets eventually + pub blocked_keys: Vec<(u32, u32)>, /// The date the buckets were last encrypted to make the encbucket. /// The encbucket must be rebuilt each day so that the Bucket /// Reachability credentials in the buckets can be refreshed. pub date_last_enc: u32, } -// Invariant: the lengths of the keys and buckets vectors are the same. -// The encbuckets vector only gets updated when encrypt_table is called. +// Invariant: the lengths of the keys and bucket hashmap are the same. +// The encbuckets hashmap only gets updated when encrypt_table is called. impl BridgeTable { /// Get the number of buckets in the bridge table @@ -270,6 +276,54 @@ impl BridgeTable { self.buckets.len() } + /// Get today's (real or simulated) date + fn today(&self) -> u32 { + // We will not encounter negative Julian dates (~6700 years ago) + // or ones larger than 32 bits + (time::OffsetDateTime::now_utc().date()) + .to_julian_day() + .try_into() + .unwrap() + } + + // Since buckets are moved around in the bridge_table, finding a lookup key that + // does not overwrite existing bridges could become an issue. We keep a list + // of recycleable lookup keys from buckets that have been removed and prioritize + // this list before increasing the counter + fn find_next_available_key(&mut self) -> u32 { + // First check if there are any blocked indexes that are old enough to be replaced + if !self.blocked_keys.is_empty() + && self.blocked_keys.iter().any(|&x| x.1 > self.today() + 30) + //Perhaps 30 should be changed to a later/earlier time + { + let blocked_keys_clone = self.blocked_keys.clone(); + // If so, separate them from the fresh blockages + let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = blocked_keys_clone + .into_iter() + .partition(|&x| x.1 > self.today() + 30); + for item in expired { + let new_item = item.0; + //and add them to the recyclable keys + self.recycleable_keys.push(new_item); + } + // update the blocked_keys vector to only include the fresh keys + self.blocked_keys = fresh + } + if self.recycleable_keys.is_empty() { + let mut test_index = 1; + let mut test_counter = self.counter.wrapping_add(test_index); + while self.buckets.contains_key(&test_counter) { + for i in 0..5000 { + test_index += i; + test_counter = self.counter.wrapping_add(test_index); + } + } + self.counter = self.counter.wrapping_add(test_index); + self.counter + } else { + self.recycleable_keys.pop().unwrap() + } + } /// Append a new bucket to the bridge table, returning its index pub fn new_bucket(&mut self, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) -> u32 { // Pick a random key to encrypt this bucket @@ -277,17 +331,18 @@ impl BridgeTable { let mut key: [u8; 16] = [0; 16]; rng.fill_bytes(&mut key); // Increase the counter to identify the bucket, wrap value if beyond u32::MAX - self.counter = self.counter.wrapping_add(1); - self.keys.insert(self.counter, key); - self.buckets.insert(self.counter, *bucket); + let index = self.find_next_available_key(); + //self.counter = self.counter.wrapping_add(1); + self.keys.insert(index, key); + self.buckets.insert(index, *bucket); // TODO: maybe we don't need this if the hashtable can keep track of available bridges // Mark the new bridges as available for (i, b) in bucket.iter().enumerate() { if b.port > 0 { if let Some(v) = self.reachable.get_mut(b) { - v.push((self.counter, i)); + v.push((index, i)); } else { - let v = vec![(self.counter, i)]; + let v = vec![(index, i)]; self.reachable.insert(*b, v); } } diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index b69e63e..2e90b78 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -445,6 +445,7 @@ impl BridgeAuth { // Get the first spare and remove it from the spares set. let spare = *self.bridge_table.spares.iter().next().unwrap(); self.bridge_table.spares.remove(&spare); + self.bridge_table.recycleable_keys.push(spare); // Get the actual bridges from the spare bucket let spare_bucket = match self.bridge_table.buckets.remove(&spare) { Some(spare_bucket) => spare_bucket, @@ -567,6 +568,9 @@ impl BridgeAuth { // set. let spare = *self.bridge_table.spares.iter().next().unwrap(); self.bridge_table.spares.remove(&spare); + self.bridge_table + .blocked_keys + .push((*bucketnum, self.today())); // Add a blockage migration from this bucket to the spare self.blockage_migration_table .table