use std::collections::VecDeque;
use nanoservices_utils::errors::{NanoServiceError, NanoServiceErrorStatus};
#[derive(Debug)]
pub struct Allocator<T: Clone> {
pub connection_pool: Vec<Option<T>>,
pub free_connections: VecDeque<usize>,
pub allocated_connections: VecDeque<usize>,
}
impl<T: Clone> Default for Allocator<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Clone> Allocator<T> {
pub fn new() -> Self {
Self {
connection_pool: Vec::new(),
free_connections: VecDeque::new(),
allocated_connections: VecDeque::new(),
}
}
pub fn allocate(&mut self, connection: T) -> usize {
if let Some(index) = self.free_connections.pop_front() {
self.connection_pool[index] = Some(connection);
self.allocated_connections.push_back(index);
index
} else {
self.connection_pool.push(Some(connection));
self.allocated_connections.push_back(self.connection_pool.len() - 1);
self.connection_pool.len() - 1
}
}
pub fn yield_next_allocated_index(&mut self) -> Option<usize> {
#[allow(unused_assignments)]
let mut index: Option<usize> = None;
loop {
index = self.allocated_connections.pop_front();
if index.is_none() {
break;
}
let i = index.unwrap();
let connection = self.connection_pool[i].as_ref();
match connection {
Some(_) => {
self.allocated_connections.push_back(i);
break;
}
None => continue,
}
}
index
}
pub fn extract_connection(&self, index: usize) -> Result<(T, usize), NanoServiceError> {
if index + 1 > self.connection_pool.len() {
return Err(NanoServiceError::new(
format!("Connection at index {index} is unavailable"),
NanoServiceErrorStatus::Unknown,
));
}
let connection = self.connection_pool[index].as_ref();
match connection {
Some(c) => Ok((c.clone(), index)),
None => Err(NanoServiceError::new(
format!("Connection at index {index} is deallocated"),
NanoServiceErrorStatus::Unknown,
)),
}
}
pub fn deallocate(&mut self, index: usize) -> Result<T, NanoServiceError> {
if index + 1 > self.connection_pool.len() {
return Err(NanoServiceError::new(
format!("Connection at index {index} is unavailable"),
NanoServiceErrorStatus::Unknown,
));
}
let placeholder = self.connection_pool[index].take();
if placeholder.is_none() {
return Err(NanoServiceError::new(
format!("Connection at index {index} is already deallocated"),
NanoServiceErrorStatus::Unknown,
));
}
self.free_connections.push_back(index);
Ok(placeholder.unwrap())
}
pub fn yield_connections(&self) -> Vec<T> {
let mut connections = Vec::new();
for i in &self.allocated_connections {
let connection = self.connection_pool[*i].as_ref();
match connection {
Some(c) => connections.push(c.clone()),
None => continue,
}
}
connections
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let allocator = Allocator::<usize>::new();
assert_eq!(allocator.connection_pool.len(), 0);
assert_eq!(allocator.free_connections.len(), 0);
assert_eq!(allocator.allocated_connections.len(), 0);
}
#[test]
fn test_allocate() {
let mut allocator = Allocator::<usize>::new();
let index = allocator.allocate(5);
assert_eq!(index, 0);
assert_eq!(allocator.connection_pool[index], Some(5));
assert_eq!(allocator.allocated_connections.len(), 1);
assert_eq!(allocator.free_connections.len(), 0);
assert_eq!(allocator.connection_pool.len(), 1);
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
let index = allocator.allocate(6);
assert_eq!(index, 1);
assert_eq!(allocator.connection_pool[index], Some(6));
assert_eq!(allocator.allocated_connections.len(), 2);
assert_eq!(allocator.free_connections.len(), 0);
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
}
#[test]
fn test_deallocate() {
let mut allocator = Allocator::<usize>::new();
let index = allocator.allocate(5);
let index_two = allocator.allocate(6);
assert_eq!(index, 0);
assert_eq!(index_two, 1);
assert_eq!(allocator.connection_pool[index_two], Some(6));
assert_eq!(allocator.allocated_connections.len(), 2);
assert_eq!(allocator.free_connections.len(), 0);
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(Ok(5), allocator.deallocate(0));
assert_eq!(allocator.connection_pool[0], None);
assert_eq!(allocator.allocated_connections.len(), 2);
assert_eq!(allocator.free_connections.len(), 1);
assert_eq!(allocator.free_connections[0], 0);
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
let index = allocator.allocate(7);
assert_eq!(index, 0);
assert_eq!(allocator.connection_pool[index], Some(7));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
assert_eq!(allocator.yield_next_allocated_index(), Some(0));
assert_eq!(allocator.yield_next_allocated_index(), Some(1));
let result = allocator.deallocate(20);
assert!(result.is_err());
if let Err(e) = result {
assert_eq!(e.message, "Connection at index 20 is unavailable");
}
}
#[test]
fn test_double_deallocate() {
let mut allocator = Allocator::<usize>::new();
let _ = allocator.allocate(5);
let _ = allocator.allocate(6);
assert_eq!(Ok(5), allocator.deallocate(0));
assert_eq!(Ok(6), allocator.deallocate(1));
assert_eq!(allocator.connection_pool[0], None);
assert_eq!(allocator.connection_pool[1], None);
assert_eq!(allocator.allocated_connections.len(), 2);
assert_eq!(allocator.free_connections.len(), 2);
assert_eq!(allocator.free_connections[0], 0);
assert_eq!(allocator.free_connections[1], 1);
let result = allocator.deallocate(0);
assert!(result.is_err());
if let Err(e) = result {
assert_eq!(e.message, "Connection at index 0 is already deallocated");
}
}
#[test]
fn test_extract_connection() {
let mut allocator = Allocator::<usize>::new();
let outcome = allocator.extract_connection(20);
assert!(outcome.is_err());
if let Err(e) = outcome {
assert_eq!(e.message, "Connection at index 20 is unavailable");
}
let index = allocator.allocate(5);
let connection = allocator.extract_connection(index);
let result = connection.unwrap();
assert_eq!(result.0, 5);
assert_eq!(result.1, 0);
let _ = allocator.deallocate(index);
let outcome = allocator.extract_connection(index);
assert!(outcome.is_err());
if let Err(e) = outcome {
assert_eq!(e.message, "Connection at index 0 is deallocated");
}
}
#[test]
fn test_yield_connections() {
let mut allocator = Allocator::<usize>::new();
let _ = allocator.allocate(5);
let _ = allocator.allocate(6);
let connections = allocator.yield_connections();
assert_eq!(connections.len(), 2);
assert_eq!(connections[0], 5);
assert_eq!(connections[1], 6);
let _ = allocator.deallocate(0);
let connections = allocator.yield_connections();
assert_eq!(connections.len(), 1);
assert_eq!(connections[0], 6);
}
}