use core::ops::{Deref, DerefMut};
pub struct InnerVec<T: Sized> {
pub ptr: *mut T,
pub capacity: u32,
pub len: u32,
}
impl<T: core::fmt::Debug> core::fmt::Debug for InnerVec<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T: Sized> InnerVec<T> {
pub fn zero() -> InnerVec<T> {
InnerVec {
ptr: core::ptr::null_mut(),
capacity: 0,
len: 0,
}
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
self.capacity as usize
}
pub fn push(&mut self, value: T) {
assert!(self.len < self.capacity);
unsafe {
core::ptr::write(self.ptr.add(self.len as usize), value);
}
self.len += 1;
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
unsafe { Some(core::ptr::read(self.ptr.add(self.len as usize))) }
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
(**self).iter()
}
}
impl<T> Deref for InnerVec<T> {
type Target = [T];
fn deref(&self) -> &[T] {
if self.ptr.is_null() {
&[]
} else {
unsafe { core::slice::from_raw_parts(self.ptr, self.len as usize) }
}
}
}
impl<T> DerefMut for InnerVec<T> {
fn deref_mut(&mut self) -> &mut [T] {
if self.ptr.is_null() {
&mut []
} else {
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len as usize) }
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zero() {
let vec: InnerVec<i32> = InnerVec::zero();
assert_eq!(vec.len(), 0);
assert_eq!(vec.capacity(), 0);
assert!(vec.ptr.is_null());
}
#[test]
fn test_deref_empty() {
let vec: InnerVec<i32> = InnerVec::zero();
let slice: &[i32] = &vec;
assert_eq!(slice.len(), 0);
}
#[test]
fn test_deref_mut_empty() {
let mut vec: InnerVec<i32> = InnerVec::zero();
let slice: &mut [i32] = &mut vec;
assert_eq!(slice.len(), 0);
}
}
#[cfg(kani)]
mod kani_proofs {
use super::*;
use crate::alloc::Allocator;
use crate::test_support::RustSystemAllocator;
use core::alloc::Layout;
unsafe fn create_valid_inner_vec<T>(capacity: u32, alloc: &RustSystemAllocator) -> InnerVec<T> {
if capacity == 0 {
return InnerVec::zero();
}
let layout = Layout::array::<T>(capacity as usize).unwrap();
let ptr = unsafe { alloc.alloc(layout).unwrap() as *mut T };
InnerVec {
ptr,
capacity,
len: 0,
}
}
unsafe fn dealloc_inner_vec<T>(vec: InnerVec<T>, alloc: &RustSystemAllocator) {
if vec.capacity > 0 && !vec.ptr.is_null() {
let layout = Layout::array::<T>(vec.capacity as usize).unwrap();
unsafe { alloc.dealloc(vec.ptr as *mut u8, layout) };
}
}
struct Droppable {
value: u32,
drop_counter: *mut u32,
}
impl Drop for Droppable {
fn drop(&mut self) {
unsafe {
*self.drop_counter += 1;
}
}
}
#[kani::proof]
fn verify_len_invariants() {
unsafe {
let alloc = RustSystemAllocator;
let capacity: u32 = kani::any();
kani::assume(capacity > 0 && capacity <= 10);
let mut vec = create_valid_inner_vec::<u32>(capacity, &alloc);
let initial_len: u32 = kani::any();
kani::assume(initial_len <= capacity);
vec.len = initial_len;
let op: u8 = kani::any();
match op % 2 {
0 if vec.len < vec.capacity => {
let old_len = vec.len;
vec.push(42);
assert!(vec.len == old_len + 1, "len must increment by 1");
let len_usize = vec.len as usize;
assert!(len_usize == vec.len as usize);
}
1 if vec.len > 0 => {
let old_len = vec.len;
vec.pop();
assert!(vec.len == old_len - 1, "len must decrement by 1");
}
_ => {}
}
assert!(vec.len <= vec.capacity, "len must never exceed capacity");
let offset = vec.len as usize;
assert!(offset <= capacity as usize, "offset must fit in usize");
dealloc_inner_vec(vec, &alloc);
}
}
#[kani::proof]
fn verify_pointer_arithmetic_in_bounds() {
unsafe {
let alloc = RustSystemAllocator;
let capacity: u32 = kani::any();
kani::assume(capacity > 0 && capacity <= 10);
let vec = create_valid_inner_vec::<u32>(capacity, &alloc);
let index: u32 = kani::any();
kani::assume(index < capacity);
let _offset_ptr = vec.ptr.add(index as usize);
let _end_ptr = vec.ptr.add(capacity as usize);
dealloc_inner_vec(vec, &alloc);
}
}
#[kani::proof]
fn verify_null_pointer_safety() {
let mut vec: InnerVec<u32> = InnerVec::zero();
assert!(vec.ptr.is_null());
assert_eq!(vec.capacity, 0);
assert_eq!(vec.len, 0);
let slice: &[u32] = &*vec;
assert_eq!(slice.len(), 0);
let slice_mut: &mut [u32] = &mut *vec;
assert_eq!(slice_mut.len(), 0);
assert!(vec.pop().is_none());
assert_eq!(vec.len(), 0);
assert_eq!(vec.capacity(), 0);
}
#[kani::proof]
#[kani::unwind(4)] fn verify_push_pop_operations() {
let alloc = RustSystemAllocator;
let capacity: u32 = kani::any();
kani::assume(capacity > 0 && capacity <= 3);
let mut vec = unsafe { create_valid_inner_vec::<u32>(capacity, &alloc) };
let initial_len: u32 = kani::any();
kani::assume(initial_len < capacity);
vec.len = initial_len;
let value: u32 = kani::any();
let push_position = vec.len;
vec.push(value);
assert_eq!(vec.len, initial_len + 1, "push must increment len");
let written_value = unsafe { core::ptr::read(vec.ptr.add(push_position as usize)) };
assert_eq!(written_value, value, "push must write at correct index");
let old_len = vec.len;
let popped = vec.pop();
assert!(popped.is_some(), "pop on non-empty vec must return Some");
assert_eq!(popped.unwrap(), value, "pop must return the pushed value");
assert_eq!(vec.len, old_len - 1, "pop must decrement len");
assert_eq!(vec.len, initial_len, "push then pop restores original len");
unsafe {
dealloc_inner_vec(vec, &alloc);
}
}
#[kani::proof]
#[kani::unwind(4)] fn verify_deref_only_initialized_region() {
let alloc = RustSystemAllocator;
let capacity: u32 = kani::any();
kani::assume(capacity > 0 && capacity <= 3);
let mut vec = unsafe { create_valid_inner_vec::<u32>(capacity, &alloc) };
let len: u32 = kani::any();
kani::assume(len <= capacity);
for i in 0..len {
vec.len = i;
vec.push(i);
}
let slice: &[u32] = &*vec;
assert_eq!(slice.len(), len as usize);
if len > 0 {
let _val = slice[0]; }
unsafe {
dealloc_inner_vec(vec, &alloc);
}
}
#[kani::proof]
fn verify_drop_semantics() {
unsafe {
let mut drop_count: u32 = 0;
let alloc = RustSystemAllocator;
let mut vec = create_valid_inner_vec::<Droppable>(2, &alloc);
vec.push(Droppable {
value: 100,
drop_counter: &mut drop_count as *mut u32,
});
vec.push(Droppable {
value: 200,
drop_counter: &mut drop_count as *mut u32,
});
let original_len = vec.len;
assert_eq!(original_len, 2, "Should have 2 elements");
{
for _val in vec.iter() {
}
}
let current_len = vec.len;
let drops_after_first = drop_count;
assert!(
drop_count <= (original_len - current_len),
"Drops must not exceed removed elements"
);
{
for _val in vec.iter() {
}
}
let drops_after_second = drop_count;
assert_eq!(
drops_after_second, drops_after_first,
"No additional drops should occur on second iteration"
);
dealloc_inner_vec(vec, &alloc);
}
}
}