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}