faiss 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.cpp +3 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +9 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +14 -3
  7. data/vendor/faiss/faiss/Index.h +2 -2
  8. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +9 -10
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizerFastScan.cpp +2 -3
  10. data/vendor/faiss/faiss/IndexBinaryFromFloat.cpp +1 -2
  11. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +4 -5
  12. data/vendor/faiss/faiss/IndexBinaryHash.cpp +5 -9
  13. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +2 -4
  14. data/vendor/faiss/faiss/IndexEDEN.cpp +273 -0
  15. data/vendor/faiss/faiss/IndexEDEN.h +57 -0
  16. data/vendor/faiss/faiss/IndexFastScan.cpp +15 -4
  17. data/vendor/faiss/faiss/IndexFlat.cpp +13 -50
  18. data/vendor/faiss/faiss/IndexHNSW.cpp +10 -11
  19. data/vendor/faiss/faiss/IndexIDMap.cpp +16 -3
  20. data/vendor/faiss/faiss/IndexIDMap.h +2 -0
  21. data/vendor/faiss/faiss/IndexIVF.cpp +17 -6
  22. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +1 -1
  23. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +3 -4
  24. data/vendor/faiss/faiss/IndexIVFEDEN.cpp +302 -0
  25. data/vendor/faiss/faiss/IndexIVFEDEN.h +70 -0
  26. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +5 -6
  27. data/vendor/faiss/faiss/IndexIVFFlat.cpp +3 -4
  28. data/vendor/faiss/faiss/IndexIVFIndependentQuantizer.cpp +1 -1
  29. data/vendor/faiss/faiss/IndexIVFPQ.cpp +40 -22
  30. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +0 -1
  31. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +19 -49
  32. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +180 -76
  33. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +5 -4
  34. data/vendor/faiss/faiss/IndexIVFSpectralHash.cpp +8 -6
  35. data/vendor/faiss/faiss/IndexLSH.cpp +2 -3
  36. data/vendor/faiss/faiss/IndexLattice.cpp +5 -0
  37. data/vendor/faiss/faiss/IndexNNDescent.cpp +9 -2
  38. data/vendor/faiss/faiss/IndexNSG.cpp +7 -2
  39. data/vendor/faiss/faiss/IndexPQ.cpp +6 -8
  40. data/vendor/faiss/faiss/IndexPreTransform.cpp +15 -0
  41. data/vendor/faiss/faiss/IndexRaBitQ.cpp +2 -2
  42. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +1 -2
  43. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +5 -1
  44. data/vendor/faiss/faiss/IndexRefine.cpp +30 -1
  45. data/vendor/faiss/faiss/IndexReplicas.cpp +1 -2
  46. data/vendor/faiss/faiss/IndexShards.cpp +2 -2
  47. data/vendor/faiss/faiss/IndexShardsIVF.cpp +2 -2
  48. data/vendor/faiss/faiss/MetaIndexes.cpp +2 -4
  49. data/vendor/faiss/faiss/SuperKMeans.cpp +256 -240
  50. data/vendor/faiss/faiss/SuperKMeans.h +30 -0
  51. data/vendor/faiss/faiss/VectorTransform.cpp +33 -2
  52. data/vendor/faiss/faiss/clone_index.cpp +5 -0
  53. data/vendor/faiss/faiss/factory_tools.cpp +47 -4
  54. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +11 -11
  55. data/vendor/faiss/faiss/gpu/GpuIndex.h +34 -11
  56. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +47 -0
  57. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +17 -0
  58. data/vendor/faiss/faiss/gpu/GpuIndexIVFScalarQuantizer.h +16 -0
  59. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +1 -1
  60. data/vendor/faiss/faiss/gpu/perf/PerfIVFPQAdd.cpp +2 -2
  61. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFScalarQuantizer.cpp +180 -0
  62. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFFlat.h +1 -5
  63. data/vendor/faiss/faiss/gpu_metal/MetalIndexIVFPQ.h +88 -0
  64. data/vendor/faiss/faiss/gpu_metal/impl/MetalIVFPQ.h +134 -0
  65. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +2 -2
  66. data/vendor/faiss/faiss/impl/DistanceComputer.h +34 -0
  67. data/vendor/faiss/faiss/impl/EDENQuantizer.h +119 -0
  68. data/vendor/faiss/faiss/impl/HNSW.cpp +109 -152
  69. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +2 -2
  70. data/vendor/faiss/faiss/impl/NSG.cpp +3 -1
  71. data/vendor/faiss/faiss/impl/Panorama.h +9 -7
  72. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +152 -84
  73. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +34 -22
  74. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +44 -36
  75. data/vendor/faiss/faiss/impl/RaBitQUtils.h +35 -0
  76. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +168 -67
  77. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +19 -0
  78. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +2 -11
  79. data/vendor/faiss/faiss/impl/ResultHandler.h +25 -31
  80. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +258 -57
  81. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +20 -0
  82. data/vendor/faiss/faiss/impl/ThreadedIndex-inl.h +2 -2
  83. data/vendor/faiss/faiss/impl/VisitedTable.cpp +22 -2
  84. data/vendor/faiss/faiss/impl/VisitedTable.h +20 -0
  85. data/vendor/faiss/faiss/impl/binary_hamming/IndexBinaryIVF_impl.h +1 -1
  86. data/vendor/faiss/faiss/impl/binary_hamming/avx2.cpp +4 -4
  87. data/vendor/faiss/faiss/impl/fast_scan/dispatching.h +35 -2
  88. data/vendor/faiss/faiss/impl/hnsw/LockVector.cpp +1 -1
  89. data/vendor/faiss/faiss/impl/index_read.cpp +376 -36
  90. data/vendor/faiss/faiss/impl/index_write.cpp +55 -4
  91. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +8 -9
  92. data/vendor/faiss/faiss/impl/platform_macros.h +3 -1
  93. data/vendor/faiss/faiss/impl/polysemous_training/avx512.cpp +284 -0
  94. data/vendor/faiss/faiss/impl/polysemous_training/dispatch.h +115 -0
  95. data/vendor/faiss/faiss/impl/pq_code_distance/IVFPQ_QueryTables.cpp +0 -1
  96. data/vendor/faiss/faiss/impl/pq_code_distance/PQDistanceComputer_impl.h +26 -15
  97. data/vendor/faiss/faiss/impl/pq_code_distance/avx2.cpp +4 -4
  98. data/vendor/faiss/faiss/impl/result_handler/ResultHandler.cpp +195 -0
  99. data/vendor/faiss/faiss/impl/result_handler/avx2.cpp +133 -0
  100. data/vendor/faiss/faiss/impl/result_handler/avx512.cpp +281 -0
  101. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx2.cpp +72 -0
  102. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer-avx512.cpp +228 -0
  103. data/vendor/faiss/faiss/impl/scalar_quantizer/EDENQuantizer.cpp +882 -0
  104. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +9 -8
  105. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +85 -23
  106. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-impl.h +30 -30
  107. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512-spr.cpp +4 -5
  108. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +101 -34
  109. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +136 -0
  110. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +16 -16
  111. data/vendor/faiss/faiss/impl/simd_dispatch.h +30 -9
  112. data/vendor/faiss/faiss/index_factory.cpp +32 -6
  113. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  114. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +2 -2
  115. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +19 -4
  116. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -1
  117. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +60 -0
  118. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +26 -1
  119. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +13 -0
  120. data/vendor/faiss/faiss/svs/IndexSVSIVF.cpp +1 -1
  121. data/vendor/faiss/faiss/svs/IndexSVSIVFLeanVec.cpp +1 -1
  122. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +31 -1
  123. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +15 -2
  124. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +1 -2
  125. data/vendor/faiss/faiss/utils/approx_topk_hamming/approx_topk_hamming.h +1 -1
  126. data/vendor/faiss/faiss/utils/distances.cpp +14 -2
  127. data/vendor/faiss/faiss/utils/distances_simd.cpp +4 -3
  128. data/vendor/faiss/faiss/utils/extra_distances.cpp +4 -14
  129. data/vendor/faiss/faiss/utils/extra_distances.h +1 -2
  130. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  131. data/vendor/faiss/faiss/utils/quantize_lut.cpp +29 -8
  132. data/vendor/faiss/faiss/utils/rabitq_simd.h +202 -0
  133. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +0 -1
  134. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +263 -15
  135. data/vendor/faiss/faiss/utils/simd_impl/distances_rvv.cpp +160 -18
  136. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx2.cpp +245 -0
  137. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512.cpp +273 -0
  138. data/vendor/faiss/faiss/utils/simd_impl/rabitq_avx512_spr.cpp +92 -0
  139. data/vendor/faiss/faiss/utils/simd_impl/rabitq_neon.cpp +11 -0
  140. data/vendor/faiss/faiss/utils/simd_impl/rabitq_rvv.cpp +143 -6
  141. data/vendor/faiss/faiss/utils/simd_levels.cpp +44 -0
  142. data/vendor/faiss/faiss/utils/simd_levels.h +14 -0
  143. data/vendor/faiss/faiss/utils/utils.cpp +9 -27
  144. metadata +16 -1
