use crate::{read_control::StorageReadControl, StorageBackendResult};
use std::sync::Arc;
use uqa_core::{
memory::{Budgeted, BudgetedVec},
DocId,
};
mod fingerprint;
mod ordered;
mod selection;
pub(crate) use fingerprint::selected_fingerprint;
pub(crate) use ordered::{copy_vector, ordinal_count};
pub use selection::SelectedVectorRead;
#[cfg(test)]
mod tests;
pub trait VectorRead: Send + Sync {
fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()>;
fn dimensions(&self) -> u32;
fn corpus_fingerprint(
&self,
control: &StorageReadControl,
) -> StorageBackendResult<Option<[u8; 32]>> {
self.check_control(control)?;
Ok(None)
}
fn next_document_after(
&self,
after: Option<DocId>,
control: &StorageReadControl,
) -> StorageBackendResult<Option<DocId>>;
fn document_vector_count(
&self,
document: DocId,
control: &StorageReadControl,
) -> StorageBackendResult<u64>;
fn read_vector(
&self,
document: DocId,
ordinal: u32,
control: &StorageReadControl,
) -> StorageBackendResult<Option<BudgetedVec<f32>>>;
}
pub type VectorReadSnapshot = Arc<dyn VectorRead>;
pub(crate) fn canonical_vectors_equal(
source: &dyn VectorRead,
document: DocId,
vectors: &[Vec<f32>],
control: &StorageReadControl,
) -> StorageBackendResult<bool> {
source.check_control(control)?;
for vector in vectors {
super::validate_vector_values_controlled(source.dimensions(), vector, Some(control))?;
}
if source.document_vector_count(document, control)? != vectors.len() as u64 {
return Ok(false);
}
for (ordinal, expected) in vectors.iter().enumerate() {
let ordinal =
u32::try_from(ordinal).map_err(|_| uqa_core::memory::MemoryError::SizeOverflow)?;
let Some(actual) = source.read_vector(document, ordinal, control)? else {
return Ok(false);
};
if actual.len() != expected.len() {
return Ok(false);
}
for (actual, expected) in actual.chunks(1024).zip(expected.chunks(1024)) {
source.check_control(control)?;
if actual
.iter()
.zip(expected)
.any(|(a, b)| a.to_bits() != b.to_bits())
{
return Ok(false);
}
}
}
source.check_control(control)?;
Ok(true)
}
pub fn decode_vector_bytes(
bytes: &[u8],
dimensions: u32,
control: &StorageReadControl,
) -> StorageBackendResult<BudgetedVec<f32>> {
control.check()?;
let expected = usize::try_from(dimensions)
.map_err(|_| uqa_core::memory::MemoryError::SizeOverflow)?
.checked_mul(4)
.ok_or(uqa_core::memory::MemoryError::SizeOverflow)?;
if bytes.len() != expected {
return Err(
crate::mvcc::VersionError::InvalidEncoding("canonical vector width mismatch")
.into_storage_error(),
);
}
let mut vector = BudgetedVec::new(control.memory());
vector.reserve(bytes.len() / 4)?;
for (position, bytes) in bytes.as_chunks::<4>().0.iter().enumerate() {
if position.is_multiple_of(1024) {
control.check()?;
}
vector.push(f32::from_le_bytes(*bytes))?;
}
super::validate_vector_values_controlled(dimensions, &vector, Some(control))?;
Ok(vector)
}
impl<T: VectorRead> VectorRead for Budgeted<T> {
fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
(**self).check_control(control)
}
fn dimensions(&self) -> u32 {
(**self).dimensions()
}
fn corpus_fingerprint(
&self,
control: &StorageReadControl,
) -> StorageBackendResult<Option<[u8; 32]>> {
(**self).corpus_fingerprint(control)
}
fn next_document_after(
&self,
after: Option<DocId>,
control: &StorageReadControl,
) -> StorageBackendResult<Option<DocId>> {
(**self).next_document_after(after, control)
}
fn document_vector_count(
&self,
document: DocId,
control: &StorageReadControl,
) -> StorageBackendResult<u64> {
(**self).document_vector_count(document, control)
}
fn read_vector(
&self,
document: DocId,
ordinal: u32,
control: &StorageReadControl,
) -> StorageBackendResult<Option<BudgetedVec<f32>>> {
(**self).read_vector(document, ordinal, control)
}
}