implemented ranged_number_allocator

main
Inga 🏳‍🌈 1 year ago
parent 122fa2f689
commit c38a234691
  1. 2
      hostnames_allocator/Cargo.toml
  2. 3
      hostnames_allocator/src/lib.rs
  3. 88
      hostnames_allocator/src/ranged_number_allocator.rs
  4. 111
      hostnames_allocator/tests/ranged_number_allocator_tests.rs

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dev-dependencies]
itertools = "0.11.0"

@ -0,0 +1,3 @@
#![feature(btree_cursors)]
pub mod ranged_number_allocator;

@ -0,0 +1,88 @@
use std::{collections::BTreeMap, ops::Bound};
pub struct RangedNumberAllocator {
pub tree: BTreeMap<u32, u32>,
}
impl RangedNumberAllocator {
pub fn new() -> Self {
RangedNumberAllocator {
tree: BTreeMap::new()
}
}
pub fn remove(&mut self, value: u32) {
let mut cursor = self.tree.upper_bound_mut(Bound::Included(&value));
if let Some((&range_start, &range_end)) = cursor.key_value() {
if range_end >= value {
// The found range covers the requested value, meaning this value was previously used. Now there are four scenarios:
// 1. It's [value, value]; we should remove it;
// 2. It's [value, x] where x > value; we should replace it with [value+1, x];
// 3. It's [x, value] where value > x; we should change it to [x, value-1];
// 4. It's [x, y] where x < value < y; we should replace it with [x, value-1] and [value+1, y].
if range_start == value && range_end == value {
// Scenario 1
cursor.remove_current().unwrap();
} else if range_start == value {
// Scenario 2
cursor.insert_after(value + 1, range_end);
cursor.remove_current().unwrap();
} else if range_end == value {
// Scenario 3
*cursor.value_mut().unwrap() = value - 1;
} else {
// Scenario 4
cursor.insert_after(value + 1, range_end);
*cursor.value_mut().unwrap() = value - 1;
}
}
}
}
pub fn allocate(&mut self) -> u32 {
let mut cursor = self.tree.lower_bound_mut(Bound::Unbounded);
// Six scenarios:
// 1. Tree is empty; we should add [1, 1] range and return 1;
// 2. Tree has only one range [1, x]; we should change it to [1, x+1] and return x+1;
// 3. Tree has at least first two ranges, [1, x] and [x+2, y]; we should replace them with [1, y] and return x+1;
// 4. Tree has at least first two ranges, [1, x] and [y, z] where y > x+2; we should change the first range to [1, x+1] and return x+1;
// 5. Tree has at least first range [2, x]; we should replace it with [1, x] and return 1;
// 6. Tree has at least first range [x, y] where x > 2; we should add [1, 1] and return 1.
if let Some((&range_start, &range_end)) = cursor.key_value() {
// There is at least one range; eliminated scenario 1, left with 2-6.
if range_start == 1 {
// First range starts with 1; eliminated scenarios 5-6, left with 2-4.
if let Some((&next_range_start, &mut next_range_end)) = cursor.peek_next() {
// There is another range; eliminated scenario 2, left with 3-4
if next_range_start == range_end + 2 {
// Scenario 3
*cursor.value_mut().unwrap() = next_range_end;
cursor.move_next();
cursor.remove_current_and_move_back().unwrap();
return range_end + 1;
} else {
// Scenario 4
*cursor.value_mut().unwrap() = range_end + 1;
return range_end + 1;
}
} else {
// Scenario 2
*cursor.value_mut().unwrap() = range_end + 1;
return range_end + 1;
}
} else if range_start == 2 {
// Scenario 5
cursor.insert_before(1, range_end);
cursor.remove_current().unwrap();
return 1;
} else {
// Scenario 6
cursor.insert_before(1, 1);
return 1;
}
} else {
cursor.insert_after(1, 1);
return 1;
}
}
}