@@ -30,11 +30,13 @@
30
30
  #include <faiss/Index2Layer.h>
31
31
  #include <faiss/IndexAdditiveQuantizer.h>
32
32
  #include <faiss/IndexAdditiveQuantizerFastScan.h>
33
+ #include <faiss/IndexEDEN.h>
33
34
  #include <faiss/IndexFlat.h>
34
35
  #include <faiss/IndexHNSW.h>
35
36
  #include <faiss/IndexIVF.h>
36
37
  #include <faiss/IndexIVFAdditiveQuantizer.h>
37
38
  #include <faiss/IndexIVFAdditiveQuantizerFastScan.h>
39
+ #include <faiss/IndexIVFEDEN.h>
38
40
  #include <faiss/IndexIVFFlat.h>
39
41
  #include <faiss/IndexIVFFlatPanorama.h>
40
42
  #include <faiss/IndexIVFIndependentQuantizer.h>
@@ -68,6 +70,7 @@
68
70
  #include <faiss/IndexScalarQuantizer.h>
69
71
  #include <faiss/MetaIndexes.h>
70
72
  #include <faiss/VectorTransform.h>
73
+ #include <faiss/impl/EDENQuantizer.h>
71
74
 
72
75
  #include <faiss/IndexBinaryFlat.h>
73
76
  #include <faiss/IndexBinaryFromFloat.h>
