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