use crate::{
Value,
arena::{Arena, Index},
};
const CHUNK_SIZE: usize = 32;
pub type ArrayHandle = Index;
pub type ChunkHandle = Index;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ArrayHeader {
first_chunk: Option<ChunkHandle>,
length: usize,
chunks_count: usize,
is_marked: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ArrayChunk {
data: [Option<Value>; CHUNK_SIZE],
next_chunk: Option<ChunkHandle>,
used: usize, is_marked: bool,
}
#[derive(Debug, Default, Clone)]
pub struct ArrayArena {
heads: Arena<ArrayHeader>,
chunks: Arena<ArrayChunk>,
}
impl ArrayArena {
pub fn new() -> Self {
Self::with_capacity(64)
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
heads: Arena::with_capacity(capacity),
chunks: Arena::with_capacity(capacity),
}
}
pub fn create_array(&mut self, length: usize) -> ArrayHandle {
let chunks_needed = if length == 0 {
0
} else {
(length - 1) / CHUNK_SIZE + 1
};
let mut first_chunk = None;
let mut prev_chunk = None;
for _ in 0..chunks_needed {
let chunk = ArrayChunk {
data: [None; CHUNK_SIZE],
next_chunk: None,
used: 0,
is_marked: false,
};
let chunk_handle = self.chunks.insert(chunk);
if first_chunk.is_none() {
first_chunk = Some(chunk_handle);
}
if let Some(prev) = prev_chunk {
self.chunks[prev].next_chunk = Some(chunk_handle);
}
prev_chunk = Some(chunk_handle);
}
let header = ArrayHeader {
first_chunk,
length,
chunks_count: chunks_needed,
is_marked: false,
};
self.heads.insert(header)
}
pub fn insert(&mut self, handle: ArrayHandle, index: usize, value: Value) -> bool {
let header = &self.heads[handle];
if index >= header.length {
return false;
}
let chunk_index = index / CHUNK_SIZE;
let slot_index = index % CHUNK_SIZE;
let mut current_chunk = header.first_chunk;
for _ in 0..chunk_index {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
} else {
return false;
}
}
if let Some(chunk_handle) = current_chunk {
let chunk = &mut self.chunks[chunk_handle];
let old_value = chunk.data[slot_index].replace(value);
if old_value.is_none() {
chunk.used += 1;
}
true
} else {
false
}
}
pub fn get(&self, handle: ArrayHandle, index: isize) -> Value {
let header = &self.heads[handle];
let length = header.length as isize;
let actual_index = if index < 0 { length + index } else { index };
if actual_index < 0 || actual_index >= length {
return Value::nil();
}
let actual_index = actual_index as usize;
let chunk_index = actual_index / CHUNK_SIZE;
let slot_index = actual_index % CHUNK_SIZE;
let mut current_chunk = header.first_chunk;
for _ in 0..chunk_index {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
} else {
return Value::nil();
}
}
if let Some(chunk_handle) = current_chunk {
self.chunks[chunk_handle].data[slot_index].unwrap_or_default()
} else {
Value::nil()
}
}
pub fn remove_at(&mut self, handle: ArrayHandle, index: isize) -> Option<Value> {
let length = self.heads[handle].length as isize;
let actual_index = if index < 0 { length + index } else { index };
if actual_index < 0 || actual_index >= length {
return None;
}
let actual_index = actual_index as usize;
let chunk_index = actual_index / CHUNK_SIZE;
let slot_index = actual_index % CHUNK_SIZE;
let mut current_chunk = self.heads[handle].first_chunk;
for _ in 0..chunk_index {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
} else {
return None;
}
}
let removed_value = if let Some(chunk_handle) = current_chunk {
let value = self.chunks[chunk_handle].data[slot_index]
.take()
.unwrap_or_default();
if self.chunks[chunk_handle].used > 0 {
self.chunks[chunk_handle].used -= 1;
}
value
} else {
return None;
};
let length_usize = length as usize;
for i in actual_index..(length_usize - 1) {
let next_index = i + 1;
let value = self.get(handle, next_index as isize);
let curr_chunk_idx = i / CHUNK_SIZE;
let curr_slot_idx = i % CHUNK_SIZE;
let mut curr_chunk = self.heads[handle].first_chunk;
for _ in 0..curr_chunk_idx {
if let Some(chunk_handle) = curr_chunk {
curr_chunk = self.chunks[chunk_handle].next_chunk;
}
}
if let Some(chunk_handle) = curr_chunk {
let old_value = self.chunks[chunk_handle].data[curr_slot_idx].replace(value);
if old_value.is_none() && !value.is_nil() {
self.chunks[chunk_handle].used += 1;
} else if old_value.is_some() && value.is_nil() {
if self.chunks[chunk_handle].used > 0 {
self.chunks[chunk_handle].used -= 1;
}
}
}
}
let last_index = (length_usize - 1) as usize;
let last_chunk_idx = last_index / CHUNK_SIZE as usize;
let last_slot_idx = last_index % CHUNK_SIZE as usize;
let mut last_chunk = self.heads[handle].first_chunk;
for _ in 0..last_chunk_idx {
if let Some(chunk_handle) = last_chunk {
last_chunk = self.chunks[chunk_handle].next_chunk;
}
}
if let Some(chunk_handle) = last_chunk {
if self.chunks[chunk_handle].data[last_slot_idx].is_some() {
self.chunks[chunk_handle].data[last_slot_idx] = None;
if self.chunks[chunk_handle].used > 0 {
self.chunks[chunk_handle].used -= 1;
}
}
}
self.heads[handle].length -= 1;
Some(removed_value)
}
pub fn pop(&mut self, handle: ArrayHandle) -> Option<Value> {
let length = self.heads[handle].length;
if length == 0 {
return None;
}
let last_index = length - 1;
let chunk_index = last_index / CHUNK_SIZE;
let slot_index = last_index % CHUNK_SIZE;
let mut current_chunk = self.heads[handle].first_chunk;
for _ in 0..chunk_index {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
} else {
return None;
}
}
if let Some(chunk_handle) = current_chunk {
let value = self.chunks[chunk_handle].data[slot_index]
.take()
.unwrap_or_default();
if self.chunks[chunk_handle].used > 0 {
self.chunks[chunk_handle].used -= 1;
}
self.heads[handle].length -= 1;
Some(value)
} else {
None
}
}
pub fn push(&mut self, handle: ArrayHandle, value: Value) {
let header = &mut self.heads[handle];
let new_index = header.length;
let chunks_needed = if new_index == 0 {
0
} else {
(new_index - 1) / CHUNK_SIZE + 1
};
if chunks_needed >= header.chunks_count {
let new_chunk = ArrayChunk {
data: [None; CHUNK_SIZE],
next_chunk: None,
used: 0,
is_marked: false,
};
let new_chunk_handle = self.chunks.insert(new_chunk);
if let Some(first) = header.first_chunk {
let mut current = first;
while let Some(next) = self.chunks[current].next_chunk {
current = next;
}
self.chunks[current].next_chunk = Some(new_chunk_handle);
} else {
header.first_chunk = Some(new_chunk_handle);
}
header.chunks_count += 1;
}
let chunk_index = new_index / CHUNK_SIZE;
let slot_index = new_index % CHUNK_SIZE;
let mut current_chunk = header.first_chunk;
for _ in 0..chunk_index {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
}
}
if let Some(chunk_handle) = current_chunk {
self.chunks[chunk_handle].data[slot_index] = Some(value);
self.chunks[chunk_handle].used += 1;
}
header.length += 1;
}
pub fn reverse(&mut self, handle: ArrayHandle) {
let header = &self.heads[handle];
let length = header.length;
if length <= 1 {
return;
}
for i in 0..(length / 2) {
let start_idx = i;
let end_idx = length - 1 - i;
let start_chunk_idx = start_idx / CHUNK_SIZE;
let start_slot_idx = start_idx % CHUNK_SIZE;
let end_chunk_idx = end_idx / CHUNK_SIZE;
let end_slot_idx = end_idx % CHUNK_SIZE;
let mut current_chunk = header.first_chunk;
for _ in 0..start_chunk_idx {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
}
}
let start_chunk_handle = current_chunk;
let mut current_chunk = header.first_chunk;
for _ in 0..end_chunk_idx {
if let Some(chunk_handle) = current_chunk {
current_chunk = self.chunks[chunk_handle].next_chunk;
}
}
let end_chunk_handle = current_chunk;
if let (Some(start_handle), Some(end_handle)) = (start_chunk_handle, end_chunk_handle) {
if start_handle == end_handle {
let chunk = &mut self.chunks[start_handle];
chunk.data.swap(start_slot_idx, end_slot_idx);
} else {
let start_value = self.chunks[start_handle].data[start_slot_idx];
let end_value = self.chunks[end_handle].data[end_slot_idx];
self.chunks[start_handle].data[start_slot_idx] = end_value;
self.chunks[end_handle].data[end_slot_idx] = start_value;
}
}
}
}
pub fn length(&self, handle: ArrayHandle) -> usize {
self.heads[handle].length
}
pub fn concat(&mut self, handle1: ArrayHandle, handle2: ArrayHandle) -> ArrayHandle {
let len1 = self.heads[handle1].length;
let len2 = self.heads[handle2].length;
let total_length = len1 + len2;
let new_handle = self.create_array(total_length);
for i in 0..len1 {
let value = self.get(handle1, i as isize);
self.insert(new_handle, i, value);
}
for i in 0..len2 {
let value = self.get(handle2, i as isize);
self.insert(new_handle, len1 + i, value);
}
new_handle
}
pub fn slice(&mut self, handle: ArrayHandle, begin: isize, end: Option<isize>) -> ArrayHandle {
let source_length = self.heads[handle].length as isize;
let start = if begin < 0 {
(source_length + begin).max(0) as usize
} else {
(begin.min(source_length) as usize).min(source_length as usize)
};
let finish = if let Some(end_val) = end {
if end_val < 0 {
(source_length + end_val).max(0) as usize
} else {
(end_val.min(source_length) as usize).min(source_length as usize)
}
} else {
source_length as usize
};
if start >= finish {
return self.create_array(0);
}
let slice_length = finish - start;
let new_handle = self.create_array(slice_length);
for i in 0..slice_length {
let value = self.get(handle, (start + i) as isize);
self.insert(new_handle, i, value);
}
new_handle
}
pub fn shallow_copy(&mut self, handle: ArrayHandle) -> ArrayHandle {
let source_length = self.heads[handle].length;
let new_handle = self.create_array(source_length);
for i in 0..source_length {
let value = self.get(handle, i as isize);
self.insert(new_handle, i, value);
}
new_handle
}
pub fn iter(&self, handle: ArrayHandle) -> ArrayIterator<'_> {
let length = self.length(handle);
ArrayIterator {
arena: self,
handle,
current_index: 0,
current_back_index: length,
}
}
pub fn mark_array(&mut self, handle: ArrayHandle) {
self.heads[handle].is_marked = true;
let mut current_chunk = self.heads[handle].first_chunk;
while let Some(chunk_handle) = current_chunk {
self.chunks[chunk_handle].is_marked = true;
current_chunk = self.chunks[chunk_handle].next_chunk;
}
}
pub fn check_is_marked(&self, handle: ArrayHandle) -> bool {
self.heads[handle].is_marked
}
pub fn collect_garbage(&mut self) {
let estimated_deletions = (self.chunks.len() / 4).max(8);
let mut handles_to_remove = Vec::with_capacity(estimated_deletions);
for (handle, head) in self.heads.iter_mut() {
if head.is_marked {
head.is_marked = false;
} else {
handles_to_remove.push(handle);
}
}
for handle in handles_to_remove.drain(..) {
let _ = self.heads.remove(handle);
}
for (handle, chunk) in self.chunks.iter_mut() {
if chunk.is_marked {
chunk.is_marked = false;
} else {
handles_to_remove.push(handle);
}
}
for handle in handles_to_remove.drain(..) {
let _ = self.chunks.remove(handle);
}
}
pub fn get_allocated_bytes(&self) -> usize {
std::mem::size_of::<ArrayHeader>() * self.heads.len()
+ std::mem::size_of::<ArrayChunk>() * self.chunks.len()
}
}
pub struct ArrayIterator<'a> {
arena: &'a ArrayArena,
handle: ArrayHandle,
current_index: usize,
current_back_index: usize,
}
impl<'a> Iterator for ArrayIterator<'a> {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
while self.current_index < self.current_back_index {
let value = self.arena.get(self.handle, self.current_index as isize);
self.current_index += 1;
if !value.is_nil() {
return Some(value);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.current_back_index.saturating_sub(self.current_index);
(0, Some(remaining)) }
}
impl<'a> DoubleEndedIterator for ArrayIterator<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
while self.current_index < self.current_back_index {
self.current_back_index -= 1;
let value = self
.arena
.get(self.handle, self.current_back_index as isize);
if !value.is_nil() {
return Some(value);
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_empty_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
assert_eq!(arena.length(handle), 0);
assert_eq!(arena.get(handle, 0), Value::nil());
}
#[test]
fn test_create_small_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
assert_eq!(arena.length(handle), 5);
for i in 0..5 {
assert_eq!(arena.get(handle, i), Value::nil());
}
}
#[test]
fn test_create_large_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(100);
assert_eq!(arena.length(handle), 100);
assert_eq!(arena.get(handle, 99), Value::nil());
assert_eq!(arena.get(handle, 100), Value::nil()); }
#[test]
fn test_insert_and_get() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
assert!(arena.insert(handle, 0, Value::number(1.0)));
assert!(arena.insert(handle, 1, Value::number(2.0)));
assert!(arena.insert(handle, 2, Value::number(3.0)));
assert_eq!(arena.get(handle, 0), Value::number(1.0));
assert_eq!(arena.get(handle, 1), Value::number(2.0));
assert_eq!(arena.get(handle, 2), Value::number(3.0));
}
#[test]
fn test_insert_out_of_bounds() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(2);
assert!(arena.insert(handle, 0, Value::number(1.0)));
assert!(!arena.insert(handle, 2, Value::number(3.0))); assert!(!arena.insert(handle, 10, Value::number(10.0))); }
#[test]
fn test_push_and_pop() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
arena.push(handle, Value::number(1.0));
arena.push(handle, Value::number(2.0));
arena.push(handle, Value::number(3.0));
assert_eq!(arena.length(handle), 3);
assert_eq!(arena.get(handle, 0), Value::number(1.0));
assert_eq!(arena.get(handle, 1), Value::number(2.0));
assert_eq!(arena.get(handle, 2), Value::number(3.0));
assert_eq!(arena.pop(handle), Some(Value::number(3.0)));
assert_eq!(arena.pop(handle), Some(Value::number(2.0)));
assert_eq!(arena.length(handle), 1);
assert_eq!(arena.pop(handle), Some(Value::number(1.0)));
assert_eq!(arena.length(handle), 0);
assert_eq!(arena.pop(handle), None);
}
#[test]
fn test_cross_chunk_operations() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
assert_eq!(arena.length(handle), 50);
for i in 0..50 {
assert_eq!(arena.get(handle, i), Value::number(i as f64));
}
for i in (0..50).rev() {
assert_eq!(arena.pop(handle), Some(Value::number(i as f64)));
}
assert_eq!(arena.length(handle), 0);
}
#[test]
fn test_reverse() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
arena.reverse(handle);
for i in 0..5 {
assert_eq!(arena.get(handle, i), Value::number((4 - i) as f64));
}
}
#[test]
fn test_reverse_large_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..70 {
arena.push(handle, Value::number(i as f64));
}
arena.reverse(handle);
for i in 0..70 {
assert_eq!(arena.get(handle, i), Value::number((69 - i) as f64));
}
}
#[test]
fn test_concat() {
let mut arena = ArrayArena::new();
let handle1 = arena.create_array(3);
let handle2 = arena.create_array(2);
arena.insert(handle1, 0, Value::number(1.0));
arena.insert(handle1, 1, Value::number(2.0));
arena.insert(handle1, 2, Value::number(3.0));
arena.insert(handle2, 0, Value::number(4.0));
arena.insert(handle2, 1, Value::number(5.0));
let concat_handle = arena.concat(handle1, handle2);
assert_eq!(arena.length(concat_handle), 5);
assert_eq!(arena.get(concat_handle, 0), Value::number(1.0));
assert_eq!(arena.get(concat_handle, 1), Value::number(2.0));
assert_eq!(arena.get(concat_handle, 2), Value::number(3.0));
assert_eq!(arena.get(concat_handle, 3), Value::number(4.0));
assert_eq!(arena.get(concat_handle, 4), Value::number(5.0));
}
#[test]
fn test_slice() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(10);
for i in 0..10 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice_handle = arena.slice(handle, 2, Some(7));
assert_eq!(arena.length(slice_handle), 5);
for i in 0..5 {
assert_eq!(arena.get(slice_handle, i), Value::number((i + 2) as f64));
}
}
#[test]
fn test_slice_bounds_clamping() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice1 = arena.slice(handle, 10, Some(20)); assert_eq!(arena.length(slice1), 0);
let slice2 = arena.slice(handle, 2, Some(10)); assert_eq!(arena.length(slice2), 3);
assert_eq!(arena.get(slice2, 0), Value::number(2.0));
let slice3 = arena.slice(handle, 3, Some(2)); assert_eq!(arena.length(slice3), 0);
}
#[test]
fn test_slice_with_optional_end() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(8);
for i in 0..8 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice_handle = arena.slice(handle, 3, None);
assert_eq!(arena.length(slice_handle), 5);
for i in 0..5 {
assert_eq!(arena.get(slice_handle, i), Value::number((i + 3) as f64));
}
let full_slice = arena.slice(handle, 0, None);
assert_eq!(arena.length(full_slice), 8);
for i in 0..8 {
assert_eq!(arena.get(full_slice, i), Value::number(i as f64));
}
let empty_slice = arena.slice(handle, 10, None);
assert_eq!(arena.length(empty_slice), 0);
}
#[test]
fn test_negative_indexing() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
assert_eq!(arena.get(handle, -1), Value::number(4.0)); assert_eq!(arena.get(handle, -2), Value::number(3.0)); assert_eq!(arena.get(handle, -3), Value::number(2.0)); assert_eq!(arena.get(handle, -4), Value::number(1.0)); assert_eq!(arena.get(handle, -5), Value::number(0.0));
assert_eq!(arena.get(handle, -6), Value::nil()); assert_eq!(arena.get(handle, -10), Value::nil());
assert_eq!(arena.get(handle, 0), Value::number(0.0));
assert_eq!(arena.get(handle, 4), Value::number(4.0));
assert_eq!(arena.get(handle, 5), Value::nil());
let empty_handle = arena.create_array(0);
assert_eq!(arena.get(empty_handle, -1), Value::nil());
assert_eq!(arena.get(empty_handle, 0), Value::nil());
}
#[test]
fn test_iterator_empty_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
let mut iter = arena.iter(handle);
assert_eq!(iter.next(), None);
}
#[test]
fn test_iterator_basic() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(1.0));
arena.insert(handle, 1, Value::number(2.0));
arena.insert(handle, 2, Value::number(3.0));
let values: Vec<Value> = arena.iter(handle).collect();
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::number(1.0));
assert_eq!(values[1], Value::number(2.0));
assert_eq!(values[2], Value::number(3.0));
}
#[test]
fn test_iterator_with_empty_slots() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
arena.insert(handle, 0, Value::number(1.0));
arena.insert(handle, 2, Value::number(3.0));
arena.insert(handle, 4, Value::number(5.0));
let values: Vec<Value> = arena.iter(handle).collect();
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::number(1.0));
assert_eq!(values[1], Value::number(3.0));
assert_eq!(values[2], Value::number(5.0));
}
#[test]
fn test_iterator_cross_chunks() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
let values: Vec<Value> = arena.iter(handle).collect();
assert_eq!(values.len(), 50);
for (i, value) in values.iter().enumerate() {
assert_eq!(*value, Value::number(i as f64));
}
}
#[test]
fn test_iterator_for_loop() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..10 {
arena.push(handle, Value::number(i as f64));
}
let mut expected = 0.0;
for value in arena.iter(handle) {
assert_eq!(value, Value::number(expected));
expected += 1.0;
}
assert_eq!(expected, 10.0);
}
#[test]
fn test_iterator_size_hint() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
arena.insert(handle, 0, Value::number(1.0));
arena.insert(handle, 1, Value::number(2.0));
arena.insert(handle, 4, Value::number(5.0));
let iter = arena.iter(handle);
let (lower, upper) = iter.size_hint();
assert_eq!(lower, 0); assert_eq!(upper, Some(5)); }
#[test]
fn test_iterator_after_operations() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
arena.push(handle, Value::number(1.0));
arena.push(handle, Value::number(2.0));
arena.push(handle, Value::number(3.0));
arena.pop(handle);
arena.push(handle, Value::number(4.0));
let values: Vec<Value> = arena.iter(handle).collect();
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::number(1.0));
assert_eq!(values[1], Value::number(2.0));
assert_eq!(values[2], Value::number(4.0));
}
#[test]
fn test_mark_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..40 {
arena.push(handle, Value::number(i as f64));
}
assert!(!arena.heads[handle].is_marked);
arena.mark_array(handle);
assert!(arena.heads[handle].is_marked);
let mut current_chunk = arena.heads[handle].first_chunk;
while let Some(chunk_handle) = current_chunk {
assert!(arena.chunks[chunk_handle].is_marked);
current_chunk = arena.chunks[chunk_handle].next_chunk;
}
}
#[test]
fn test_collect_garbage_marked_arrays() {
let mut arena = ArrayArena::new();
let handle1 = arena.create_array(5);
let handle2 = arena.create_array(3);
for i in 0..5 {
arena.insert(handle1, i, Value::number(i as f64));
}
for i in 0..3 {
arena.insert(handle2, i, Value::number((i + 10) as f64));
}
arena.mark_array(handle1);
arena.collect_garbage();
assert_eq!(arena.length(handle1), 5);
assert_eq!(arena.get(handle1, 0), Value::number(0.0));
assert!(!arena.heads[handle1].is_marked);
}
#[test]
fn test_collect_garbage_unmarked_arrays() {
let mut arena = ArrayArena::new();
let handle1 = arena.create_array(3);
let handle2 = arena.create_array(3);
arena.insert(handle1, 0, Value::number(1.0));
arena.insert(handle2, 0, Value::number(2.0));
arena.collect_garbage();
}
#[test]
fn test_mark_and_sweep_cycle() {
let mut arena = ArrayArena::new();
let handle1 = arena.create_array(0);
let handle2 = arena.create_array(0);
for i in 0..35 {
arena.push(handle1, Value::number(i as f64));
arena.push(handle2, Value::number((i + 100) as f64));
}
arena.mark_array(handle1);
assert!(arena.heads[handle1].is_marked);
assert!(!arena.heads[handle2].is_marked);
arena.collect_garbage();
assert!(!arena.heads[handle1].is_marked);
assert_eq!(arena.length(handle1), 35);
arena.mark_array(handle1);
arena.collect_garbage();
assert_eq!(arena.get(handle1, 0), Value::number(0.0));
assert_eq!(arena.get(handle1, 34), Value::number(34.0));
}
#[test]
fn test_gc_with_empty_arena() {
let mut arena = ArrayArena::new();
arena.collect_garbage();
}
#[test]
fn test_gc_preserves_chunks_of_marked_arrays() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
arena.mark_array(handle);
arena.collect_garbage();
for i in 0..50 {
assert_eq!(arena.get(handle, i), Value::number(i as f64));
}
let values: Vec<Value> = arena.iter(handle).collect();
assert_eq!(values.len(), 50);
}
#[test]
fn test_slice_with_negative_indices() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(10);
for i in 0..10 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice1 = arena.slice(handle, -3, None); assert_eq!(arena.length(slice1), 3);
assert_eq!(arena.get(slice1, 0), Value::number(7.0));
assert_eq!(arena.get(slice1, 1), Value::number(8.0));
assert_eq!(arena.get(slice1, 2), Value::number(9.0));
let slice2 = arena.slice(handle, 2, Some(-2)); assert_eq!(arena.length(slice2), 6);
assert_eq!(arena.get(slice2, 0), Value::number(2.0));
assert_eq!(arena.get(slice2, 5), Value::number(7.0));
let slice3 = arena.slice(handle, -5, Some(-2)); assert_eq!(arena.length(slice3), 3);
assert_eq!(arena.get(slice3, 0), Value::number(5.0));
assert_eq!(arena.get(slice3, 1), Value::number(6.0));
assert_eq!(arena.get(slice3, 2), Value::number(7.0));
let slice4 = arena.slice(handle, -15, Some(5)); assert_eq!(arena.length(slice4), 5);
assert_eq!(arena.get(slice4, 0), Value::number(0.0));
assert_eq!(arena.get(slice4, 4), Value::number(4.0));
let slice5 = arena.slice(handle, 3, Some(-15)); assert_eq!(arena.length(slice5), 0);
}
#[test]
fn test_slice_negative_begin_greater_than_end() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(10);
for i in 0..10 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice1 = arena.slice(handle, -2, Some(3)); assert_eq!(arena.length(slice1), 0);
let slice2 = arena.slice(handle, -2, Some(-5)); assert_eq!(arena.length(slice2), 0);
let slice3 = arena.slice(handle, 7, Some(-4)); assert_eq!(arena.length(slice3), 0);
}
#[test]
fn test_slice_edge_cases_with_negative_indices() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
let slice1 = arena.slice(handle, -1, None);
assert_eq!(arena.length(slice1), 1);
assert_eq!(arena.get(slice1, 0), Value::number(4.0));
let slice2 = arena.slice(handle, 0, Some(-1));
assert_eq!(arena.length(slice2), 4);
assert_eq!(arena.get(slice2, 0), Value::number(0.0));
assert_eq!(arena.get(slice2, 3), Value::number(3.0));
let slice3 = arena.slice(handle, -5, None); assert_eq!(arena.length(slice3), 5);
for i in 0..5 {
assert_eq!(arena.get(slice3, i), Value::number(i as f64));
}
let slice4 = arena.slice(handle, -3, Some(-2)); assert_eq!(arena.length(slice4), 1);
assert_eq!(arena.get(slice4, 0), Value::number(2.0));
}
#[test]
fn test_slice_empty_array_with_negative_indices() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
let slice1 = arena.slice(handle, -1, None);
assert_eq!(arena.length(slice1), 0);
let slice2 = arena.slice(handle, -5, Some(-2));
assert_eq!(arena.length(slice2), 0);
let slice3 = arena.slice(handle, 0, Some(-1));
assert_eq!(arena.length(slice3), 0);
}
#[test]
fn test_shallow_clone() {
let mut arena = ArrayArena::new();
let original = arena.create_array(5);
for i in 0..5 {
arena.insert(original, i, Value::number(i as f64));
}
let cloned = arena.shallow_copy(original);
assert_eq!(arena.length(cloned), 5);
for i in 0..5 {
assert_eq!(arena.get(cloned, i), Value::number(i as f64));
assert_eq!(arena.get(original, i), arena.get(cloned, i));
}
arena.insert(original, 2, Value::number(99.0));
assert_eq!(arena.get(original, 2), Value::number(99.0));
assert_eq!(arena.get(cloned, 2), Value::number(2.0));
arena.insert(cloned, 1, Value::number(88.0));
assert_eq!(arena.get(cloned, 1), Value::number(88.0));
assert_eq!(arena.get(original, 1), Value::number(1.0));
}
#[test]
fn test_shallow_clone_empty_array() {
let mut arena = ArrayArena::new();
let original = arena.create_array(0);
let cloned = arena.shallow_copy(original);
assert_eq!(arena.length(cloned), 0);
assert_eq!(arena.get(cloned, 0), Value::nil());
}
#[test]
fn test_shallow_clone_large_array() {
let mut arena = ArrayArena::new();
let original = arena.create_array(0);
for i in 0..100 {
arena.push(original, Value::number(i as f64));
}
let cloned = arena.shallow_copy(original);
assert_eq!(arena.length(cloned), 100);
for i in 0..100 {
assert_eq!(arena.get(cloned, i), Value::number(i as f64));
}
arena.push(original, Value::number(999.0));
assert_eq!(arena.length(original), 101);
assert_eq!(arena.length(cloned), 100);
assert_eq!(arena.get(original, 100), Value::number(999.0));
assert_eq!(arena.get(cloned, 100), Value::nil());
}
#[test]
fn test_shallow_clone_with_empty_slots() {
let mut arena = ArrayArena::new();
let original = arena.create_array(5);
arena.insert(original, 0, Value::number(1.0));
arena.insert(original, 2, Value::number(3.0));
arena.insert(original, 4, Value::number(5.0));
let cloned = arena.shallow_copy(original);
assert_eq!(arena.length(cloned), 5);
assert_eq!(arena.get(cloned, 0), Value::number(1.0));
assert_eq!(arena.get(cloned, 1), Value::nil());
assert_eq!(arena.get(cloned, 2), Value::number(3.0));
assert_eq!(arena.get(cloned, 3), Value::nil());
assert_eq!(arena.get(cloned, 4), Value::number(5.0));
arena.insert(original, 1, Value::number(2.0));
assert_eq!(arena.get(original, 1), Value::number(2.0));
assert_eq!(arena.get(cloned, 1), Value::nil());
}
#[test]
fn test_iterator_reverse() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number((i + 1) as f64));
}
let values: Vec<Value> = arena.iter(handle).rev().collect();
assert_eq!(values.len(), 5);
assert_eq!(values[0], Value::number(5.0));
assert_eq!(values[1], Value::number(4.0));
assert_eq!(values[2], Value::number(3.0));
assert_eq!(values[3], Value::number(2.0));
assert_eq!(values[4], Value::number(1.0));
}
#[test]
fn test_iterator_reverse_with_empty_slots() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(6);
arena.insert(handle, 0, Value::number(1.0));
arena.insert(handle, 2, Value::number(3.0));
arena.insert(handle, 5, Value::number(6.0));
let values: Vec<Value> = arena.iter(handle).rev().collect();
assert_eq!(values.len(), 3);
assert_eq!(values[0], Value::number(6.0));
assert_eq!(values[1], Value::number(3.0));
assert_eq!(values[2], Value::number(1.0));
}
#[test]
fn test_iterator_reverse_large_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
let values: Vec<Value> = arena.iter(handle).rev().collect();
assert_eq!(values.len(), 50);
for (i, value) in values.iter().enumerate() {
assert_eq!(*value, Value::number((49 - i) as f64));
}
}
#[test]
fn test_remove_at_basic() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
let removed = arena.remove_at(handle, 2);
assert_eq!(removed, Some(Value::number(2.0)));
assert_eq!(arena.length(handle), 4);
assert_eq!(arena.get(handle, 0), Value::number(0.0));
assert_eq!(arena.get(handle, 1), Value::number(1.0));
assert_eq!(arena.get(handle, 2), Value::number(3.0));
assert_eq!(arena.get(handle, 3), Value::number(4.0));
}
#[test]
fn test_remove_at_first_element() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(10.0));
arena.insert(handle, 1, Value::number(20.0));
arena.insert(handle, 2, Value::number(30.0));
let removed = arena.remove_at(handle, 0);
assert_eq!(removed, Some(Value::number(10.0)));
assert_eq!(arena.length(handle), 2);
assert_eq!(arena.get(handle, 0), Value::number(20.0));
assert_eq!(arena.get(handle, 1), Value::number(30.0));
}
#[test]
fn test_remove_at_last_element() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(10.0));
arena.insert(handle, 1, Value::number(20.0));
arena.insert(handle, 2, Value::number(30.0));
let removed = arena.remove_at(handle, 2);
assert_eq!(removed, Some(Value::number(30.0)));
assert_eq!(arena.length(handle), 2);
assert_eq!(arena.get(handle, 0), Value::number(10.0));
assert_eq!(arena.get(handle, 1), Value::number(20.0));
assert_eq!(arena.get(handle, 2), Value::nil());
}
#[test]
fn test_remove_at_out_of_bounds() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(10.0));
arena.insert(handle, 1, Value::number(20.0));
arena.insert(handle, 2, Value::number(30.0));
let removed = arena.remove_at(handle, 3);
assert_eq!(removed, None);
assert_eq!(arena.length(handle), 3);
let removed = arena.remove_at(handle, 10);
assert_eq!(removed, None);
assert_eq!(arena.length(handle), 3);
}
#[test]
fn test_remove_at_empty_array() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
let removed = arena.remove_at(handle, 0);
assert_eq!(removed, None);
assert_eq!(arena.length(handle), 0);
}
#[test]
fn test_remove_at_multiple_times() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
arena.remove_at(handle, 2); assert_eq!(arena.length(handle), 4);
arena.remove_at(handle, 0); assert_eq!(arena.length(handle), 3);
arena.remove_at(handle, 2); assert_eq!(arena.length(handle), 2);
assert_eq!(arena.get(handle, 0), Value::number(1.0));
assert_eq!(arena.get(handle, 1), Value::number(3.0));
}
#[test]
fn test_remove_at_cross_chunks() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
let removed = arena.remove_at(handle, 5);
assert_eq!(removed, Some(Value::number(5.0)));
assert_eq!(arena.length(handle), 49);
assert_eq!(arena.get(handle, 4), Value::number(4.0));
assert_eq!(arena.get(handle, 5), Value::number(6.0));
assert_eq!(arena.get(handle, 48), Value::number(49.0));
let removed = arena.remove_at(handle, 35);
assert_eq!(removed, Some(Value::number(36.0)));
assert_eq!(arena.length(handle), 48);
assert_eq!(arena.get(handle, 35), Value::number(37.0));
assert_eq!(arena.get(handle, 47), Value::number(49.0));
}
#[test]
fn test_remove_at_until_empty() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(10.0));
arena.insert(handle, 1, Value::number(20.0));
arena.insert(handle, 2, Value::number(30.0));
assert_eq!(arena.remove_at(handle, 0), Some(Value::number(10.0)));
assert_eq!(arena.length(handle), 2);
assert_eq!(arena.remove_at(handle, 0), Some(Value::number(20.0)));
assert_eq!(arena.length(handle), 1);
assert_eq!(arena.remove_at(handle, 0), Some(Value::number(30.0)));
assert_eq!(arena.length(handle), 0);
assert_eq!(arena.remove_at(handle, 0), None);
}
#[test]
fn test_remove_at_negative_indexing() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
let removed = arena.remove_at(handle, -1);
assert_eq!(removed, Some(Value::number(4.0)));
assert_eq!(arena.length(handle), 4);
assert_eq!(arena.get(handle, 3), Value::number(3.0));
assert_eq!(arena.get(handle, 4), Value::nil());
let removed = arena.remove_at(handle, -2);
assert_eq!(removed, Some(Value::number(2.0)));
assert_eq!(arena.length(handle), 3);
assert_eq!(arena.get(handle, 0), Value::number(0.0));
assert_eq!(arena.get(handle, 1), Value::number(1.0));
assert_eq!(arena.get(handle, 2), Value::number(3.0));
}
#[test]
fn test_remove_at_negative_out_of_bounds() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(3);
arena.insert(handle, 0, Value::number(10.0));
arena.insert(handle, 1, Value::number(20.0));
arena.insert(handle, 2, Value::number(30.0));
let removed = arena.remove_at(handle, -4);
assert_eq!(removed, None);
assert_eq!(arena.length(handle), 3);
let removed = arena.remove_at(handle, -10);
assert_eq!(removed, None);
assert_eq!(arena.length(handle), 3);
}
#[test]
fn test_remove_at_negative_first_element() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(5);
for i in 0..5 {
arena.insert(handle, i, Value::number(i as f64));
}
let removed = arena.remove_at(handle, -5);
assert_eq!(removed, Some(Value::number(0.0)));
assert_eq!(arena.length(handle), 4);
assert_eq!(arena.get(handle, 0), Value::number(1.0));
assert_eq!(arena.get(handle, 1), Value::number(2.0));
assert_eq!(arena.get(handle, 2), Value::number(3.0));
assert_eq!(arena.get(handle, 3), Value::number(4.0));
}
#[test]
fn test_remove_at_negative_middle_element() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(7);
for i in 0..7 {
arena.insert(handle, i, Value::number(i as f64));
}
let removed = arena.remove_at(handle, -4);
assert_eq!(removed, Some(Value::number(3.0)));
assert_eq!(arena.length(handle), 6);
assert_eq!(arena.get(handle, 0), Value::number(0.0));
assert_eq!(arena.get(handle, 1), Value::number(1.0));
assert_eq!(arena.get(handle, 2), Value::number(2.0));
assert_eq!(arena.get(handle, 3), Value::number(4.0));
assert_eq!(arena.get(handle, 4), Value::number(5.0));
assert_eq!(arena.get(handle, 5), Value::number(6.0));
}
#[test]
fn test_remove_at_negative_cross_chunks() {
let mut arena = ArrayArena::new();
let handle = arena.create_array(0);
for i in 0..50 {
arena.push(handle, Value::number(i as f64));
}
let removed = arena.remove_at(handle, -10);
assert_eq!(removed, Some(Value::number(40.0)));
assert_eq!(arena.length(handle), 49);
assert_eq!(arena.get(handle, 39), Value::number(39.0));
assert_eq!(arena.get(handle, 40), Value::number(41.0));
assert_eq!(arena.get(handle, 48), Value::number(49.0));
}
}