@@ -337,7 +340,9 @@ std::unique_ptr<VectorTransform> read_VectorTransform_up(IOReader* f) {
337
340
  READVECTOR(lt->b);
338
341
  FAISS_THROW_IF_NOT(
339
342
  lt->A.size() >= size_t(lt->d_in) * size_t(lt->d_out));
340
- FAISS_THROW_IF_NOT(!lt->have_bias || lt->b.size() >= size_t(lt->d_out));
343
+ FAISS_THROW_IF_MSG(
344
+ lt->have_bias && lt->b.size() < size_t(lt->d_out),
345
+ "bias vector smaller than d_out");
341
346
  lt->set_is_orthonormal();
342
347
  vt = std::move(lt);
343
348
  } else if (h == fourcc("RmDT")) {
@@ -732,7 +737,9 @@ void read_ProductQuantizer(ProductQuantizer* pq, IOReader* f) {
732
737
  FAISS_THROW_IF_NOT_FMT(
733
738
  pq->M > 0, "invalid ProductQuantizer M=%zd (must be > 0)", pq->M);
734
739
  FAISS_THROW_IF_NOT_FMT(
735
- pq->nbits <= 24, "invalid ProductQuantizer nbits=%zd", pq->nbits);
740
+ pq->nbits >= 1 && pq->nbits <= 24,
741
+ "invalid ProductQuantizer nbits=%zd (must be in [1, 24])",
742
+ pq->nbits);
736
743
  {
737
744
  size_t ksub = size_t{1} << pq->nbits;
738
745
  size_t n = mul_no_overflow(pq->d, ksub, "PQ centroids");
@@ -862,6 +869,11 @@ static void validate_fastscan_fields(
862
869
  index_type,
863
870
  M,
864
871
  ksub);
872
+ FAISS_THROW_IF_NOT_FMT(
873
+ ksub == 16,
874
+ "%s: invalid ksub=%zd (fast-scan requires nbits=4 / ksub=16)",
875
+ index_type,
876
+ ksub);
865
877
  FAISS_THROW_IF_NOT_FMT(
866
878
  bbs > 0 && bbs % 32 == 0,
867
879
  "%s: invalid bbs=%d (must be > 0 and a multiple of 32)",
@@ -1086,6 +1098,30 @@ void read_ScalarQuantizer(
1086
1098
  case ScalarQuantizer::QT_8bit_tqmse:
1087
1099
  expected = 256 + 255;
1088
1100
  break;
1101
+ case ScalarQuantizer::QT_1bit_eden:
1102
+ expected = 2 + 1; // 2^bits centroids + (2^bits - 1) boundaries
1103
+ break;
1104
+ case ScalarQuantizer::QT_2bit_eden:
1105
+ expected = 4 + 3;
1106
+ break;
1107
+ case ScalarQuantizer::QT_3bit_eden:
1108
+ expected = 8 + 7;
1109
+ break;
1110
+ case ScalarQuantizer::QT_4bit_eden:
1111
+ expected = 16 + 15;
1112
+ break;
1113
+ case ScalarQuantizer::QT_5bit_eden:
1114
+ expected = 32 + 31;
1115
+ break;
1116
+ case ScalarQuantizer::QT_6bit_eden:
1117
+ expected = 64 + 63;
1118
+ break;
1119
+ case ScalarQuantizer::QT_7bit_eden:
1120
+ expected = 128 + 127;
1121
+ break;
1122
+ case ScalarQuantizer::QT_8bit_eden:
1123
+ expected = 256 + 255;
1124
+ break;
1089
1125
  case ScalarQuantizer::QT_2bit_tq:
1090
1126
  case ScalarQuantizer::QT_3bit_tq:
1091
1127
  case ScalarQuantizer::QT_4bit_tq:
@@ -1339,13 +1375,15 @@ static void read_NNDescent(NNDescent& nnd, IOReader* f) {
1339
1375
  READVECTOR(nnd.final_graph);
1340
1376
  // Validate neighbor IDs in the graph
1341
1377
  if (nnd.has_built && nnd.K > 0 && nnd.ntotal > 0) {
1378
+ const size_t expected_final_graph_size = mul_no_overflow(
1379
+ (size_t)nnd.ntotal, (size_t)nnd.K, "NNDescent final_graph");
1342
1380
  FAISS_THROW_IF_NOT_FMT(
1343
- nnd.final_graph.size() == (size_t)nnd.ntotal * (size_t)nnd.K,
1381
+ nnd.final_graph.size() == expected_final_graph_size,
1344
1382
  "NNDescent final_graph size %zu != ntotal * K (%d * %d = %zu)",
1345
1383
  nnd.final_graph.size(),
1346
1384
  nnd.ntotal,
1347
1385
  nnd.K,
1348
- (size_t)nnd.ntotal * (size_t)nnd.K);
1386
+ expected_final_graph_size);
1349
1387
  for (size_t i = 0; i < nnd.final_graph.size(); i++) {
1350
1388
  int id = nnd.final_graph[i];
1351
1389
  FAISS_THROW_IF_NOT_FMT(
@@ -1402,6 +1440,64 @@ static void read_RaBitQuantizer(
1402
1440
  expected_d);
1403
1441
  }
1404
1442
 
1443
+ static void read_EDENScalarQuantizer(
1444
+ ScalarQuantizer& sq,
1445
+ EDENScaleType& scale_type,
1446
+ IOReader* f,
1447
+ int expected_d,
1448
+ MetricType expected_metric_type,
1449
+ bool read_scale_type) {
1450
+ size_t d;
1451
+ size_t stored_code_size;
1452
+ size_t nb_bits;
1453
+
1454
+ READ1(d);
1455
+ READ1(stored_code_size);
1456
+ int metric_type_int;
1457
+ READ1(metric_type_int);
1458
+ const MetricType metric_type = metric_type_from_int(metric_type_int);
1459
+ READ1(nb_bits);
1460
+ if (read_scale_type) {
1461
+ int scale_type_int;
1462
+ READ1(scale_type_int);
1463
+ if (scale_type_int == 0) {
1464
+ scale_type = EDENScaleType_UNBIASED;
1465
+ } else {
1466
+ scale_type = static_cast<EDENScaleType>(scale_type_int);
1467
+ }
1468
+ } else {
1469
+ scale_type = EDENScaleType_UNBIASED;
1470
+ }
1471
+
1472
+ FAISS_THROW_IF_NOT_FMT(
1473
+ d == static_cast<size_t>(expected_d),
1474
+ "EDEN ScalarQuantizer dimension mismatch: sq.d=%zu vs index d=%d",
1475
+ d,
1476
+ expected_d);
1477
+ FAISS_THROW_IF_NOT_FMT(
1478
+ metric_type == expected_metric_type,
1479
+ "EDEN ScalarQuantizer metric mismatch: stored=%d vs index=%d",
1480
+ metric_type_int,
1481
+ static_cast<int>(expected_metric_type));
1482
+ FAISS_THROW_IF_NOT_FMT(
1483
+ nb_bits >= 1 && nb_bits <= 8,
1484
+ "invalid EDEN nb_bits=%zu (must be in [1, 8])",
1485
+ nb_bits);
1486
+ FAISS_THROW_IF_NOT_FMT(
1487
+ scale_type == EDENScaleType_UNBIASED ||
1488
+ scale_type == EDENScaleType_BIASED,
1489
+ "invalid EDEN scale_type=%d",
1490
+ static_cast<int>(scale_type));
1491
+ sq = ScalarQuantizer(d, eden_utils::quantizer_type_for_bits(nb_bits));
1492
+ sq.train(0, nullptr);
1493
+ const size_t expected_code_size = eden_utils::code_size(d, nb_bits);
1494
+ FAISS_THROW_IF_NOT_FMT(
1495
+ stored_code_size == expected_code_size,
1496
+ "EDEN ScalarQuantizer code_size mismatch: stored=%zu vs expected=%zu",
1497
+ stored_code_size,
1498
+ expected_code_size);
1499
+ }
1500
+
1405
1501
  void read_direct_map(DirectMap* dm, IOReader* f) {
1406
1502
  char maintain_direct_map;
1407
1503
  READ1(maintain_direct_map);
@@ -1453,6 +1549,45 @@ ArrayInvertedLists* set_array_invlist(
1453
1549
  return result;
1454
1550
  }
1455
1551
 
1552
+ static void validate_ivfpq_precomputed_table_size(
1553
+ const Index* quantizer,
1554
+ const ProductQuantizer& pq) {
1555
+ // The precomputed table is not stored; precompute_table() rebuilds it on
1556
+ // load at a size derived from attacker-controlled header fields. Bound
1557
+ // every table initialize_IVFPQ_precomputed_table() may allocate.
1558
+ const size_t m_ksub =
1559
+ mul_no_overflow(pq.M, pq.ksub, "IVFPQ precomputed_table");
1560
+ // type 1: nlist (== quantizer->ntotal) * pq.M * pq.ksub.
1561
+ size_t precompute_elems = mul_no_overflow(
1562
+ static_cast<size_t>(quantizer->ntotal),
1563
+ m_ksub,
1564
+ "IVFPQ precomputed_table");
1565
+ // type 2 (MultiIndexQuantizer coarse quantizer): cpq.ksub * pq.M * pq.ksub,
1566
+ // plus a temporary quantizer->d * cpq.ksub centroid table. Both derive from
1567
+ // the coarse PQ's ksub, which is independent of quantizer->ntotal, so the
1568
+ // type-1 bound above does not cover them.
1569
+ if (const auto* miq = dynamic_cast<const MultiIndexQuantizer*>(quantizer)) {
1570
+ const size_t cpq_ksub = miq->pq.ksub;
1571
+ const size_t type2_table =
1572
+ mul_no_overflow(cpq_ksub, m_ksub, "IVFPQ precomputed_table");
1573
+ const size_t type2_centroids = mul_no_overflow(
1574
+ static_cast<size_t>(quantizer->d),
1575
+ cpq_ksub,
1576
+ "IVFPQ precomputed_table");
1577
+ if (type2_table > precompute_elems) {
1578
+ precompute_elems = type2_table;
1579
+ }
1580
+ if (type2_centroids > precompute_elems) {
1581
+ precompute_elems = type2_centroids;
1582
+ }
1583
+ }
1584
+ FAISS_THROW_IF_NOT_MSG(
1585
+ precompute_elems <
1586
+ get_deserialization_vector_byte_limit() / sizeof(float),
1587
+ "IVFPQ precomputed_table allocation would exceed deserialization "
1588
+ "byte limit");
1589
+ }
1590
+
1456
1591
  static std::unique_ptr<IndexIVFPQ> read_ivfpq(
1457
1592
  IOReader* f,
1458
1593
  uint32_t h,
@@ -1470,6 +1605,8 @@ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
1470
1605
 
1471
1606
  std::vector<std::vector<idx_t>> ids;
1472
1607
  read_ivf_header(ivpq.get(), f, legacy ? &ids : nullptr);
1608
+ FAISS_THROW_IF_NOT_MSG(
1609
+ ivpq->quantizer != nullptr, "IVFPQ coarse quantizer is null");
1473
1610
  READ1_BOOL(ivpq->by_residual);
1474
1611
  READ1(ivpq->code_size);
1475
1612
  read_ProductQuantizer(&ivpq->pq, f);
@@ -1488,6 +1625,8 @@ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
1488
1625
  ivpq->use_precomputed_table = 0;
1489
1626
  if (ivpq->by_residual) {
1490
1627
  if ((io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE) == 0) {
1628
+ validate_ivfpq_precomputed_table_size(
1629
+ ivpq->quantizer, ivpq->pq);
1491
1630
  ivpq->precompute_table();
1492
1631
  }
1493
1632
  }
@@ -1516,7 +1655,29 @@ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
1516
1655
 
1517
1656
  int read_old_fmt_hack = 0;
1518
1657
 
1658
+ namespace {
1659
+
1660
+ constexpr int kMaxIndexNestingDepth = 50;
1661
+ thread_local int index_read_nesting_depth = 0;
1662
+
1663
+ struct IndexNestingGuard {
1664
+ IndexNestingGuard() {
1665
+ FAISS_THROW_IF_NOT_FMT(
1666
+ index_read_nesting_depth < kMaxIndexNestingDepth,
1667
+ "faiss index nesting depth exceeds limit of %d; "
1668
+ "input may be corrupt or malicious",
1669
+ kMaxIndexNestingDepth);
1670
+ ++index_read_nesting_depth;
1671
+ }
1672
+ ~IndexNestingGuard() {
1673
+ --index_read_nesting_depth;
1674
+ }
1675
+ };
1676
+
1677
+ } // namespace
1678
+
1519
1679
  std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1680
+ IndexNestingGuard nesting_guard;
1520
1681
  std::unique_ptr<Index> idx;
1521
1682
  uint32_t h;
1522
1683
  READ1(h);
@@ -1530,6 +1691,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1530
1691
  READ1(n_levels);
1531
1692
  FAISS_THROW_IF_NOT_FMT(n_levels > 0, "invalid n_levels %zd", n_levels);
1532
1693
  READ1(batch_size);
1694
+ FAISS_THROW_IF_NOT_FMT(
1695
+ batch_size > 0, "invalid IxFP batch_size %zd", batch_size);
1533
1696
  std::unique_ptr<IndexFlatPanorama> idxp;
1534
1697
  if (h == fourcc("IxFP")) {
1535
1698
  idxp = std::make_unique<IndexFlatL2Panorama>(
@@ -1542,6 +1705,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1542
1705
  READ1_BOOL(idxp->is_trained);
1543
1706
  READVECTOR(idxp->codes);
1544
1707
  READVECTOR(idxp->cum_sums);
1708
+ size_t num_slots = mul_no_overflow(
1709
+ ((size_t)idxp->ntotal + idxp->batch_size - 1) /
1710
+ idxp->batch_size,
1711
+ idxp->batch_size,
1712
+ "IndexFlatPanorama num_batches*batch_size");
1713
+ FAISS_THROW_IF_NOT(
1714
+ idxp->codes.size() ==
1715
+ mul_no_overflow(
1716
+ num_slots, idxp->code_size, "IndexFlatPanorama codes"));
1717
+ FAISS_THROW_IF_NOT(
1718
+ idxp->cum_sums.size() ==
1719
+ mul_no_overflow(
1720
+ num_slots,
1721
+ idxp->pano.n_levels + 1,
1722
+ "IndexFlatPanorama cum_sums"));
1545
1723
  idxp->verbose = false;
1546
1724
  idx = std::move(idxp);
1547
1725
  } else if (
@@ -1558,7 +1736,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1558
1736
  idxf->code_size = idxf->d * sizeof(float);
1559
1737
  read_xb_vector(idxf->codes, f);
1560
1738
  FAISS_THROW_IF_NOT(
1561
- idxf->codes.size() == idxf->ntotal * idxf->code_size);
1739
+ idxf->codes.size() ==
1740
+ mul_no_overflow(
1741
+ (size_t)idxf->ntotal,
1742
+ idxf->code_size,
1743
+ "IndexFlat codes"));
1562
1744
  idx = std::move(idxf);
1563
1745
  } else if (h == fourcc("IxHE") || h == fourcc("IxHe")) {
1564
1746
  auto idxl = std::make_unique<IndexLSH>();
@@ -1597,7 +1779,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1597
1779
  FAISS_THROW_IF_NOT(
1598
1780
  idxl->rrot.d_in == idxl->d && idxl->rrot.d_out == idxl->nbits);
1599
1781
  FAISS_THROW_IF_NOT(
1600
- idxl->codes.size() == idxl->ntotal * idxl->code_size);
1782
+ idxl->codes.size() ==
1783
+ mul_no_overflow(
1784
+ (size_t)idxl->ntotal,
1785
+ idxl->code_size,
1786
+ "IndexLSH codes"));
1601
1787
  idx = std::move(idxl);
1602
1788
  } else if (
1603
1789
  h == fourcc("IxPQ") || h == fourcc("IxPo") || h == fourcc("IxPq")) {
@@ -1607,8 +1793,16 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1607
1793
  read_ProductQuantizer(&idxp->pq, f);
1608
1794
  idxp->code_size = idxp->pq.code_size;
1609
1795
  read_vector(idxp->codes, f);
1796
+ FAISS_THROW_IF_NOT_MSG(
1797
+ idxp->code_size > 0 || idxp->ntotal == 0,
1798
+ "IndexPQ with ntotal > 0 must have code_size > 0 "
1799
+ "(corrupt ProductQuantizer nbits?)");
1610
1800
  FAISS_THROW_IF_NOT(
1611
- idxp->codes.size() == idxp->ntotal * idxp->code_size);
1801
+ idxp->codes.size() ==
1802
+ mul_no_overflow(
1803
+ (size_t)idxp->ntotal,
1804
+ idxp->code_size,
1805
+ "IndexPQ codes"));
1612
1806
  if (h == fourcc("IxPo") || h == fourcc("IxPq")) {
1613
1807
  READ1(idxp->search_type);
1614
1808
  READ1_BOOL(idxp->encode_signs);
@@ -1636,7 +1830,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1636
1830
  idxr->code_size, idxr->rq.code_size, "IndexResidualQuantizer");
1637
1831
  read_vector(idxr->codes, f);
1638
1832
  FAISS_THROW_IF_NOT(
1639
- idxr->codes.size() == idxr->ntotal * idxr->code_size);
1833
+ idxr->codes.size() ==
1834
+ mul_no_overflow(
1835
+ (size_t)idxr->ntotal,
1836
+ idxr->code_size,
1837
+ "IndexResidualQuantizer codes"));
1640
1838
  idx = std::move(idxr);
1641
1839
  } else if (h == fourcc("IxLS")) {
1642
1840
  auto idxr = std::make_unique<IndexLocalSearchQuantizer>();
@@ -1651,7 +1849,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1651
1849
  "IndexLocalSearchQuantizer");
1652
1850
  read_vector(idxr->codes, f);
1653
1851
  FAISS_THROW_IF_NOT(
1654
- idxr->codes.size() == idxr->ntotal * idxr->code_size);
1852
+ idxr->codes.size() ==
1853
+ mul_no_overflow(
1854
+ (size_t)idxr->ntotal,
1855
+ idxr->code_size,
1856
+ "IndexLocalSearchQuantizer codes"));
1655
1857
  idx = std::move(idxr);
1656
1858
  } else if (h == fourcc("IxPR")) {
1657
1859
  auto idxpr = std::make_unique<IndexProductResidualQuantizer>();
@@ -1666,7 +1868,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1666
1868
  "IndexProductResidualQuantizer");
1667
1869
  read_vector(idxpr->codes, f);
1668
1870
  FAISS_THROW_IF_NOT(
1669
- idxpr->codes.size() == idxpr->ntotal * idxpr->code_size);
1871
+ idxpr->codes.size() ==
1872
+ mul_no_overflow(
1873
+ (size_t)idxpr->ntotal,
1874
+ idxpr->code_size,
1875
+ "IndexProductResidualQuantizer codes"));
1670
1876
  idx = std::move(idxpr);
1671
1877
  } else if (h == fourcc("IxPL")) {
1672
1878
  auto idxpl = std::make_unique<IndexProductLocalSearchQuantizer>();
@@ -1681,7 +1887,11 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1681
1887
  "IndexProductLocalSearchQuantizer");
1682
1888
  read_vector(idxpl->codes, f);
1683
1889
  FAISS_THROW_IF_NOT(
1684
- idxpl->codes.size() == idxpl->ntotal * idxpl->code_size);
1890
+ idxpl->codes.size() ==
1891
+ mul_no_overflow(
1892
+ (size_t)idxpl->ntotal,
1893
+ idxpl->code_size,
1894
+ "IndexProductLocalSearchQuantizer codes"));
1685
1895
  idx = std::move(idxpl);