@ -0,0 +1,111 @@
extern crate hostnames_allocator;
use hostnames_allocator::ranged_number_allocator::RangedNumberAllocator;
fn create_allocator(data: &[(u32, u32)]) -> RangedNumberAllocator {
RangedNumberAllocator {
tree: data.to_owned().into_iter().collect()
}
}
fn check_allocator(allocator: RangedNumberAllocator, expected: &[(u32, u32)]) {
itertools::assert_equal(allocator.tree, expected.to_owned());
}
#[test]
fn remove_preserves_tree_when_empty() {
let mut allocator = create_allocator(&[]);
allocator.remove(5);
check_allocator(allocator, &[]);
}
#[test]
fn remove_preserves_tree_when_not_found() {
let mut allocator = create_allocator(&[(1, 3), (11, 13)]);
allocator.remove(5);
check_allocator(allocator, &[(1, 3), (11, 13)]);
}
#[test]
fn remove_removes_range_with_single_value() {
let mut allocator = create_allocator(&[(1, 3), (5, 5), (11, 13)]);
allocator.remove(5);
check_allocator(allocator, &[(1, 3), (11, 13)]);
}
#[test]
fn remove_replaces_range_starting_with_value() {
let mut allocator = create_allocator(&[(1, 3), (5, 7), (11, 13)]);
allocator.remove(5);
check_allocator(allocator, &[(1, 3), (6, 7), (11, 13)]);
}
#[test]
fn remove_changes_range_ending_with_value() {
let mut allocator = create_allocator(&[(1, 3), (5, 7), (11, 13)]);
allocator.remove(7);
check_allocator(allocator, &[(1, 3), (5, 6), (11, 13)]);
}
#[test]
fn remove_splits_range_containing_value() {
let mut allocator = create_allocator(&[(1, 3), (5, 9), (11, 13)]);
allocator.remove(7);
check_allocator(allocator, &[(1, 3), (5, 6), (8, 9), (11, 13)]);
}
#[test]
fn remove_removes_last_value() {
let mut allocator = create_allocator(&[(5, 5)]);
allocator.remove(5);
check_allocator(allocator, &[]);
}
#[test]
fn allocate_initializes_empty() {
let mut allocator = create_allocator(&[]);
assert_eq!(allocator.allocate(), 1);
check_allocator(allocator, &[(1, 1)]);
}
#[test]
fn allocate_adds_second_value() {
let mut allocator = create_allocator(&[(1, 1)]);
assert_eq!(allocator.allocate(), 2);
check_allocator(allocator, &[(1, 2)]);
}
#[test]
fn allocate_adds_tenth_value() {
let mut allocator = create_allocator(&[(1, 9)]);
assert_eq!(allocator.allocate(), 10);
check_allocator(allocator, &[(1, 10)]);
}
#[test]
fn allocate_adds_value_for_two_ranges_starting_with_one() {
let mut allocator = create_allocator(&[(1, 5), (10, 15)]);
assert_eq!(allocator.allocate(), 6);
check_allocator(allocator, &[(1, 6), (10, 15)]);
}
#[test]
fn allocate_combines_two_adjacent_ranges_starting_with_one() {
let mut allocator = create_allocator(&[(1, 5), (7, 15)]);
assert_eq!(allocator.allocate(), 6);
check_allocator(allocator, &[(1, 15)]);
}
#[test]
fn allocate_adds_value_for_range_starting_with_two() {
let mut allocator = create_allocator(&[(2, 10)]);
assert_eq!(allocator.allocate(), 1);
check_allocator(allocator, &[(1, 10)]);
}
#[test]
fn allocate_adds_value_for_range_starting_with_ten() {
let mut allocator = create_allocator(&[(10, 20)]);
assert_eq!(allocator.allocate(), 1);
check_allocator(allocator, &[(1, 1), (10, 20)]);
}
Loading…
Cancel
Save