diskann_disk/search/provider/aligned_file_reader/
aligned_read.rs1use std::marker::PhantomData;
7
8use diskann::{ANNError, ANNResult};
9use diskann_quantization::num::PowerOfTwo;
10
11pub trait Alignment {
17 const VALUE: PowerOfTwo;
19}
20
21macro_rules! alignment_marker {
22 ($name:ident, $value:expr) => {
23 #[doc = concat!("Alignment witness for ", stringify!($value), " bytes.")]
24 #[derive(Debug, Clone, Copy)]
25 pub struct $name;
26 impl Alignment for $name {
27 const VALUE: PowerOfTwo = $value;
28 }
29 };
30}
31
32alignment_marker!(A1, PowerOfTwo::V1);
33alignment_marker!(A512, PowerOfTwo::V512);
34
35#[derive(Debug)]
47pub struct AlignedRead<'a, T, A: Alignment = A1> {
48 offset: u64,
49 aligned_buf: &'a mut [T],
50 _alignment: PhantomData<A>,
51}
52
53impl<'a, T, A: Alignment> AlignedRead<'a, T, A> {
54 pub fn new(offset: u64, aligned_buf: &'a mut [T]) -> ANNResult<Self> {
57 Self::assert_is_aligned(aligned_buf.as_ptr() as usize, "buffer pointer")?;
58 Self::assert_is_aligned(std::mem::size_of_val(aligned_buf), "buffer length")?;
59 Self::assert_is_aligned(offset as usize, "offset")?;
60 Ok(Self {
61 offset,
62 aligned_buf,
63 _alignment: PhantomData,
64 })
65 }
66
67 fn assert_is_aligned(val: usize, kind: &str) -> ANNResult<()> {
68 let align = A::VALUE.raw();
69 if val.is_multiple_of(align) {
70 Ok(())
71 } else {
72 Err(ANNError::log_disk_io_request_alignment_error(format!(
73 "{kind} {val} not aligned to {align}",
74 )))
75 }
76 }
77
78 pub fn offset(&self) -> u64 {
79 self.offset
80 }
81
82 pub fn aligned_buf(&self) -> &[T] {
83 self.aligned_buf
84 }
85
86 pub fn aligned_buf_mut(&mut self) -> &mut [T] {
87 self.aligned_buf
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use diskann::ANNErrorKind;
95 use diskann_quantization::alloc::{AlignedAllocator, Poly};
96
97 fn aligned_512(len: usize) -> Poly<[u8], AlignedAllocator> {
98 Poly::broadcast(0u8, len, AlignedAllocator::A512).unwrap()
99 }
100
101 #[test]
102 fn aligned_read_carries_offset_and_buffer() {
103 let mut buffer = vec![0u8; 512];
104 let read = AlignedRead::<u8, A1>::new(512, &mut buffer).unwrap();
105 assert_eq!(read.offset(), 512);
106 assert_eq!(read.aligned_buf().len(), 512);
107 }
108
109 #[test]
110 fn aligned_read_buffer_access() {
111 let mut buffer = vec![42u8; 512];
112 let mut read = AlignedRead::<u8, A1>::new(0, &mut buffer).unwrap();
113 assert_eq!(read.aligned_buf()[0], 42);
114 read.aligned_buf_mut()[0] = 100;
115 assert_eq!(read.aligned_buf()[0], 100);
116 }
117
118 #[test]
119 fn a512_accepts_fully_aligned_request() {
120 let mut buf = aligned_512(512);
121 AlignedRead::<u8, A512>::new(0, &mut buf).expect("aligned request should pass");
122 }
123
124 #[test]
125 fn a1_default_accepts_anything() {
126 let mut buffer = vec![0u8; 100];
127 AlignedRead::<u8, A1>::new(1, &mut buffer).expect("A1 alignment should accept any request");
128 }
129
130 #[test]
131 fn rejects_unaligned_buffer_pointer() {
132 let mut buf = aligned_512(1024);
133 let slice = &mut buf[1..513]; let err = AlignedRead::<u8, A512>::new(0, slice)
135 .expect_err("misaligned buffer pointer should be rejected");
136 assert_eq!(err.kind(), ANNErrorKind::DiskIOAlignmentError);
137 }
138
139 #[test]
140 fn rejects_unaligned_buffer_length() {
141 let mut buf = aligned_512(1024);
142 let slice = &mut buf[..100]; let err = AlignedRead::<u8, A512>::new(0, slice)
144 .expect_err("buffer length 100 (not a multiple of 512) should be rejected");
145 assert_eq!(err.kind(), ANNErrorKind::DiskIOAlignmentError);
146 }
147
148 #[test]
149 fn rejects_unaligned_offset() {
150 let mut buf = aligned_512(1024);
151 let slice = &mut buf[..512]; let err = AlignedRead::<u8, A512>::new(1, slice)
153 .expect_err("offset 1 (not a multiple of 512) should be rejected");
154 assert_eq!(err.kind(), ANNErrorKind::DiskIOAlignmentError);
155 }
156}