1686
1896
  } else if (h == fourcc("ImRQ")) {
1687
1897
  auto idxr = std::make_unique<ResidualCoarseQuantizer>();
@@ -1725,6 +1935,16 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1725
1935
  idxr->ntotal,
1726
1936
  idxr->rq.M);
1727
1937
  }
1938
+ FAISS_THROW_IF_NOT_MSG(
1939
+ idxr->rq.tot_bits <= 63,
1940
+ "ResidualCoarseQuantizer tot_bits too large (max 63)");
1941
+ FAISS_THROW_IF_NOT_FMT(
1942
+ static_cast<size_t>(idxr->ntotal) ==
1943
+ (((size_t)1) << idxr->rq.tot_bits),
1944
+ "ResidualCoarseQuantizer ntotal %" PRId64
1945
+ " inconsistent with 2^tot_bits (tot_bits=%zu)",
1946
+ idxr->ntotal,
1947
+ idxr->rq.tot_bits);
1728
1948
  idxr->set_beam_factor(idxr->beam_factor);
1729
1949
  idx = std::move(idxr);
1730
1950
  } else if (
@@ -1859,9 +2079,28 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1859
2079
  ivfl->code_size = ivfl->d * sizeof(float);
1860
2080
  ArrayInvertedLists* ail = set_array_invlist(ivfl.get(), ids);
1861
2081
 
2082
+ // Legacy IVF serialized ids and codes as separate vectors.
2083
+ // Check not required in default IVF, due to single sizes vector.
2084
+ auto validate_legacy_codes_size = [&](size_t i) {
2085
+ const size_t expected_codes_bytes = mul_no_overflow(
2086
+ ail->ids[i].size(),
2087
+ ivfl->code_size,
2088
+ "legacy IVFFlat inverted list codes");
2089
+ FAISS_THROW_IF_NOT_FMT(
2090
+ ail->codes[i].size() == expected_codes_bytes,
2091
+ "Legacy IVFFlat inverted list %zu: codes size %zu bytes "
2092
+ "does not match ids size %zu * code_size %zu = %zu bytes",
2093
+ i,
2094
+ ail->codes[i].size(),
2095
+ ail->ids[i].size(),
2096
+ (size_t)ivfl->code_size,
2097
+ expected_codes_bytes);
2098
+ };
2099
+
1862
2100
  if (h == fourcc("IvFL")) {
1863
2101
  for (size_t i = 0; i < ivfl->nlist; i++) {
1864
2102
  READVECTOR(ail->codes[i]);
2103
+ validate_legacy_codes_size(i);
1865
2104
  }
1866
2105
  } else { // old format
1867
2106
  for (size_t i = 0; i < ivfl->nlist; i++) {
@@ -1869,6 +2108,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1869
2108
  READVECTOR(vec);
1870
2109
  ail->codes[i].resize(vec.size() * sizeof(float));
1871
2110
  memcpy(ail->codes[i].data(), vec.data(), ail->codes[i].size());
2111
+ validate_legacy_codes_size(i);
1872
2112
  }
1873
2113
  }
1874
2114
  idx = std::move(ivfl);
@@ -1905,6 +2145,10 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1905
2145
  ivfp->code_size = ivfp->d * sizeof(float);
1906
2146
  READ1(ivfp->n_levels);
1907
2147
  READ1(ivfp->batch_size);
2148
+ FAISS_THROW_IF_NOT_FMT(
2149
+ ivfp->batch_size > 0,
2150
+ "invalid IwP2 batch_size %zd",
2151
+ ivfp->batch_size);
1908
2152
  read_InvertedLists(*ivfp, f, io_flags);
1909
2153
  idx = std::move(ivfp);
1910
2154
  } else if (h == fourcc("IwFl")) {
@@ -1983,6 +2227,16 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1983
2227
  r2);
