iqdb_types/vector.rs
1//! Dense `f32` vectors: an owned [`Vector`] and a borrowed [`VectorRef`].
2//!
3//! These are the raw embeddings the iqdb spine indexes and searches. They are
4//! thin, immutable wrappers — construct one from its data through the
5//! fallible [`Vector::new`] (or the equivalent [`TryFrom<Vec<f32>>`] impl),
6//! read it back through the accessors, and (for [`Vector`]) reclaim the
7//! buffer with [`Vector::into_inner`]. There is no in-place mutation.
8
9use crate::error::{IqdbError, Result};
10
11/// An owned dense vector of `f32` components.
12///
13/// Construct one with the fallible [`Vector::new`] (which rejects empty
14/// inputs and non-finite components) or its [`TryFrom<Vec<f32>>`] sibling;
15/// read its components with [`as_slice`](Vector::as_slice) or reclaim the
16/// buffer with [`into_inner`](Vector::into_inner).
17///
18/// Validation at this boundary keeps the rest of the spine free of input
19/// checks: once a `Vector` exists, the math never has to defend against
20/// empty, NaN, or infinite components.
21///
22/// # Representation
23///
24/// Components are stored in a `Box<[f32]>`, not a `Vec<f32>`: a `Vector` is
25/// immutable after construction, so it never needs spare capacity. This makes
26/// the value one machine word smaller than a `Vec`-backed wrapper and
27/// guarantees the backing allocation is sized exactly to the data — meaningful
28/// when millions of vectors are held resident.
29///
30/// # Examples
31///
32/// ```
33/// use iqdb_types::{IqdbError, Vector};
34///
35/// let v = Vector::new(vec![1.0, 0.0, 0.0]).unwrap();
36/// assert_eq!(v.dim(), 3);
37/// assert_eq!(v.as_slice(), &[1.0, 0.0, 0.0]);
38///
39/// // Empty and non-finite inputs are rejected at construction.
40/// assert_eq!(Vector::new(Vec::new()).unwrap_err(), IqdbError::InvalidVector);
41/// assert_eq!(
42/// Vector::new(vec![1.0, f32::NAN]).unwrap_err(),
43/// IqdbError::InvalidVector,
44/// );
45/// ```
46#[derive(Debug, Clone, PartialEq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct Vector(Box<[f32]>);
49
50impl Vector {
51 /// Builds a `Vector` from `data`, validating the contents.
52 ///
53 /// Returns [`IqdbError::InvalidVector`] when:
54 ///
55 /// - `data` is empty, or
56 /// - any component is not finite (NaN or ±infinity).
57 ///
58 /// Validating at the type boundary keeps the rest of the spine — and
59 /// every consumer crate — free of input checks. Once a `Vector` is in
60 /// hand the math can trust its contents.
61 ///
62 /// The `data` buffer is shrunk to fit (`into_boxed_slice`) on success, so
63 /// the stored allocation carries no spare capacity.
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use iqdb_types::{IqdbError, Vector};
69 ///
70 /// let v = Vector::new(vec![0.1, 0.2, 0.3]).unwrap();
71 /// assert_eq!(v.dim(), 3);
72 ///
73 /// assert_eq!(
74 /// Vector::new(Vec::new()).unwrap_err(),
75 /// IqdbError::InvalidVector,
76 /// );
77 /// assert_eq!(
78 /// Vector::new(vec![1.0, f32::INFINITY]).unwrap_err(),
79 /// IqdbError::InvalidVector,
80 /// );
81 /// ```
82 #[inline]
83 pub fn new(data: Vec<f32>) -> Result<Self> {
84 if data.is_empty() {
85 return Err(IqdbError::InvalidVector);
86 }
87 if data.iter().any(|v| !v.is_finite()) {
88 return Err(IqdbError::InvalidVector);
89 }
90 Ok(Self(data.into_boxed_slice()))
91 }
92
93 /// Builds a `Vector` from `data` without validating it.
94 ///
95 /// Available only when the crate is built with the `testing` feature.
96 /// Production code MUST use [`Vector::new`] (or `TryFrom`); a production
97 /// build of `iqdb-types` cannot compile a call to this constructor.
98 ///
99 /// Reserved for tests that deliberately need to construct otherwise-
100 /// invalid vectors to assert downstream behavior on bad input.
101 ///
102 /// # Examples
103 ///
104 /// ```
105 /// # #[cfg(feature = "testing")]
106 /// # {
107 /// use iqdb_types::Vector;
108 ///
109 /// // Constructible only under the `testing` feature.
110 /// let v = Vector::new_unchecked(vec![f32::NAN]);
111 /// assert_eq!(v.len(), 1);
112 /// # }
113 /// ```
114 #[cfg(any(test, feature = "testing"))]
115 #[inline]
116 #[must_use]
117 pub fn new_unchecked(data: Vec<f32>) -> Self {
118 Self(data.into_boxed_slice())
119 }
120
121 /// Borrows the components as a slice.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// use iqdb_types::Vector;
127 ///
128 /// let v = Vector::new(vec![0.5, 0.5]).unwrap();
129 /// assert_eq!(v.as_slice(), &[0.5, 0.5]);
130 /// ```
131 #[inline]
132 #[must_use]
133 pub fn as_slice(&self) -> &[f32] {
134 &self.0
135 }
136
137 /// Returns the number of components.
138 ///
139 /// # Examples
140 ///
141 /// ```
142 /// use iqdb_types::Vector;
143 ///
144 /// assert_eq!(Vector::new(vec![1.0, 2.0]).unwrap().len(), 2);
145 /// ```
146 #[inline]
147 #[must_use]
148 pub fn len(&self) -> usize {
149 self.0.len()
150 }
151
152 /// Returns `true` if the vector has no components.
153 ///
154 /// A `Vector` produced by [`Vector::new`] is never empty (empty inputs
155 /// are rejected at construction); this method is `false` for every
156 /// `Vector` outside the `testing`-gated `Vector::new_unchecked`.
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use iqdb_types::Vector;
162 ///
163 /// assert!(!Vector::new(vec![1.0]).unwrap().is_empty());
164 /// ```
165 #[inline]
166 #[must_use]
167 pub fn is_empty(&self) -> bool {
168 self.0.is_empty()
169 }
170
171 /// Returns the dimensionality of the vector (its component count).
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use iqdb_types::Vector;
177 ///
178 /// assert_eq!(Vector::new(vec![1.0, 2.0, 3.0]).unwrap().dim(), 3);
179 /// ```
180 #[inline]
181 #[must_use]
182 pub fn dim(&self) -> usize {
183 self.0.len()
184 }
185
186 /// Consumes the vector and returns the underlying buffer as a `Vec<f32>`.
187 ///
188 /// This is allocation-free: the boxed slice is converted back to a `Vec`
189 /// with capacity equal to its length (`Box<[f32]>::into_vec`), no copy.
190 ///
191 /// # Examples
192 ///
193 /// ```
194 /// use iqdb_types::Vector;
195 ///
196 /// let v = Vector::new(vec![1.0, 2.0]).unwrap();
197 /// assert_eq!(v.into_inner(), vec![1.0, 2.0]);
198 /// ```
199 #[inline]
200 #[must_use]
201 pub fn into_inner(self) -> Vec<f32> {
202 self.0.into_vec()
203 }
204}
205
206impl TryFrom<Vec<f32>> for Vector {
207 type Error = IqdbError;
208
209 /// Delegates to [`Vector::new`]: rejects empty and non-finite inputs
210 /// with [`IqdbError::InvalidVector`].
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// use iqdb_types::Vector;
216 ///
217 /// let v: Vector = vec![1.0, 0.0].try_into().unwrap();
218 /// assert_eq!(v.dim(), 2);
219 /// ```
220 #[inline]
221 fn try_from(data: Vec<f32>) -> Result<Self> {
222 Self::new(data)
223 }
224}
225
226/// A borrowed dense vector of `f32` components.
227///
228/// A zero-copy view over a `&[f32]`, for passing query vectors without taking
229/// ownership. It is [`Copy`]. With the `serde` feature it derives
230/// [`Serialize`](https://docs.rs/serde) only — a borrowed view cannot be
231/// deserialized into, since there is nowhere to own the decoded data.
232///
233/// # Examples
234///
235/// ```
236/// use iqdb_types::VectorRef;
237///
238/// let data = [1.0, 0.0, 0.0];
239/// let v = VectorRef::from(&data[..]);
240/// assert_eq!(v.dim(), 3);
241/// assert_eq!(v.as_slice(), &[1.0, 0.0, 0.0]);
242/// ```
243#[derive(Debug, Clone, Copy, PartialEq)]
244#[cfg_attr(feature = "serde", derive(serde::Serialize))]
245pub struct VectorRef<'a>(&'a [f32]);
246
247impl<'a> VectorRef<'a> {
248 /// Borrows the components as a slice.
249 ///
250 /// # Examples
251 ///
252 /// ```
253 /// use iqdb_types::VectorRef;
254 ///
255 /// let data = [0.5, 0.5];
256 /// assert_eq!(VectorRef::from(&data[..]).as_slice(), &[0.5, 0.5]);
257 /// ```
258 #[inline]
259 #[must_use]
260 pub fn as_slice(&self) -> &[f32] {
261 self.0
262 }
263
264 /// Returns the number of components.
265 ///
266 /// # Examples
267 ///
268 /// ```
269 /// use iqdb_types::VectorRef;
270 ///
271 /// let data = [1.0, 2.0];
272 /// assert_eq!(VectorRef::from(&data[..]).len(), 2);
273 /// ```
274 #[inline]
275 #[must_use]
276 pub fn len(&self) -> usize {
277 self.0.len()
278 }
279
280 /// Returns `true` if the view has no components.
281 ///
282 /// # Examples
283 ///
284 /// ```
285 /// use iqdb_types::VectorRef;
286 ///
287 /// let empty: [f32; 0] = [];
288 /// assert!(VectorRef::from(&empty[..]).is_empty());
289 /// ```
290 #[inline]
291 #[must_use]
292 pub fn is_empty(&self) -> bool {
293 self.0.is_empty()
294 }
295
296 /// Returns the dimensionality of the view (its component count).
297 ///
298 /// # Examples
299 ///
300 /// ```
301 /// use iqdb_types::VectorRef;
302 ///
303 /// let data = [1.0, 2.0, 3.0];
304 /// assert_eq!(VectorRef::from(&data[..]).dim(), 3);
305 /// ```
306 #[inline]
307 #[must_use]
308 pub fn dim(&self) -> usize {
309 self.0.len()
310 }
311
312 /// Returns the borrowed slice with its original lifetime.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// use iqdb_types::VectorRef;
318 ///
319 /// let data = [1.0, 2.0];
320 /// let v = VectorRef::from(&data[..]);
321 /// let slice: &[f32] = v.into_inner();
322 /// assert_eq!(slice, &[1.0, 2.0]);
323 /// ```
324 #[inline]
325 #[must_use]
326 pub fn into_inner(self) -> &'a [f32] {
327 self.0
328 }
329}
330
331impl<'a> From<&'a [f32]> for VectorRef<'a> {
332 #[inline]
333 fn from(data: &'a [f32]) -> Self {
334 Self(data)
335 }
336}