use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
struct DataBlock<T, const BLOCK_SIZE: usize> {
free_space: usize,
data: [Option<T>; BLOCK_SIZE],
}
impl<T, const BLOCK_SIZE: usize> DataBlock<T, BLOCK_SIZE> {
const EMPTY_ELEMENT: Option<T> = None;
fn new() -> Self {
Self {
free_space: BLOCK_SIZE,
data: [Self::EMPTY_ELEMENT; BLOCK_SIZE],
}
}
fn insert(&mut self, val: T) -> usize {
assert!(self.free_space > 0);
let mut stored_offset = 0;
for (i, item) in self.data.iter_mut().enumerate() {
if item.is_none() {
let _ = item.insert(val);
stored_offset = i;
break;
}
}
self.free_space -= 1;
stored_offset
}
}
pub(crate) struct FaVecIndex<const BLOCK_SIZE: usize> {
absolute_index: usize,
}
impl<const BLOCK_SIZE: usize> FaVecIndex<BLOCK_SIZE> {
fn index_from_block_offset(block_index: usize, offset: usize) -> Self {
FaVecIndex {
absolute_index: block_index * BLOCK_SIZE + offset,
}
}
fn index_to_block_offset(&self) -> (usize, usize) {
(
self.absolute_index / BLOCK_SIZE,
self.absolute_index % BLOCK_SIZE,
)
}
fn as_absolute_index(&self) -> usize {
self.absolute_index
}
pub(crate) fn from_absolute_index(absolute_index: usize) -> Self {
FaVecIndex { absolute_index }
}
}
pub(crate) struct FaVec<T, const BLOCK_SIZE: usize> {
data_blocks: Vec<Box<DataBlock<T, BLOCK_SIZE>>>,
free_space_map: BTreeMap<usize, BTreeSet<usize>>,
}
impl<T, const BLOCK_SIZE: usize> FaVec<T, BLOCK_SIZE> {
const MAX_BLOCK_COUNT: usize = usize::MAX / (BLOCK_SIZE + 1);
pub fn new() -> Self {
let mut initial_set = BTreeSet::new();
initial_set.insert(0usize);
let mut initial_map = BTreeMap::new();
initial_map.insert(BLOCK_SIZE, initial_set);
Self {
data_blocks: vec![Box::new(DataBlock::new())],
free_space_map: initial_map,
}
}
fn update_block_free_spaces(&mut self, block_index: usize, new_free_space: usize) {
assert!(block_index < self.data_blocks.len());
assert!(new_free_space <= BLOCK_SIZE);
assert!(block_index < self.data_blocks.len());
let block = self.data_blocks[block_index].as_mut();
let old_free_space = block.free_space;
assert!(self.free_space_map.contains_key(&old_free_space));
let old_free_blocks = self.free_space_map.get_mut(&old_free_space).unwrap();
assert!(!old_free_blocks.is_empty());
assert!(old_free_blocks.contains(&block_index));
old_free_blocks.remove(&block_index);
if old_free_blocks.is_empty() {
self.free_space_map.remove(&old_free_space);
}
match self.free_space_map.entry(new_free_space) {
Entry::Occupied(mut entry) => {
entry.get_mut().insert(block_index);
}
Entry::Vacant(entry) => {
let mut new_set = BTreeSet::new();
new_set.insert(block_index);
entry.insert(new_set);
}
}
block.free_space = new_free_space;
}
fn get_or_create_block_for_insert(&mut self) -> usize {
let lowest_free_space = self.free_space_map.keys().find(|key| **key > 0);
let lowest_free_space = match lowest_free_space {
Some(lowest_free_space) => {
*lowest_free_space
}
None => {
if self.data_blocks.len() == Self::MAX_BLOCK_COUNT {
std::process::abort(); }
let new_index = self.data_blocks.len();
self.data_blocks.push(Box::new(DataBlock::new()));
let mut new_set = BTreeSet::new();
new_set.insert(new_index);
self.free_space_map.insert(BLOCK_SIZE, new_set);
BLOCK_SIZE
}
};
let possible_blocks = self.free_space_map.get_mut(&lowest_free_space).unwrap();
assert!(!possible_blocks.is_empty());
let block_index = possible_blocks.pop_first().unwrap();
if possible_blocks.is_empty() {
self.free_space_map.remove(&lowest_free_space);
}
assert!(block_index < self.data_blocks.len());
block_index
}
pub fn push(&mut self, val: T) -> FaVecIndex<BLOCK_SIZE> {
let block_index = self.get_or_create_block_for_insert();
let data_block = self.data_blocks.get_mut(block_index).unwrap();
let stored_offset = data_block.insert(val);
match self.free_space_map.get_mut(&data_block.free_space) {
Some(block_indexes) => {
block_indexes.insert(block_index);
}
None => {
let mut new_indexes = BTreeSet::new();
new_indexes.insert(block_index);
self.free_space_map
.insert(data_block.free_space, new_indexes);
}
}
FaVecIndex::index_from_block_offset(block_index, stored_offset)
}
pub fn get(&self, index: &FaVecIndex<BLOCK_SIZE>) -> Option<&T> {
let (block_index, offset) = FaVecIndex::index_to_block_offset(index);
if block_index >= self.data_blocks.len() || offset >= BLOCK_SIZE {
return None;
}
match self.data_blocks.get(block_index) {
Some(block) => block.data[offset].as_ref(),
None => None,
}
}
pub fn get_mut(&mut self, index: &FaVecIndex<BLOCK_SIZE>) -> Option<&mut T> {
let (block_index, offset) = FaVecIndex::index_to_block_offset(index);
if block_index >= self.data_blocks.len() || offset >= BLOCK_SIZE {
return None;
}
match self.data_blocks.get_mut(block_index) {
Some(block) => block.data[offset].as_mut(),
None => None,
}
}
fn prune_end_blocks(&mut self) {
if let Some(empty_blocks) = self.free_space_map.get_mut(&BLOCK_SIZE) {
while self.data_blocks.len() > 1
&& self.data_blocks.last().unwrap().free_space == BLOCK_SIZE
{
self.data_blocks.pop();
let old_end_index = self.data_blocks.len();
assert!(!empty_blocks.is_empty());
empty_blocks.remove(&old_end_index);
}
if empty_blocks.is_empty() {
self.free_space_map.remove(&BLOCK_SIZE);
}
};
}
pub fn remove(&mut self, index: &FaVecIndex<BLOCK_SIZE>) -> Option<T> {
let (block_index, offset) = FaVecIndex::index_to_block_offset(index);
assert!(block_index < self.data_blocks.len());
assert!(offset < BLOCK_SIZE);
if block_index >= self.data_blocks.len() || offset >= BLOCK_SIZE {
return None;
}
let removed_item = match self.data_blocks.get_mut(block_index) {
Some(block) => {
let removed = block.data.get_mut(offset).unwrap().take();
if removed.is_some() {
let _old_free_space = block.free_space;
let new_free_space = block.free_space + 1;
self.update_block_free_spaces(block_index, new_free_space);
assert!(self.free_space_map.contains_key(&new_free_space));
assert!(!self.free_space_map[&new_free_space].is_empty());
self.prune_end_blocks();
}
removed
}
None => None,
};
removed_item
}
pub fn capacity(&self) -> usize {
self.data_blocks.len() * BLOCK_SIZE
}
pub fn len(&self) -> usize {
self.free_space_map
.iter()
.fold(0, |acc, (free_space, items)| {
let used_space = BLOCK_SIZE - free_space;
let block_count = items.len();
acc + used_space * block_count
})
}
pub fn iter(&self) -> FaVecIter<T, BLOCK_SIZE> {
FaVecIter {
block_index: 0,
offset: 0,
real_count: 0,
fa_vec: self,
}
}
}
pub struct FaVecIter<'a, T, const BLOCK_SIZE: usize> {
block_index: usize,
offset: usize,
real_count: usize,
fa_vec: &'a FaVec<T, BLOCK_SIZE>,
}
impl<'a, T, const BLOCK_SIZE: usize> FaVecIter<'a, T, BLOCK_SIZE> {
fn increment(&mut self) {
self.offset += 1;
if self.offset >= BLOCK_SIZE {
self.offset = 0;
self.block_index += 1;
}
}
}
impl<'a, T, const BLOCK_SIZE: usize> Iterator for FaVecIter<'a, T, BLOCK_SIZE> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
while self.block_index < self.fa_vec.data_blocks.len() {
let block_ref = self.fa_vec.data_blocks.get(self.block_index).unwrap();
let item = &block_ref.data[self.offset];
self.increment();
if item.is_some() {
self.real_count += 1;
return item.as_ref();
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.fa_vec.len() - self.real_count;
(size, Some(size))
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;
use crate::fixed_address_continuous_allocation::{FaVec, FaVecIndex};
const TEST_BLOCK_SIZE: usize = 512;
#[test]
fn fa_vec_properties_test_random_io() {
const ITEM_COUNT: usize = 100000;
let mut rng = ChaCha8Rng::seed_from_u64(0xDEADBEEF);
let mut vec = FaVec::<i64, TEST_BLOCK_SIZE>::new();
let mut keys = Vec::<FaVecIndex<TEST_BLOCK_SIZE>>::new();
while keys.len() < ITEM_COUNT {
let remove_element = rng.random_ratio(1, 3);
if remove_element && !keys.is_empty() {
let key_index = rng.random_range(0..keys.len());
let key = keys.swap_remove(key_index);
vec.remove(&key);
} else {
let new_value = rng.random::<i64>();
let new_key = vec.push(new_value);
keys.push(new_key);
}
}
assert_eq!(vec.len(), keys.len());
validate_vec_properties(&mut vec);
}
#[test]
fn fa_vec_properties_test_add_remove_add() {
const ITEM_COUNT: usize = 100000;
let mut rng = ChaCha8Rng::seed_from_u64(0xDEADBEEF);
let mut vec = FaVec::<i64, TEST_BLOCK_SIZE>::new();
let mut keys = Vec::<FaVecIndex<TEST_BLOCK_SIZE>>::new();
const INITIAL_ITEM_COUNT: usize = ITEM_COUNT * 2;
for total_items in 0..INITIAL_ITEM_COUNT {
let new_value = rng.random::<i64>();
let new_key = vec.push(new_value);
keys.push(new_key);
assert_eq!(
vec.capacity(),
TEST_BLOCK_SIZE * (1 + total_items / TEST_BLOCK_SIZE)
);
}
const HALF_ITEM_COUNT: usize = ITEM_COUNT / 2;
for _ in 0..(ITEM_COUNT + HALF_ITEM_COUNT) {
let key_index = rng.random_range(0..keys.len());
let key = keys.swap_remove(key_index);
vec.remove(&key);
}
for _ in 0..HALF_ITEM_COUNT {
let new_value = rng.random::<i64>();
let new_key = vec.push(new_value);
keys.push(new_key);
}
assert_eq!(vec.len(), keys.len());
validate_vec_properties(&mut vec);
}
#[test]
fn fa_vec_properties_test_add_remove_repeat() {
const ITEM_COUNT: usize = 100000;
let mut rng = ChaCha8Rng::seed_from_u64(0xDEADBEEF);
let mut vec = FaVec::<i64, TEST_BLOCK_SIZE>::new();
let mut keys = Vec::<FaVecIndex<TEST_BLOCK_SIZE>>::new();
for _ in 0..2 * ITEM_COUNT {
let new_value = rng.random::<i64>();
let new_key = vec.push(new_value);
keys.push(new_key);
}
for _ in 0..10 {
for _ in 0..ITEM_COUNT {
let key_index = rng.random_range(0..keys.len());
let key = keys.swap_remove(key_index);
vec.remove(&key);
}
for _ in 0..ITEM_COUNT {
let new_value = rng.random::<i64>();
let new_key = vec.push(new_value);
keys.push(new_key);
}
}
assert_eq!(vec.len(), keys.len());
validate_vec_properties(&mut vec);
}
#[test]
fn iterator_basic() {
let mut vec = FaVec::<i64, TEST_BLOCK_SIZE>::new();
let _first = vec.push(5);
let _second = vec.push(7);
let _third = vec.push(2);
vec.remove(&_second);
let vals = vec.iter().copied().collect::<Vec<i64>>();
assert_eq!(vals, vec![5, 2]);
assert_eq!(vec.len(), 2);
validate_vec_properties(&mut vec);
}
#[test]
fn ensure_correct_prune_on_block_edge() {
let mut vec = FaVec::<i64, TEST_BLOCK_SIZE>::new();
let mut last_block = FaVecIndex::from_absolute_index(0);
for i in 0..(TEST_BLOCK_SIZE + 1) {
last_block = vec.push(i as i64);
}
vec.remove(&last_block);
vec.push((TEST_BLOCK_SIZE + 1) as i64);
assert_eq!(vec.len(), TEST_BLOCK_SIZE + 1);
validate_vec_properties(&mut vec);
}
fn validate_vec_properties(vec: &mut FaVec<i64, TEST_BLOCK_SIZE>) {
let mut free_space_buckets = BTreeMap::new();
for (block_index, block) in vec.data_blocks.iter().enumerate() {
let total_free_blocks = block
.data
.iter()
.filter(|item| -> bool { item.is_none() })
.count();
assert_eq!(total_free_blocks, block.free_space);
assert!(vec.free_space_map.contains_key(&block.free_space));
assert!(vec.free_space_map[&block.free_space].contains(&block_index));
match free_space_buckets.get_mut(&block.free_space) {
Some(count) => {
*count += 1;
}
None => {
free_space_buckets.insert(block.free_space, 1usize);
}
}
}
let free_space_counts: Vec<usize> = free_space_buckets.values().copied().collect();
let least_free_block_count = *free_space_counts.first().unwrap();
let most_free_block_count = *free_space_counts.last().unwrap();
let middle_free_block_count = free_space_counts[free_space_counts.len() / 2];
println!("free space map: {:?}", vec.free_space_map);
println!("free count vec: {:?}", free_space_counts);
println!(
"least/middle/most free: {} - {} - {}",
least_free_block_count, middle_free_block_count, most_free_block_count
);
assert!(least_free_block_count >= middle_free_block_count);
assert!(least_free_block_count >= most_free_block_count);
}
}