Coverage Report

Created: 2026-06-30 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/diskann-0.53.0/src/provider.rs
Line
Count
Source
1
/*
2
 * Copyright (c) Microsoft Corporation.
3
 * Licensed under the MIT license.
4
 */
5
6
//! The [`DataProvider`] trait encompasses the following concepts:
7
//!
8
//! * Storage for arbitrary data that is contextually accessed through the [`Accessor`] trait.
9
//!
10
//! * Mapping between an "external id" (a unique external identifier for an entry in data
11
//!   store) and an "internal id", which is a simple typed used as a handle to vector data
12
//!   internally.
13
//!
14
//! * Support for deletion via the [`Delete`] sub-trait.
15
//!
16
//! # Important Related Traits
17
//!
18
//! The [`DataProvider`] trait is really an ensemble of multiple related traits all working
19
//! together to solve a problem.
20
//!
21
//! Important associated traits are described here.
22
//!
23
//! * [`SetElement`]: This is the method that allows assigning of data into the
24
//!   [`DataProvider`] via external ID. This trait is parameterized by the type of the
25
//!   "vector" being assigned, providing a mechanism for inclusion of arbitrary associated
26
//!   data along with the raw vector.
27
//!
28
//!   The responsibility of the `set_element` method is several fold:
29
//!
30
//!   * It must assign an internal ID for the provided external ID. It can do this by using
31
//!     a naive identity map.
32
//!
33
//!   * After insertion, the implementer may return a guard that will notify the provider
34
//!     of an unsuccessful operation, allowing automatic cleanup and consistency control
35
//!     over the external ID to internal ID mapping.
36
//!
37
//! * [`Delete`]: A sub-trait of [`DataProvider`] indicating that the provider supports the
38
//!   notion of deleted items.
39
//!
40
//!   We differentiate between two different kinds of deletion:
41
//!
42
//!   * Soft Deletion: Where a external ID is marked as deleted, but the internal vector id
43
//!     may still be reachable in an index.
44
//!
45
//!   * Hard Deletion (aka "release): This deletes an item by internal ID, removing it
46
//!     completely from the store.
47
//!
48
//!   Note that the exact semantics of deletion are defined by the implementer. Some
49
//!   implementations may allow internal IDs to be reused immediately following the deletion
50
//!   of the external ID while others may wait for a "release".
51
//!
52
//! * [`HasId`]: Traits such as [`Accessor`], [`NeighborAccessor`] and [`DelegateNeighbors`]
53
//!   all need to interact with the underlying [`DataProvider`] using an internal ID type.
54
//!
55
//!   The [`HasId`] trait provides a common base-trait for these related concepts, which
56
//!   ensures implementers only need to define and constrain it once.
57
//!
58
//! * [`Accessor`]: A contextual proxy object for retrieving data from the [`DataProvider`].
59
//!   The key idea behind an accessor is that data to be retrieved from a data store
60
//!   is contextual. In other words, in some contexts, we may retrieve one kind of data
61
//!   (for example, full precision vectors), while in other contexts we may want to
62
//!   retrieve another kind (such as quantized vectors).
63
//!
64
//!   Accessors can be implemented as simple handles to a provider, or can have local
65
//!   scratch to assist in bulk operations.
66
//!
67
//!   Finally, one handy feature of accessors is that they can carry a lifetime, which
68
//!   surprisingly can make writing algorithms involving borrows easier than trying to
69
//!   manage a lifetime strictly through associated types with lifetimes.
70
//!
71
//! * [`BuildDistanceComputer`]: A sub-trait of [`Accessor`] that allows for random-access
72
//!   distance computations on the retrieved elements.
73
//!
74
//! * [`BuildQueryComputer`]: A sub-trait of [`Accessor`] that allows for specialized query
75
//!   based computations. This allows a query to be pre-processed in a way that allows
76
//!   faster computations.
77
//!
78
//! # Neighbor Delegation
79
//!
80
//! Index search requires that accessor types implement both the data-centric [`Accessor`]
81
//! trait and the graph retrieval [`NeighborAccessor`]/[`NeighborAccessorMut`] traits.
82
//!
83
//! While having multiple implementations of [`Accessor`] is common to support different
84
//! kinds of searches in the quantized space, neighbor retrieval commonly need not vary.
85
//! Instead of requiring all implementations of [`Accessor`] to manually forward the methods
86
//! (both required and provided) in the [`NeighborAccesosr`] traits, we use a delegation
87
//! technique supplied by the [`DelegateNeighbor`] trait.
88
//!
89
//! [`Accessor`] types should implement [`DelegateNeighbor`] to return a [`NeighborAccessor`].
90
//! Implementation of [`DelegateNeighbors`] will automatically implement [`AsNeighbor`] and
91
//! [`AsNeighborMut`] (the latter is only applicable if the returned type implements
92
//! [`NeighborAccessorMut`].
93
//!
94
//! Similarly, algorithms requiring graph access should accept `&mut T` where
95
//! `T: AsNeighbor` or `T: AsNeighborMut`. This provides access to blanket implementations
96
//! of [`NeighborAccessor`] and [`NeighborAccessorMut`] for `&mut T`.
97
98
use std::ops::Deref;
99
100
use diskann_utils::Reborrow;
101
use diskann_vector::{DistanceFunction, PreprocessedDistanceFunction};
102
use sealed::{BoundTo, Sealed};
103
104
use crate::{ANNError, ANNResult, error::ToRanked, graph::AdjacencyList, utils::VectorId};
105
106
//////////////////////
107
// ExecutionContext //
108
//////////////////////
109
110
/// An execution context given to various providers, threaded through insertions, searches
111
/// etc. for information forwarding.
112
pub trait ExecutionContext: Send + Sync + Clone + 'static {
113
    //////////////////////
114
    // Provided Methods //
115
    //////////////////////
116
117
    /// Provide a customization point for tasks spawned while under this context.
118
    ///
119
    /// The future `f` is a Future that DiskANN intends to spawn as a task using an spawn
120
    /// method such as `tokio::spawn`. `DiskANN will pass this Future through this function
121
    /// before creating the task.
122
    ///
123
    /// This allows the `ExecutionContext` to nest that Future inside another Future if desired.
124
    /// An example of such a nesting would be to nest `f` inside of a profiling future
125
    /// to record the CPU cycles spent executing `f`.
126
    ///
127
    /// The default implementation of this method is the identity, simply passing through
128
    /// the future unmodified.
129
0
    fn wrap_spawn<F, T>(&self, f: F) -> impl std::future::Future<Output = T> + Send + 'static
130
0
    where
131
0
        F: std::future::Future<Output = T> + Send + 'static,
132
    {
133
0
        f
134
0
    }
135
}
136
137
//////////////////
138
// DataProvider //
139
//////////////////
140
141
/// A base trait for the struct providing data into the index.
142
///
143
/// The requirements on this trait are quite sparse. Instead, additional behavior is accessed
144
/// through derived and related traits, namely
145
///
146
/// * [`SetElement`]: An overloadable version of `set_vector`.
147
/// * [`Accessor`]: An overloadable, contextual class for retrieving data elements from
148
///   the data provider.
149
///
150
/// Indexing algorithms explose overloadable "strategies" that allow data provider to
151
/// select the accessor.
152
///
153
/// Example strategies include:
154
///
155
/// * [`crate::graph::glue::SearchStrategy`]
156
/// * [`crate::graph::glue::PruneStrategy`]
157
/// * [`crate::graph::glue::InsertStrategy`]
158
///
159
/// # Type Constraints:
160
///
161
/// * `Sized`: Data provider types are used in contexts where `Self` is used to instantiate
162
///   a generic. This can only be done if `Self: Sized`.
163
///
164
/// * `Send` and `Sync`: Mostly to help async code compile and be `Send`.
165
///
166
/// * `'static`: Helpful for avoiding excess lifetime constraints.
167
pub trait DataProvider: Sized + Send + Sync + 'static {
168
    type Context: ExecutionContext;
