diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index a9070a4..ec2e0f6 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/src/bridge_table.rs @@ -224,8 +224,17 @@ pub struct BridgeTable { /// them. When a new Migration credential is needed, a bucket is /// removed from this set and used for that purpose. pub spares: HashSet, + /// bucket ids of "recyclable" buckets. These buckets have not been handed out + /// to users, nor do they have any Migration credentials pointing to + /// them. When a single bridge is needed and there are no more readily available bridges, + /// bridges are taken from a bucket of hot spares, making the unallocated spare bucket empty + /// but still useable as it has not been handed out previously. + pub recyclable: HashSet, + /// In some instances a single bridge may need to be added to a bucket + /// 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, /// 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, diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index f609d59..81e9d6b 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -366,11 +366,125 @@ impl BridgeAuth { return res; } } - if !res { - // Assume the bridge is new and should be added to unallocated bridges or else a new bucket + // If this is returned, we assume that the bridge wasn't found in the bridge table + // and therefore should be treated as a "new bridge" + res + } + pub fn bridge_replace( + &mut self, + bridge: &BridgeLine, + available_bridge: Option<&BridgeLine>, + bdb: &mut BridgeDb, + ) -> bool { + let mut res: bool = false; + let reachable_bridges = &self.bridge_table.reachable.clone(); + if let Some(positions) = reachable_bridges.get(bridge) { + for (bucketnum, offset) in positions.iter() { + // Remove the bridge from the bucket + assert!(self.bridge_table.buckets[*bucketnum as usize][*offset] == *bridge); + self.bridge_table.reachable.remove(bridge); + if available_bridge.is_some() { + self.bridge_table.buckets[*bucketnum as usize][*offset] = + *available_bridge.unwrap(); + // Remove the bridge from the reachable bridges and add new bridge + self.bridge_table + .reachable + .insert(*available_bridge.unwrap(), positions.clone()); + res = true + } else if !self.bridge_table.unallocated_bridges.is_empty() { + self.bridge_table.buckets[*bucketnum as usize][*offset] = + self.bridge_table.unallocated_bridges.pop().unwrap(); + // Remove the bridge from the reachable bridges and add new bridge + self.bridge_table.reachable.insert( + self.bridge_table.unallocated_bridges.pop().unwrap(), + positions.clone(), + ); + res = true + } else if !self.bridge_table.spares.is_empty() { + // 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); + // Indicate the removed bucket as a recyclable bucket + self.bridge_table.recyclable.insert(spare); + // Get the actual bridges from the spare bucket + let mut replacement: &BridgeLine = &BridgeLine::default(); + // Make the first spare the replacement bridge, add the others to the set of + // unallocated_bridges + let spare_bucket = self.bridge_table.buckets[spare as usize].clone(); + for spare_bridge in spare_bucket.iter() { + if replacement.port > 0 { + self.bridge_table.unallocated_bridges.push(*spare_bridge); + } else { + replacement = spare_bridge; + } + } + self.bridge_table.buckets[*bucketnum as usize][*offset] = *replacement; + self.bridge_table + .reachable + .insert(*replacement, positions.clone()); + res = true + } + // If there are no available bridges that can be assigned here, the only thing + // that can be done is return an indication that updating the gone bridge + // didn't work. + + // Now we should check that the bucket hasn't become "blocked" due to having no available + // bridges + if !res { + // Count how many bridges in this bucket are reachable + let numreachable = self.bridge_table.buckets[*bucketnum as usize] + .iter() + .filter(|br| self.bridge_table.reachable.get(br).is_some()) + .count(); + + // Is this bucket an open-invitation bucket? + if bdb.openinv_buckets.contains(bucketnum) { + bdb.openinv_buckets.remove(bucketnum); + self.trustup_migration_table.table.remove(bucketnum); + continue; + } + + // Has the bucket fallen below the + // threshold? + if numreachable == MIN_BUCKET_REACHABILITY { + // This bucket is now unreachable. Get a spare bucket + if self.bridge_table.spares.is_empty() { + // Uh, oh. No spares available. Just delete any + // migrations leading to this bucket. + self.trustup_migration_table + .table + .retain(|_, &mut v| v != *bucketnum); + self.blockage_migration_table + .table + .retain(|_, &mut v| v != *bucketnum); + } else { + // 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); + // Add a blockage migration from this bucket to the spare + self.blockage_migration_table + .table + .insert(*bucketnum, spare); + // Remove any trust upgrade migrations to this + // bucket + self.trustup_migration_table + .table + .retain(|_, &mut v| v != *bucketnum); + // Change any blockage migrations with this bucket + // as the destination to the spare + for (_, v) in self.blockage_migration_table.table.iter_mut() { + if *v == *bucketnum { + *v = spare; + } + } + } + } + } + } } - res }