1984
2228
  }
1985
2229
  read_index_header(*idxl, f);
2230
+ FAISS_THROW_IF_NOT_FMT(
2231
+ idxl->ntotal == 0,
2232
+ "IndexLattice deserialization carries no code storage; "
2233
+ "ntotal=%zd != 0 is corrupt",
2234
+ (size_t)idxl->ntotal);
2235
+ FAISS_THROW_IF_NOT_FMT(
2236
+ idxl->d == d,
2237
+ "IndexLattice header d=%d inconsistent with encoded d=%d",
2238
+ idxl->d,
2239
+ d);
1986
2240
  READVECTOR(idxl->trained);
1987
2241
  idx = std::move(idxl);
1988
2242
  } else if (h == fourcc("IvSQ")) { // legacy
@@ -2158,6 +2412,8 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2158
2412
  read_index_header(*idxrf, f);
2159
2413
  auto base = read_index_up(f, io_flags);
2160
2414
  auto refine = read_index_up(f, io_flags);
2415
+ FAISS_THROW_IF_NOT_MSG(base, "IndexRefine base index is null");
2416
+ FAISS_THROW_IF_NOT_MSG(refine, "IndexRefine refine index is null");
2161
2417
  READ1(idxrf->k_factor);
2162
2418
  // Same rationale as IndexIVFPQR k_factor above.
2163
2419
  FAISS_THROW_IF_NOT_FMT(
@@ -2218,6 +2474,10 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2218
2474
  READ1(idxp->code_size_1);
2219
2475
  READ1(idxp->code_size_2);
2220
2476
  READ1(idxp->code_size);
2477
+ validate_code_size_match(
2478
+ idxp->code_size_1,
2479
+ idxp->q1.coarse_code_size(),
2480
+ "Index2Layer code_size_1");
2221
2481
  validate_code_size_match(
2222
2482
  idxp->code_size_2,
2223
2483
  idxp->pq.code_size,
@@ -2227,31 +2487,33 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2227
2487
  idxp->code_size_1 + idxp->code_size_2,
2228
2488
  "Index2Layer");
2229
2489
  read_vector(idxp->codes, f);
2490
+ FAISS_THROW_IF_NOT(
2491
+ idxp->codes.size() ==
2492
+ mul_no_overflow(
2493
+ (size_t)idxp->ntotal,
2494
+ idxp->code_size,
2495
+ "Index2Layer codes"));
2230
2496
  idx = std::move(idxp);
2231
2497
  } else if (
2232
2498
  h == fourcc("IHNf") || h == fourcc("IHNp") || h == fourcc("IHNs") ||
2233
2499
  h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2") ||
2234
- h == fourcc("IHfP")) {
2500
+ h == fourcc("IHfP") || h == fourcc("IH00")) {
2235
2501
  std::unique_ptr<IndexHNSW> idxhnsw;
2236
- if (h == fourcc("IHNf")) {
2502
+ if (h == fourcc("IH00")) {
2503
+ idxhnsw = std::make_unique<IndexHNSW>();
2504
+ } else if (h == fourcc("IHNf")) {
2237
2505
  idxhnsw = std::make_unique<IndexHNSWFlat>();
2238
- }
2239
- if (h == fourcc("IHfP")) {
2506
+ } else if (h == fourcc("IHfP")) {
2240
2507
  idxhnsw = std::make_unique<IndexHNSWFlatPanorama>();
2241
- }
2242
- if (h == fourcc("IHNp")) {
2508
+ } else if (h == fourcc("IHNp")) {
2243
2509
  idxhnsw = std::make_unique<IndexHNSWPQ>();
2244
- }
2245
- if (h == fourcc("IHNs")) {
2510
+ } else if (h == fourcc("IHNs")) {
2246
2511
  idxhnsw = std::make_unique<IndexHNSWSQ>();
2247
- }
2248
- if (h == fourcc("IHN2")) {
2512
+ } else if (h == fourcc("IHN2")) {
2249
2513
  idxhnsw = std::make_unique<IndexHNSW2Level>();
2250
- }
2251
- if (h == fourcc("IHNc")) {
2514
+ } else if (h == fourcc("IHNc")) {
2252
2515
  idxhnsw = std::make_unique<IndexHNSWCagra>();
2253
- }
2254
- if (h == fourcc("IHc2")) {
2516
+ } else if (h == fourcc("IHc2")) {
2255
2517
  idxhnsw = std::make_unique<IndexHNSWCagra>();
2256
2518
  }
2257
2519
  read_index_header(*idxhnsw, f);
@@ -2267,8 +2529,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2267
2529
  const_cast<Panorama&>(idx_panorama->pano) =
2268
2530
  Panorama(idx_panorama->d * sizeof(float), nlevels, 1);
2269
2531
  READVECTOR(idx_panorama->cum_sums);
2270
- }
2271
- if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
2532
+ } else if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
2272
2533
  READ1_BOOL(idxhnsw->keep_max_size_level0);
2273
2534
  auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw.get());
