use std::cmp::Ordering;
use std::fmt::Debug;
use std::ops::{Bound, RangeBounds};
#[derive(Clone)]
struct Range {
start: u32,
end: u32,
}
pub struct RangePool {
free: Vec<Range>,
}
impl Debug for RangePool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RangePool {{")?;
for range in &self.free {
write!(f, " [{}, {}]", range.start, range.end)?;
}
write!(f, " }}")?;
Ok(())
}
}
impl Default for RangePool {
fn default() -> Self {
Self::ranged(..)
}
}
impl RangePool {
pub fn ranged(range: impl RangeBounds<u32>) -> Self {
let start = match range.start_bound() {
Bound::Included(s) => *s,
Bound::Excluded(s) => *s + 1,
Bound::Unbounded => u32::MIN,
};
let end = match range.end_bound() {
Bound::Included(e) => *e,
Bound::Excluded(e) => *e - 1,
Bound::Unbounded => u32::MAX,
};
let free = vec![Range { start, end }];
Self { free }
}
pub fn new_id(&mut self) -> Option<u32> {
let Some(range) = self.free.last_mut() else {
return None;
};
let id = range.start;
if range.start < range.end {
range.start += 1;
} else {
self.free.pop();
}
Some(id)
}
pub fn request_id(&mut self, id: u32) -> Option<u32> {
let pos = self.free.binary_search_by(|range| {
if range.start <= id && id <= range.end {
Ordering::Equal
} else {
range.start.cmp(&id)
}
});
let Ok(pos) = pos else {
return None;
};
let range = self.free.get_mut(pos).unwrap();
if range.start == range.end {
self.free.remove(pos);
} else if range.start == id {
range.start += 1;
} else if range.end == id {
range.end -= 1;
} else {
let new = Range {
start: range.start,
end: id - 1,
};
range.start = id + 1;
self.free.insert(pos, new);
}
Some(id)
}
pub fn return_id(&mut self, id: u32) -> Result<(), u32> {
let pos = self.free.binary_search_by(|range| {
if range.start <= id && id <= range.end {
Ordering::Equal
} else {
range.start.cmp(&id)
}
});
let Err(pos) = pos else {
return Err(id);
};
let adjacent_next = self.free.get(pos).is_some_and(|r| r.start == id + 1);
let adjacent_prev = pos
.checked_sub(1)
.map(|i| &self.free[i])
.is_some_and(|r| r.end == id - 1);
match (adjacent_prev, adjacent_next) {
(true, true) => {
self.free[pos - 1].end = self.free[pos].end;
self.free.remove(pos);
}
(false, true) => {
self.free[pos].start = id;
}
(true, false) => {
self.free[pos - 1].end = id;
}
(false, false) => {
self.free.insert(pos, Range { start: id, end: id });
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn basic() {
let mut pool = RangePool::ranged(..);
assert_eq!(pool.request_id(1), Some(1));
assert_eq!(pool.request_id(3), Some(3));
assert_eq!(pool.request_id(5), Some(5));
assert_eq!(pool.request_id(7), Some(7));
}
}