use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::fmt;
use std::ptr::{self, NonNull};
pub struct ContiguousVectors {
data: NonNull<f32>,
dimension: usize,
count: usize,
capacity: usize,
}
unsafe impl Send for ContiguousVectors {}
unsafe impl Sync for ContiguousVectors {}
impl fmt::Debug for ContiguousVectors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ContiguousVectors")
.field("dimension", &self.dimension)
.field("count", &self.count)
.field("capacity", &self.capacity)
.finish_non_exhaustive()
}
}
impl ContiguousVectors {
#[allow(clippy::cast_ptr_alignment)] pub fn new(dimension: usize, capacity: usize) -> crate::error::Result<Self> {
if dimension == 0 {
return Err(crate::error::Error::InvalidDimension {
dimension: 0,
min: 1,
max: 65_536,
});
}
let capacity = capacity.max(16); let layout = Self::layout(dimension, capacity)?;
let ptr = unsafe { alloc_zeroed(layout) };
let data = NonNull::new(ptr.cast::<f32>()).ok_or_else(|| {
crate::error::Error::AllocationFailed(
"ContiguousVectors: allocator returned null".to_string(),
)
})?;
Ok(Self {
data,
dimension,
count: 0,
capacity,
})
}
fn layout(dimension: usize, capacity: usize) -> crate::error::Result<Layout> {
let size = dimension
.checked_mul(capacity)
.and_then(|s| s.checked_mul(std::mem::size_of::<f32>()))
.ok_or_else(|| {
crate::error::Error::AllocationFailed(format!(
"Size overflow: {dimension} * {capacity} * {}",
std::mem::size_of::<f32>()
))
})?;
let align = 64; Layout::from_size_align(size.max(64), align)
.map_err(|e| crate::error::Error::AllocationFailed(format!("Invalid layout: {e}")))
}
#[inline]
#[must_use]
pub const fn dimension(&self) -> usize {
self.dimension
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
#[must_use]
pub const fn capacity(&self) -> usize {
self.capacity
}
#[inline]
#[must_use]
pub fn as_flat_slice(&self) -> &[f32] {
if self.count == 0 {
return &[];
}
let total = self.count * self.dimension;
unsafe { std::slice::from_raw_parts(self.data.as_ptr(), total) }
}
#[must_use]
pub fn gather_flat(&self, indices: &[usize]) -> Vec<f32> {
let mut result = Vec::with_capacity(indices.len() * self.dimension);
for &idx in indices {
if let Some(vec) = self.get(idx) {
result.extend_from_slice(vec);
}
}
result
}
#[inline]
#[must_use]
pub const fn memory_bytes(&self) -> usize {
self.capacity * self.dimension * std::mem::size_of::<f32>()
}
pub fn ensure_capacity(&mut self, required_capacity: usize) -> crate::error::Result<()> {
if required_capacity > self.capacity {
let new_capacity = required_capacity.max(self.capacity * 2);
self.resize(new_capacity)?;
}
Ok(())
}
pub fn insert_at(&mut self, index: usize, vector: &[f32]) -> crate::error::Result<()> {
if vector.len() != self.dimension {
return Err(crate::error::Error::DimensionMismatch {
expected: self.dimension,
actual: vector.len(),
});
}
self.ensure_capacity(index + 1)?;
let offset = index * self.dimension;
unsafe {
ptr::copy_nonoverlapping(
vector.as_ptr(),
self.data.as_ptr().add(offset),
self.dimension,
);
}
if index >= self.count {
self.count = index + 1;
}
Ok(())
}
pub fn push(&mut self, vector: &[f32]) -> crate::error::Result<()> {
self.insert_at(self.count, vector)
}
pub fn push_batch(&mut self, vectors: &[&[f32]]) -> crate::error::Result<usize> {
if vectors.is_empty() {
return Ok(0);
}
for vector in vectors {
if vector.len() != self.dimension {
return Err(crate::error::Error::DimensionMismatch {
expected: self.dimension,
actual: vector.len(),
});
}
}
self.ensure_capacity(self.count + vectors.len())?;
for vector in vectors {
let offset = self.count * self.dimension;
unsafe {
std::ptr::copy_nonoverlapping(
vector.as_ptr(),
self.data.as_ptr().add(offset),
self.dimension,
);
}
self.count += 1;
}
Ok(vectors.len())
}
#[inline]
#[must_use]
pub fn get(&self, index: usize) -> Option<&[f32]> {
if index >= self.count {
return None;
}
let offset = index * self.dimension;
Some(unsafe { std::slice::from_raw_parts(self.data.as_ptr().add(offset), self.dimension) })
}
#[inline]
#[must_use]
pub unsafe fn get_unchecked(&self, index: usize) -> &[f32] {
debug_assert!(
index < self.count,
"index out of bounds: index={index}, count={}",
self.count
);
let offset = index * self.dimension;
std::slice::from_raw_parts(self.data.as_ptr().add(offset), self.dimension)
}
#[inline]
pub fn prefetch(&self, index: usize) {
if index < self.count {
let offset = index * self.dimension;
let vector = unsafe {
std::slice::from_raw_parts(self.data.as_ptr().add(offset), self.dimension)
};
crate::simd_native::prefetch_vector_multi_cache_line(vector);
}
}
#[inline]
pub fn prefetch_batch(&self, indices: &[usize]) {
for &idx in indices {
self.prefetch(idx);
}
}
fn resize(&mut self, new_capacity: usize) -> crate::error::Result<()> {
if new_capacity <= self.capacity {
return Ok(());
}
let old_layout = Self::layout(self.dimension, self.capacity)?;
let new_layout = Self::layout(self.dimension, new_capacity)?;
let new_data = Self::alloc_and_copy(new_layout, self.data, self.count, self.dimension)?;
unsafe {
dealloc(self.data.as_ptr().cast::<u8>(), old_layout);
}
self.data = new_data;
self.capacity = new_capacity;
Ok(())
}
#[allow(clippy::cast_ptr_alignment)] fn alloc_and_copy(
new_layout: Layout,
src: NonNull<f32>,
count: usize,
dimension: usize,
) -> crate::error::Result<NonNull<f32>> {
use crate::alloc_guard::AllocGuard;
let guard = AllocGuard::new_zeroed(new_layout).ok_or_else(|| {
crate::error::Error::AllocationFailed(format!(
"Failed to allocate {} bytes for ContiguousVectors resize",
new_layout.size()
))
})?;
let new_data = NonNull::new(guard.cast::<f32>()).ok_or_else(|| {
crate::error::Error::AllocationFailed("AllocGuard returned null pointer".to_string())
})?;
if count > 0 {
let copy_size = count * dimension;
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), new_data.as_ptr(), copy_size);
}
}
let _ = guard.into_raw();
Ok(new_data)
}
pub fn reorder(&mut self, new_order: &[usize]) -> crate::error::Result<()> {
if new_order.len() != self.count {
return Err(crate::error::Error::Internal(format!(
"Reorder permutation length {} != vector count {}",
new_order.len(),
self.count
)));
}
if self.count == 0 {
return Ok(());
}
self.reorder_copy(new_order)
}
fn reorder_copy(&mut self, new_order: &[usize]) -> crate::error::Result<()> {
use crate::alloc_guard::AllocGuard;
let new_layout = Self::layout(self.dimension, self.count)?;
let guard = AllocGuard::new_zeroed(new_layout).ok_or_else(|| {
crate::error::Error::AllocationFailed(format!(
"Reorder: failed to allocate {} bytes",
new_layout.size()
))
})?;
let new_ptr = NonNull::new(guard.cast::<f32>()).ok_or_else(|| {
crate::error::Error::AllocationFailed(
"Reorder: AllocGuard returned null pointer".to_string(),
)
})?;
self.copy_permuted_vectors(new_ptr.as_ptr(), new_order)?;
let _ = guard.into_raw();
let old_layout = Self::layout(self.dimension, self.capacity)?;
unsafe { dealloc(self.data.as_ptr().cast::<u8>(), old_layout) };
self.data = new_ptr;
self.capacity = self.count;
Ok(())
}
fn copy_permuted_vectors(
&self,
dst: *mut f32,
new_order: &[usize],
) -> crate::error::Result<()> {
let dim = self.dimension;
for (new_idx, &old_idx) in new_order.iter().enumerate() {
if old_idx >= self.count {
return Err(crate::error::Error::Internal(format!(
"Reorder index {old_idx} out of bounds (count={})",
self.count
)));
}
unsafe {
ptr::copy_nonoverlapping(
self.data.as_ptr().add(old_idx * dim),
dst.add(new_idx * dim),
dim,
);
}
}
Ok(())
}
#[inline]
#[must_use]
pub fn dot_product(&self, index: usize, query: &[f32]) -> Option<f32> {
let vector = self.get(index)?;
Some(crate::simd_native::dot_product_native(vector, query))
}
const PREFETCH_DISTANCE: usize = 4;
#[must_use]
pub fn batch_dot_products(&self, indices: &[usize], query: &[f32]) -> Vec<f32> {
let mut results = Vec::with_capacity(indices.len());
for (i, &idx) in indices.iter().enumerate() {
if i + Self::PREFETCH_DISTANCE < indices.len() {
self.prefetch(indices[i + Self::PREFETCH_DISTANCE]);
}
if let Some(score) = self.dot_product(idx, query) {
results.push(score);
}
}
results
}
}
impl Drop for ContiguousVectors {
fn drop(&mut self) {
let Ok(layout) = Self::layout(self.dimension, self.capacity) else {
tracing::error!(
"ContiguousVectors::drop: layout computation failed \
(dim={}, cap={}), leaking memory",
self.dimension,
self.capacity,
);
return;
};
unsafe {
dealloc(self.data.as_ptr().cast::<u8>(), layout);
}
}
}
#[must_use]
pub fn batch_dot_products_simd(vectors: &[&[f32]], query: &[f32]) -> Vec<f32> {
crate::simd_native::batch_dot_product_native(vectors, query)
}
const SIMD_WIDTH: usize = 8;
#[must_use]
pub fn pad_to_simd_width(vector: &[f32]) -> Vec<f32> {
let len = vector.len();
if len == 0 {
return Vec::new();
}
let padded_len = len.div_ceil(SIMD_WIDTH) * SIMD_WIDTH;
let mut padded = vec![0.0_f32; padded_len];
padded[..len].copy_from_slice(vector);
padded
}
#[must_use]
pub fn batch_cosine_similarities(vectors: &[&[f32]], query: &[f32]) -> Vec<f32> {
let prefetch_distance = crate::simd_native::calculate_prefetch_distance(query.len());
let mut results = Vec::with_capacity(vectors.len());
for (i, v) in vectors.iter().enumerate() {
if i + prefetch_distance < vectors.len() {
crate::simd_native::prefetch_vector(vectors[i + prefetch_distance]);
}
results.push(crate::simd_native::cosine_similarity_native(v, query));
}
results
}