Skip to main content

uqa_storage/read_only_snapshot/
canonical_vectors.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Immutable vector facets share captured data, allocation leases and one snapshot identity.
8
9use super::ReadOnlySnapshot;
10use crate::{read_control::StorageReadControl, vector_index::VectorRead, StorageBackendResult};
11use sha2::{Digest, Sha256};
12use std::sync::Arc;
13use uqa_core::{
14    memory::{Budgeted, BudgetedVec},
15    DocId,
16};
17
18struct CanonicalVectors<T: ?Sized> {
19    owner: ReadOnlySnapshot<T>,
20    fingerprint: [u8; 32],
21}
22
23impl<T: VectorRead + 'static> ReadOnlySnapshot<T> {
24    /// Expose canonical values from an already captured immutable owner without copying coordinates or its directory. Cache owners may remain unbound; each reader later binds its own cancellation. A supplied control admits the facet header, while None preserves the existing uncontrolled snapshot API. The unique view identity is conservative across independently captured equal corpora and stable across shared/nested readers.
25    pub fn with_canonical_vectors(
26        mut self,
27        control: Option<&StorageReadControl>,
28    ) -> StorageBackendResult<Self> {
29        if let Some(control) = control {
30            control.check()?;
31        }
32        if self.3.is_some() {
33            return Ok(self);
34        }
35        let identity =
36            crate::catalog::new_nonzero_catalog_identity("vector snapshot", "canonical values")?;
37        let mut digest = Sha256::new();
38        digest.update(b"uqa-immutable-vector-view-v1\0");
39        digest.update(identity);
40        // This clone has no canonical facet yet: the ownership graph remains acyclic.
41        let source = CanonicalVectors {
42            owner: self.clone(),
43            fingerprint: digest.finalize().into(),
44        };
45        self.3 = Some(match control {
46            Some(control) => {
47                Budgeted::new(source, control.memory().empty_reservation()).into_shared()?
48            }
49            None => Arc::new(source),
50        });
51        Ok(self)
52    }
53}
54
55impl<T: VectorRead + ?Sized> VectorRead for CanonicalVectors<T> {
56    fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
57        self.owner.check_control(control)
58    }
59    fn dimensions(&self) -> u32 {
60        self.owner.dimensions()
61    }
62    fn corpus_fingerprint(
63        &self,
64        control: &StorageReadControl,
65    ) -> StorageBackendResult<Option<[u8; 32]>> {
66        self.check_control(control)?;
67        Ok(Some(self.fingerprint))
68    }
69    fn next_document_after(
70        &self,
71        after: Option<DocId>,
72        control: &StorageReadControl,
73    ) -> StorageBackendResult<Option<DocId>> {
74        self.owner.next_document_after(after, control)
75    }
76    fn document_vector_count(
77        &self,
78        document: DocId,
79        control: &StorageReadControl,
80    ) -> StorageBackendResult<u64> {
81        self.owner.document_vector_count(document, control)
82    }
83    fn read_vector(
84        &self,
85        document: DocId,
86        ordinal: u32,
87        control: &StorageReadControl,
88    ) -> StorageBackendResult<Option<BudgetedVec<f32>>> {
89        self.owner.read_vector(document, ordinal, control)
90    }
91}