2274
2535
  FAISS_THROW_IF_NOT_MSG(
@@ -2430,6 +2691,9 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2430
2691
  } else if (h == fourcc("IwPf")) {
2431
2692
  auto ivpq = std::make_unique<IndexIVFPQFastScan>();
2432
2693
  read_ivf_header(ivpq.get(), f);
2694
+ FAISS_THROW_IF_NOT_MSG(
2695
+ ivpq->quantizer != nullptr,
2696
+ "IVFPQFastScan coarse quantizer is null");
2433
2697
  READ1_BOOL(ivpq->by_residual);
2434
2698
  READ1(ivpq->code_size);
2435
2699
  READ1(ivpq->bbs);
@@ -2438,6 +2702,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2438
2702
  READ1(ivpq->qbs2);
2439
2703
  read_ProductQuantizer(&ivpq->pq, f);
2440
2704
  read_InvertedLists(*ivpq, f, io_flags);
2705
+ validate_ivfpq_precomputed_table_size(ivpq->quantizer, ivpq->pq);
2441
2706
  ivpq->precompute_table();
2442
2707
 
2443
2708
  const auto& pq = ivpq->pq;
@@ -2467,6 +2732,45 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2467
2732
  imm->own_fields = true;
2468
2733
 
2469
2734
  idx = std::move(imm);
2735
+ } else if (h == fourcc("IxEd") || h == fourcc("IxEe")) {
2736
+ auto idxe = std::make_unique<IndexEDEN>();
2737
+ read_index_header(*idxe, f);
2738
+ read_EDENScalarQuantizer(
2739
+ idxe->sq,
2740
+ idxe->scale_type,
2741
+ f,
2742
+ idxe->d,
2743
+ idxe->metric_type,
2744
+ h == fourcc("IxEe"));
2745
+ READVECTOR(idxe->codes);
2746
+ READVECTOR(idxe->center);
2747
+
2748
+ idxe->code_size = eden_utils::code_size(idxe->d, idxe->sq.bits);
2749
+ FAISS_THROW_IF_NOT(
2750
+ idxe->codes.size() == idxe->ntotal * idxe->code_size);
2751
+ idx = std::move(idxe);
2752
+ } else if (h == fourcc("IwEd") || h == fourcc("IwEe")) {
2753
+ auto iveden = std::make_unique<IndexIVFEDEN>();
2754
+ read_ivf_header(iveden.get(), f);
2755
+ read_EDENScalarQuantizer(
2756
+ iveden->sq,
2757
+ iveden->scale_type,
2758
+ f,
2759
+ iveden->d,
2760
+ iveden->metric_type,
2761
+ h == fourcc("IwEe"));
2762
+ size_t stored_ivf_code_size;
2763
+ READ1(stored_ivf_code_size);
2764
+ READ1(iveden->by_residual);
2765
+
2766
+ iveden->code_size = eden_utils::code_size(iveden->d, iveden->sq.bits);
2767
+ FAISS_THROW_IF_NOT_FMT(
2768
+ stored_ivf_code_size == iveden->code_size,
2769
+ "IndexIVFEDEN code_size mismatch: stored=%zu vs expected=%zu",
2770
+ stored_ivf_code_size,
2771
+ iveden->code_size);
2772
+ read_InvertedLists(*iveden, f, io_flags);
2773
+ idx = std::move(iveden);
2470
2774
  } else if (h == fourcc("Irfn") || h == fourcc("Irfs")) {
2471
2775
  // Irfn = new format (aux data embedded in SIMD blocks)
2472
2776
  // Irfs = legacy format (flat_storage separate, needs migration)
@@ -2645,10 +2949,22 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2645
2949
  bool initialized;
2646
2950
  READ1_BOOL(initialized);
2647
2951
  if (initialized) {
2648
- faiss::svs_io::ReaderStreambuf rbuf(
2649
- f, get_deserialization_vector_byte_limit());
2650
- std::istream is(&rbuf);
2651
- svs->deserialize_impl(is);
2952
+ if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC &&
2953
+ svs->is_static) {
2954
+ // Use memory-mapped I/O for static indices
2955
+ auto* mf = dynamic_cast<MappedFileIOReader*>(f);
2956
+ FAISS_THROW_IF_NOT_MSG(
2957
+ mf,
2958
+ "IO_FLAG_MMAP_IFC flag set but IOReader is not "
2959
+ "MappedFileIOReader");
2960
+ svs->map_to(mf);
2961
+ } else {
2962
+ // Use standard deserialization
2963
+ faiss::svs_io::ReaderStreambuf rbuf(
2964
+ f, get_deserialization_vector_byte_limit());
2965
+ std::istream is(&rbuf);
2966
+ svs->deserialize_impl(is);
2967
+ }
2652
2968
  }
