Skip to main content

uqa_storage/vector_index/
canonical.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Immutable raw vectors are independent of a physical index's mutation provenance.
8
9use crate::{read_control::StorageReadControl, StorageBackendResult};
10use std::sync::Arc;
11use uqa_core::{
12    memory::{Budgeted, BudgetedVec},
13    DocId,
14};
15
16mod fingerprint;
17mod ordered;
18mod selection;
19pub(crate) use fingerprint::selected_fingerprint;
20pub(crate) use ordered::{copy_vector, ordinal_count};
21pub use selection::SelectedVectorRead;
22
23/// A fixed canonical field view. Owners retain their original visibility, allowance and cancellation. Metadata enumeration never decodes coordinates; a point read owns at most one vector and releases provider locks before returning. This interface makes no claim about physical index lineage or mutation origins.
24pub trait VectorRead: Send + Sync {
25    fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()>;
26    fn dimensions(&self) -> u32;
27    /// Trustworthy identity of this complete fixed field, obtained from retained metadata. None means fixed-model reuse cannot be verified; callers must not replace it with a user-supplied label.
28    fn corpus_fingerprint(
29        &self,
30        control: &StorageReadControl,
31    ) -> StorageBackendResult<Option<[u8; 32]>> {
32        self.check_control(control)?;
33        Ok(None)
34    }
35    fn next_document_after(
36        &self,
37        after: Option<DocId>,
38        control: &StorageReadControl,
39    ) -> StorageBackendResult<Option<DocId>>;
40    /// Validate contiguous ordinal keys and return their count. Missing and explicitly empty tensors both have zero vectors.
41    fn document_vector_count(
42        &self,
43        document: DocId,
44        control: &StorageReadControl,
45    ) -> StorageBackendResult<u64>;
46    /// Read one ordinal without invoking a caller while a provider read lock is held. Absent ordinals return None; malformed values fail.
47    fn read_vector(
48        &self,
49        document: DocId,
50        ordinal: u32,
51        control: &StorageReadControl,
52    ) -> StorageBackendResult<Option<BudgetedVec<f32>>>;
53}
54
55pub type VectorReadSnapshot = Arc<dyn VectorRead>;
56
57/// Decode one little-endian canonical float vector under the invoking read allowance.
58pub fn decode_vector_bytes(
59    bytes: &[u8],
60    dimensions: u32,
61    control: &StorageReadControl,
62) -> StorageBackendResult<BudgetedVec<f32>> {
63    control.check()?;
64    let expected = usize::try_from(dimensions)
65        .map_err(|_| uqa_core::memory::MemoryError::SizeOverflow)?
66        .checked_mul(4)
67        .ok_or(uqa_core::memory::MemoryError::SizeOverflow)?;
68    if bytes.len() != expected {
69        return Err(
70            crate::mvcc::VersionError::InvalidEncoding("canonical vector width mismatch")
71                .into_storage_error(),
72        );
73    }
74    let mut vector = BudgetedVec::new(control.memory());
75    vector.reserve(bytes.len() / 4)?;
76    for (position, bytes) in bytes.as_chunks::<4>().0.iter().enumerate() {
77        if position.is_multiple_of(1024) {
78            control.check()?;
79        }
80        vector.push(f32::from_le_bytes(*bytes))?;
81    }
82    super::validate_vector_values_controlled(dimensions, &vector, Some(control))?;
83    Ok(vector)
84}
85
86impl<T: VectorRead> VectorRead for Budgeted<T> {
87    fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
88        (**self).check_control(control)
89    }
90    fn dimensions(&self) -> u32 {
91        (**self).dimensions()
92    }
93    fn corpus_fingerprint(
94        &self,
95        control: &StorageReadControl,
96    ) -> StorageBackendResult<Option<[u8; 32]>> {
97        (**self).corpus_fingerprint(control)
98    }
99    fn next_document_after(
100        &self,
101        after: Option<DocId>,
102        control: &StorageReadControl,
103    ) -> StorageBackendResult<Option<DocId>> {
104        (**self).next_document_after(after, control)
105    }
106    fn document_vector_count(
107        &self,
108        document: DocId,
109        control: &StorageReadControl,
110    ) -> StorageBackendResult<u64> {
111        (**self).document_vector_count(document, control)
112    }
113    fn read_vector(
114        &self,
115        document: DocId,
116        ordinal: u32,
117        control: &StorageReadControl,
118    ) -> StorageBackendResult<Option<BudgetedVec<f32>>> {
119        (**self).read_vector(document, ordinal, control)
120    }
121}