169
    type InternalId: VectorId;
170
    type ExternalId: PartialEq + Send + Sync + 'static;
171
172
    type Error: ToRanked + std::fmt::Debug + Send + Sync + 'static;
173
174
    /// The operation guard returned by [`SetElement::set_element`] to notify `self` if an
175
    /// operation fails.
176
    ///
177
    /// This is required to be `'static` for multi-insert compatibility (where it is required
178
    /// to cross spawn boundaries).
179
    type Guard: Guard<Id = Self::InternalId> + 'static;
180
181
    /// Translate an external id to its corresponding internal id.
182
    ///
183
    /// The vector referenced by `gid` must already have been added to the provider via
184
    /// [`SetElement`]. The mapping is undefined until then.
185
    fn to_internal_id(
186
        &self,
187
        context: &Self::Context,
188
        gid: &Self::ExternalId,
189
    ) -> Result<Self::InternalId, Self::Error>;
190
191
    /// Translate an internal id to its corresponding external id.
192
    fn to_external_id(
193
        &self,
194
        context: &Self::Context,
195
        id: Self::InternalId,
196
    ) -> Result<Self::ExternalId, Self::Error>;
197
}
198
199
////////////
200
// Delete //
201
////////////
202
203
pub trait Delete: DataProvider {
204
    /// Delete an item by external ID.
205
    ///
206
    /// Note that internal vector IDs may still be reachable. In the context of a graph
207
    /// index, this is equivalent to a "soft" delete where the deleted ID should no longer
208
    /// be returned as the result of search methods, but may still be accessed by its
209
    /// private ID during graph node expansion.
210
    fn delete(
211
        &self,
212
        context: &Self::Context,
213
        gid: &Self::ExternalId,
214
    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
215
216
    /// Release a node by an internal ID.
217
    ///
218
    /// This is called by the index only when there are no longer any incoming edges to
219
    /// a particular data point.
220
    ///
221
    /// In particular, the index makes the guarantee that when it invokes `release` on an
222
    /// internal ID, it will not try to retrive an element via the same internal ID via
223
    /// an accessor derived from `self` until `SetElement` yields the internal ID.
224
    fn release(
225
        &self,
226
        context: &Self::Context,
227
        id: Self::InternalId,
228
    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
229
230
    /// Check the status via internal ID.
231
    fn status_by_internal_id(
232
        &self,
233
        context: &Self::Context,
234
        id: Self::InternalId,
235
    ) -> impl std::future::Future<Output = Result<ElementStatus, Self::Error>> + Send;
236
237
    /// Check the status via external ID.
238
    fn status_by_external_id(
239
        &self,
240
        context: &Self::Context,
241
        gid: &Self::ExternalId,
242
    ) -> impl std::future::Future<Output = Result<ElementStatus, Self::Error>> + Send;
243
244
    /// A potentially optimized bulk version of `status_by_internal_id`.
245
0
    fn statuses_unordered<Itr, F>(
246
0
        &self,
247
0
        context: &Self::Context,
248
0
        itr: Itr,
249
0
        mut f: F,
250
0
    ) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send
251
0
    where
252
0
        Itr: Iterator<Item = Self::InternalId> + Send,
253
0
        F: FnMut(Result<ElementStatus, Self::Error>, Self::InternalId) + Send,
254
    {
255
0
        async move {
256
0
            for i in itr {
257
0
                f(self.status_by_internal_id(context, i).await, i);
258
            }
259
0
            Ok(())
260
0
        }
261
0
    }
262
}
263
264
/// Describe the status of accessing a vector by internal or external id.
265
#[derive(Debug, Clone, Copy, PartialEq)]
266
pub enum ElementStatus {
267
    /// The ID is valid.
268
    Valid,
269
    /// The ID used to be valid but points to a deleted element. Some values behind the
270
    /// deleted may still be accessible until it is removed entirely from the graph.
271
    Deleted,
272
}
273
274
impl ElementStatus {
275
    /// Return `true` if `ElementStatus::Valid`.
276
0
    pub fn is_valid(self) -> bool {
277
0
        self == Self::Valid
278
0
    }
279
280
    /// Return `true` if `ElementStatus::Deleted`.
281
0
    pub fn is_deleted(self) -> bool {
282
0
        self == Self::Deleted
283
0
    }
284
}
285
286
///////////
287
// HasId //
288
///////////
289
290
/// Indicate an association with an Id type.
291
pub trait HasId {
292
    type Id: VectorId;
293
}
294
295
impl<T> HasId for &T
296
where
297
    T: HasId,
298
{
299
    type Id = T::Id;
300
}
301
302
impl<T> HasId for &mut T
303
where
304
    T: HasId,
305
{
306
    type Id = T::Id;
307
}
308
309
////////////////
310
// SetElement //
311
////////////////
312
313
/// An overloadable `DataProvider` sub-trait allowing element assignment.
314
pub trait SetElement<T>: DataProvider {
315
    /// The kind of error yielded by `set_element`.
316
    type SetError: ToRanked + std::fmt::Debug + Send + Sync + 'static;
317
318
    /// Internally store the value of `element` and associate it with `id`.
319
    ///
320
    /// The storing does not necessarily need to be lossless if, for example, the parent
321
    /// data provider solely uses a quantized representation.
322
    ///
323
    /// Note that it is suggests that a well-behaved implementation rolls-back internal
324
    /// state in the event that an error is returned.
325
    ///
326
    /// Furthermore, a guard is returned. The caller of `set_element` will `complete` the
327
    /// guard once operation completes successfully. Unsuccessful execution may roll back
328
    /// external to internal mappings, but may not reclaim the local slot.
329
    fn set_element(
330
        &self,
331
        context: &Self::Context,
332
        id: &Self::ExternalId,
333
        element: T,
334
    ) -> impl std::future::Future<Output = Result<Self::Guard, Self::SetError>> + Send;
335
}
336
337
/// A guard object that will be completed when an insert operation is successful.
338
///
339
/// This is used as the return type of [`SetElement`] (for example, at the beginning of
340
/// an insert operation), and has three main jobs:
341
///
342
/// 1. It provides a means for the data provider to associate a private ID with a public ID
343
///    and communicate that association to algorithms.
344
///
345
/// 2. The guard's lifetime is associated with the duration of an operation (e.g. insert),
346
///    and calling the `complete` method indicates a successful completion of that operation.
347
///
348
/// 3. Dropping the guard before calling `complete` can notifies the data provider of an
349
///    unsuccessful completion of the operation, allowing the provider to clean up internal
350
///    state.
351
pub trait Guard: Send + Sync + 'static {
352
    /// The Id type associated with the Guard.
353
    type Id;
354
355
    /// Successfully complete the guarded operation.
356
    fn complete(self) -> impl std::future::Future<Output = ()> + Send;
357
358
    /// Retrieve the inernal ID the data provider assigns to the external ID.
359
    fn id(&self) -> Self::Id;
360
}
361
362
/// A simple `Guard` implementation where completion and dropping is a no-op.
363
#[derive(Debug, Default)]
364
pub struct NoopGuard<I>(I);
365
366
impl<I> NoopGuard<I> {
367
    /// Construct a new guard that yields `id` on `retrieve`.
368
0
    pub fn new(id: I) -> Self {
369
0
        Self(id)
370
0
    }
Unexecuted instantiation: <diskann::provider::NoopGuard<u64>>::new
Unexecuted instantiation: <diskann::provider::NoopGuard<_>>::new
371
}
372
373
impl<I> Guard for NoopGuard<I>
374
where
375
    I: Send + Sync + Copy + 'static,
376
{
377
    type Id = I;
378
0
    async fn complete(self) {}
Unexecuted instantiation: <diskann::provider::NoopGuard<u64> as diskann::provider::Guard>::complete
Unexecuted instantiation: <diskann::provider::NoopGuard<_> as diskann::provider::Guard>::complete
Unexecuted instantiation: <diskann::provider::NoopGuard<u64> as diskann::provider::Guard>::complete::{closure#0}
Unexecuted instantiation: <diskann::provider::NoopGuard<_> as diskann::provider::Guard>::complete::{closure#0}
379
0
    fn id(&self) -> Self::Id {
380
0
        self.0
381
0
    }
Unexecuted instantiation: <diskann::provider::NoopGuard<u64> as diskann::provider::Guard>::id
Unexecuted instantiation: <diskann::provider::NoopGuard<_> as diskann::provider::Guard>::id
382
}
383
384
//////////////
385
// Accessor //
386
//////////////
387
388
/// A lens through which [`DataProvider`]s contextually viewed.
389
///
390
/// Accessors are **not** required to be `'static` and almost always contain a scoped
391
/// reference to their parent provider.
392
///
393
/// # Element Relationship
394
///
395
/// Accessors are expected to define two associated element types:
396
///
397
/// * `Element<'_>`: The type returned by `get_element`. This is scoped to the borrow
398
///   of the accessor at the `get_element` call site. As a consequence, there may only
399
///   be one such `Element` active at a time.
400
///
401
/// * `ElementRef<'_>`: A generalized borrowed form of `Element` obtainable via
402
///   `Reborrow`. This is the type on which distance computations are defined and is the
403
///   element type provided to the `on_element_unordered` bulk operation.
404
///
405
/// The below diagram summarizes the relationship.
406
///
407
/// ```text
408
/// Element<'_> ------ Reborrow ----> ElementRef<'_>
409
///        ~~~~                                 ~~~~
410
///         ^                                    ^
411
///         |                                    |
412
///   Lifetime tied                       Arbitrarily short
413
///  to the Accessor                     lifetime decoupled
414
///                                       from the Accessor
415
/// ```
416
///
417
/// ## Technical Details
418
///
419
/// The need for `ElementRef` arises to allow HRTB bounds to distance computers without
420
/// inducing a `'static` bound on `Self`. In traits like [`BuildQueryComputer`], attempting
421
/// to use `Element` directly will result in such a requirement on the implementing Accessor.
422
pub trait Accessor: HasId + Send + Sync {
423
    /// A generalized reference type used for distance computations.
424
    ///
425
    /// Note that the lifetime of `ElementRef` is unconstrained and thus using it in a
426
    /// [HRTB](https://doc.rust-lang.org/nomicon/hrtb.html) will not induce a `'static`
427
    /// requirement on `Self`.
428
    type ElementRef<'a>;
429
430
    /// The concrete type of the data element associated with this accessor.
431
    ///
432
    /// For distance computations, this should be cheaply convertible via [`Reborrow`] to
433
    /// `Self::ElementRef`.
434
    type Element<'a>: for<'b> Reborrow<'b, Target = Self::ElementRef<'b>> + Send + Sync
435
    where
436
        Self: 'a;
437
438
    /// The error (if any) returned by [`Self::get_element`].
439
    type GetError: ToRanked + std::fmt::Debug + Send + Sync + 'static;
440
441
    /// Return the value associated with the key `id`.
442
    ///
443
    /// It is expected that index algorithms will only invoke `get_element` on valid IDs,
444
    /// that can be derived from [`SetElement::set_element`] or by some other means.
445
    ///
446
    /// Implementations are suggested to return an error if this invariant is broken, but
447
    /// may also panic if that is an acceptable error mode.
448
    fn get_element(
449
        &mut self,
450
        id: Self::Id,
451
    ) -> impl std::future::Future<Output = Result<Self::Element<'_>, Self::GetError>> + Send;
452
453
    /// A bulk interface for invoking [`Self::get_element`] on each item in an iterator and
454
    /// invoking the closure with the reborrowed element.
455
    ///
456
    /// Algorithms are encouraged to use this interface if appropriate as accessor
457
    /// implementations may specialize the implementation for better performance.
458
0
    fn on_elements_unordered<Itr, F>(
459
0
        &mut self,
460
0
        itr: Itr,
461
0
        mut f: F,
462
0
    ) -> impl std::future::Future<Output = Result<(), Self::GetError>> + Send
463
0
    where
464
0
        Self: Sync,
465
0
        Itr: Iterator<Item = Self::Id> + Send,
466
0
        F: Send + for<'a> FnMut(Self::ElementRef<'a>, Self::Id),
467
    {
468
0
        async move {
469
0
            for i in itr {
470
0
                f(self.get_element(i).await?.reborrow(), i);
471
            }
472
0
            Ok(())
473
0
        }
474
0
    }
475
}
476
477
/// A specialized [`Accessor`] that provides random-access distance computations.
478
pub trait BuildDistanceComputer: Accessor {
479
    /// The error type (if any) associated with distance computer construction.
480
    ///
481
    /// Implementations are encouraged to make distance computer construction infallible.
482
    type DistanceComputerError: std::error::Error + Into<ANNError> + Send + Sync + 'static;
483
484
    /// The concrete type of the distance computer, which must be applicable to all pairs
485
    /// of elements yielded by the [`Accessor`].
486
    type DistanceComputer: for<'a, 'b> DistanceFunction<Self::ElementRef<'a>, Self::ElementRef<'b>>
487
        + Send
488
        + Sync;
489
490
    /// Build the random-access distance computer for this accessor.
491
    ///
492
    /// This method is expected to be relatively cheap to invoke and implementations are
493
    /// encouraged to make this method infallible.
494
    fn build_distance_computer(
495
        &self,
496
    ) -> Result<Self::DistanceComputer, Self::DistanceComputerError>;
497
}
498
499
/// A specialized [`Accessor`] that provides query computations for a query type `T`.
500
///
501
/// Query computers are allowed to preprocess the query to enable more efficient distance
502
/// computations.
503
pub trait BuildQueryComputer<T>: Accessor {
504
    /// The error type (if any) associated with distance computer construction.
505
    type QueryComputerError: std::error::Error + Into<ANNError> + Send + Sync + 'static;
506
507
    /// The concrete type of the distance computer, which must be applicable for all
508
    /// elements yielded by the [`Accessor`].
509
    type QueryComputer: for<'a> PreprocessedDistanceFunction<Self::ElementRef<'a>, f32>
510
        + Send
511
        + Sync;
512
513
    /// Build the query computer for this accessor.
514
    ///
515
    /// This method is encouraged to be as fast as possible, but will generally only be
516
    /// invoked once per search or graph insert.
517
    fn build_query_computer(
518
        &self,
519
        from: T,
520
    ) -> Result<Self::QueryComputer, Self::QueryComputerError>;
521
522
    /// Compute the distances for the elements in the iterator `itr` using the
523
    /// `computer` and apply the closure `f` to each distance and ID. The default
524
    /// implementation uses on_elements_unordered to iterate over the elements
525
    /// and compute the distances using `computer` parameter.
526
0
    fn distances_unordered<Itr, F>(
527
0
        &mut self,
528
0
        vec_id_itr: Itr,
529
0
        computer: &Self::QueryComputer,
530
0
        mut f: F,
531
0
    ) -> impl std::future::Future<Output = Result<(), Self::GetError>> + Send
532
0
    where
533
0
        Itr: Iterator<Item = Self::Id> + Send,
534
0
        F: Send + FnMut(f32, Self::Id),
535
    {
536
0
        self.on_elements_unordered(vec_id_itr, move |element, i| {
537
            // Default is to use the computer to evaluate the similarity.
538
0
            let distance = computer.evaluate_similarity(element);
539
0
            f(distance, i);
540
0
        })
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::BuildQueryComputer<&[half::binary16::f16]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::graph::glue::ExpandBeam<&[half::binary16::f16]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16>, &[half::binary16::f16], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::BuildQueryComputer<&[half::binary16::f16]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::graph::glue::ExpandBeam<&[half::binary16::f16]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16>, &[half::binary16::f16], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::BuildQueryComputer<&[i8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::graph::glue::ExpandBeam<&[i8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8>, &[i8], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::BuildQueryComputer<&[i8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::graph::glue::ExpandBeam<&[i8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8>, &[i8], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::BuildQueryComputer<&[f32]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::graph::glue::ExpandBeam<&[f32]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32>, &[f32], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::BuildQueryComputer<&[f32]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::graph::glue::ExpandBeam<&[f32]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32>, &[f32], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::BuildQueryComputer<&[u8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::graph::glue::ExpandBeam<&[u8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8>, &[u8], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::BuildQueryComputer<&[u8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::graph::glue::ExpandBeam<&[u8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8>, &[u8], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>::{closure#0}
Unexecuted instantiation: <_ as diskann::provider::BuildQueryComputer<_>>::distances_unordered::<_, _>::{closure#0}
541
0
    }
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::BuildQueryComputer<&[half::binary16::f16]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::graph::glue::ExpandBeam<&[half::binary16::f16]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16>, &[half::binary16::f16], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::BuildQueryComputer<&[half::binary16::f16]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::graph::glue::ExpandBeam<&[half::binary16::f16]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16>, &[half::binary16::f16], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::BuildQueryComputer<&[i8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::graph::glue::ExpandBeam<&[i8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8>, &[i8], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::BuildQueryComputer<&[i8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::graph::glue::ExpandBeam<&[i8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8>, &[i8], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::BuildQueryComputer<&[f32]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::graph::glue::ExpandBeam<&[f32]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32>, &[f32], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::BuildQueryComputer<&[f32]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::graph::glue::ExpandBeam<&[f32]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32>, &[f32], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::BuildQueryComputer<&[u8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::graph::glue::ExpandBeam<&[u8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8>, &[u8], diskann::graph::search::record::VisitedSearchRecord<u64>, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::BuildQueryComputer<&[u8]>>::distances_unordered::<std::collections::hash::set::IntoIter<u64>, <surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::graph::glue::ExpandBeam<&[u8]>>::expand_beam<core::iter::adapters::copied::Copied<core::slice::iter::Iter<u64>>, diskann::graph::glue::NotInMut<u64>, <diskann::graph::index::DiskANNIndex<surrealdb_core::idx::trees::diskann::provider::DiskAnnProvider>>::search_internal<surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8>, &[u8], diskann::graph::search::record::NoopSearchRecord, diskann::neighbor::queue::NeighborPriorityQueue<u64>>::{closure#0}::{closure#0}>::{closure#0}::{closure#2}>
Unexecuted instantiation: <_ as diskann::provider::BuildQueryComputer<_>>::distances_unordered::<_, _>
542
}
543
544
/////////////////////////
545
// Neighbor Delegation //
546
/////////////////////////
547
548
/// An accessor that provides random-access neighbor retrieval from a graph.
549
///
550
/// Generally, neighbor access and data access are logically decoupled, being served from
551
/// different stores. However, there are situations where data and neighbors are
552
/// interleaved in the underlying storage medium.
553
///
554
/// As such, [`Accessors`] used in congunction with graph operations need to additionally
555
/// provide an implementation of this trait.
556
///
557
/// To avoid repeating implementations for every [`Accessor`] flavor, the trait
558
/// [`DelegateNeighbor`] should be used instead to route the implementation of
559
/// [`NeighborAccessor`] to a single type if applicable.
560
///
561
/// # Note
562
///
563
/// The `NeighborAccessor` method receive by value. Implementations are strongly encouraged
564
/// to be cheap to construct, copy, or clone. This can generally be achieved by implementing
565
/// `NeighborAccessor` for `&T`, `&mut T`, or a thing wrapper around such references.
566
pub trait NeighborAccessor: HasId + Sized + Send + Sync {
567
    /// Get the neighbors for the node associated with `id`.
568
    ///
569
    /// Populate the neighbors into the `neighbors` out parameter.
570
    ///
571
    /// Implementations are expected to clear `neighbors` prior to populating.
572
    fn get_neighbors(
573
        self,
574
        id: Self::Id,
575
        neighbors: &mut AdjacencyList<Self::Id>,
576
    ) -> impl std::future::Future<Output = ANNResult<Self>> + Send;
577
}
578
579
/// A mutable extension of [`NeighborAccessor`] that enables the underlying graph to be
580
/// mutated.
581
///
582
/// Generally, [`Accessors`] should implement [`DelegateNeighbor`] instead of extending this
583
/// trait if graph and data access are naturally decoupled.
584
pub trait NeighborAccessorMut: NeighborAccessor {
585
    /// Overwrite the neighbor list for the node associated with `id`.
586
    fn set_neighbors(
587
        self,
588
        id: Self::Id,
589
        neighbors: &[Self::Id],
590
    ) -> impl std::future::Future<Output = ANNResult<Self>> + Send;
591
592
    /// Append all entries in `neighbors` tothe neighbor list currently associated with `id`.
593
    ///
594
    /// The behavior when the resulting list exceeds some pre-configured capacity or
595
    /// contains duplicates is implementation defined.
596
    fn append_vector(
597
        self,
598
        id: Self::Id,
599
        neighbors: &[Self::Id],
600
    ) -> impl std::future::Future<Output = ANNResult<Self>> + Send;
601
602
    /// A potentially optimized bulk implementation of [`Self::set_neighbors`].
603
    ///
604
    /// For each `id`/`neighbors` pair in `iter`, set the adjacency list for the node associated
605
    /// with `id` to `neighbors`.
606
    ///
607
    /// Implementations are allowed to commit entries out of order.
608
0
    fn set_neighbors_bulk<I, T>(
609
0
        mut self,
610
0
        iter: I,
611
0
    ) -> impl std::future::Future<Output = ANNResult<Self>> + Send
612
0
    where
613
0
        I: Iterator<Item = (Self::Id, T)> + Send,
614
0
        T: Deref<Target = [Self::Id]> + Send,
615
    {
616
0
        async move {
617
0
            for (vector_id, neighbors) in iter {
618
0
                self = self.set_neighbors(vector_id, neighbors.deref()).await?;
619
            }
620
0
            Ok(self)
621
0
        }
622
0
    }
623
}
624
625
/// This implementation allows `&mut T` to be used as a [`NeighborAccessor`] without
626
/// requiring manual invocation of [`DelegateNeighbor`].
627
impl<T> NeighborAccessor for &mut T
628
where
629
    T: AsNeighbor,
630
{
631
0
    async fn get_neighbors(
632
0
        self,
633
0
        id: Self::Id,
634
0
        neighbors: &mut AdjacencyList<Self::Id>,
635
0
    ) -> ANNResult<Self> {
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessor>::get_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessor>::get_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessor>::get_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessor>::get_neighbors
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessor>::get_neighbors
636
0
        self.delegate_neighbor()
637
0
            .get_neighbors(id, neighbors)
638
0
            .await?;
639
0
        Ok(self)
640
0
    }
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessor>::get_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessor>::get_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessor>::get_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessor>::get_neighbors::{closure#0}
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessor>::get_neighbors::{closure#0}
641
}
642
643
/// This implementation allows `&mut T` to be used as a [`NeighborAccessorMut`] without
644
/// requiring manual invocation of [`DelegateNeighbor`].
645
impl<T> NeighborAccessorMut for &mut T
646
where
647
    T: AsNeighborMut,
648
{
649
0
    async fn set_neighbors(self, id: Self::Id, neighbors: &[Self::Id]) -> ANNResult<Self> {
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessorMut>::set_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessorMut>::set_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessorMut>::set_neighbors
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessorMut>::set_neighbors
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessorMut>::set_neighbors
650
0
        self.delegate_neighbor()
651
0
            .set_neighbors(id, neighbors)
652
0
            .await?;
653
0
        Ok(self)
654
0
    }
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessorMut>::set_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessorMut>::set_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessorMut>::set_neighbors::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessorMut>::set_neighbors::{closure#0}
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessorMut>::set_neighbors::{closure#0}
655
0
    async fn append_vector(self, id: Self::Id, neighbors: &[Self::Id]) -> ANNResult<Self> {
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessorMut>::append_vector
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessorMut>::append_vector
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessorMut>::append_vector
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessorMut>::append_vector
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessorMut>::append_vector
656
0
        self.delegate_neighbor()
657
0
            .append_vector(id, neighbors)
658
0
            .await?;
659
0
        Ok(self)
660
0
    }
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<half::binary16::f16> as diskann::provider::NeighborAccessorMut>::append_vector::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<i8> as diskann::provider::NeighborAccessorMut>::append_vector::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<f32> as diskann::provider::NeighborAccessorMut>::append_vector::{closure#0}
Unexecuted instantiation: <&mut surrealdb_core::idx::trees::diskann::provider::DiskAnnAccessor<u8> as diskann::provider::NeighborAccessorMut>::append_vector::{closure#0}
Unexecuted instantiation: <&mut _ as diskann::provider::NeighborAccessorMut>::append_vector::{closure#0}
661
0
    async fn set_neighbors_bulk<I, U>(self, iter: I) -> ANNResult<Self>
662
0
    where
663
0
        I: Iterator<Item = (Self::Id, U)> + Send,
664
0
        U: Deref<Target = [Self::Id]> + Send,
665
0
    {
666
0
        self.delegate_neighbor().set_neighbors_bulk(iter).await?;
667
0
        Ok(self)
668
0
    }
669
}
670
671
/// Accessor may delegate the responsibility of being a [`NeighborAccessor`] to an auxiliary
672
/// type by implementing this trait.
673
///
674
/// If the implementation of a [`NeighborAccessor`]/[`NeighborAccessorMut`] are coupled with
675
/// the [`Accessor`] itself (meaning that no delegation can take place), then implementations
676
/// may do the following. Assume the accessor has type `T`. Then:
677
///
678
/// 1. Implement [`NeighborAccessor`]/[`NeighborAccessorMut`] for a thin wrapper around
679
///    `&[mut] T`.
680
///
681
/// 2. Implement [`DelegateNeighbor`] to return this wrapper as its delegate.
682
///
683
/// Implementation code can use this trait through the [`AsNeighbor`] and [`AsNeighborMut`]
684
/// convenience traits to ensure proper application of the HRTB requirements.
685
///
686
/// Additionally, the `&mut T` blanket implementation of [`NeighborAccessor`] and
687
/// [`NeighborAccessorMut`] can be used to avoid manually invoking `delegate_neighbor`.
688
pub trait DelegateNeighbor<'this, Lifetime: Sealed = BoundTo<&'this Self>>:
689
    HasId + Send + Sync
690
{
691
    /// The type of the delegated [`NeighborAccessor`].
692
    type Delegate: NeighborAccessor<Id = Self::Id>;
693
694
    /// Construct the delegate.
695
    fn delegate_neighbor(&'this mut self) -> Self::Delegate;
696
}
697
698
impl<'this, T> DelegateNeighbor<'this> for T
699
where
700
    T: Copy + NeighborAccessor,
701
{
702
    type Delegate = Self;
703
0
    fn delegate_neighbor(&'this mut self) -> Self::Delegate {
704
0
        *self
705
0
    }
706
}
707
708
/// A convenience HRTB wrapper for [`DelegateNeighbor`]. Accessors should implement
709
/// [`DelegateNeighbor`].
710
///
711
/// # Note
712
///
713
/// This trait should never be implemented manually. Instead, this trait is automatically
714
/// implemented for a type `T` when it implements [`DelegateNeighbor`].
715
pub trait AsNeighbor: for<'a> DelegateNeighbor<'a> {}
716
717
/// A convenience HRTB wrapper for [`DelegateNeighbor`]. Accessors should implement
718
/// [`DelegateNeighbor`].
719
///
720
/// # Note
721
///
722
/// This trait should never be implemented manually. Instead, this trait is automatically
723
/// implemented for a type `T` when it implements [`DelegateNeighbor`].
724
pub trait AsNeighborMut: for<'a> DelegateNeighbor<'a, Delegate: NeighborAccessorMut> {}
725
726
impl<T> AsNeighbor for T where T: for<'a> DelegateNeighbor<'a> {}
727
impl<T> AsNeighborMut for T where T: for<'a> DelegateNeighbor<'a, Delegate: NeighborAccessorMut> {}
728
729
/// Get a default accessor from a provider.
730
///
731
/// Some providers are able to produce accessors directly from the provides, and will implement
732
/// this to make getting an accessor convenient.
733
pub trait DefaultAccessor: DataProvider {
734
    type Accessor<'a>: HasId<Id = Self::InternalId>
735
    where
736
        Self: 'a;
737
    fn default_accessor(&self) -> Self::Accessor<'_>;
738
}
739
740
////////////////////
741
// DefaultContext //
742
////////////////////
743
744
/// A light-weight struct implementing [`ExecutionContext`].
745
///
746
/// Used for situations where a more refined execution context is not needed.
747
#[derive(Default, Clone)]
748
pub struct DefaultContext;
749
750
impl std::fmt::Display for DefaultContext {
751
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
752
0
        write!(f, "default context")
753
0
    }
754
}
755
756
impl ExecutionContext for DefaultContext {}
757
758
//////////
759
// Misc //
760
//////////
761
762
// Constraint for HRBT-style associated types.
763
mod sealed {
764
    pub trait Sealed: Sized {}
765
    pub struct BoundTo<T>(T);
766
    impl<T> Sealed for BoundTo<T> {}
767
}
768
769
///////////
770
// Tests //
771
///////////
772
773
#[cfg(test)]
774
mod tests {
775
    use std::{
776
        collections::HashMap,
777
        future::Future,
778
        pin::Pin,
779
        sync::{
780
            Arc, Mutex,
781
            atomic::{AtomicUsize, Ordering},
782
        },
783
        task,
784
    };
785
786
    use pin_project::{pin_project, pinned_drop};
787
788
    use super::*;
789
    use crate::{always_escalate, error::Infallible};
790
791
    ////////////////////
792
    // DefaultContext //
793
    ////////////////////
794
795
    #[test]
796
    fn test_default_context() {
797
        let ctx = DefaultContext;
798
799
        // Check that the implementation of `Display` is correct.
800
        assert_eq!(ctx.to_string(), "default context");
801
802
        assert_eq!(
803
            std::mem::size_of::<DefaultContext>(),
804
            0,
805
            "expected DefaultContext to be an empty class"
806
        );
807
    }
808
809
    //////////////////
810
    // Test Context //
811
    //////////////////
812
813
    #[derive(Debug)]
814
    struct TestContextInner {
815
        /// The number of tasks spawned.
816
        spawned: AtomicUsize,
817
        /// THe number of tasks dropped.
818
        dropped: AtomicUsize,
819
    }
820
821
    /// The goal of this test is to exercise the functionality of `wrap_spawn`.
822
    /// We want to make sure that we can correctly hook into tasks spawning.
823
    #[derive(Debug, Clone)]
824
    struct TestContext {
825
        inner: Arc<TestContextInner>,
826
    }
827
828
    impl Default for TestContext {
829
        fn default() -> Self {
830
            Self {
831
                inner: Arc::new(TestContextInner {
832
                    spawned: AtomicUsize::new(0),
833
                    dropped: AtomicUsize::new(0),
834
                }),
835
            }
836
        }
837
    }
838
839
    impl std::fmt::Display for TestContext {
840
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
841
            write!(f, "test context")
842
        }
843
    }
844
845
    /// A Future wrapper that when dropped, increments the `dropped` field of its parent
846
    /// `TestContext`.
847
    #[pin_project(PinnedDrop)]
848
    pub struct SpawnCounter<F> {
849
        #[pin]
850
        inner: F,
851
        parent: TestContext,
852
    }
853
854
    #[pinned_drop]
855
    impl<F> PinnedDrop for SpawnCounter<F> {
856
        fn drop(self: Pin<&mut Self>) {
857
            self.parent.inner.dropped.fetch_add(1, Ordering::AcqRel);
858
        }
859
    }
860
861
    impl<F> Future for SpawnCounter<F>
862
    where
863
        F: Future,
864
    {
865
        type Output = F::Output;
866
        fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
867
            self.project().inner.poll(cx)
868
        }
869
    }
870
871
    impl ExecutionContext for TestContext {
872
        /// Override task spawning to record the number of tasks spawned and the number
873
        /// of tasks dropped.
874
        fn wrap_spawn<F, T>(&self, f: F) -> impl Future<Output = T> + Send + 'static
875
        where
876
            F: Future<Output = T> + Send + 'static,
877
        {
878
            // Increment spawn count.
879
            self.inner.spawned.fetch_add(1, Ordering::AcqRel);
880
881
            // Create a future that will increment drop count when dropped.
882
            SpawnCounter {
883
                inner: f,
884
                parent: self.clone(),
885
            }
886
        }
887
    }
888
889
    ///////////////////
890
    // Spawning Test //
891
    ///////////////////
892
893
    /// This is a recursive function. At each level, it spawns `width` new instances of
894
    /// itself with `depth` decreased by 1.
895
    ///
896
    /// Each spawned instance uses `context.wrap_spawn`.
897
    ///
898
    /// This needs to be manually `async` so we can aply the `'static` bound. Since it's
899
    /// recursive, Rust struggles to properly deduce the hidden type for the opaque return
900
    /// type.
901
    #[allow(clippy::manual_async_fn)]
902
    fn test_spawning<Context>(
903
        context: Context,
904
        width: usize,
905
        depth: usize,
906
    ) -> impl Future<Output = ()> + Send + 'static
907
    where
908
        Context: ExecutionContext + 'static + std::fmt::Debug,
909
    {
910
        async move {
911
            if depth == 0 {
912
                return;
913
            }
914
915
            let handles: Box<[_]> = (0..width)
916
                .map(|_| {
917
                    let clone = context.clone();
918
                    tokio::spawn(context.wrap_spawn(test_spawning(clone, width, depth - 1)))
919
                })
920
                .collect();
921
922
            for h in handles {
923
                h.await.unwrap();
924
            }
925
        }
926
    }
927
928
    #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
929
    async fn test_task_spawning() {
930
        let context = TestContext::default();
931
        assert_eq!(context.inner.spawned.load(Ordering::Acquire), 0);
932
        assert_eq!(context.inner.dropped.load(Ordering::Acquire), 0);
933
934
        // How to we compute the number of spawned tasks:
935
        // 1. The first invocation of `test_spawning` spawned `width` tasks.
936
        // 2. Each tasks spawns `width` tasks, meaning the second level creates `width ^ 2`
937
        //    tasks.
938
        // 3. In general, depth `d` spawnS `width ^ d` tasks.
939
        //
940
        // The total number of tasks is then:
941
        // ```
942
        // S = width + width^2 + width^3 ... width^depth
943
        // ```
944
        // This forms a finite geometric series with a closed for solution of
945
        // ```
946
        // S = (width ^ (depth + 1) - 1) / (width - 1) - 1
947
        // ```
948
        let width = 10;
949
        let depth = 3;
950
        test_spawning(context.clone(), width, depth).await;
951
952
        let expected = (width.pow((depth + 1).try_into().unwrap()) - 1) / (width - 1) - 1;
953
        assert_eq!(context.inner.spawned.load(Ordering::Acquire), expected);
954
        assert_eq!(context.inner.dropped.load(Ordering::Acquire), expected);
955
    }
956
957
    ///////////////////
958
    // Data Provider //
959
    ///////////////////
960
961
    #[tokio::test]
962
    async fn test_noop_guard() {
963
        // A guard that completes successfully.
964
        {
965
            let guard = NoopGuard::<usize>::new(10);
966
            assert_eq!(guard.id(), 10);
967
            guard.complete().await;
968
        }
969
970
        // A guard that completes unsuccessfully.
971
        // The Noop guard specifically does not complain if `complete` is not invoked.
972
        {
973
            let guard = NoopGuard::<usize>::new(5);
974
            assert_eq!(guard.id(), 5);
975
            // Destructor runs here.
976
        }
977
    }
978
979
    #[test]
980
    fn simple_status_test() {
981
        let valid = ElementStatus::Valid;
982
        assert!(valid.is_valid());
983
        assert!(!valid.is_deleted());
984
985
        let deleted = ElementStatus::Deleted;
986
        assert!(!deleted.is_valid());
987
        assert!(deleted.is_deleted());
988
    }
989
990
    /// A simple data provider that contains values consisting of floats and strings.
991
    ///
992
    /// The start point for this provider is as `u32::MAX`.
993
    struct SimpleProvider {
994
        data: Mutex<HashMap<u32, (f32, String)>>,
995
    }
996
997
    impl SimpleProvider {
998
        fn new(v: f32, st: String) -> Self {
999
            let mut data = HashMap::new();
1000
            data.insert(u32::MAX, (v, st));
1001
            Self {
1002
                data: Mutex::new(data),
1003
            }
1004
        }
1005
    }
1006
1007
    impl DataProvider for SimpleProvider {
1008
        type Context = DefaultContext;
1009
        // Use the identity mapping for IDs.
1010
        type InternalId = u32;
1011
        type ExternalId = u32;
1012
        type Error = ANNError;
1013
        type Guard = NoopGuard<u32>;
1014
1015
        fn to_internal_id(&self, _context: &DefaultContext, gid: &u32) -> Result<u32, ANNError> {
1016
            Ok(*gid)
1017
        }
1018
1019
        fn to_external_id(&self, _context: &DefaultContext, id: u32) -> Result<u32, ANNError> {
1020
            Ok(id)
1021
        }
1022
    }
1023
1024
    #[derive(Debug, Clone, Copy, PartialEq)]
1025
    pub struct Missing;
1026
1027
    impl std::fmt::Display for Missing {
1028
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1029
            write!(f, "key is missing")
1030
        }
1031
    }
1032
1033
    impl std::error::Error for Missing {}
1034
    impl From<Missing> for ANNError {
1035
        #[cold]
1036
        fn from(missing: Missing) -> ANNError {
1037
            ANNError::log_async_error(missing)
1038
        }
1039
    }
1040
1041
    always_escalate!(Missing);
1042
1043
    // An accessor for the `f32` portion of the data stored in the SimpleProvider.
1044
    struct FloatAccessor<'a>(&'a SimpleProvider);
1045
    impl HasId for FloatAccessor<'_> {
1046
        type Id = u32;
1047
    }
1048
    impl Accessor for FloatAccessor<'_> {
1049
        type Element<'a>
1050
            = f32
1051
        where
1052
            Self: 'a;
1053
        type ElementRef<'a> = f32;
1054
1055
        type GetError = Missing;
1056
1057
        fn get_element(
1058
            &mut self,
1059
            id: u32,
1060
        ) -> impl Future<Output = Result<Self::Element<'_>, Self::GetError>> + Send {
1061
            let guard = self.0.data.lock().unwrap();
1062
            let v = match guard.get(&id) {
1063
                None => Err(Missing),
1064
                Some(v) => Ok(v.0),
1065
            };
1066
            std::future::ready(v)
1067
        }
1068
1069
        // Implement `on_elements_unordered` by only acquiring the lock once.
1070
        //
1071
        // Real implementations will need to take care to avoid deadlocks.
1072
        async fn on_elements_unordered<Itr, F>(
1073
            &mut self,
1074
            itr: Itr,
1075
            mut f: F,
1076
        ) -> Result<(), Self::GetError>
1077
        where
1078
            Self: Sync,
1079
            Itr: Iterator<Item = u32>,
1080
            F: Send + FnMut(f32, u32),
1081
        {
1082
            let guard = self.0.data.lock().unwrap();
1083
            for i in itr {
1084
                match guard.get(&i) {
1085
                    None => return Err(Missing),
1086
                    Some(v) => f(v.0, i),
1087
                }
1088
            }
1089
            Ok(())
1090
        }
1091
    }
1092
1093
    // An accessor for the `String` portion of the data stored in the SimpleProvider.
1094
    //
1095
    // We keep a local buffer `buf` into which the contents of the string are copied.
1096
    // This allows us to elide allocating on `get_element` calls.
1097
    struct StringAccessor<'a> {
1098
        provider: &'a SimpleProvider,
1099
        buf: String,
1100
    }
1101
1102
    impl<'a> StringAccessor<'a> {
1103
        fn new(provider: &'a SimpleProvider) -> Self {
1104
            Self {
1105
                provider,
1106
                buf: String::new(),
1107
            }
1108
        }
1109
    }
1110
1111
    impl HasId for StringAccessor<'_> {
1112
        type Id = u32;
1113
    }
1114
    impl Accessor for StringAccessor<'_> {
1115
        type Element<'a>
1116
            = &'a str
1117
        where
1118
            Self: 'a;
1119
        type ElementRef<'a> = &'a str;
1120
1121
        type GetError = Missing;
1122
1123
        fn get_element(
1124
            &mut self,
1125
            id: u32,
1126
        ) -> impl Future<Output = Result<Self::Element<'_>, Self::GetError>> + Send {
1127
            let guard = self.provider.data.lock().unwrap();
1128
            let v = match guard.get(&id) {
1129
                None => Err(Missing),
1130
                Some(v) => {
1131
                    self.buf.clone_from(&v.1);
1132
                    Ok(&*self.buf)
1133
                }
1134
            };
1135
            std::future::ready(v)
1136
        }
1137
    }
1138
1139
    #[tokio::test]
1140
    async fn test_default_implementations() {
1141
        let provider = SimpleProvider::new(-1.0, "hello".to_string());
1142
        {
1143
            let mut data = provider.data.lock().unwrap();
1144
            data.insert(0, (0.0, "world".to_string()));
1145
            data.insert(1, (1.0, "foo".to_string()));
1146
            data.insert(2, (2.0, "bar".to_string()));
1147
        }
1148
1149
        // Float accessor
1150
        {
1151
            let mut accessor = FloatAccessor(&provider);
1152
            assert_eq!(accessor.get_element(0).await.unwrap(), 0.0);
1153
            assert_eq!(accessor.get_element(1).await.unwrap(), 1.0);
1154
            assert_eq!(accessor.get_element(u32::MAX).await.unwrap(), -1.0);
1155
1156
            let mut v = Vec::new();
1157
            accessor
1158
                .on_elements_unordered([2, 1, 0].into_iter(), |element, id| v.push((element, id)))
1159
                .await
1160
                .unwrap();
1161
1162
            assert_eq!(&v, &[(2.0, 2), (1.0, 1), (0.0, 0)]);
1163
1164
            // Test error propagation.
1165
            // Trying to access element 3 will result in an error, which should be propagated
1166
            // up.
1167
            let err = accessor
1168
                .on_elements_unordered([2, 1, 0, 3].into_iter(), |element, id| {
1169
                    v.push((element, id))
1170
                })
1171
                .await
1172
                .unwrap_err();
1173
            assert_eq!(err, Missing);
1174
        }
1175
1176
        // String accessor
1177
        {
1178
            let mut accessor = StringAccessor::new(&provider);
1179
            assert_eq!(accessor.get_element(0).await.unwrap(), "world");
1180
            assert_eq!(accessor.get_element(1).await.unwrap(), "foo");
1181
            assert_eq!(accessor.get_element(u32::MAX).await.unwrap(), "hello");
1182
1183
            // This method tests the provided implementation of `on_elements_unordered`.
1184
            let expected = [("bar", 2), ("foo", 1), ("world", 0)];
1185
1186
            let mut expected_iter = expected.into_iter();
1187
            accessor
1188
                .on_elements_unordered([2, 1, 0].into_iter(), |element, id| {
1189
                    assert_eq!((element, id), expected_iter.next().unwrap());
1190
                })
1191
                .await
1192
                .unwrap();
1193
            assert!(expected_iter.next().is_none());
1194
1195
            // Test error propagation.
1196
            // Trying to access element 3 will result in an error, which should be propagated
1197
            // up.
1198
            let mut expected_iter = expected.into_iter();
1199
            let err = accessor
1200
                .on_elements_unordered([2, 1, 0, 3].into_iter(), |element, id| {
1201
                    assert_eq!((element, id), expected_iter.next().unwrap());
1202
                })
1203
                .await
1204
                .unwrap_err();
1205
            assert_eq!(err, Missing);
1206
            assert!(expected_iter.next().is_none());
1207
        }
1208
    }
1209
1210
    /////////////////////////////////
1211
    // Supported Accessor Patterns //
1212
    /////////////////////////////////
1213
1214
    // This suite of tests ensure that patterns we want out of the `Accessor` associated
1215
    // trait hierarchy are all supported.
1216
    //
1217
    // These include:
1218
    //
1219
    // * Accessors that always allocate.
1220
    // * Accessors that simply reference the underlying store directly.
1221
    // * Accessors that use a local buffer.
1222
1223
    #[derive(Debug)]
1224
    struct Store {
1225
        data: Box<[u8]>,
1226
    }
1227
1228
    impl Store {
1229
        fn new() -> Self {
1230
            Self {
1231
                data: Box::from([1, 2, 3, 4]),
1232
            }
1233
        }
1234
1235
        fn dim(&self) -> usize {
1236
            self.data.len()
1237
        }
1238
    }
1239
1240
    macro_rules! common_test_accessor {
1241
        ($T:ty) => {
1242
            impl HasId for $T {
1243
                type Id = u32;
1244
            }
1245
1246
            impl BuildDistanceComputer for $T {
1247
                type DistanceComputerError = Infallible;
1248
                type DistanceComputer = <u8 as crate::utils::VectorRepr>::Distance;
1249
1250
                fn build_distance_computer(&self) -> Result<Self::DistanceComputer, Infallible> {
1251
                    Ok(<u8 as crate::utils::VectorRepr>::distance(
1252
                        diskann_vector::distance::Metric::L2,
1253
                        None,
1254
                    ))
1255
                }
1256
            }
1257
        };
1258
    }
1259
1260
    // An accessor that always allocates.
1261
    struct Allocating<'a> {
1262
        store: &'a Store,
1263
    }
1264
1265
    impl<'a> Allocating<'a> {
1266
        fn new(store: &'a Store) -> Self {
1267
            Self { store }
1268
        }
1269
    }
1270
1271
    common_test_accessor!(Allocating<'_>);
1272
1273
    impl Accessor for Allocating<'_> {
1274
        type Element<'a>
1275
            = Box<[u8]>
1276
        where
1277
            Self: 'a;
1278
        type ElementRef<'a> = &'a [u8];
1279
        type GetError = Infallible;
1280
1281
        async fn get_element(&mut self, _: u32) -> Result<Box<[u8]>, Infallible> {
1282
            Ok(self.store.data.clone())
1283
        }
1284
    }
1285
1286
    // An accessor that forwards - returning references directly into the underlying
1287
    // store without reallocation or copying.
1288
    struct Forwarding<'a> {
1289
        store: &'a Store,
1290
    }
1291
1292
    impl<'a> Forwarding<'a> {
1293
        fn new(store: &'a Store) -> Self {
1294
            Self { store }
1295
        }
1296
    }
1297
1298
    common_test_accessor!(Forwarding<'_>);
1299
1300
    impl<'provider> Accessor for Forwarding<'provider> {
1301
        // NOTE: The lifetime of `Element` is `'provider` - not `'a`. This is what makes
1302
        // it a forwarding accessor.
1303
        type Element<'a>
1304
            = &'provider [u8]
1305
        where
1306
            Self: 'a;
1307
        type ElementRef<'a> = &'a [u8];
1308
        type GetError = Infallible;
1309
1310
        async fn get_element(&mut self, _: u32) -> Result<&'provider [u8], Infallible> {
1311
            Ok(&*self.store.data)
1312
        }
1313
    }
1314
1315
    // An accessor that returns a non-reference type with a lifetime.
1316
    struct Wrapping<'a> {
1317
        store: &'a Store,
1318
    }
1319
1320
    impl<'a> Wrapping<'a> {
1321
        fn new(store: &'a Store) -> Self {
1322
            Self { store }
1323
        }
1324
    }
1325
1326
    #[derive(Debug)]
1327
    struct Wrapped<'a>(&'a [u8]);
1328
1329
    impl<'a> Reborrow<'a> for Wrapped<'_> {
1330
        type Target = &'a [u8];
1331
        fn reborrow(&'a self) -> Self::Target {
1332
            self.0
1333
        }
1334
    }
1335
1336
    impl From<Wrapped<'_>> for Box<[u8]> {
1337
        fn from(wrapped: Wrapped<'_>) -> Self {
1338
            wrapped.0.into()
1339
        }
1340
    }
1341
1342
    common_test_accessor!(Wrapping<'_>);
1343
1344
    impl Accessor for Wrapping<'_> {
1345
        type Element<'a>
1346
            = Wrapped<'a>
1347
        where
1348
            Self: 'a;
1349
        type ElementRef<'a> = &'a [u8];
1350
        type GetError = Infallible;
1351
1352
        async fn get_element(&mut self, _: u32) -> Result<Wrapped<'_>, Infallible> {
1353
            Ok(Wrapped(&self.store.data))
1354
        }
1355
    }
1356
1357
    // An accessor that shares local state.
1358
    #[derive(Debug)]
1359
    struct Sharing<'a> {
1360
        store: &'a Store,
1361
        local: Box<[u8]>,
1362
    }
1363
1364
    impl<'a> Sharing<'a> {
1365
        fn new(store: &'a Store) -> Self {
1366
            Self {
1367
                store,
1368
                local: (0..store.dim()).map(|_| 0).collect(),
1369
            }
1370
        }
1371
    }
1372
1373
    common_test_accessor!(Sharing<'_>);
1374
1375
    impl Accessor for Sharing<'_> {
1376
        type Element<'a>
1377
            = &'a [u8]
1378
        where
1379
            Self: 'a;
1380
        type ElementRef<'a> = &'a [u8];
1381
        type GetError = Infallible;
1382
1383
        async fn get_element(&mut self, _: u32) -> Result<&[u8], Infallible> {
1384
            self.local.copy_from_slice(&self.store.data);
1385
            Ok(&self.local)
1386
        }
1387
    }
1388
1389
    #[tokio::test]
1390
    async fn test_accessor_patterns() {
1391
        let store = Store::new();
1392
1393
        // A slice against which we compute distances.
1394
        let base: &[u8] = &[2, 3, 4, 5];
1395
1396
        {
1397
            let mut accessor = Allocating::new(&store);
1398
            let computer = accessor.build_distance_computer().unwrap();
1399
1400
            let element = accessor.get_element(0).await.unwrap();
1401
            assert_eq!(computer.evaluate_similarity(base, element.reborrow()), 4.0);
1402
        }
1403
1404
        {
1405
            let mut accessor = Forwarding::new(&store);
1406
            let computer = accessor.build_distance_computer().unwrap();
1407
1408
            let element = accessor.get_element(0).await.unwrap();
1409
            assert_eq!(computer.evaluate_similarity(base, element.reborrow()), 4.0);
1410
        }
1411
1412
        {
1413
            let mut accessor = Wrapping::new(&store);
1414
            let computer = accessor.build_distance_computer().unwrap();
1415
1416
            let element = accessor.get_element(0).await.unwrap();
1417
            assert_eq!(computer.evaluate_similarity(base, element.reborrow()), 4.0);
1418
        }
1419
1420
        {
1421
            let mut accessor = Sharing::new(&store);
1422
            let computer = accessor.build_distance_computer().unwrap();
1423
1424
            let element = accessor.get_element(0).await.unwrap();
1425
            assert_eq!(computer.evaluate_similarity(base, element.reborrow()), 4.0);
1426
        }
1427
    }
1428
}