2653
2969
  if (h == fourcc("ISVL")) {
2654
2970
  bool trained;
@@ -2666,6 +2982,13 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2666
2982
  }
2667
2983
  if (h == fourcc("ISV2")) {
2668
2984
  READVECTOR(svs->stored_vectors);
2985
+ FAISS_THROW_IF_NOT_MSG(
2986
+ svs->stored_vectors.size() ==
2987
+ mul_no_overflow(
2988
+ (size_t)svs->ntotal,
2989
+ (size_t)svs->d,
2990
+ "IndexSVSVamana stored_vectors"),
2991
+ "ISV2: stored_vectors size inconsistent with ntotal * d");
2669
2992
  } else {
2670
2993
  svs->stored_vectors_valid = false;
2671
2994
  }
@@ -2677,10 +3000,21 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
2677
3000
  bool initialized;
2678
3001
  READ1_BOOL(initialized);
2679
3002
  if (initialized) {
2680
- faiss::svs_io::ReaderStreambuf rbuf(
2681
- f, get_deserialization_vector_byte_limit());
2682
- std::istream is(&rbuf);
2683
- svs->deserialize_impl(is);
3003
+ if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
3004
+ // Use memory-mapped I/O
3005
+ auto* mf = dynamic_cast<MappedFileIOReader*>(f);
3006
+ FAISS_THROW_IF_NOT_MSG(
3007
+ mf,
3008
+ "IO_FLAG_MMAP_IFC flag set but IOReader is not "
3009
+ "MappedFileIOReader");
3010
+ svs->map_to(mf);
3011
+ } else {
3012
+ // Use standard deserialization
3013
+ faiss::svs_io::ReaderStreambuf rbuf(
3014
+ f, get_deserialization_vector_byte_limit());
3015
+ std::istream is(&rbuf);
3016
+ svs->deserialize_impl(is);
3017
+ }
2684
3018
  }
2685
3019
  idx = std::move(svs);
2686
3020
  } else if (
@@ -3024,6 +3358,7 @@ static void read_binary_multi_hash_map(
3024
3358
  }
3025
3359
 
3026
3360
  std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
3361
+ IndexNestingGuard nesting_guard;
3027
3362
  std::unique_ptr<IndexBinary> idx;
3028
3363
  uint32_t h;
3029
3364
  READ1(h);
@@ -3031,7 +3366,12 @@ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
3031
3366
  auto idxf = std::make_unique<IndexBinaryFlat>();
3032
3367
  read_index_binary_header(*idxf, f);
3033
3368
  read_vector(idxf->xb, f);
3034
- FAISS_THROW_IF_NOT(idxf->xb.size() == idxf->ntotal * idxf->code_size);
3369
+ FAISS_THROW_IF_NOT(
3370
+ idxf->xb.size() ==
3371
+ mul_no_overflow(
3372
+ (size_t)idxf->ntotal,
3373
+ idxf->code_size,
3374
+ "IndexBinaryFlat xb"));
3035
3375
  idx = std::move(idxf);
3036
3376
  } else if (h == fourcc("IBwF")) {
3037
3377
  auto ivf = std::make_unique<IndexBinaryIVF>();