1use std::io;
4use std::mem::size_of;
5
6use serde::{Deserialize, Serialize};
7
8use crate::directories::{FileHandle, OwnedBytes};
9use crate::dsl::DenseVectorQuantization;
10use crate::segment::format::{DOC_ID_ENTRY_SIZE, FLAT_BINARY_HEADER_SIZE, FLAT_BINARY_MAGIC};
11use crate::structures::simd::{batch_f32_to_f16, batch_f32_to_u8, f16_to_f32, u8_to_f32};
12
13#[inline]
19pub fn dequantize_raw(
20 raw: &[u8],
21 quant: DenseVectorQuantization,
22 num_floats: usize,
23 out: &mut [f32],
24) -> io::Result<()> {
25 if out.len() < num_floats {
26 return Err(io::Error::new(
27 io::ErrorKind::InvalidInput,
28 format!(
29 "dequantization output is too short: need {num_floats} floats, got {}",
30 out.len()
31 ),
32 ));
33 }
34
35 let element_size = match quant {
36 DenseVectorQuantization::F32 => size_of::<f32>(),
37 DenseVectorQuantization::F16 => size_of::<u16>(),
38 DenseVectorQuantization::UInt8 => size_of::<u8>(),
39 DenseVectorQuantization::Binary => {
40 return Err(io::Error::new(
41 io::ErrorKind::InvalidInput,
42 "binary vectors cannot be dequantized to f32",
43 ));
44 }
45 };
46 let expected_bytes = num_floats.checked_mul(element_size).ok_or_else(|| {
47 io::Error::new(
48 io::ErrorKind::InvalidInput,
49 "dequantization byte length overflows usize",
50 )
51 })?;
52 if raw.len() != expected_bytes {
53 return Err(io::Error::new(
54 io::ErrorKind::InvalidData,
55 format!(
56 "dequantization input length mismatch: need {expected_bytes} bytes, got {}",
57 raw.len()
58 ),
59 ));
60 }
61
62 match quant {
63 DenseVectorQuantization::F32 => {
64 if expected_bytes > 0
65 && !(raw.as_ptr() as usize).is_multiple_of(std::mem::align_of::<f32>())
66 {
67 return Err(io::Error::new(
68 io::ErrorKind::InvalidData,
69 "f32 vector data is not 4-byte aligned",
70 ));
71 }
72 out[..num_floats].copy_from_slice(unsafe {
73 std::slice::from_raw_parts(raw.as_ptr() as *const f32, num_floats)
75 });
76 }
77 DenseVectorQuantization::F16 => {
78 if expected_bytes > 0
79 && !(raw.as_ptr() as usize).is_multiple_of(std::mem::align_of::<u16>())
80 {
81 return Err(io::Error::new(
82 io::ErrorKind::InvalidData,
83 "f16 vector data is not 2-byte aligned",
84 ));
85 }
86 let f16_slice = unsafe {
87 std::slice::from_raw_parts(raw.as_ptr() as *const u16, num_floats)
89 };
90 for (i, &h) in f16_slice.iter().enumerate() {
91 out[i] = f16_to_f32(h);
92 }
93 }
94 DenseVectorQuantization::UInt8 => {
95 for (i, &b) in raw.iter().enumerate() {
96 out[i] = u8_to_f32(b);
97 }
98 }
99 DenseVectorQuantization::Binary => unreachable!("validated above"),
100 }
101 Ok(())
102}
103
104pub struct FlatVectorData;
117
118impl FlatVectorData {
119 fn validate_shape(
120 dim: usize,
121 num_vectors: usize,
122 quant: DenseVectorQuantization,
123 ) -> io::Result<usize> {
124 if dim == 0 {
125 return Err(io::Error::new(
126 io::ErrorKind::InvalidInput,
127 "flat vector dimension must be greater than zero",
128 ));
129 }
130 if quant == DenseVectorQuantization::Binary && !dim.is_multiple_of(8) {
131 return Err(io::Error::new(
132 io::ErrorKind::InvalidInput,
133 format!("binary flat vector dimension must be a multiple of 8, got {dim}"),
134 ));
135 }
136 u32::try_from(dim).map_err(|_| {
137 io::Error::new(
138 io::ErrorKind::InvalidInput,
139 format!("flat vector dimension {dim} exceeds u32::MAX"),
140 )
141 })?;
142 u32::try_from(num_vectors).map_err(|_| {
143 io::Error::new(
144 io::ErrorKind::InvalidInput,
145 format!("flat vector count {num_vectors} exceeds u32::MAX"),
146 )
147 })?;
148
149 match quant {
150 DenseVectorQuantization::Binary => dim.checked_add(7).map(|bits| bits / 8),
151 _ => dim.checked_mul(quant.element_size()),
152 }
153 .ok_or_else(|| {
154 io::Error::new(
155 io::ErrorKind::InvalidInput,
156 "flat vector byte size overflows usize",
157 )
158 })
159 }
160
161 fn validate_doc_ids(doc_ids: &[(u32, u16)]) -> io::Result<()> {
162 if let Some(pair) = doc_ids.windows(2).find(|pair| pair[0] >= pair[1]) {
163 return Err(io::Error::new(
164 io::ErrorKind::InvalidInput,
165 format!(
166 "flat vector doc map must be strictly sorted by (doc_id, ordinal), found {:?} before {:?}",
167 pair[0], pair[1]
168 ),
169 ));
170 }
171 Ok(())
172 }
173
174 pub(crate) fn validate_dense_input(
177 dim: usize,
178 flat_vectors: &[f32],
179 doc_ids: &[(u32, u16)],
180 quant: DenseVectorQuantization,
181 ) -> io::Result<usize> {
182 if quant == DenseVectorQuantization::Binary {
183 return Err(io::Error::new(
184 io::ErrorKind::InvalidInput,
185 "binary quantization must use serialize_binary_from_bits_streaming",
186 ));
187 }
188 let num_vectors = doc_ids.len();
189 let expected_floats = num_vectors.checked_mul(dim).ok_or_else(|| {
190 io::Error::new(
191 io::ErrorKind::InvalidInput,
192 "flat f32 vector count overflows usize",
193 )
194 })?;
195 if flat_vectors.len() != expected_floats {
196 return Err(io::Error::new(
197 io::ErrorKind::InvalidInput,
198 format!(
199 "flat vector input has {} floats, expected {num_vectors} x {dim} = {expected_floats}",
200 flat_vectors.len()
201 ),
202 ));
203 }
204 Self::validate_doc_ids(doc_ids)?;
205 Self::serialized_binary_size(dim, num_vectors, quant)
206 }
207
208 pub(crate) fn validate_binary_input(
211 dim_bits: usize,
212 packed_vectors: &[u8],
213 doc_ids: &[(u32, u16)],
214 ) -> io::Result<usize> {
215 let num_vectors = doc_ids.len();
216 let byte_len =
217 Self::validate_shape(dim_bits, num_vectors, DenseVectorQuantization::Binary)?;
218 let expected_bytes = num_vectors.checked_mul(byte_len).ok_or_else(|| {
219 io::Error::new(
220 io::ErrorKind::InvalidInput,
221 "packed binary vector size overflows usize",
222 )
223 })?;
224 if packed_vectors.len() != expected_bytes {
225 return Err(io::Error::new(
226 io::ErrorKind::InvalidInput,
227 format!(
228 "packed binary input has {} bytes, expected {num_vectors} x {byte_len} = {expected_bytes}",
229 packed_vectors.len()
230 ),
231 ));
232 }
233 Self::validate_doc_ids(doc_ids)?;
234 Self::serialized_binary_size(dim_bits, num_vectors, DenseVectorQuantization::Binary)
235 }
236
237 pub fn write_binary_header(
239 dim: usize,
240 num_vectors: usize,
241 quant: DenseVectorQuantization,
242 writer: &mut dyn std::io::Write,
243 ) -> std::io::Result<()> {
244 Self::validate_shape(dim, num_vectors, quant)?;
245 let dim = u32::try_from(dim).map_err(|_| {
246 io::Error::new(
247 io::ErrorKind::InvalidInput,
248 "flat vector dimension exceeds u32",
249 )
250 })?;
251 let num_vectors = u32::try_from(num_vectors).map_err(|_| {
252 io::Error::new(io::ErrorKind::InvalidInput, "flat vector count exceeds u32")
253 })?;
254 writer.write_all(&FLAT_BINARY_MAGIC.to_le_bytes())?;
255 writer.write_all(&dim.to_le_bytes())?;
256 writer.write_all(&num_vectors.to_le_bytes())?;
257 writer.write_all(&[quant.tag(), 0, 0, 0])?; Ok(())
259 }
260
261 pub fn serialized_binary_size(
263 dim: usize,
264 num_vectors: usize,
265 quant: DenseVectorQuantization,
266 ) -> io::Result<usize> {
267 let bytes_per_vector = Self::validate_shape(dim, num_vectors, quant)?;
268 let vector_bytes = num_vectors.checked_mul(bytes_per_vector).ok_or_else(|| {
269 io::Error::new(
270 io::ErrorKind::InvalidInput,
271 "flat vector payload size overflows usize",
272 )
273 })?;
274 let doc_id_bytes = num_vectors.checked_mul(DOC_ID_ENTRY_SIZE).ok_or_else(|| {
275 io::Error::new(
276 io::ErrorKind::InvalidInput,
277 "flat vector doc-map size overflows usize",
278 )
279 })?;
280 FLAT_BINARY_HEADER_SIZE
281 .checked_add(vector_bytes)
282 .and_then(|size| size.checked_add(doc_id_bytes))
283 .ok_or_else(|| {
284 io::Error::new(
285 io::ErrorKind::InvalidInput,
286 "flat vector serialized size overflows usize",
287 )
288 })
289 }
290
291 pub fn serialize_binary_from_flat_streaming(
296 dim: usize,
297 flat_vectors: &[f32],
298 doc_ids: &[(u32, u16)],
299 quant: DenseVectorQuantization,
300 writer: &mut dyn std::io::Write,
301 ) -> std::io::Result<()> {
302 Self::validate_dense_input(dim, flat_vectors, doc_ids, quant)?;
303 let num_vectors = doc_ids.len();
304 Self::write_binary_header(dim, num_vectors, quant, writer)?;
305
306 match quant {
307 DenseVectorQuantization::F32 => {
308 let bytes: &[u8] = unsafe {
309 std::slice::from_raw_parts(
310 flat_vectors.as_ptr() as *const u8,
311 std::mem::size_of_val(flat_vectors),
312 )
313 };
314 writer.write_all(bytes)?;
315 }
316 DenseVectorQuantization::F16 => {
317 let mut buf = vec![0u16; dim];
318 for v in flat_vectors.chunks_exact(dim) {
319 batch_f32_to_f16(v, &mut buf);
320 let bytes: &[u8] =
321 unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, dim * 2) };
322 writer.write_all(bytes)?;
323 }
324 }
325 DenseVectorQuantization::UInt8 => {
326 let mut buf = vec![0u8; dim];
327 for v in flat_vectors.chunks_exact(dim) {
328 batch_f32_to_u8(v, &mut buf);
329 writer.write_all(&buf)?;
330 }
331 }
332 DenseVectorQuantization::Binary => unreachable!("validated above"),
333 }
334
335 for &(doc_id, ordinal) in doc_ids {
336 writer.write_all(&doc_id.to_le_bytes())?;
337 writer.write_all(&ordinal.to_le_bytes())?;
338 }
339
340 Ok(())
341 }
342
343 pub fn serialize_binary_from_bits_streaming(
348 dim_bits: usize,
349 packed_vectors: &[u8],
350 doc_ids: &[(u32, u16)],
351 writer: &mut dyn std::io::Write,
352 ) -> std::io::Result<()> {
353 Self::validate_binary_input(dim_bits, packed_vectors, doc_ids)?;
354 let num_vectors = doc_ids.len();
355
356 Self::write_binary_header(
357 dim_bits,
358 num_vectors,
359 DenseVectorQuantization::Binary,
360 writer,
361 )?;
362 writer.write_all(packed_vectors)?;
363
364 for &(doc_id, ordinal) in doc_ids {
365 writer.write_all(&doc_id.to_le_bytes())?;
366 writer.write_all(&ordinal.to_le_bytes())?;
367 }
368
369 Ok(())
370 }
371
372 pub fn write_raw_vector_bytes(
376 raw_bytes: &[u8],
377 writer: &mut dyn std::io::Write,
378 ) -> std::io::Result<()> {
379 writer.write_all(raw_bytes)
380 }
381}
382
383#[derive(Debug, Clone)]
395pub struct LazyFlatVectorData {
396 pub dim: usize,
398 pub num_vectors: usize,
400 num_docs_with_vectors: usize,
402 pub quantization: DenseVectorQuantization,
404 doc_ids_bytes: OwnedBytes,
406 handle: FileHandle,
408 vectors_offset: u64,
410 vbs: usize,
412 vectors_byte_len: u64,
414}
415
416impl LazyFlatVectorData {
417 pub async fn open(handle: FileHandle) -> io::Result<Self> {
422 Self::open_with_doc_limit(handle, None).await
423 }
424
425 pub(crate) async fn open_with_doc_limit(
431 handle: FileHandle,
432 total_docs: Option<u32>,
433 ) -> io::Result<Self> {
434 let header_len = u64::try_from(FLAT_BINARY_HEADER_SIZE).map_err(|_| {
435 io::Error::new(
436 io::ErrorKind::InvalidData,
437 "flat vector header size does not fit in u64",
438 )
439 })?;
440 if handle.len() < header_len {
441 return Err(io::Error::new(
442 io::ErrorKind::UnexpectedEof,
443 format!(
444 "flat vector payload is {} bytes, shorter than its {FLAT_BINARY_HEADER_SIZE}-byte header",
445 handle.len()
446 ),
447 ));
448 }
449
450 let header = handle.read_bytes_range(0..header_len).await?;
452 if header.len() != FLAT_BINARY_HEADER_SIZE {
453 return Err(io::Error::new(
454 io::ErrorKind::UnexpectedEof,
455 format!(
456 "flat vector header read returned {} bytes, expected {FLAT_BINARY_HEADER_SIZE}",
457 header.len()
458 ),
459 ));
460 }
461 let hdr = header.as_slice();
462
463 let magic = u32::from_le_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
464 if magic != FLAT_BINARY_MAGIC {
465 return Err(io::Error::new(
466 io::ErrorKind::InvalidData,
467 "Invalid FlatVectorData binary magic",
468 ));
469 }
470
471 let dim = u32::from_le_bytes([hdr[4], hdr[5], hdr[6], hdr[7]]) as usize;
472 let num_vectors = u32::from_le_bytes([hdr[8], hdr[9], hdr[10], hdr[11]]) as usize;
473 let quantization = DenseVectorQuantization::from_tag(hdr[12]).ok_or_else(|| {
474 io::Error::new(
475 io::ErrorKind::InvalidData,
476 format!("Unknown quantization tag: {}", hdr[12]),
477 )
478 })?;
479 if hdr[13..] != [0, 0, 0] {
480 return Err(io::Error::new(
481 io::ErrorKind::InvalidData,
482 "flat vector header has non-zero reserved bytes",
483 ));
484 }
485
486 let vbs =
488 FlatVectorData::validate_shape(dim, num_vectors, quantization).map_err(|error| {
489 io::Error::new(
490 io::ErrorKind::InvalidData,
491 format!("invalid flat vector shape: {error}"),
492 )
493 })?;
494 let vectors_byte_len_usize = num_vectors.checked_mul(vbs).ok_or_else(|| {
495 io::Error::new(
496 io::ErrorKind::InvalidData,
497 "flat vector payload size overflows usize",
498 )
499 })?;
500 let doc_ids_byte_len_usize =
501 num_vectors.checked_mul(DOC_ID_ENTRY_SIZE).ok_or_else(|| {
502 io::Error::new(
503 io::ErrorKind::InvalidData,
504 "flat vector doc-map size overflows usize",
505 )
506 })?;
507 let expected_len_usize = FLAT_BINARY_HEADER_SIZE
508 .checked_add(vectors_byte_len_usize)
509 .and_then(|size| size.checked_add(doc_ids_byte_len_usize))
510 .ok_or_else(|| {
511 io::Error::new(
512 io::ErrorKind::InvalidData,
513 "flat vector serialized size overflows usize",
514 )
515 })?;
516 let expected_len = u64::try_from(expected_len_usize).map_err(|_| {
517 io::Error::new(
518 io::ErrorKind::InvalidData,
519 "flat vector serialized size does not fit in u64",
520 )
521 })?;
522 if handle.len() != expected_len {
523 return Err(io::Error::new(
524 io::ErrorKind::InvalidData,
525 format!(
526 "flat vector payload has {} bytes, expected exactly {expected_len}",
527 handle.len()
528 ),
529 ));
530 }
531
532 let vectors_byte_len = u64::try_from(vectors_byte_len_usize).map_err(|_| {
533 io::Error::new(
534 io::ErrorKind::InvalidData,
535 "flat vector payload size does not fit in u64",
536 )
537 })?;
538 let doc_ids_byte_len = u64::try_from(doc_ids_byte_len_usize).map_err(|_| {
539 io::Error::new(
540 io::ErrorKind::InvalidData,
541 "flat vector doc-map size does not fit in u64",
542 )
543 })?;
544 let doc_ids_start = header_len.checked_add(vectors_byte_len).ok_or_else(|| {
545 io::Error::new(
546 io::ErrorKind::InvalidData,
547 "flat vector doc-map offset overflows u64",
548 )
549 })?;
550 let doc_ids_end = doc_ids_start.checked_add(doc_ids_byte_len).ok_or_else(|| {
551 io::Error::new(
552 io::ErrorKind::InvalidData,
553 "flat vector doc-map range overflows u64",
554 )
555 })?;
556
557 let doc_ids_bytes = handle.read_bytes_range(doc_ids_start..doc_ids_end).await?;
558 if doc_ids_bytes.len() != doc_ids_byte_len_usize {
559 return Err(io::Error::new(
560 io::ErrorKind::UnexpectedEof,
561 format!(
562 "flat vector doc-map read returned {} bytes, expected {doc_ids_byte_len_usize}",
563 doc_ids_bytes.len()
564 ),
565 ));
566 }
567
568 let mut previous = None;
569 let mut num_docs_with_vectors = 0usize;
570 for entry in doc_ids_bytes.as_slice().chunks_exact(DOC_ID_ENTRY_SIZE) {
571 let doc_id = u32::from_le_bytes([entry[0], entry[1], entry[2], entry[3]]);
572 let ordinal = u16::from_le_bytes([entry[4], entry[5]]);
573 let current = (doc_id, ordinal);
574 if let Some(previous) = previous
575 && previous >= current
576 {
577 return Err(io::Error::new(
578 io::ErrorKind::InvalidData,
579 format!(
580 "flat vector doc map must be strictly sorted by (doc_id, ordinal), found {previous:?} before {current:?}"
581 ),
582 ));
583 }
584 if let Some(limit) = total_docs
585 && doc_id >= limit
586 {
587 return Err(io::Error::new(
588 io::ErrorKind::InvalidData,
589 format!(
590 "flat vector doc map references document {doc_id}, but segment contains only {} documents",
591 limit
592 ),
593 ));
594 }
595 if previous.is_none_or(|(previous_doc_id, _)| previous_doc_id != doc_id) {
596 num_docs_with_vectors = num_docs_with_vectors.checked_add(1).ok_or_else(|| {
597 io::Error::new(
598 io::ErrorKind::InvalidData,
599 "flat vector distinct-document count overflows usize",
600 )
601 })?;
602 }
603 previous = Some(current);
604 }
605
606 Ok(Self {
607 dim,
608 num_vectors,
609 num_docs_with_vectors,
610 quantization,
611 doc_ids_bytes,
612 handle,
613 vectors_offset: header_len,
614 vbs,
615 vectors_byte_len,
616 })
617 }
618
619 fn checked_vector_range(
620 &self,
621 start_idx: usize,
622 count: usize,
623 ) -> io::Result<(std::ops::Range<u64>, usize)> {
624 let end_idx = start_idx.checked_add(count).ok_or_else(|| {
625 io::Error::new(
626 io::ErrorKind::InvalidInput,
627 "flat vector index range overflows usize",
628 )
629 })?;
630 if end_idx > self.num_vectors {
631 return Err(io::Error::new(
632 io::ErrorKind::InvalidInput,
633 format!(
634 "flat vector range {start_idx}..{end_idx} exceeds {} vectors",
635 self.num_vectors
636 ),
637 ));
638 }
639
640 let relative_offset = start_idx.checked_mul(self.vbs).ok_or_else(|| {
641 io::Error::new(
642 io::ErrorKind::InvalidData,
643 "flat vector byte offset overflows usize",
644 )
645 })?;
646 let byte_len = count.checked_mul(self.vbs).ok_or_else(|| {
647 io::Error::new(
648 io::ErrorKind::InvalidInput,
649 "flat vector byte length overflows usize",
650 )
651 })?;
652 let relative_offset = u64::try_from(relative_offset).map_err(|_| {
653 io::Error::new(
654 io::ErrorKind::InvalidData,
655 "flat vector byte offset does not fit in u64",
656 )
657 })?;
658 let byte_len_u64 = u64::try_from(byte_len).map_err(|_| {
659 io::Error::new(
660 io::ErrorKind::InvalidInput,
661 "flat vector byte length does not fit in u64",
662 )
663 })?;
664 let start = self
665 .vectors_offset
666 .checked_add(relative_offset)
667 .ok_or_else(|| {
668 io::Error::new(
669 io::ErrorKind::InvalidData,
670 "flat vector byte offset overflows u64",
671 )
672 })?;
673 let end = start.checked_add(byte_len_u64).ok_or_else(|| {
674 io::Error::new(
675 io::ErrorKind::InvalidData,
676 "flat vector byte range overflows u64",
677 )
678 })?;
679 let vectors_end = self
680 .vectors_offset
681 .checked_add(self.vectors_byte_len)
682 .ok_or_else(|| {
683 io::Error::new(
684 io::ErrorKind::InvalidData,
685 "flat vector payload boundary overflows u64",
686 )
687 })?;
688 if end > vectors_end || end > self.handle.len() {
689 return Err(io::Error::new(
690 io::ErrorKind::InvalidData,
691 format!(
692 "flat vector byte range {start}..{end} exceeds payload boundary {vectors_end}"
693 ),
694 ));
695 }
696 Ok((start..end, byte_len))
697 }
698
699 #[cfg(feature = "native")]
702 pub(crate) fn pin_doc_ids(
703 &mut self,
704 mode: crate::segment::pin::PinMode,
705 remaining: &mut u64,
706 report: &mut crate::segment::pin::PinReport,
707 ) {
708 crate::segment::pin::pin_section(
709 &mut self.doc_ids_bytes,
710 "flat doc_ids",
711 mode,
712 remaining,
713 report,
714 );
715 }
716
717 #[cfg(feature = "native")]
724 pub fn advise_random_access(&self) {
725 let Some(vectors_end) = self.vectors_offset.checked_add(self.vectors_byte_len) else {
726 return;
727 };
728 self.handle
729 .madvise_range(self.vectors_offset..vectors_end, libc::MADV_RANDOM);
730 }
731
732 #[cfg(feature = "native")]
739 pub fn prefetch_vectors(&self, sorted_flat_indexes: impl IntoIterator<Item = usize>) {
740 const COALESCE_GAP: u64 = 64 * 1024;
742 let mut ranges = sorted_flat_indexes.into_iter().filter_map(|idx| {
743 self.checked_vector_range(idx, 1)
744 .ok()
745 .map(|(range, _)| range)
746 });
747 let Some(first) = ranges.next() else {
748 return;
749 };
750 let mut run_start = first.start;
751 let mut run_end = first.end;
752 for range in ranges {
753 if range.start <= run_end.saturating_add(COALESCE_GAP) {
754 run_end = run_end.max(range.end);
755 } else {
756 self.handle
757 .madvise_range(run_start..run_end, libc::MADV_WILLNEED);
758 run_start = range.start;
759 run_end = range.end;
760 }
761 }
762 self.handle
763 .madvise_range(run_start..run_end, libc::MADV_WILLNEED);
764 }
765
766 pub async fn read_vector_into(&self, idx: usize, out: &mut [f32]) -> io::Result<()> {
771 if out.len() < self.dim {
772 return Err(io::Error::new(
773 io::ErrorKind::InvalidInput,
774 format!(
775 "flat vector output is too short: need {} floats, got {}",
776 self.dim,
777 out.len()
778 ),
779 ));
780 }
781 let bytes = self.read_vectors_batch(idx, 1).await?;
782 dequantize_raw(bytes.as_slice(), self.quantization, self.dim, out)
783 }
784
785 pub async fn get_vector(&self, idx: usize) -> io::Result<Vec<f32>> {
787 let mut vector = vec![0f32; self.dim];
788 self.read_vector_into(idx, &mut vector).await?;
789 Ok(vector)
790 }
791
792 pub async fn read_vector_raw_into(&self, idx: usize, out: &mut [u8]) -> io::Result<()> {
797 self.read_vector_prefix_raw_into(idx, self.vector_byte_size(), out)
798 .await
799 }
800
801 pub async fn read_vector_prefix_raw_into(
807 &self,
808 idx: usize,
809 prefix_byte_len: usize,
810 out: &mut [u8],
811 ) -> io::Result<()> {
812 let vbs = self.vector_byte_size();
813 if prefix_byte_len > vbs {
814 return Err(io::Error::new(
815 io::ErrorKind::InvalidInput,
816 format!(
817 "vector prefix is {prefix_byte_len} bytes, but a vector has only {vbs} bytes"
818 ),
819 ));
820 }
821 if out.len() < prefix_byte_len {
822 return Err(io::Error::new(
823 io::ErrorKind::InvalidInput,
824 format!(
825 "vector prefix output is too short: need {prefix_byte_len} bytes, got {}",
826 out.len()
827 ),
828 ));
829 }
830 let (full_range, _) = self.checked_vector_range(idx, 1)?;
831 if prefix_byte_len == 0 {
832 return Ok(());
833 }
834 let prefix_byte_len_u64 = u64::try_from(prefix_byte_len).map_err(|_| {
835 io::Error::new(
836 io::ErrorKind::InvalidInput,
837 "vector prefix length does not fit in u64",
838 )
839 })?;
840 let byte_end = full_range
841 .start
842 .checked_add(prefix_byte_len_u64)
843 .ok_or_else(|| {
844 io::Error::new(
845 io::ErrorKind::InvalidData,
846 "vector byte range overflows u64",
847 )
848 })?;
849 let bytes = self
850 .handle
851 .read_bytes_range(full_range.start..byte_end)
852 .await?;
853 if bytes.len() != prefix_byte_len {
854 return Err(io::Error::new(
855 io::ErrorKind::UnexpectedEof,
856 format!(
857 "vector prefix read returned {} bytes, expected {prefix_byte_len}",
858 bytes.len()
859 ),
860 ));
861 }
862 out[..prefix_byte_len].copy_from_slice(bytes.as_slice());
863 Ok(())
864 }
865
866 pub async fn read_vectors_batch(
872 &self,
873 start_idx: usize,
874 count: usize,
875 ) -> io::Result<OwnedBytes> {
876 let (range, expected_len) = self.checked_vector_range(start_idx, count)?;
877 let bytes = self.handle.read_bytes_range(range).await?;
878 if bytes.len() != expected_len {
879 return Err(io::Error::new(
880 io::ErrorKind::UnexpectedEof,
881 format!(
882 "flat vector batch read returned {} bytes, expected {expected_len}",
883 bytes.len()
884 ),
885 ));
886 }
887 Ok(bytes)
888 }
889
890 #[cfg(feature = "sync")]
892 pub fn read_vector_raw_into_sync(&self, idx: usize, out: &mut [u8]) -> io::Result<()> {
893 let vbs = self.vector_byte_size();
894 if out.len() < vbs {
895 return Err(io::Error::new(
896 io::ErrorKind::InvalidInput,
897 format!(
898 "flat vector output is too short: need {vbs} bytes, got {}",
899 out.len()
900 ),
901 ));
902 }
903 let bytes = self.read_vectors_batch_sync(idx, 1)?;
904 out[..vbs].copy_from_slice(bytes.as_slice());
905 Ok(())
906 }
907
908 #[cfg(feature = "sync")]
910 pub fn read_vectors_batch_sync(
911 &self,
912 start_idx: usize,
913 count: usize,
914 ) -> io::Result<OwnedBytes> {
915 let (range, expected_len) = self.checked_vector_range(start_idx, count)?;
916 let bytes = self.handle.read_bytes_range_sync(range)?;
917 if bytes.len() != expected_len {
918 return Err(io::Error::new(
919 io::ErrorKind::UnexpectedEof,
920 format!(
921 "flat vector batch read returned {} bytes, expected {expected_len}",
922 bytes.len()
923 ),
924 ));
925 }
926 Ok(bytes)
927 }
928
929 pub fn flat_indexes_for_doc_range(&self, doc_id: u32) -> (usize, usize) {
935 let n = self.num_vectors;
936 let start = {
937 let mut lo = 0usize;
938 let mut hi = n;
939 while lo < hi {
940 let mid = lo + (hi - lo) / 2;
941 if self.doc_id_at(mid) < doc_id {
942 lo = mid + 1;
943 } else {
944 hi = mid;
945 }
946 }
947 lo
948 };
949 let mut count = 0;
950 let mut i = start;
951 while i < n && self.doc_id_at(i) == doc_id {
952 count += 1;
953 i += 1;
954 }
955 (start, count)
956 }
957
958 pub fn flat_indexes_for_doc(&self, doc_id: u32) -> (usize, Vec<(u32, u16)>) {
965 let n = self.num_vectors;
966 let start = {
968 let mut lo = 0usize;
969 let mut hi = n;
970 while lo < hi {
971 let mid = lo + (hi - lo) / 2;
972 if self.doc_id_at(mid) < doc_id {
973 lo = mid + 1;
974 } else {
975 hi = mid;
976 }
977 }
978 lo
979 };
980 let mut entries = Vec::new();
982 let mut i = start;
983 while i < n {
984 let (did, ord) = self.get_doc_id(i);
985 if did != doc_id {
986 break;
987 }
988 entries.push((did, ord));
989 i += 1;
990 }
991 (start, entries)
992 }
993
994 #[inline]
996 fn doc_id_at(&self, idx: usize) -> u32 {
997 let off = idx * DOC_ID_ENTRY_SIZE;
998 let d = &self.doc_ids_bytes[off..];
999 u32::from_le_bytes([d[0], d[1], d[2], d[3]])
1000 }
1001
1002 #[inline]
1004 pub fn get_doc_id(&self, idx: usize) -> (u32, u16) {
1005 let off = idx * DOC_ID_ENTRY_SIZE;
1006 let d = &self.doc_ids_bytes[off..];
1007 let doc_id = u32::from_le_bytes([d[0], d[1], d[2], d[3]]);
1008 let ordinal = u16::from_le_bytes([d[4], d[5]]);
1009 (doc_id, ordinal)
1010 }
1011
1012 #[inline]
1014 pub fn vector_byte_size(&self) -> usize {
1015 self.vbs
1016 }
1017
1018 #[inline]
1020 pub fn num_docs_with_vectors(&self) -> usize {
1021 self.num_docs_with_vectors
1022 }
1023
1024 pub fn vector_bytes_len(&self) -> u64 {
1026 self.vectors_byte_len
1027 }
1028
1029 pub fn vectors_byte_offset(&self) -> u64 {
1031 self.vectors_offset
1032 }
1033
1034 pub fn handle(&self) -> &FileHandle {
1036 &self.handle
1037 }
1038
1039 pub fn estimated_memory_bytes(&self) -> usize {
1041 size_of::<Self>() + size_of::<OwnedBytes>()
1042 }
1043}
1044
1045#[derive(Debug, Clone, Serialize, Deserialize)]
1050pub struct IVFRaBitQIndexData {
1051 pub index: crate::structures::IVFRaBitQIndex,
1052 pub codebook: crate::structures::RaBitQCodebook,
1053}
1054
1055impl IVFRaBitQIndexData {
1056 pub fn to_bytes(&self) -> std::io::Result<Vec<u8>> {
1057 bincode::serde::encode_to_vec(self, bincode::config::standard())
1058 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
1059 }
1060
1061 pub fn from_bytes(data: &[u8]) -> std::io::Result<Self> {
1062 let value: Self = crate::structures::vector::decode_ann_bincode_exact(data, "IVF-RaBitQ")?;
1063 value
1064 .codebook
1065 .validate()
1066 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
1067 if value.index.config.default_nprobe == 0
1068 || value.index.config.dim != value.codebook.config.dim
1069 || value.index.codebook_version != value.codebook.version
1070 {
1071 return Err(std::io::Error::new(
1072 std::io::ErrorKind::InvalidData,
1073 "IVF-RaBitQ index/codebook metadata mismatch",
1074 ));
1075 }
1076 let mut total_vectors = 0usize;
1077 for (_, cluster) in value.index.clusters.iter() {
1078 if cluster.doc_ids.len() != cluster.ordinals.len()
1079 || cluster.doc_ids.len() != cluster.codes.len()
1080 {
1081 return Err(std::io::Error::new(
1082 std::io::ErrorKind::InvalidData,
1083 "IVF-RaBitQ cluster column lengths differ",
1084 ));
1085 }
1086 total_vectors = total_vectors
1087 .checked_add(cluster.codes.len())
1088 .ok_or_else(|| {
1089 std::io::Error::new(
1090 std::io::ErrorKind::InvalidData,
1091 "IVF-RaBitQ vector count overflow",
1092 )
1093 })?;
1094 for code in &cluster.codes {
1095 value
1096 .codebook
1097 .validate_vector(code)
1098 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
1099 }
1100 }
1101 if total_vectors != value.index.clusters.total_vectors {
1102 return Err(std::io::Error::new(
1103 std::io::ErrorKind::InvalidData,
1104 "IVF-RaBitQ total vector count is inconsistent",
1105 ));
1106 }
1107 Ok(value)
1108 }
1109}
1110
1111#[derive(Debug, Clone, Serialize, Deserialize)]
1116pub struct ScaNNIndexData {
1117 pub index: crate::structures::IVFPQIndex,
1118 pub codebook: crate::structures::PQCodebook,
1119}
1120
1121impl ScaNNIndexData {
1122 pub fn to_bytes(&self) -> std::io::Result<Vec<u8>> {
1123 bincode::serde::encode_to_vec(self, bincode::config::standard())
1124 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
1125 }
1126
1127 pub fn from_bytes(data: &[u8]) -> std::io::Result<Self> {
1128 let value: Self = crate::structures::vector::decode_ann_bincode_exact(data, "ScaNN")?;
1129 value
1130 .codebook
1131 .validate()
1132 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
1133 if value.index.config.default_nprobe == 0
1134 || value.index.config.dim != value.codebook.config.dim
1135 || value.index.codebook_version != value.codebook.version
1136 {
1137 return Err(std::io::Error::new(
1138 std::io::ErrorKind::InvalidData,
1139 "ScaNN index/codebook metadata mismatch",
1140 ));
1141 }
1142 let expected_codes = value.codebook.config.num_subspaces;
1143 let num_centroids = value.codebook.config.num_centroids;
1144 let mut total_vectors = 0usize;
1145 for (_, cluster) in value.index.clusters.iter() {
1146 if cluster.doc_ids.len() != cluster.ordinals.len()
1147 || cluster.doc_ids.len() != cluster.codes.len()
1148 || cluster.codes.iter().any(|code| {
1149 code.codes.len() != expected_codes
1150 || !code.norm.is_finite()
1151 || code.norm < 0.0
1152 || code
1153 .codes
1154 .iter()
1155 .any(|¢roid| centroid as usize >= num_centroids)
1156 })
1157 {
1158 return Err(std::io::Error::new(
1159 std::io::ErrorKind::InvalidData,
1160 "ScaNN cluster columns or PQ code lengths are invalid",
1161 ));
1162 }
1163 total_vectors = total_vectors
1164 .checked_add(cluster.codes.len())
1165 .ok_or_else(|| {
1166 std::io::Error::new(
1167 std::io::ErrorKind::InvalidData,
1168 "ScaNN vector count overflow",
1169 )
1170 })?;
1171 }
1172 if total_vectors != value.index.clusters.total_vectors {
1173 return Err(std::io::Error::new(
1174 std::io::ErrorKind::InvalidData,
1175 "ScaNN total vector count is inconsistent",
1176 ));
1177 }
1178 Ok(value)
1179 }
1180}
1181
1182#[cfg(test)]
1183mod tests {
1184 use super::*;
1185
1186 #[test]
1187 fn dequantize_raw_accepts_valid_storage_formats() {
1188 let f32_values = [1.25f32, -2.5];
1189 let f32_bytes = unsafe {
1190 std::slice::from_raw_parts(
1192 f32_values.as_ptr().cast::<u8>(),
1193 std::mem::size_of_val(&f32_values),
1194 )
1195 };
1196 let mut out = [0.0; 2];
1197 dequantize_raw(f32_bytes, DenseVectorQuantization::F32, 2, &mut out).unwrap();
1198 assert_eq!(out, f32_values);
1199
1200 let f16_values = [0x3c00u16, 0xc000u16]; let f16_bytes = unsafe {
1202 std::slice::from_raw_parts(
1204 f16_values.as_ptr().cast::<u8>(),
1205 std::mem::size_of_val(&f16_values),
1206 )
1207 };
1208 dequantize_raw(f16_bytes, DenseVectorQuantization::F16, 2, &mut out).unwrap();
1209 assert_eq!(out, [1.0, -2.0]);
1210
1211 dequantize_raw(&[0, u8::MAX], DenseVectorQuantization::UInt8, 2, &mut out).unwrap();
1212 assert_eq!(out, [u8_to_f32(0), u8_to_f32(u8::MAX)]);
1213 }
1214
1215 #[test]
1216 fn dequantize_raw_rejects_invalid_lengths_and_binary_storage() {
1217 let mut out = [0.0; 2];
1218 assert_eq!(
1219 dequantize_raw(&[0; 7], DenseVectorQuantization::F32, 2, &mut out)
1220 .unwrap_err()
1221 .kind(),
1222 io::ErrorKind::InvalidData
1223 );
1224 assert_eq!(
1225 dequantize_raw(&[0; 8], DenseVectorQuantization::F32, 2, &mut out[..1])
1226 .unwrap_err()
1227 .kind(),
1228 io::ErrorKind::InvalidInput
1229 );
1230 assert_eq!(
1231 dequantize_raw(&[], DenseVectorQuantization::Binary, 0, &mut [])
1232 .unwrap_err()
1233 .kind(),
1234 io::ErrorKind::InvalidInput
1235 );
1236 }
1237
1238 #[test]
1239 fn dequantize_raw_rejects_misaligned_typed_storage() {
1240 let storage = [0u8; 9];
1241 let offset = if (storage.as_ptr() as usize).is_multiple_of(4) {
1242 1
1243 } else {
1244 0
1245 };
1246 let raw = &storage[offset..offset + 8];
1247 assert!(!(raw.as_ptr() as usize).is_multiple_of(4));
1248
1249 let mut out = [0.0; 2];
1250 assert_eq!(
1251 dequantize_raw(raw, DenseVectorQuantization::F32, 2, &mut out)
1252 .unwrap_err()
1253 .kind(),
1254 io::ErrorKind::InvalidData
1255 );
1256 }
1257
1258 #[test]
1259 fn flat_vector_writers_reject_inconsistent_shapes_and_doc_maps() {
1260 let mut encoded = Vec::new();
1261 assert!(
1262 FlatVectorData::serialize_binary_from_flat_streaming(
1263 0,
1264 &[],
1265 &[],
1266 DenseVectorQuantization::F32,
1267 &mut encoded,
1268 )
1269 .is_err()
1270 );
1271 assert!(encoded.is_empty());
1272
1273 assert!(
1274 FlatVectorData::serialize_binary_from_flat_streaming(
1275 2,
1276 &[1.0],
1277 &[(0, 0)],
1278 DenseVectorQuantization::F32,
1279 &mut encoded,
1280 )
1281 .is_err()
1282 );
1283 assert!(encoded.is_empty());
1284
1285 assert!(
1286 FlatVectorData::serialize_binary_from_flat_streaming(
1287 1,
1288 &[1.0],
1289 &[(0, 0)],
1290 DenseVectorQuantization::Binary,
1291 &mut encoded,
1292 )
1293 .is_err()
1294 );
1295 assert!(encoded.is_empty());
1296
1297 assert!(
1298 FlatVectorData::serialize_binary_from_flat_streaming(
1299 1,
1300 &[1.0, 2.0],
1301 &[(1, 0), (0, 0)],
1302 DenseVectorQuantization::F32,
1303 &mut encoded,
1304 )
1305 .is_err()
1306 );
1307 assert!(encoded.is_empty());
1308
1309 assert!(
1310 FlatVectorData::serialize_binary_from_flat_streaming(
1311 1,
1312 &[1.0, 2.0],
1313 &[(0, 0), (0, 0)],
1314 DenseVectorQuantization::F32,
1315 &mut encoded,
1316 )
1317 .is_err()
1318 );
1319 assert!(encoded.is_empty());
1320
1321 assert!(
1322 FlatVectorData::serialize_binary_from_bits_streaming(7, &[0], &[(0, 0)], &mut encoded,)
1323 .is_err()
1324 );
1325 assert!(encoded.is_empty());
1326
1327 assert!(
1328 FlatVectorData::serialize_binary_from_bits_streaming(8, &[], &[(0, 0)], &mut encoded,)
1329 .is_err()
1330 );
1331 assert!(encoded.is_empty());
1332
1333 let vectors = [1.0f32, 2.0, 3.0, 4.0];
1334 let doc_ids = [(0, 0), (1, 0)];
1335 FlatVectorData::serialize_binary_from_flat_streaming(
1336 2,
1337 &vectors,
1338 &doc_ids,
1339 DenseVectorQuantization::F32,
1340 &mut encoded,
1341 )
1342 .unwrap();
1343 assert_eq!(
1344 encoded.len(),
1345 FlatVectorData::serialized_binary_size(2, 2, DenseVectorQuantization::F32).unwrap()
1346 );
1347 }
1348
1349 fn encoded_two_vector_payload() -> Vec<u8> {
1350 let mut encoded = Vec::new();
1351 FlatVectorData::serialize_binary_from_flat_streaming(
1352 2,
1353 &[1.0, 2.0, 3.0, 4.0],
1354 &[(0, 0), (1, 0)],
1355 DenseVectorQuantization::F32,
1356 &mut encoded,
1357 )
1358 .unwrap();
1359 encoded
1360 }
1361
1362 #[tokio::test]
1363 async fn flat_vector_open_rejects_corrupt_layout_and_doc_map() {
1364 let valid = encoded_two_vector_payload();
1365
1366 let mut multi_value = Vec::new();
1367 FlatVectorData::serialize_binary_from_flat_streaming(
1368 1,
1369 &[1.0, 2.0, 3.0],
1370 &[(0, 0), (0, 1), (2, 0)],
1371 DenseVectorQuantization::F32,
1372 &mut multi_value,
1373 )
1374 .unwrap();
1375 let multi_value = LazyFlatVectorData::open_with_doc_limit(
1376 FileHandle::from_bytes(OwnedBytes::new(multi_value)),
1377 Some(3),
1378 )
1379 .await
1380 .unwrap();
1381 assert_eq!(multi_value.num_docs_with_vectors(), 2);
1382
1383 let mut trailing = valid.clone();
1384 trailing.push(0);
1385 assert!(
1386 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(trailing)))
1387 .await
1388 .is_err()
1389 );
1390
1391 let mut truncated = valid.clone();
1392 truncated.pop();
1393 assert!(
1394 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(truncated)))
1395 .await
1396 .is_err()
1397 );
1398
1399 let mut reserved = valid.clone();
1400 reserved[13] = 1;
1401 assert!(
1402 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(reserved)))
1403 .await
1404 .is_err()
1405 );
1406
1407 let doc_map_start = FLAT_BINARY_HEADER_SIZE + 2 * 2 * size_of::<f32>();
1408 let mut unsorted = valid.clone();
1409 let (first, second) = unsorted[doc_map_start..doc_map_start + 2 * DOC_ID_ENTRY_SIZE]
1410 .split_at_mut(DOC_ID_ENTRY_SIZE);
1411 first.swap_with_slice(second);
1412 assert!(
1413 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(unsorted)))
1414 .await
1415 .is_err()
1416 );
1417
1418 let mut duplicate = valid.clone();
1419 duplicate.copy_within(
1420 doc_map_start..doc_map_start + DOC_ID_ENTRY_SIZE,
1421 doc_map_start + DOC_ID_ENTRY_SIZE,
1422 );
1423 assert!(
1424 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(duplicate)))
1425 .await
1426 .is_err()
1427 );
1428
1429 assert!(
1430 LazyFlatVectorData::open_with_doc_limit(
1431 FileHandle::from_bytes(OwnedBytes::new(valid)),
1432 Some(1),
1433 )
1434 .await
1435 .is_err()
1436 );
1437
1438 let mut invalid_binary = Vec::new();
1439 FlatVectorData::serialize_binary_from_bits_streaming(
1440 8,
1441 &[0],
1442 &[(0, 0)],
1443 &mut invalid_binary,
1444 )
1445 .unwrap();
1446 invalid_binary[4..8].copy_from_slice(&7u32.to_le_bytes());
1447 assert!(
1448 LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(invalid_binary)))
1449 .await
1450 .is_err()
1451 );
1452 }
1453
1454 #[tokio::test]
1455 async fn flat_vector_batch_and_dequantized_reads_are_checked() {
1456 let flat = LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(
1457 encoded_two_vector_payload(),
1458 )))
1459 .await
1460 .unwrap();
1461
1462 assert_eq!(flat.read_vectors_batch(0, 2).await.unwrap().len(), 16);
1463 assert_eq!(flat.read_vectors_batch(2, 0).await.unwrap().len(), 0);
1464 assert!(flat.read_vectors_batch(1, 2).await.is_err());
1465 assert!(flat.read_vectors_batch(usize::MAX, 1).await.is_err());
1466 assert!(flat.read_vectors_batch(0, usize::MAX).await.is_err());
1467
1468 let mut values = [0.0; 2];
1469 flat.read_vector_into(1, &mut values).await.unwrap();
1470 assert_eq!(values, [3.0, 4.0]);
1471 assert!(flat.read_vector_into(2, &mut values).await.is_err());
1472 assert!(flat.read_vector_into(0, &mut values[..1]).await.is_err());
1473
1474 #[cfg(feature = "sync")]
1475 {
1476 assert_eq!(flat.read_vectors_batch_sync(0, 2).unwrap().len(), 16);
1477 assert!(flat.read_vectors_batch_sync(1, 2).is_err());
1478 assert!(flat.read_vectors_batch_sync(usize::MAX, 1).is_err());
1479 let mut too_short = [0; 7];
1480 assert!(flat.read_vector_raw_into_sync(0, &mut too_short).is_err());
1481 }
1482 }
1483
1484 #[cfg(not(target_arch = "wasm32"))]
1485 #[tokio::test]
1486 async fn flat_vector_reads_reject_short_lazy_range_results() {
1487 let payload = std::sync::Arc::new(encoded_two_vector_payload());
1488 let payload_len = payload.len() as u64;
1489 let read_payload = std::sync::Arc::clone(&payload);
1490 let read_fn: crate::directories::RangeReadFn = std::sync::Arc::new(move |range| {
1491 let payload = std::sync::Arc::clone(&read_payload);
1492 Box::pin(async move {
1493 let start = usize::try_from(range.start).unwrap();
1494 let mut end = usize::try_from(range.end).unwrap();
1495 if range.start == FLAT_BINARY_HEADER_SIZE as u64 {
1498 end -= 1;
1499 }
1500 Ok(OwnedBytes::new(payload[start..end].to_vec()))
1501 })
1502 });
1503 let flat = LazyFlatVectorData::open(FileHandle::lazy(payload_len, read_fn))
1504 .await
1505 .unwrap();
1506
1507 let error = flat.read_vectors_batch(0, 1).await.unwrap_err();
1508 assert_eq!(error.kind(), io::ErrorKind::UnexpectedEof);
1509 let mut raw = [0; 8];
1510 let error = flat.read_vector_raw_into(0, &mut raw).await.unwrap_err();
1511 assert_eq!(error.kind(), io::ErrorKind::UnexpectedEof);
1512 }
1513
1514 #[test]
1515 fn ann_bincode_decoders_reject_trailing_and_invalid_semantics() {
1516 let rabitq_codebook =
1517 crate::structures::RaBitQCodebook::new(crate::structures::RaBitQConfig::new(8));
1518 let rabitq = IVFRaBitQIndexData {
1519 index: crate::structures::IVFRaBitQIndex::new(
1520 crate::structures::IVFRaBitQConfig::new(8),
1521 1,
1522 rabitq_codebook.version,
1523 ),
1524 codebook: rabitq_codebook,
1525 };
1526 let mut rabitq_bytes = rabitq.to_bytes().unwrap();
1527 assert!(IVFRaBitQIndexData::from_bytes(&rabitq_bytes).is_ok());
1528 let mut invalid_rabitq = rabitq.clone();
1529 invalid_rabitq.index.config.default_nprobe = 0;
1530 assert!(IVFRaBitQIndexData::from_bytes(&invalid_rabitq.to_bytes().unwrap()).is_err());
1531 invalid_rabitq.index.config.default_nprobe = 1;
1532 invalid_rabitq.codebook.config.query_bits = 0;
1533 assert!(IVFRaBitQIndexData::from_bytes(&invalid_rabitq.to_bytes().unwrap()).is_err());
1534 rabitq_bytes.push(0);
1535 assert!(IVFRaBitQIndexData::from_bytes(&rabitq_bytes).is_err());
1536
1537 let pq_config = crate::structures::PQConfig::new(2);
1538 let pq_codebook = crate::structures::PQCodebook {
1539 centroids: vec![
1540 0.0;
1541 pq_config.num_subspaces
1542 * pq_config.num_centroids
1543 * pq_config.dims_per_block
1544 ],
1545 rotation_matrix: None,
1546 centroid_norms: None,
1547 version: 2,
1548 config: pq_config,
1549 };
1550 let scann = ScaNNIndexData {
1551 index: crate::structures::IVFPQIndex::new(
1552 crate::structures::IVFPQConfig::new(2),
1553 1,
1554 pq_codebook.version,
1555 ),
1556 codebook: pq_codebook,
1557 };
1558 let mut scann_bytes = scann.to_bytes().unwrap();
1559 assert!(ScaNNIndexData::from_bytes(&scann_bytes).is_ok());
1560 let mut invalid_scann = scann.clone();
1561 invalid_scann.index.config.default_nprobe = 0;
1562 assert!(ScaNNIndexData::from_bytes(&invalid_scann.to_bytes().unwrap()).is_err());
1563 invalid_scann.index.config.default_nprobe = 1;
1564 invalid_scann.codebook.config.aniso_eta = f32::NAN;
1565 assert!(ScaNNIndexData::from_bytes(&invalid_scann.to_bytes().unwrap()).is_err());
1566 scann_bytes.push(0);
1567 assert!(ScaNNIndexData::from_bytes(&scann_bytes).is_err());
1568 }
1569
1570 #[tokio::test]
1571 async fn vector_prefix_reads_are_checked_and_do_not_fetch_the_tail() {
1572 let vectors = [1.0f32, 2.0, 3.0, 4.0];
1573 let doc_ids = [(0, 0), (1, 0)];
1574 let mut encoded = Vec::new();
1575 FlatVectorData::serialize_binary_from_flat_streaming(
1576 2,
1577 &vectors,
1578 &doc_ids,
1579 DenseVectorQuantization::F32,
1580 &mut encoded,
1581 )
1582 .unwrap();
1583 let flat = LazyFlatVectorData::open(FileHandle::from_bytes(OwnedBytes::new(encoded)))
1584 .await
1585 .unwrap();
1586
1587 let mut prefix = [0xa5; 8];
1588 flat.read_vector_prefix_raw_into(1, 4, &mut prefix)
1589 .await
1590 .unwrap();
1591 assert_eq!(&prefix[..4], &3.0f32.to_ne_bytes());
1592 assert_eq!(&prefix[4..], &[0xa5; 4]);
1593
1594 let mut full = [0; 8];
1595 flat.read_vector_raw_into(1, &mut full).await.unwrap();
1596 assert_eq!(&full[..4], &3.0f32.to_ne_bytes());
1597 assert_eq!(&full[4..], &4.0f32.to_ne_bytes());
1598
1599 assert!(
1600 flat.read_vector_prefix_raw_into(2, 4, &mut prefix)
1601 .await
1602 .is_err()
1603 );
1604 assert!(
1605 flat.read_vector_prefix_raw_into(0, 9, &mut prefix)
1606 .await
1607 .is_err()
1608 );
1609 assert!(
1610 flat.read_vector_prefix_raw_into(0, 4, &mut prefix[..3])
1611 .await
1612 .is_err()
1613 );
1614 }
1615}