Skip to main content

diskann_vector/
unaligned.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::marker::PhantomData;
7
8/// A minimally functional span over a potentially unaligned slice of elements of type `T`.
9///
10/// Like `&[T]`, this type guarantees that the memory
11/// `[self.as_ptr(), self.as_ptr().add(self.len()))` is valid for reads.
12///
13/// However, unlike `&[T]`, the pointer [`Self::as_ptr`] is **not** guaranteed to be aligned
14/// to `std::mem::align_of::<T>()`.
15///
16/// If the type `T` is [`Copy`], then [`std::ptr::read_unaligned`] can be used on the valid
17/// memory region for this slice to access values of type `T`.
18#[derive(Debug)]
19pub struct UnalignedSlice<'a, T> {
20    ptr: *const T,
21    len: usize,
22    _lifetime: PhantomData<&'a T>,
23}
24
25impl<'a, T> UnalignedSlice<'a, T> {
26    /// Construct a new [`UnalignedSlice`] over the region `[ptr, ptr.add(len))`.
27    ///
28    /// # Safety
29    ///
30    /// The provided memory region must be valid for reading via [`std::ptr::read_unaligned`]
31    /// for the duration of the associated lifetime.
32    ///
33    /// Note that `ptr` need not be aligned to `std::mem::align_of::<T>()`.
34    ///
35    /// Argument `ptr` may be null if `len == 0`.
36    pub const unsafe fn new(ptr: *const T, len: usize) -> Self {
37        Self {
38            ptr,
39            len,
40            _lifetime: PhantomData,
41        }
42    }
43
44    /// Return the number of elements of type `T` available for reading from [`Self::as_ptr`].
45    pub const fn len(&self) -> usize {
46        self.len
47    }
48
49    /// Return `true` only if the associated slice is empty.
50    pub const fn is_empty(&self) -> bool {
51        self.len() == 0
52    }
53
54    /// Return the base pointer of the slice.
55    ///
56    /// **NOTE**: It is **not** guaranteed that the returned pointer is aligned!
57    pub const fn as_ptr(&self) -> *const T {
58        self.ptr
59    }
60}
61
62impl<T> Clone for UnalignedSlice<'_, T> {
63    fn clone(&self) -> Self {
64        *self
65    }
66}
67
68impl<T> Copy for UnalignedSlice<'_, T> {}
69
70impl<'a, T> From<&'a [T]> for UnalignedSlice<'a, T> {
71    fn from(slice: &'a [T]) -> Self {
72        // SAFETY: Slices are inherently valid, so this construction is safe.
73        unsafe { Self::new(slice.as_ptr(), slice.len()) }
74    }
75}
76
77impl<'a, T, const N: usize> From<&'a [T; N]> for UnalignedSlice<'a, T> {
78    fn from(slice: &'a [T; N]) -> Self {
79        // SAFETY: Slices are inherently valid, so this construction is safe.
80        unsafe { Self::new(slice.as_ptr(), N) }
81    }
82}
83
84/// View `self` as an [`UnalignedSlice`].
85pub trait AsUnaligned {
86    /// The element type of the slice.
87    type Element;
88
89    /// Return an [`UnalignedSlice`] view of `self`.
90    fn as_unaligned(&self) -> UnalignedSlice<'_, Self::Element>;
91}
92
93impl<T> AsUnaligned for UnalignedSlice<'_, T> {
94    type Element = T;
95    fn as_unaligned(&self) -> UnalignedSlice<'_, T> {
96        *self
97    }
98}
99
100impl<T> AsUnaligned for &[T] {
101    type Element = T;
102    fn as_unaligned(&self) -> UnalignedSlice<'_, T> {
103        (*self).into()
104    }
105}
106
107impl<T, const N: usize> AsUnaligned for &[T; N] {
108    type Element = T;
109    fn as_unaligned(&self) -> UnalignedSlice<'_, T> {
110        (*self).into()
111    }
112}
113
114impl<T, const N: usize> AsUnaligned for [T; N] {
115    type Element = T;
116    fn as_unaligned(&self) -> UnalignedSlice<'_, T> {
117        self.into()
118    }
119}
120
121/// A utility that offsets a collection of `T` by one byte to test the guarantee that
122/// distance functions can operate on unaligned pointers.
123///
124/// # Invariant
125///
126/// We maintain the following invariants
127/// * `self.data.as_ptr().add(1)` is always safe (i.e., `data.len() >= 1`)
128/// * `data.len() == self.len * std::mem::size_of::<T>() + 1`: We can construct an
129///   [`UnalignedSlice`] of length `self.len` starting from `self.data.as_ptr().add(1)`.
130#[cfg(test)]
131#[derive(Debug)]
132pub(crate) struct Buffer<T>
133where
134    T: bytemuck::Pod,
135{
136    data: Vec<u8>,
137    len: usize,
138    _type: PhantomData<T>,
139}
140
141#[cfg(test)]
142impl<T> Default for Buffer<T>
143where
144    T: bytemuck::Pod,
145{
146    fn default() -> Self {
147        Self {
148            // NOTE: Length 1 is important to maintain struct invariants.
149            data: vec![0u8; 1],
150            len: 0,
151            _type: PhantomData,
152        }
153    }
154}
155
156#[cfg(test)]
157impl<T> Buffer<T>
158where
159    T: bytemuck::Pod,
160{
161    pub(crate) fn new(x: &[T]) -> Self {
162        let mut this = Self::default();
163        this.copy(x);
164        this
165    }
166
167    pub(crate) fn copy(&mut self, x: &[T]) {
168        let bytes = std::mem::size_of_val(x);
169        self.data.resize(bytes.checked_add(1).unwrap(), 0u8);
170
171        // SAFETY: We maintain the invariant that `data.len() >= 1`, so `ptr::add` is valid.
172        let dst = unsafe { self.data.as_mut_ptr().add(1) };
173
174        // SAFETY: The `bytemuck::Pod` bound guarantees `Copy`, so we need not worry about
175        // destructors. Further:
176        //
177        // * `src` is valid for `bytes` since that is how we obtained the value `bytes` in
178        //   the first place.
179        // * `dst` is valid for `bytes` since we just resized.
180        // * Both `src` and `dst` are `u8` and are trivially aligned.
181        // * Neither region can overlap because we borrow self by mutable reference -
182        //   therefore `data` must be disjoint from the memory for `x`.
183        unsafe {
184            std::ptr::copy_nonoverlapping::<u8>(
185                bytemuck::must_cast_slice::<T, u8>(x).as_ptr(),
186                dst,
187                bytes,
188            );
189        }
190
191        self.len = x.len();
192    }
193
194    pub(crate) fn as_unaligned(&self) -> UnalignedSlice<'_, T> {
195        // SAFETY: The invariants maintained by this class guarantee the validity of the
196        // memory of the returned slice.
197        //
198        // The `bytemuck::Pod` bound means the resulting slice is useable via
199        // [`ptr::read_unaligned`].
200        unsafe { UnalignedSlice::new(self.data.as_ptr().add(1).cast::<T>(), self.len) }
201    }
202}