parent
122fa2f689
commit
c38a234691
@ -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…
Reference in new issue