faiss 0.5.2 → 0.6.0

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE.txt +1 -1
  4. data/ext/faiss/ext.cpp +1 -1
  5. data/ext/faiss/extconf.rb +5 -6
  6. data/ext/faiss/index_binary.cpp +76 -17
  7. data/ext/faiss/{index.cpp → index_rb.cpp} +108 -35
  8. data/ext/faiss/kmeans.cpp +12 -9
  9. data/ext/faiss/numo.hpp +11 -9
  10. data/ext/faiss/pca_matrix.cpp +10 -8
  11. data/ext/faiss/product_quantizer.cpp +14 -12
  12. data/ext/faiss/{utils.cpp → utils_rb.cpp} +10 -3
  13. data/ext/faiss/{utils.h → utils_rb.h} +6 -0
  14. data/lib/faiss/version.rb +1 -1
  15. data/lib/faiss.rb +1 -1
  16. data/vendor/faiss/faiss/AutoTune.cpp +130 -11
  17. data/vendor/faiss/faiss/AutoTune.h +14 -1
  18. data/vendor/faiss/faiss/Clustering.cpp +59 -10
  19. data/vendor/faiss/faiss/Clustering.h +12 -0
  20. data/vendor/faiss/faiss/IVFlib.cpp +31 -28
  21. data/vendor/faiss/faiss/Index.cpp +20 -8
  22. data/vendor/faiss/faiss/Index.h +25 -3
  23. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +19 -24
  24. data/vendor/faiss/faiss/IndexBinary.cpp +1 -0
  25. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +9 -4
  26. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +45 -11
  27. data/vendor/faiss/faiss/IndexFastScan.cpp +35 -22
  28. data/vendor/faiss/faiss/IndexFastScan.h +10 -1
  29. data/vendor/faiss/faiss/IndexFlat.cpp +193 -136
  30. data/vendor/faiss/faiss/IndexFlat.h +16 -1
  31. data/vendor/faiss/faiss/IndexFlatCodes.cpp +46 -22
  32. data/vendor/faiss/faiss/IndexFlatCodes.h +7 -1
  33. data/vendor/faiss/faiss/IndexHNSW.cpp +24 -50
  34. data/vendor/faiss/faiss/IndexHNSW.h +14 -12
  35. data/vendor/faiss/faiss/IndexIDMap.cpp +1 -1
  36. data/vendor/faiss/faiss/IndexIVF.cpp +76 -49
  37. data/vendor/faiss/faiss/IndexIVF.h +14 -4
  38. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.cpp +11 -8
  39. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizerFastScan.h +2 -2
  40. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +25 -14
  41. data/vendor/faiss/faiss/IndexIVFFastScan.h +26 -22
  42. data/vendor/faiss/faiss/IndexIVFFlat.cpp +10 -61
  43. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +39 -111
  44. data/vendor/faiss/faiss/IndexIVFPQ.cpp +89 -147
  45. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +37 -5
  46. data/vendor/faiss/faiss/IndexIVFPQR.cpp +2 -1
  47. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +42 -30
  48. data/vendor/faiss/faiss/IndexIVFRaBitQ.h +2 -2
  49. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +246 -97
  50. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +32 -29
  51. data/vendor/faiss/faiss/IndexLSH.cpp +8 -6
  52. data/vendor/faiss/faiss/IndexLattice.cpp +29 -24
  53. data/vendor/faiss/faiss/IndexNNDescent.cpp +1 -0
  54. data/vendor/faiss/faiss/IndexNSG.cpp +2 -1
  55. data/vendor/faiss/faiss/IndexNSG.h +0 -2
  56. data/vendor/faiss/faiss/IndexNeuralNetCodec.cpp +1 -1
  57. data/vendor/faiss/faiss/IndexPQ.cpp +19 -10
  58. data/vendor/faiss/faiss/IndexRaBitQ.cpp +26 -13
  59. data/vendor/faiss/faiss/IndexRaBitQ.h +2 -2
  60. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +132 -78
  61. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +14 -12
  62. data/vendor/faiss/faiss/IndexRefine.cpp +0 -30
  63. data/vendor/faiss/faiss/IndexShards.cpp +3 -4
  64. data/vendor/faiss/faiss/MetricType.h +16 -0
  65. data/vendor/faiss/faiss/VectorTransform.cpp +120 -0
  66. data/vendor/faiss/faiss/VectorTransform.h +23 -0
  67. data/vendor/faiss/faiss/clone_index.cpp +7 -4
  68. data/vendor/faiss/faiss/{cppcontrib/factory_tools.cpp → factory_tools.cpp} +1 -1
  69. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +1 -1
  70. data/vendor/faiss/faiss/impl/AdditiveQuantizer.cpp +37 -11
  71. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +0 -28
  72. data/vendor/faiss/faiss/impl/ClusteringInitialization.cpp +367 -0
  73. data/vendor/faiss/faiss/impl/ClusteringInitialization.h +107 -0
  74. data/vendor/faiss/faiss/impl/CodePacker.cpp +4 -0
  75. data/vendor/faiss/faiss/impl/CodePacker.h +11 -3
  76. data/vendor/faiss/faiss/impl/CodePackerRaBitQ.cpp +83 -0
  77. data/vendor/faiss/faiss/impl/CodePackerRaBitQ.h +47 -0
  78. data/vendor/faiss/faiss/impl/FaissAssert.h +60 -2
  79. data/vendor/faiss/faiss/impl/HNSW.cpp +25 -34
  80. data/vendor/faiss/faiss/impl/HNSW.h +8 -6
  81. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +34 -27
  82. data/vendor/faiss/faiss/impl/NNDescent.cpp +1 -1
  83. data/vendor/faiss/faiss/impl/NSG.cpp +6 -5
  84. data/vendor/faiss/faiss/impl/NSG.h +17 -7
  85. data/vendor/faiss/faiss/impl/Panorama.cpp +53 -46
  86. data/vendor/faiss/faiss/impl/Panorama.h +22 -6
  87. data/vendor/faiss/faiss/impl/PolysemousTraining.cpp +16 -5
  88. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +70 -58
  89. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +92 -0
  90. data/vendor/faiss/faiss/impl/RaBitQUtils.h +93 -31
  91. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +12 -28
  92. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +3 -10
  93. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +15 -41
  94. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.h +0 -4
  95. data/vendor/faiss/faiss/impl/ResidualQuantizer.cpp +14 -9
  96. data/vendor/faiss/faiss/impl/ResultHandler.h +131 -50
  97. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +67 -2358
  98. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +0 -2
  99. data/vendor/faiss/faiss/impl/VisitedTable.cpp +42 -0
  100. data/vendor/faiss/faiss/impl/VisitedTable.h +69 -0
  101. data/vendor/faiss/faiss/impl/expanded_scanners.h +158 -0
  102. data/vendor/faiss/faiss/impl/index_read.cpp +829 -471
  103. data/vendor/faiss/faiss/impl/index_read_utils.h +0 -1
  104. data/vendor/faiss/faiss/impl/index_write.cpp +17 -8
  105. data/vendor/faiss/faiss/impl/lattice_Zn.cpp +47 -20
  106. data/vendor/faiss/faiss/impl/mapped_io.cpp +9 -2
  107. data/vendor/faiss/faiss/impl/pq4_fast_scan.cpp +7 -2
  108. data/vendor/faiss/faiss/impl/pq4_fast_scan.h +11 -3
  109. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_1.cpp +19 -13
  110. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_qbs.cpp +29 -21
  111. data/vendor/faiss/faiss/impl/{code_distance/code_distance-avx2.h → pq_code_distance/pq_code_distance-avx2.cpp} +42 -215
  112. data/vendor/faiss/faiss/impl/{code_distance/code_distance-avx512.h → pq_code_distance/pq_code_distance-avx512.cpp} +68 -107
  113. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-generic.cpp +141 -0
  114. data/vendor/faiss/faiss/impl/pq_code_distance/pq_code_distance-inl.h +23 -0
  115. data/vendor/faiss/faiss/impl/{code_distance/code_distance-sve.h → pq_code_distance/pq_code_distance-sve.cpp} +57 -144
  116. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +9 -6
  117. data/vendor/faiss/faiss/impl/scalar_quantizer/codecs.h +121 -0
  118. data/vendor/faiss/faiss/impl/scalar_quantizer/distance_computers.h +136 -0
  119. data/vendor/faiss/faiss/impl/scalar_quantizer/quantizers.h +280 -0
  120. data/vendor/faiss/faiss/impl/scalar_quantizer/scanners.h +164 -0
  121. data/vendor/faiss/faiss/impl/scalar_quantizer/similarities.h +94 -0
  122. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx2.cpp +455 -0
  123. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-avx512.cpp +430 -0
  124. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-dispatch.h +329 -0
  125. data/vendor/faiss/faiss/impl/scalar_quantizer/sq-neon.cpp +467 -0
  126. data/vendor/faiss/faiss/impl/scalar_quantizer/training.cpp +203 -0
  127. data/vendor/faiss/faiss/impl/scalar_quantizer/training.h +42 -0
  128. data/vendor/faiss/faiss/impl/simd_dispatch.h +139 -0
  129. data/vendor/faiss/faiss/impl/simd_result_handlers.h +18 -18
  130. data/vendor/faiss/faiss/index_factory.cpp +35 -16
  131. data/vendor/faiss/faiss/index_io.h +29 -3
  132. data/vendor/faiss/faiss/invlists/BlockInvertedLists.cpp +7 -4
  133. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +1 -1
  134. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +9 -19
  135. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +2 -0
  136. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +2 -1
  137. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +9 -1
  138. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +9 -0
  139. data/vendor/faiss/faiss/utils/Heap.cpp +46 -0
  140. data/vendor/faiss/faiss/utils/Heap.h +21 -0
  141. data/vendor/faiss/faiss/utils/NeuralNet.cpp +10 -7
  142. data/vendor/faiss/faiss/utils/distances.cpp +141 -23
  143. data/vendor/faiss/faiss/utils/distances.h +98 -0
  144. data/vendor/faiss/faiss/utils/distances_dispatch.h +170 -0
  145. data/vendor/faiss/faiss/utils/distances_simd.cpp +74 -3511
  146. data/vendor/faiss/faiss/utils/extra_distances-inl.h +164 -157
  147. data/vendor/faiss/faiss/utils/extra_distances.cpp +52 -95
  148. data/vendor/faiss/faiss/utils/extra_distances.h +47 -1
  149. data/vendor/faiss/faiss/utils/hamming_distance/generic-inl.h +0 -1
  150. data/vendor/faiss/faiss/utils/partitioning.cpp +1 -1
  151. data/vendor/faiss/faiss/utils/pq_code_distance.h +251 -0
  152. data/vendor/faiss/faiss/utils/rabitq_simd.h +260 -0
  153. data/vendor/faiss/faiss/utils/simd_impl/distances_aarch64.cpp +150 -0
  154. data/vendor/faiss/faiss/utils/simd_impl/distances_arm_sve.cpp +568 -0
  155. data/vendor/faiss/faiss/utils/simd_impl/distances_autovec-inl.h +153 -0
  156. data/vendor/faiss/faiss/utils/simd_impl/distances_avx2.cpp +1185 -0
  157. data/vendor/faiss/faiss/utils/simd_impl/distances_avx512.cpp +1092 -0
  158. data/vendor/faiss/faiss/utils/simd_impl/distances_sse-inl.h +391 -0
  159. data/vendor/faiss/faiss/utils/simd_levels.cpp +322 -0
  160. data/vendor/faiss/faiss/utils/simd_levels.h +91 -0
  161. data/vendor/faiss/faiss/utils/simdlib_avx2.h +12 -1
  162. data/vendor/faiss/faiss/utils/simdlib_avx512.h +69 -0
  163. data/vendor/faiss/faiss/utils/simdlib_neon.h +6 -0
  164. data/vendor/faiss/faiss/utils/sorting.cpp +4 -4
  165. data/vendor/faiss/faiss/utils/utils.cpp +16 -9
  166. metadata +47 -18
  167. data/vendor/faiss/faiss/impl/code_distance/code_distance-generic.h +0 -81
  168. data/vendor/faiss/faiss/impl/code_distance/code_distance.h +0 -186
  169. /data/vendor/faiss/faiss/{cppcontrib/factory_tools.h → factory_tools.h} +0 -0
@@ -12,14 +12,19 @@
12
12
 
13
13
  #include <cstdio>
14
14
  #include <cstdlib>
15
+ #include <cstring>
16
+ #include <memory>
15
17
  #include <optional>
16
18
 
17
19
  #include <faiss/impl/FaissAssert.h>
20
+ #include <faiss/impl/RaBitQUtils.h>
18
21
  #include <faiss/impl/io.h>
19
22
  #include <faiss/utils/hamming.h>
20
23
 
21
24
  #include <faiss/invlists/InvertedListsIOHook.h>
22
25
 
26
+ #include <faiss/invlists/BlockInvertedLists.h>
27
+
23
28
  #include <faiss/Index2Layer.h>
24
29
  #include <faiss/IndexAdditiveQuantizer.h>
25
30
  #include <faiss/IndexAdditiveQuantizerFastScan.h>
@@ -205,34 +210,41 @@ void read_xb_vector(VectorT& target, IOReader* f) {
205
210
  * Read
206
211
  **************************************************************/
207
212
 
208
- void read_index_header(Index* idx, IOReader* f) {
209
- READ1(idx->d);
210
- READ1(idx->ntotal);
213
+ static void read_index_header(Index& idx, IOReader* f) {
214
+ READ1(idx.d);
215
+ READ1(idx.ntotal);
216
+ FAISS_CHECK_RANGE(idx.d, 0, (1 << 20) + 1);
217
+ FAISS_THROW_IF_NOT_FMT(
218
+ idx.ntotal >= 0,
219
+ "invalid ntotal %" PRId64 " read from index",
220
+ (int64_t)idx.ntotal);
211
221
  idx_t dummy;
212
222
  READ1(dummy);
213
223
  READ1(dummy);
214
- READ1(idx->is_trained);
215
- READ1(idx->metric_type);
216
- if (idx->metric_type > 1) {
217
- READ1(idx->metric_arg);
224
+ READ1(idx.is_trained);
225
+ int metric_type_int;
226
+ READ1(metric_type_int);
227
+ idx.metric_type = metric_type_from_int(metric_type_int);
228
+ if (idx.metric_type > 1) {
229
+ READ1(idx.metric_arg);
218
230
  }
219
- idx->verbose = false;
231
+ idx.verbose = false;
220
232
  }
221
233
 
222
- VectorTransform* read_VectorTransform(IOReader* f) {
234
+ std::unique_ptr<VectorTransform> read_VectorTransform_up(IOReader* f) {
223
235
  uint32_t h;
224
236
  READ1(h);
225
- VectorTransform* vt = nullptr;
237
+ std::unique_ptr<VectorTransform> vt;
226
238
 
227
239
  if (h == fourcc("rrot") || h == fourcc("PCAm") || h == fourcc("LTra") ||
228
240
  h == fourcc("PcAm") || h == fourcc("Viqm") || h == fourcc("Pcam")) {
229
- LinearTransform* lt = nullptr;
241
+ std::unique_ptr<LinearTransform> lt;
230
242
  if (h == fourcc("rrot")) {
231
- lt = new RandomRotationMatrix();
243
+ lt = std::make_unique<RandomRotationMatrix>();
232
244
  } else if (
233
245
  h == fourcc("PCAm") || h == fourcc("PcAm") ||
234
246
  h == fourcc("Pcam")) {
235
- PCAMatrix* pca = new PCAMatrix();
247
+ auto pca = std::make_unique<PCAMatrix>();
236
248
  READ1(pca->eigen_power);
237
249
  if (h == fourcc("Pcam")) {
238
250
  READ1(pca->epsilon);
@@ -244,14 +256,14 @@ VectorTransform* read_VectorTransform(IOReader* f) {
244
256
  READVECTOR(pca->mean);
245
257
  READVECTOR(pca->eigenvalues);
246
258
  READVECTOR(pca->PCAMat);
247
- lt = pca;
259
+ lt = std::move(pca);
248
260
  } else if (h == fourcc("Viqm")) {
249
- ITQMatrix* itqm = new ITQMatrix();
261
+ auto itqm = std::make_unique<ITQMatrix>();
250
262
  READ1(itqm->max_iter);
251
263
  READ1(itqm->seed);
252
- lt = itqm;
264
+ lt = std::move(itqm);
253
265
  } else if (h == fourcc("LTra")) {
254
- lt = new LinearTransform();
266
+ lt = std::make_unique<LinearTransform>();
255
267
  }
256
268
  READ1(lt->have_bias);
257
269
  READVECTOR(lt->A);
@@ -259,38 +271,43 @@ VectorTransform* read_VectorTransform(IOReader* f) {
259
271
  FAISS_THROW_IF_NOT(lt->A.size() >= lt->d_in * lt->d_out);
260
272
  FAISS_THROW_IF_NOT(!lt->have_bias || lt->b.size() >= lt->d_out);
261
273
  lt->set_is_orthonormal();
262
- vt = lt;
274
+ vt = std::move(lt);
263
275
  } else if (h == fourcc("RmDT")) {
264
- RemapDimensionsTransform* rdt = new RemapDimensionsTransform();
276
+ auto rdt = std::make_unique<RemapDimensionsTransform>();
265
277
  READVECTOR(rdt->map);
266
- vt = rdt;
278
+ vt = std::move(rdt);
267
279
  } else if (h == fourcc("VNrm")) {
268
- NormalizationTransform* nt = new NormalizationTransform();
280
+ auto nt = std::make_unique<NormalizationTransform>();
269
281
  READ1(nt->norm);
270
- vt = nt;
282
+ vt = std::move(nt);
271
283
  } else if (h == fourcc("VCnt")) {
272
- CenteringTransform* ct = new CenteringTransform();
284
+ auto ct = std::make_unique<CenteringTransform>();
273
285
  READVECTOR(ct->mean);
274
- vt = ct;
286
+ vt = std::move(ct);
275
287
  } else if (h == fourcc("Viqt")) {
276
- ITQTransform* itqt = new ITQTransform();
288
+ auto itqt = std::make_unique<ITQTransform>();
277
289
 
278
290
  READVECTOR(itqt->mean);
279
291
  READ1(itqt->do_pca);
280
292
  {
281
- ITQMatrix* itqm = dynamic_cast<ITQMatrix*>(read_VectorTransform(f));
293
+ // Read, dereference, discard.
294
+ auto sub_vt = read_VectorTransform_up(f);
295
+ ITQMatrix* itqm = dynamic_cast<ITQMatrix*>(sub_vt.get());
282
296
  FAISS_THROW_IF_NOT(itqm);
283
297
  itqt->itq = *itqm;
284
- delete itqm;
285
298
  }
286
299
  {
287
- LinearTransform* pi =
288
- dynamic_cast<LinearTransform*>(read_VectorTransform(f));
300
+ // Read, dereference, discard.
301
+ auto sub_vt = read_VectorTransform_up(f);
302
+ LinearTransform* pi = dynamic_cast<LinearTransform*>(sub_vt.get());
289
303
  FAISS_THROW_IF_NOT(pi);
290
304
  itqt->pca_then_itq = *pi;
291
- delete pi;
292
305
  }
293
- vt = itqt;
306
+ vt = std::move(itqt);
307
+ } else if (h == fourcc("HRot")) {
308
+ auto hr = std::make_unique<HadamardRotation>();
309
+ READ1(hr->seed);
310
+ vt = std::move(hr);
294
311
  } else {
295
312
  FAISS_THROW_FMT(
296
313
  "fourcc %ud (\"%s\") not recognized in %s",
@@ -301,9 +318,17 @@ VectorTransform* read_VectorTransform(IOReader* f) {
301
318
  READ1(vt->d_in);
302
319
  READ1(vt->d_out);
303
320
  READ1(vt->is_trained);
321
+ if (h == fourcc("HRot")) {
322
+ auto* hr = dynamic_cast<HadamardRotation*>(vt.get());
323
+ hr->init(hr->seed);
324
+ }
304
325
  return vt;
305
326
  }
306
327
 
328
+ VectorTransform* read_VectorTransform(IOReader* f) {
329
+ return read_VectorTransform_up(f).release();
330
+ }
331
+
307
332
  static void read_ArrayInvertedLists_sizes(
308
333
  IOReader* f,
309
334
  std::vector<size_t>& sizes) {
@@ -316,6 +341,10 @@ static void read_ArrayInvertedLists_sizes(
316
341
  } else if (list_type == fourcc("sprs")) {
317
342
  std::vector<size_t> idsizes;
318
343
  READVECTOR(idsizes);
344
+ FAISS_THROW_IF_NOT_FMT(
345
+ idsizes.size() % 2 == 0,
346
+ "invalid sparse inverted list size: %zd (must be even)",
347
+ idsizes.size());
319
348
  for (size_t j = 0; j < idsizes.size(); j += 2) {
320
349
  FAISS_THROW_IF_NOT(idsizes[j] < sizes.size());
321
350
  sizes[idsizes[j]] = idsizes[j + 1];
@@ -328,7 +357,9 @@ static void read_ArrayInvertedLists_sizes(
328
357
  }
329
358
  }
330
359
 
331
- InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
360
+ std::unique_ptr<InvertedLists> read_InvertedLists_up(
361
+ IOReader* f,
362
+ int io_flags) {
332
363
  uint32_t h;
333
364
  READ1(h);
334
365
  if (h == fourcc("il00")) {
@@ -341,7 +372,8 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
341
372
  READ1(nlist);
342
373
  READ1(code_size);
343
374
  READ1(n_levels);
344
- auto ailp = new ArrayInvertedListsPanorama(nlist, code_size, n_levels);
375
+ auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
376
+ nlist, code_size, n_levels);
345
377
  std::vector<size_t> sizes(nlist);
346
378
  read_ArrayInvertedLists_sizes(f, sizes);
347
379
  for (size_t i = 0; i < nlist; i++) {
@@ -365,7 +397,7 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
365
397
  }
366
398
  return ailp;
367
399
  } else if (h == fourcc("ilar") && !(io_flags & IO_FLAG_SKIP_IVF_DATA)) {
368
- auto ails = new ArrayInvertedLists(0, 0);
400
+ auto ails = std::make_unique<ArrayInvertedLists>(0, 0);
369
401
  READ1(ails->nlist);
370
402
  READ1(ails->code_size);
371
403
  ails->ids.resize(ails->nlist);
@@ -374,13 +406,17 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
374
406
  read_ArrayInvertedLists_sizes(f, sizes);
375
407
  for (size_t i = 0; i < ails->nlist; i++) {
376
408
  ails->ids[i].resize(sizes[i]);
377
- ails->codes[i].resize(sizes[i] * ails->code_size);
409
+ ails->codes[i].resize(mul_no_overflow(
410
+ sizes[i], ails->code_size, "inverted list codes"));
378
411
  }
379
412
  for (size_t i = 0; i < ails->nlist; i++) {
380
413
  size_t n = ails->ids[i].size();
381
414
  if (n > 0) {
382
415
  read_vector_with_known_size(
383
- ails->codes[i], f, n * ails->code_size);
416
+ ails->codes[i],
417
+ f,
418
+ mul_no_overflow(
419
+ n, ails->code_size, "inverted list codes"));
384
420
  read_vector_with_known_size(ails->ids[i], f, n);
385
421
  }
386
422
  }
@@ -396,132 +432,150 @@ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
396
432
  READ1(code_size);
397
433
  std::vector<size_t> sizes(nlist);
398
434
  read_ArrayInvertedLists_sizes(f, sizes);
399
- return InvertedListsIOHook::lookup(h2)->read_ArrayInvertedLists(
400
- f, io_flags, nlist, code_size, sizes);
435
+ return std::unique_ptr<InvertedLists>(
436
+ InvertedListsIOHook::lookup(h2)->read_ArrayInvertedLists(
437
+ f, io_flags, nlist, code_size, sizes));
401
438
  } else {
402
- return InvertedListsIOHook::lookup(h)->read(f, io_flags);
439
+ return std::unique_ptr<InvertedLists>(
440
+ InvertedListsIOHook::lookup(h)->read(f, io_flags));
403
441
  }
404
442
  }
405
443
 
406
- void read_InvertedLists(IndexIVF* ivf, IOReader* f, int io_flags) {
407
- InvertedLists* ils = read_InvertedLists(f, io_flags);
444
+ InvertedLists* read_InvertedLists(IOReader* f, int io_flags) {
445
+ return read_InvertedLists_up(f, io_flags).release();
446
+ }
447
+
448
+ void read_InvertedLists(IndexIVF& ivf, IOReader* f, int io_flags) {
449
+ auto ils = read_InvertedLists_up(f, io_flags);
408
450
  if (ils) {
409
- FAISS_THROW_IF_NOT(ils->nlist == ivf->nlist);
451
+ FAISS_THROW_IF_NOT(ils->nlist == ivf.nlist);
410
452
  FAISS_THROW_IF_NOT(
411
453
  ils->code_size == InvertedLists::INVALID_CODE_SIZE ||
412
- ils->code_size == ivf->code_size);
454
+ ils->code_size == ivf.code_size);
413
455
  }
414
- ivf->invlists = ils;
415
- ivf->own_invlists = true;
456
+ ivf.invlists = ils.release();
457
+ ivf.own_invlists = true;
416
458
  }
417
459
 
418
460
  void read_ProductQuantizer(ProductQuantizer* pq, IOReader* f) {
419
461
  READ1(pq->d);
420
462
  READ1(pq->M);
421
463
  READ1(pq->nbits);
464
+ FAISS_THROW_IF_NOT_FMT(
465
+ pq->M > 0, "invalid ProductQuantizer M=%zd (must be > 0)", pq->M);
422
466
  pq->set_derived_values();
423
467
  READVECTOR(pq->centroids);
424
468
  }
425
469
 
426
- static void read_ResidualQuantizer_old(ResidualQuantizer* rq, IOReader* f) {
427
- READ1(rq->d);
428
- READ1(rq->M);
429
- READVECTOR(rq->nbits);
430
- READ1(rq->is_trained);
431
- READ1(rq->train_type);
432
- READ1(rq->max_beam_size);
433
- READVECTOR(rq->codebooks);
434
- READ1(rq->search_type);
435
- READ1(rq->norm_min);
436
- READ1(rq->norm_max);
437
- rq->set_derived_values();
470
+ static void read_ResidualQuantizer_old(ResidualQuantizer& rq, IOReader* f) {
471
+ READ1(rq.d);
472
+ READ1(rq.M);
473
+ READVECTOR(rq.nbits);
474
+ FAISS_THROW_IF_NOT_FMT(
475
+ rq.nbits.size() == rq.M,
476
+ "ResidualQuantizer nbits size %zd != M %zd",
477
+ rq.nbits.size(),
478
+ rq.M);
479
+ READ1(rq.is_trained);
480
+ READ1(rq.train_type);
481
+ READ1(rq.max_beam_size);
482
+ READVECTOR(rq.codebooks);
483
+ READ1(rq.search_type);
484
+ READ1(rq.norm_min);
485
+ READ1(rq.norm_max);
486
+ rq.set_derived_values();
438
487
  }
439
488
 
440
- static void read_AdditiveQuantizer(AdditiveQuantizer* aq, IOReader* f) {
441
- READ1(aq->d);
442
- READ1(aq->M);
443
- READVECTOR(aq->nbits);
444
- READ1(aq->is_trained);
445
- READVECTOR(aq->codebooks);
446
- READ1(aq->search_type);
447
- READ1(aq->norm_min);
448
- READ1(aq->norm_max);
449
- if (aq->search_type == AdditiveQuantizer::ST_norm_cqint8 ||
450
- aq->search_type == AdditiveQuantizer::ST_norm_cqint4 ||
451
- aq->search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
452
- aq->search_type == AdditiveQuantizer::ST_norm_rq2x4) {
453
- read_xb_vector(aq->qnorm.codes, f);
454
- aq->qnorm.ntotal = aq->qnorm.codes.size() / 4;
455
- aq->qnorm.update_permutation();
489
+ static void read_AdditiveQuantizer(AdditiveQuantizer& aq, IOReader* f) {
490
+ READ1(aq.d);
491
+ READ1(aq.M);
492
+ READVECTOR(aq.nbits);
493
+ READ1(aq.is_trained);
494
+ READVECTOR(aq.codebooks);
495
+ FAISS_THROW_IF_NOT_FMT(
496
+ aq.nbits.size() == aq.M,
497
+ "AdditiveQuantizer nbits size %zd != M %zd",
498
+ aq.nbits.size(),
499
+ aq.M);
500
+ READ1(aq.search_type);
501
+ READ1(aq.norm_min);
502
+ READ1(aq.norm_max);
503
+ if (aq.search_type == AdditiveQuantizer::ST_norm_cqint8 ||
504
+ aq.search_type == AdditiveQuantizer::ST_norm_cqint4 ||
505
+ aq.search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
506
+ aq.search_type == AdditiveQuantizer::ST_norm_rq2x4) {
507
+ read_xb_vector(aq.qnorm.codes, f);
508
+ aq.qnorm.ntotal = aq.qnorm.codes.size() / 4;
509
+ aq.qnorm.update_permutation();
456
510
  }
457
511
 
458
- if (aq->search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
459
- aq->search_type == AdditiveQuantizer::ST_norm_rq2x4) {
460
- READVECTOR(aq->norm_tabs);
512
+ if (aq.search_type == AdditiveQuantizer::ST_norm_lsq2x4 ||
513
+ aq.search_type == AdditiveQuantizer::ST_norm_rq2x4) {
514
+ READVECTOR(aq.norm_tabs);
461
515
  }
462
516
 
463
- aq->set_derived_values();
517
+ aq.set_derived_values();
464
518
  }
465
519
 
466
520
  static void read_ResidualQuantizer(
467
- ResidualQuantizer* rq,
521
+ ResidualQuantizer& rq,
468
522
  IOReader* f,
469
523
  int io_flags) {
470
524
  read_AdditiveQuantizer(rq, f);
471
- READ1(rq->train_type);
472
- READ1(rq->max_beam_size);
473
- if ((rq->train_type & ResidualQuantizer::Skip_codebook_tables) ||
525
+ READ1(rq.train_type);
526
+ READ1(rq.max_beam_size);
527
+ if ((rq.train_type & ResidualQuantizer::Skip_codebook_tables) ||
474
528
  (io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE)) {
475
529
  // don't precompute the tables
476
530
  } else {
477
- rq->compute_codebook_tables();
531
+ rq.compute_codebook_tables();
478
532
  }
479
533
  }
480
534
 
481
- static void read_LocalSearchQuantizer(LocalSearchQuantizer* lsq, IOReader* f) {
535
+ static void read_LocalSearchQuantizer(LocalSearchQuantizer& lsq, IOReader* f) {
482
536
  read_AdditiveQuantizer(lsq, f);
483
- READ1(lsq->K);
484
- READ1(lsq->train_iters);
485
- READ1(lsq->encode_ils_iters);
486
- READ1(lsq->train_ils_iters);
487
- READ1(lsq->icm_iters);
488
- READ1(lsq->p);
489
- READ1(lsq->lambd);
490
- READ1(lsq->chunk_size);
491
- READ1(lsq->random_seed);
492
- READ1(lsq->nperts);
493
- READ1(lsq->update_codebooks_with_double);
537
+ READ1(lsq.K);
538
+ READ1(lsq.train_iters);
539
+ READ1(lsq.encode_ils_iters);
540
+ READ1(lsq.train_ils_iters);
541
+ READ1(lsq.icm_iters);
542
+ READ1(lsq.p);
543
+ READ1(lsq.lambd);
544
+ READ1(lsq.chunk_size);
545
+ READ1(lsq.random_seed);
546
+ READ1(lsq.nperts);
547
+ READ1(lsq.update_codebooks_with_double);
494
548
  }
495
549
 
496
550
  static void read_ProductAdditiveQuantizer(
497
- ProductAdditiveQuantizer* paq,
551
+ ProductAdditiveQuantizer& paq,
498
552
  IOReader* f) {
499
553
  read_AdditiveQuantizer(paq, f);
500
- READ1(paq->nsplits);
554
+ READ1(paq.nsplits);
501
555
  }
502
556
 
503
557
  static void read_ProductResidualQuantizer(
504
- ProductResidualQuantizer* prq,
558
+ ProductResidualQuantizer& prq,
505
559
  IOReader* f,
506
560
  int io_flags) {
507
561
  read_ProductAdditiveQuantizer(prq, f);
508
562
 
509
- for (size_t i = 0; i < prq->nsplits; i++) {
510
- auto rq = new ResidualQuantizer();
511
- read_ResidualQuantizer(rq, f, io_flags);
512
- prq->quantizers.push_back(rq);
563
+ for (size_t i = 0; i < prq.nsplits; i++) {
564
+ auto rq = std::make_unique<ResidualQuantizer>();
565
+ read_ResidualQuantizer(*rq, f, io_flags);
566
+ prq.quantizers.push_back(rq.release());
513
567
  }
514
568
  }
515
569
 
516
570
  static void read_ProductLocalSearchQuantizer(
517
- ProductLocalSearchQuantizer* plsq,
571
+ ProductLocalSearchQuantizer& plsq,
518
572
  IOReader* f) {
519
573
  read_ProductAdditiveQuantizer(plsq, f);
520
574
 
521
- for (size_t i = 0; i < plsq->nsplits; i++) {
522
- auto lsq = new LocalSearchQuantizer();
523
- read_LocalSearchQuantizer(lsq, f);
524
- plsq->quantizers.push_back(lsq);
575
+ for (size_t i = 0; i < plsq.nsplits; i++) {
576
+ auto lsq = std::make_unique<LocalSearchQuantizer>();
577
+ read_LocalSearchQuantizer(*lsq, f);
578
+ plsq.quantizers.push_back(lsq.release());
525
579
  }
526
580
  }
527
581
 
@@ -535,97 +589,237 @@ void read_ScalarQuantizer(ScalarQuantizer* ivsc, IOReader* f) {
535
589
  ivsc->set_derived_sizes();
536
590
  }
537
591
 
538
- static void read_HNSW(HNSW* hnsw, IOReader* f) {
539
- READVECTOR(hnsw->assign_probas);
540
- READVECTOR(hnsw->cum_nneighbor_per_level);
541
- READVECTOR(hnsw->levels);
542
- READVECTOR(hnsw->offsets);
543
- read_vector(hnsw->neighbors, f);
592
+ static void validate_HNSW(const HNSW& hnsw) {
593
+ size_t ntotal = hnsw.levels.size();
594
+ size_t nb_neighbors_size = hnsw.neighbors.size();
595
+
596
+ // cum_nneighbor_per_level must be non-empty and monotonically
597
+ // non-decreasing, starting at 0
598
+ if (!hnsw.cum_nneighbor_per_level.empty()) {
599
+ FAISS_THROW_IF_NOT_FMT(
600
+ hnsw.cum_nneighbor_per_level[0] == 0,
601
+ "HNSW cum_nneighbor_per_level[0] = %d, expected 0",
602
+ hnsw.cum_nneighbor_per_level[0]);
603
+ for (size_t i = 1; i < hnsw.cum_nneighbor_per_level.size(); i++) {
604
+ FAISS_THROW_IF_NOT_FMT(
605
+ hnsw.cum_nneighbor_per_level[i] >=
606
+ hnsw.cum_nneighbor_per_level[i - 1],
607
+ "HNSW cum_nneighbor_per_level not monotonic at %zd: "
608
+ "%d < %d",
609
+ i,
610
+ hnsw.cum_nneighbor_per_level[i],
611
+ hnsw.cum_nneighbor_per_level[i - 1]);
612
+ }
613
+ }
614
+
615
+ // every levels[i] must be a valid index into cum_nneighbor_per_level
616
+ size_t cum_size = hnsw.cum_nneighbor_per_level.size();
617
+ for (size_t i = 0; i < ntotal; i++) {
618
+ FAISS_THROW_IF_NOT_FMT(
619
+ hnsw.levels[i] >= 0 &&
620
+ static_cast<size_t>(hnsw.levels[i]) < cum_size,
621
+ "HNSW levels[%zd] = %d out of range [0, %zd)",
622
+ i,
623
+ hnsw.levels[i],
624
+ cum_size);
625
+ }
626
+
627
+ // offsets must have size ntotal + 1, be monotonically non-decreasing,
628
+ // and all values must be <= neighbors.size()
629
+ FAISS_THROW_IF_NOT_FMT(
630
+ hnsw.offsets.size() == ntotal + 1,
631
+ "HNSW offsets size %zd != levels size %zd + 1",
632
+ hnsw.offsets.size(),
633
+ ntotal);
634
+ for (size_t i = 0; i < hnsw.offsets.size(); i++) {
635
+ FAISS_THROW_IF_NOT_FMT(
636
+ hnsw.offsets[i] <= nb_neighbors_size,
637
+ "HNSW offsets[%zd] = %zd > neighbors.size() = %zd",
638
+ i,
639
+ hnsw.offsets[i],
640
+ nb_neighbors_size);
641
+ if (i > 0) {
642
+ FAISS_THROW_IF_NOT_FMT(
643
+ hnsw.offsets[i] ==
644
+ hnsw.offsets[i - 1] +
645
+ hnsw.cum_nneighbor_per_level
646
+ [hnsw.levels[i - 1]],
647
+ "HNSW offsets not increasing by cum_neighbor_per_level at %zd: %zd + %d != %zd",
648
+ i,
649
+ hnsw.offsets[i - 1],
650
+ hnsw.cum_nneighbor_per_level[hnsw.levels[i - 1]],
651
+ hnsw.offsets[i]);
652
+ }
653
+ }
654
+
655
+ // max_level must be valid
656
+ FAISS_THROW_IF_NOT_FMT(
657
+ hnsw.max_level < (int)hnsw.cum_nneighbor_per_level.size(),
658
+ "HNSW max_level %d >= cum_nneighbor_per_level size %zd",
659
+ hnsw.max_level,
660
+ hnsw.cum_nneighbor_per_level.size());
661
+
662
+ // entry_point must be -1 (empty) or a valid node id
663
+ FAISS_THROW_IF_NOT_FMT(
664
+ hnsw.entry_point >= -1 && hnsw.entry_point < (int)ntotal,
665
+ "HNSW entry_point %d out of range [-1, %zd)",
666
+ (int)hnsw.entry_point,
667
+ ntotal);
668
+
669
+ // All neighbor ids must be -1 or in [0, ntotal)
670
+ for (size_t i = 0; i < nb_neighbors_size; i++) {
671
+ auto id = hnsw.neighbors[i];
672
+ FAISS_THROW_IF_NOT_FMT(
673
+ id >= -1 && id < (int)ntotal,
674
+ "HNSW neighbors[%zd] = %d out of range [-1, %zd)",
675
+ i,
676
+ (int)id,
677
+ ntotal);
678
+ }
679
+
680
+ // For each node, verify that its level is valid and that
681
+ // offsets[i] + cum_nneighbor_per_level[levels[i]] <= neighbors.size().
682
+ // This ensures neighbor_range() can never produce an out-of-bounds offset
683
+ // into neighbors.
684
+ int cum_levels = (int)hnsw.cum_nneighbor_per_level.size();
685
+ for (size_t i = 0; i < ntotal; i++) {
686
+ int level = hnsw.levels[i];
687
+ FAISS_CHECK_RANGE(level, 1, cum_levels + 1);
688
+ size_t end = hnsw.offsets[i] + hnsw.cum_nneighbor_per_level[level];
689
+ FAISS_THROW_IF_NOT_FMT(
690
+ end <= nb_neighbors_size,
691
+ "HNSW neighbor range overflow for node %zd: "
692
+ "offsets[%zd] (%zd) + cum_nneighbor_per_level[%d] (%d) "
693
+ "= %zd > neighbors.size() (%zd)",
694
+ i,
695
+ i,
696
+ hnsw.offsets[i],
697
+ level,
698
+ hnsw.cum_nneighbor_per_level[level],
699
+ end,
700
+ nb_neighbors_size);
701
+ }
702
+ }
544
703
 
545
- READ1(hnsw->entry_point);
546
- READ1(hnsw->max_level);
547
- READ1(hnsw->efConstruction);
548
- READ1(hnsw->efSearch);
704
+ static void read_HNSW(HNSW& hnsw, IOReader* f) {
705
+ READVECTOR(hnsw.assign_probas);
706
+ READVECTOR(hnsw.cum_nneighbor_per_level);
707
+ READVECTOR(hnsw.levels);
708
+ READVECTOR(hnsw.offsets);
709
+ read_vector(hnsw.neighbors, f);
710
+
711
+ READ1(hnsw.entry_point);
712
+ READ1(hnsw.max_level);
713
+ READ1(hnsw.efConstruction);
714
+ READ1(hnsw.efSearch);
549
715
 
550
716
  // // deprecated field
551
- // READ1(hnsw->upper_beam);
717
+ // READ1(hnsw.upper_beam);
552
718
  READ1_DUMMY(int)
719
+
720
+ validate_HNSW(hnsw);
553
721
  }
554
722
 
555
- static void read_NSG(NSG* nsg, IOReader* f) {
556
- READ1(nsg->ntotal);
557
- READ1(nsg->R);
558
- READ1(nsg->L);
559
- READ1(nsg->C);
560
- READ1(nsg->search_L);
561
- READ1(nsg->enterpoint);
562
- READ1(nsg->is_built);
723
+ static void read_NSG(NSG& nsg, IOReader* f) {
724
+ READ1(nsg.ntotal);
725
+ READ1(nsg.R);
726
+ READ1(nsg.L);
727
+ READ1(nsg.C);
728
+ READ1(nsg.search_L);
729
+ READ1(nsg.enterpoint);
730
+ READ1(nsg.is_built);
563
731
 
564
- if (!nsg->is_built) {
732
+ FAISS_THROW_IF_NOT_FMT(
733
+ nsg.ntotal >= 0, "invalid NSG ntotal %d", nsg.ntotal);
734
+
735
+ if (!nsg.is_built) {
565
736
  return;
566
737
  }
567
738
 
568
739
  constexpr int EMPTY_ID = -1;
569
- int N = nsg->ntotal;
570
- int R = nsg->R;
571
- auto& graph = nsg->final_graph;
740
+ int N = nsg.ntotal;
741
+ int R = nsg.R;
742
+
743
+ auto& graph = nsg.final_graph;
572
744
  graph = std::make_shared<nsg::Graph<int>>(N, R);
573
- std::fill_n(graph->data, N * R, EMPTY_ID);
745
+ std::fill_n(graph->data, (size_t)N * R, EMPTY_ID);
574
746
 
575
747
  for (int i = 0; i < N; i++) {
576
- for (int j = 0; j < R + 1; j++) {
748
+ int j;
749
+ for (j = 0; j < R; j++) {
577
750
  int id;
578
751
  READ1(id);
579
752
  if (id != EMPTY_ID) {
753
+ FAISS_CHECK_RANGE(id, 0, N);
580
754
  graph->at(i, j) = id;
581
755
  } else {
582
756
  break;
583
757
  }
584
758
  }
759
+ if (j == R) {
760
+ // All R neighbor slots were filled; consume the trailing
761
+ // EMPTY_ID sentinel that write_NSG always appends.
762
+ int sentinel;
763
+ READ1(sentinel);
764
+ FAISS_THROW_IF_NOT(sentinel == EMPTY_ID);
765
+ }
585
766
  }
767
+
768
+ // enterpoint must be a valid node id
769
+ FAISS_CHECK_RANGE(nsg.enterpoint, 0, N);
586
770
  }
587
771
 
588
- static void read_NNDescent(NNDescent* nnd, IOReader* f) {
589
- READ1(nnd->ntotal);
590
- READ1(nnd->d);
591
- READ1(nnd->K);
592
- READ1(nnd->S);
593
- READ1(nnd->R);
594
- READ1(nnd->L);
595
- READ1(nnd->iter);
596
- READ1(nnd->search_L);
597
- READ1(nnd->random_seed);
598
- READ1(nnd->has_built);
599
-
600
- READVECTOR(nnd->final_graph);
772
+ static void read_NNDescent(NNDescent& nnd, IOReader* f) {
773
+ READ1(nnd.ntotal);
774
+ READ1(nnd.d);
775
+ READ1(nnd.K);
776
+ READ1(nnd.S);
777
+ READ1(nnd.R);
778
+ READ1(nnd.L);
779
+ READ1(nnd.iter);
780
+ READ1(nnd.search_L);
781
+ READ1(nnd.random_seed);
782
+ READ1(nnd.has_built);
783
+
784
+ FAISS_THROW_IF_NOT_FMT(
785
+ nnd.ntotal >= 0, "invalid NNDescent ntotal %d", nnd.ntotal);
786
+
787
+ READVECTOR(nnd.final_graph);
601
788
  }
602
789
 
603
- ProductQuantizer* read_ProductQuantizer(const char* fname) {
790
+ std::unique_ptr<ProductQuantizer> read_ProductQuantizer_up(const char* fname) {
604
791
  FileIOReader reader(fname);
605
- return read_ProductQuantizer(&reader);
792
+ return read_ProductQuantizer_up(&reader);
606
793
  }
607
794
 
608
- ProductQuantizer* read_ProductQuantizer(IOReader* reader) {
609
- ProductQuantizer* pq = new ProductQuantizer();
610
- std::unique_ptr<ProductQuantizer> del(pq);
795
+ ProductQuantizer* read_ProductQuantizer(const char* fname) {
796
+ return read_ProductQuantizer_up(fname).release();
797
+ }
611
798
 
612
- read_ProductQuantizer(pq, reader);
613
- del.release();
799
+ std::unique_ptr<ProductQuantizer> read_ProductQuantizer_up(IOReader* reader) {
800
+ auto pq = std::make_unique<ProductQuantizer>();
801
+ read_ProductQuantizer(pq.get(), reader);
614
802
  return pq;
615
803
  }
616
804
 
805
+ ProductQuantizer* read_ProductQuantizer(IOReader* reader) {
806
+ return read_ProductQuantizer_up(reader).release();
807
+ }
808
+
617
809
  static void read_RaBitQuantizer(
618
- RaBitQuantizer* rabitq,
810
+ RaBitQuantizer& rabitq,
619
811
  IOReader* f,
620
812
  bool multi_bit = true) {
621
- READ1(rabitq->d);
622
- READ1(rabitq->code_size);
623
- READ1(rabitq->metric_type);
813
+ READ1(rabitq.d);
814
+ READ1(rabitq.code_size);
815
+ int metric_type_int;
816
+ READ1(metric_type_int);
817
+ rabitq.metric_type = metric_type_from_int(metric_type_int);
624
818
 
625
819
  if (multi_bit) {
626
- READ1(rabitq->nb_bits);
820
+ READ1(rabitq.nb_bits);
627
821
  } else {
628
- rabitq->nb_bits = 1;
822
+ rabitq.nb_bits = 1;
629
823
  }
630
824
  }
631
825
 
@@ -649,7 +843,7 @@ void read_ivf_header(
649
843
  IndexIVF* ivf,
650
844
  IOReader* f,
651
845
  std::vector<std::vector<idx_t>>* ids) {
652
- read_index_header(ivf, f);
846
+ read_index_header(*ivf, f);
653
847
  READ1(ivf->nlist);
654
848
  READ1(ivf->nprobe);
655
849
  ivf->quantizer = read_index(f);
@@ -666,39 +860,46 @@ void read_ivf_header(
666
860
  ArrayInvertedLists* set_array_invlist(
667
861
  IndexIVF* ivf,
668
862
  std::vector<std::vector<idx_t>>& ids) {
669
- ArrayInvertedLists* ail =
670
- new ArrayInvertedLists(ivf->nlist, ivf->code_size);
863
+ auto ail = std::make_unique<ArrayInvertedLists>(ivf->nlist, ivf->code_size);
671
864
 
672
865
  ail->ids.resize(ids.size());
673
866
  for (size_t i = 0; i < ids.size(); i++) {
674
867
  ail->ids[i] = MaybeOwnedVector<idx_t>(std::move(ids[i]));
675
868
  }
676
869
 
677
- ivf->invlists = ail;
870
+ ArrayInvertedLists* result = ail.get();
871
+ ivf->invlists = ail.release();
678
872
  ivf->own_invlists = true;
679
- return ail;
873
+ return result;
680
874
  }
681
875
 
682
- static IndexIVFPQ* read_ivfpq(IOReader* f, uint32_t h, int io_flags) {
876
+ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
877
+ IOReader* f,
878
+ uint32_t h,
879
+ int io_flags) {
683
880
  bool legacy = h == fourcc("IvQR") || h == fourcc("IvPQ");
684
881
 
685
- IndexIVFPQR* ivfpqr = h == fourcc("IvQR") || h == fourcc("IwQR")
686
- ? new IndexIVFPQR()
687
- : nullptr;
688
- IndexIVFPQ* ivpq = ivfpqr ? ivfpqr : new IndexIVFPQ();
882
+ IndexIVFPQR* ivfpqr = nullptr;
883
+ std::unique_ptr<IndexIVFPQ> ivpq;
884
+ if (h == fourcc("IvQR") || h == fourcc("IwQR")) {
885
+ ivpq = std::make_unique<IndexIVFPQR>();
886
+ ivfpqr = static_cast<IndexIVFPQR*>(ivpq.get());
887
+ } else {
888
+ ivpq = std::make_unique<IndexIVFPQ>();
889
+ }
689
890
 
690
891
  std::vector<std::vector<idx_t>> ids;
691
- read_ivf_header(ivpq, f, legacy ? &ids : nullptr);
892
+ read_ivf_header(ivpq.get(), f, legacy ? &ids : nullptr);
692
893
  READ1(ivpq->by_residual);
693
894
  READ1(ivpq->code_size);
694
895
  read_ProductQuantizer(&ivpq->pq, f);
695
896
 
696
897
  if (legacy) {
697
- ArrayInvertedLists* ail = set_array_invlist(ivpq, ids);
898
+ ArrayInvertedLists* ail = set_array_invlist(ivpq.get(), ids);
698
899
  for (size_t i = 0; i < ail->nlist; i++)
699
900
  READVECTOR(ail->codes[i]);
700
901
  } else {
701
- read_InvertedLists(ivpq, f, io_flags);
902
+ read_InvertedLists(*ivpq, f, io_flags);
702
903
  }
703
904
 
704
905
  if (ivpq->is_trained) {
@@ -721,47 +922,53 @@ static IndexIVFPQ* read_ivfpq(IOReader* f, uint32_t h, int io_flags) {
721
922
 
722
923
  int read_old_fmt_hack = 0;
723
924
 
724
- Index* read_index(IOReader* f, int io_flags) {
725
- Index* idx = nullptr;
925
+ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
926
+ std::unique_ptr<Index> idx;
726
927
  uint32_t h;
727
928
  READ1(h);
728
929
  if (h == fourcc("null")) {
729
930
  // denotes a missing index, useful for some cases
730
- return nullptr;
731
- } else if (h == fourcc("IxFP")) {
931
+ return idx;
932
+ } else if (h == fourcc("IxFP") || h == fourcc("IxFp")) {
732
933
  int d;
733
934
  size_t n_levels, batch_size;
734
935
  READ1(d);
735
936
  READ1(n_levels);
937
+ FAISS_THROW_IF_NOT_FMT(n_levels > 0, "invalid n_levels %zd", n_levels);
736
938
  READ1(batch_size);
737
- IndexFlatL2Panorama* idxp =
738
- new IndexFlatL2Panorama(d, n_levels, batch_size);
939
+ std::unique_ptr<IndexFlatPanorama> idxp;
940
+ if (h == fourcc("IxFP")) {
941
+ idxp = std::make_unique<IndexFlatL2Panorama>(
942
+ d, n_levels, batch_size);
943
+ } else {
944
+ idxp = std::make_unique<IndexFlatIPPanorama>(
945
+ d, n_levels, batch_size);
946
+ }
739
947
  READ1(idxp->ntotal);
740
948
  READ1(idxp->is_trained);
741
949
  READVECTOR(idxp->codes);
742
950
  READVECTOR(idxp->cum_sums);
743
951
  idxp->verbose = false;
744
- idx = idxp;
952
+ idx = std::move(idxp);
745
953
  } else if (
746
954
  h == fourcc("IxFI") || h == fourcc("IxF2") || h == fourcc("IxFl")) {
747
- IndexFlat* idxf;
955
+ std::unique_ptr<IndexFlat> idxf;
748
956
  if (h == fourcc("IxFI")) {
749
- idxf = new IndexFlatIP();
957
+ idxf = std::make_unique<IndexFlatIP>();
750
958
  } else if (h == fourcc("IxF2")) {
751
- idxf = new IndexFlatL2();
959
+ idxf = std::make_unique<IndexFlatL2>();
752
960
  } else {
753
- idxf = new IndexFlat();
961
+ idxf = std::make_unique<IndexFlat>();
754
962
  }
755
- read_index_header(idxf, f);
963
+ read_index_header(*idxf, f);
756
964
  idxf->code_size = idxf->d * sizeof(float);
757
965
  read_xb_vector(idxf->codes, f);
758
966
  FAISS_THROW_IF_NOT(
759
967
  idxf->codes.size() == idxf->ntotal * idxf->code_size);
760
- // leak!
761
- idx = idxf;
968
+ idx = std::move(idxf);
762
969
  } else if (h == fourcc("IxHE") || h == fourcc("IxHe")) {
763
- IndexLSH* idxl = new IndexLSH();
764
- read_index_header(idxl, f);
970
+ auto idxl = std::make_unique<IndexLSH>();
971
+ read_index_header(*idxl, f);
765
972
  READ1(idxl->nbits);
766
973
  READ1(idxl->rotate_data);
767
974
  READ1(idxl->train_thresholds);
@@ -779,23 +986,24 @@ Index* read_index(IOReader* f, int io_flags) {
779
986
  idxl->code_size *= 8;
780
987
  }
781
988
  {
782
- RandomRotationMatrix* rrot = dynamic_cast<RandomRotationMatrix*>(
783
- read_VectorTransform(f));
989
+ // Read, dereference, discard.
990
+ auto sub_vt = read_VectorTransform_up(f);
991
+ RandomRotationMatrix* rrot =
992
+ dynamic_cast<RandomRotationMatrix*>(sub_vt.get());
784
993
  FAISS_THROW_IF_NOT_MSG(rrot, "expected a random rotation");
785
994
  idxl->rrot = *rrot;
786
- delete rrot;
787
995
  }
788
996
  read_vector(idxl->codes, f);
789
997
  FAISS_THROW_IF_NOT(
790
998
  idxl->rrot.d_in == idxl->d && idxl->rrot.d_out == idxl->nbits);
791
999
  FAISS_THROW_IF_NOT(
792
1000
  idxl->codes.size() == idxl->ntotal * idxl->code_size);
793
- idx = idxl;
1001
+ idx = std::move(idxl);
794
1002
  } else if (
795
1003
  h == fourcc("IxPQ") || h == fourcc("IxPo") || h == fourcc("IxPq")) {
796
1004
  // IxPQ and IxPo were merged into the same IndexPQ object
797
- IndexPQ* idxp = new IndexPQ();
798
- read_index_header(idxp, f);
1005
+ auto idxp = std::make_unique<IndexPQ>();
1006
+ read_index_header(*idxp, f);
799
1007
  read_ProductQuantizer(&idxp->pq, f);
800
1008
  idxp->code_size = idxp->pq.code_size;
801
1009
  read_vector(idxp->codes, f);
@@ -810,43 +1018,43 @@ Index* read_index(IOReader* f, int io_flags) {
810
1018
  if (h == fourcc("IxPQ") || h == fourcc("IxPo")) {
811
1019
  idxp->metric_type = METRIC_L2;
812
1020
  }
813
- idx = idxp;
1021
+ idx = std::move(idxp);
814
1022
  } else if (h == fourcc("IxRQ") || h == fourcc("IxRq")) {
815
- IndexResidualQuantizer* idxr = new IndexResidualQuantizer();
816
- read_index_header(idxr, f);
1023
+ auto idxr = std::make_unique<IndexResidualQuantizer>();
1024
+ read_index_header(*idxr, f);
817
1025
  if (h == fourcc("IxRQ")) {
818
- read_ResidualQuantizer_old(&idxr->rq, f);
1026
+ read_ResidualQuantizer_old(idxr->rq, f);
819
1027
  } else {
820
- read_ResidualQuantizer(&idxr->rq, f, io_flags);
1028
+ read_ResidualQuantizer(idxr->rq, f, io_flags);
821
1029
  }
822
1030
  READ1(idxr->code_size);
823
1031
  read_vector(idxr->codes, f);
824
- idx = idxr;
1032
+ idx = std::move(idxr);
825
1033
  } else if (h == fourcc("IxLS")) {
826
- auto idxr = new IndexLocalSearchQuantizer();
827
- read_index_header(idxr, f);
828
- read_LocalSearchQuantizer(&idxr->lsq, f);
1034
+ auto idxr = std::make_unique<IndexLocalSearchQuantizer>();
1035
+ read_index_header(*idxr, f);
1036
+ read_LocalSearchQuantizer(idxr->lsq, f);
829
1037
  READ1(idxr->code_size);
830
1038
  read_vector(idxr->codes, f);
831
- idx = idxr;
1039
+ idx = std::move(idxr);
832
1040
  } else if (h == fourcc("IxPR")) {
833
- auto idxpr = new IndexProductResidualQuantizer();
834
- read_index_header(idxpr, f);
835
- read_ProductResidualQuantizer(&idxpr->prq, f, io_flags);
1041
+ auto idxpr = std::make_unique<IndexProductResidualQuantizer>();
1042
+ read_index_header(*idxpr, f);
1043
+ read_ProductResidualQuantizer(idxpr->prq, f, io_flags);
836
1044
  READ1(idxpr->code_size);
837
1045
  read_vector(idxpr->codes, f);
838
- idx = idxpr;
1046
+ idx = std::move(idxpr);
839
1047
  } else if (h == fourcc("IxPL")) {
840
- auto idxpl = new IndexProductLocalSearchQuantizer();
841
- read_index_header(idxpl, f);
842
- read_ProductLocalSearchQuantizer(&idxpl->plsq, f);
1048
+ auto idxpl = std::make_unique<IndexProductLocalSearchQuantizer>();
1049
+ read_index_header(*idxpl, f);
1050
+ read_ProductLocalSearchQuantizer(idxpl->plsq, f);
843
1051
  READ1(idxpl->code_size);
844
1052
  read_vector(idxpl->codes, f);
845
- idx = idxpl;
1053
+ idx = std::move(idxpl);
846
1054
  } else if (h == fourcc("ImRQ")) {
847
- ResidualCoarseQuantizer* idxr = new ResidualCoarseQuantizer();
848
- read_index_header(idxr, f);
849
- read_ResidualQuantizer(&idxr->rq, f, io_flags);
1055
+ auto idxr = std::make_unique<ResidualCoarseQuantizer>();
1056
+ read_index_header(*idxr, f);
1057
+ read_ResidualQuantizer(idxr->rq, f, io_flags);
850
1058
  READ1(idxr->beam_factor);
851
1059
  if (io_flags & IO_FLAG_SKIP_PRECOMPUTE_TABLE) {
852
1060
  // then we force the beam factor to -1
@@ -854,7 +1062,7 @@ Index* read_index(IOReader* f, int io_flags) {
854
1062
  idxr->beam_factor = -1;
855
1063
  }
856
1064
  idxr->set_beam_factor(idxr->beam_factor);
857
- idx = idxr;
1065
+ idx = std::move(idxr);
858
1066
  } else if (
859
1067
  h == fourcc("ILfs") || h == fourcc("IRfs") || h == fourcc("IPRf") ||
860
1068
  h == fourcc("IPLf")) {
@@ -862,29 +1070,30 @@ Index* read_index(IOReader* f, int io_flags) {
862
1070
  bool is_RQ = h == fourcc("IRfs");
863
1071
  bool is_PLSQ = h == fourcc("IPLf");
864
1072
 
865
- IndexAdditiveQuantizerFastScan* idxaqfs;
1073
+ std::unique_ptr<IndexAdditiveQuantizerFastScan> idxaqfs;
866
1074
  if (is_LSQ) {
867
- idxaqfs = new IndexLocalSearchQuantizerFastScan();
1075
+ idxaqfs = std::make_unique<IndexLocalSearchQuantizerFastScan>();
868
1076
  } else if (is_RQ) {
869
- idxaqfs = new IndexResidualQuantizerFastScan();
1077
+ idxaqfs = std::make_unique<IndexResidualQuantizerFastScan>();
870
1078
  } else if (is_PLSQ) {
871
- idxaqfs = new IndexProductLocalSearchQuantizerFastScan();
1079
+ idxaqfs = std::make_unique<
1080
+ IndexProductLocalSearchQuantizerFastScan>();
872
1081
  } else {
873
- idxaqfs = new IndexProductResidualQuantizerFastScan();
1082
+ idxaqfs = std::make_unique<IndexProductResidualQuantizerFastScan>();
874
1083
  }
875
- read_index_header(idxaqfs, f);
1084
+ read_index_header(*idxaqfs, f);
876
1085
 
877
1086
  if (is_LSQ) {
878
- read_LocalSearchQuantizer((LocalSearchQuantizer*)idxaqfs->aq, f);
1087
+ read_LocalSearchQuantizer(*(LocalSearchQuantizer*)idxaqfs->aq, f);
879
1088
  } else if (is_RQ) {
880
1089
  read_ResidualQuantizer(
881
- (ResidualQuantizer*)idxaqfs->aq, f, io_flags);
1090
+ *(ResidualQuantizer*)idxaqfs->aq, f, io_flags);
882
1091
  } else if (is_PLSQ) {
883
1092
  read_ProductLocalSearchQuantizer(
884
- (ProductLocalSearchQuantizer*)idxaqfs->aq, f);
1093
+ *(ProductLocalSearchQuantizer*)idxaqfs->aq, f);
885
1094
  } else {
886
1095
  read_ProductResidualQuantizer(
887
- (ProductResidualQuantizer*)idxaqfs->aq, f, io_flags);
1096
+ *(ProductResidualQuantizer*)idxaqfs->aq, f, io_flags);
888
1097
  }
889
1098
 
890
1099
  READ1(idxaqfs->implem);
@@ -903,7 +1112,7 @@ Index* read_index(IOReader* f, int io_flags) {
903
1112
  READ1(idxaqfs->max_train_points);
904
1113
 
905
1114
  READVECTOR(idxaqfs->codes);
906
- idx = idxaqfs;
1115
+ idx = std::move(idxaqfs);
907
1116
  } else if (
908
1117
  h == fourcc("IVLf") || h == fourcc("IVRf") || h == fourcc("NPLf") ||
909
1118
  h == fourcc("NPRf")) {
@@ -911,28 +1120,31 @@ Index* read_index(IOReader* f, int io_flags) {
911
1120
  bool is_RQ = h == fourcc("IVRf");
912
1121
  bool is_PLSQ = h == fourcc("NPLf");
913
1122
 
914
- IndexIVFAdditiveQuantizerFastScan* ivaqfs;
1123
+ std::unique_ptr<IndexIVFAdditiveQuantizerFastScan> ivaqfs;
915
1124
  if (is_LSQ) {
916
- ivaqfs = new IndexIVFLocalSearchQuantizerFastScan();
1125
+ ivaqfs = std::make_unique<IndexIVFLocalSearchQuantizerFastScan>();
917
1126
  } else if (is_RQ) {
918
- ivaqfs = new IndexIVFResidualQuantizerFastScan();
1127
+ ivaqfs = std::make_unique<IndexIVFResidualQuantizerFastScan>();
919
1128
  } else if (is_PLSQ) {
920
- ivaqfs = new IndexIVFProductLocalSearchQuantizerFastScan();
1129
+ ivaqfs = std::make_unique<
1130
+ IndexIVFProductLocalSearchQuantizerFastScan>();
921
1131
  } else {
922
- ivaqfs = new IndexIVFProductResidualQuantizerFastScan();
1132
+ ivaqfs = std::make_unique<
1133
+ IndexIVFProductResidualQuantizerFastScan>();
923
1134
  }
924
- read_ivf_header(ivaqfs, f);
1135
+ read_ivf_header(ivaqfs.get(), f);
925
1136
 
926
1137
  if (is_LSQ) {
927
- read_LocalSearchQuantizer((LocalSearchQuantizer*)ivaqfs->aq, f);
1138
+ read_LocalSearchQuantizer(*(LocalSearchQuantizer*)ivaqfs->aq, f);
928
1139
  } else if (is_RQ) {
929
- read_ResidualQuantizer((ResidualQuantizer*)ivaqfs->aq, f, io_flags);
1140
+ read_ResidualQuantizer(
1141
+ *(ResidualQuantizer*)ivaqfs->aq, f, io_flags);
930
1142
  } else if (is_PLSQ) {
931
1143
  read_ProductLocalSearchQuantizer(
932
- (ProductLocalSearchQuantizer*)ivaqfs->aq, f);
1144
+ *(ProductLocalSearchQuantizer*)ivaqfs->aq, f);
933
1145
  } else {
934
1146
  read_ProductResidualQuantizer(
935
- (ProductResidualQuantizer*)ivaqfs->aq, f, io_flags);
1147
+ *(ProductResidualQuantizer*)ivaqfs->aq, f, io_flags);
936
1148
  }
937
1149
 
938
1150
  READ1(ivaqfs->by_residual);
@@ -951,15 +1163,15 @@ Index* read_index(IOReader* f, int io_flags) {
951
1163
  READ1(ivaqfs->norm_scale);
952
1164
  READ1(ivaqfs->max_train_points);
953
1165
 
954
- read_InvertedLists(ivaqfs, f, io_flags);
1166
+ read_InvertedLists(*ivaqfs, f, io_flags);
955
1167
  ivaqfs->init_code_packer();
956
- idx = ivaqfs;
1168
+ idx = std::move(ivaqfs);
957
1169
  } else if (h == fourcc("IvFl") || h == fourcc("IvFL")) { // legacy
958
- IndexIVFFlat* ivfl = new IndexIVFFlat();
1170
+ auto ivfl = std::make_unique<IndexIVFFlat>();
959
1171
  std::vector<std::vector<idx_t>> ids;
960
- read_ivf_header(ivfl, f, &ids);
1172
+ read_ivf_header(ivfl.get(), f, &ids);
961
1173
  ivfl->code_size = ivfl->d * sizeof(float);
962
- ArrayInvertedLists* ail = set_array_invlist(ivfl, ids);
1174
+ ArrayInvertedLists* ail = set_array_invlist(ivfl.get(), ids);
963
1175
 
964
1176
  if (h == fourcc("IvFL")) {
965
1177
  for (size_t i = 0; i < ivfl->nlist; i++) {
@@ -973,64 +1185,85 @@ Index* read_index(IOReader* f, int io_flags) {
973
1185
  memcpy(ail->codes[i].data(), vec.data(), ail->codes[i].size());
974
1186
  }
975
1187
  }
976
- idx = ivfl;
1188
+ idx = std::move(ivfl);
977
1189
  } else if (h == fourcc("IwFd")) {
978
- IndexIVFFlatDedup* ivfl = new IndexIVFFlatDedup();
979
- read_ivf_header(ivfl, f);
1190
+ auto ivfl = std::make_unique<IndexIVFFlatDedup>();
1191
+ read_ivf_header(ivfl.get(), f);
980
1192
  ivfl->code_size = ivfl->d * sizeof(float);
981
1193
  {
982
1194
  std::vector<idx_t> tab;
983
1195
  READVECTOR(tab);
1196
+ FAISS_THROW_IF_NOT_FMT(
1197
+ tab.size() % 2 == 0,
1198
+ "invalid IVFFlatDedup instances table size: %zd "
1199
+ "(must be even)",
1200
+ tab.size());
984
1201
  for (long i = 0; i < tab.size(); i += 2) {
985
1202
  std::pair<idx_t, idx_t> pair(tab[i], tab[i + 1]);
986
1203
  ivfl->instances.insert(pair);
987
1204
  }
988
1205
  }
989
- read_InvertedLists(ivfl, f, io_flags);
990
- idx = ivfl;
1206
+ read_InvertedLists(*ivfl, f, io_flags);
1207
+ idx = std::move(ivfl);
991
1208
  } else if (h == fourcc("IwPn")) {
992
- IndexIVFFlatPanorama* ivfp = new IndexIVFFlatPanorama();
993
- read_ivf_header(ivfp, f);
1209
+ auto ivfp = std::make_unique<IndexIVFFlatPanorama>();
1210
+ read_ivf_header(ivfp.get(), f);
994
1211
  ivfp->code_size = ivfp->d * sizeof(float);
995
1212
  READ1(ivfp->n_levels);
996
- read_InvertedLists(ivfp, f, io_flags);
997
- idx = ivfp;
1213
+ read_InvertedLists(*ivfp, f, io_flags);
1214
+ idx = std::move(ivfp);
998
1215
  } else if (h == fourcc("IwFl")) {
999
- IndexIVFFlat* ivfl = new IndexIVFFlat();
1000
- read_ivf_header(ivfl, f);
1216
+ auto ivfl = std::make_unique<IndexIVFFlat>();
1217
+ read_ivf_header(ivfl.get(), f);
1001
1218
  ivfl->code_size = ivfl->d * sizeof(float);
1002
- read_InvertedLists(ivfl, f, io_flags);
1003
- idx = ivfl;
1219
+ read_InvertedLists(*ivfl, f, io_flags);
1220
+ idx = std::move(ivfl);
1004
1221
  } else if (h == fourcc("IxSQ")) {
1005
- IndexScalarQuantizer* idxs = new IndexScalarQuantizer();
1006
- read_index_header(idxs, f);
1222
+ auto idxs = std::make_unique<IndexScalarQuantizer>();
1223
+ read_index_header(*idxs, f);
1007
1224
  read_ScalarQuantizer(&idxs->sq, f);
1008
1225
  read_vector(idxs->codes, f);
1009
1226
  idxs->code_size = idxs->sq.code_size;
1010
- idx = idxs;
1227
+ idx = std::move(idxs);
1011
1228
  } else if (h == fourcc("IxLa")) {
1012
1229
  int d, nsq, scale_nbit, r2;
1013
1230
  READ1(d);
1014
1231
  READ1(nsq);
1015
1232
  READ1(scale_nbit);
1016
1233
  READ1(r2);
1017
- IndexLattice* idxl = new IndexLattice(d, nsq, scale_nbit, r2);
1018
- read_index_header(idxl, f);
1234
+ FAISS_THROW_IF_NOT_FMT(
1235
+ nsq > 0, "invalid IndexLattice nsq %d (must be > 0)", nsq);
1236
+ FAISS_THROW_IF_NOT_FMT(
1237
+ d > 0 && d % nsq == 0,
1238
+ "invalid IndexLattice d=%d, nsq=%d (d must be > 0 and divisible by nsq)",
1239
+ d,
1240
+ nsq);
1241
+ FAISS_THROW_IF_NOT_FMT(
1242
+ r2 > 0, "invalid IndexLattice r2 %d (must be > 0)", r2);
1243
+ int dsq = d / nsq;
1244
+ FAISS_THROW_IF_NOT_FMT(
1245
+ dsq >= 2 && (dsq & (dsq - 1)) == 0,
1246
+ "invalid IndexLattice d=%d, nsq=%d: d/nsq=%d must be a power of 2 >= 2",
1247
+ d,
1248
+ nsq,
1249
+ dsq);
1250
+ auto idxl = std::make_unique<IndexLattice>(d, nsq, scale_nbit, r2);
1251
+ read_index_header(*idxl, f);
1019
1252
  READVECTOR(idxl->trained);
1020
- idx = idxl;
1253
+ idx = std::move(idxl);
1021
1254
  } else if (h == fourcc("IvSQ")) { // legacy
1022
- IndexIVFScalarQuantizer* ivsc = new IndexIVFScalarQuantizer();
1255
+ auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
1023
1256
  std::vector<std::vector<idx_t>> ids;
1024
- read_ivf_header(ivsc, f, &ids);
1257
+ read_ivf_header(ivsc.get(), f, &ids);
1025
1258
  read_ScalarQuantizer(&ivsc->sq, f);
1026
1259
  READ1(ivsc->code_size);
1027
- ArrayInvertedLists* ail = set_array_invlist(ivsc, ids);
1260
+ ArrayInvertedLists* ail = set_array_invlist(ivsc.get(), ids);
1028
1261
  for (int i = 0; i < ivsc->nlist; i++)
1029
1262
  READVECTOR(ail->codes[i]);
1030
- idx = ivsc;
1263
+ idx = std::move(ivsc);
1031
1264
  } else if (h == fourcc("IwSQ") || h == fourcc("IwSq")) {
1032
- IndexIVFScalarQuantizer* ivsc = new IndexIVFScalarQuantizer();
1033
- read_ivf_header(ivsc, f);
1265
+ auto ivsc = std::make_unique<IndexIVFScalarQuantizer>();
1266
+ read_ivf_header(ivsc.get(), f);
1034
1267
  read_ScalarQuantizer(&ivsc->sq, f);
1035
1268
  READ1(ivsc->code_size);
1036
1269
  if (h == fourcc("IwSQ")) {
@@ -1038,44 +1271,44 @@ Index* read_index(IOReader* f, int io_flags) {
1038
1271
  } else {
1039
1272
  READ1(ivsc->by_residual);
1040
1273
  }
1041
- read_InvertedLists(ivsc, f, io_flags);
1042
- idx = ivsc;
1274
+ read_InvertedLists(*ivsc, f, io_flags);
1275
+ idx = std::move(ivsc);
1043
1276
  } else if (
1044
1277
  h == fourcc("IwLS") || h == fourcc("IwRQ") || h == fourcc("IwPL") ||
1045
1278
  h == fourcc("IwPR")) {
1046
1279
  bool is_LSQ = h == fourcc("IwLS");
1047
1280
  bool is_RQ = h == fourcc("IwRQ");
1048
1281
  bool is_PLSQ = h == fourcc("IwPL");
1049
- IndexIVFAdditiveQuantizer* iva;
1282
+ std::unique_ptr<IndexIVFAdditiveQuantizer> iva;
1050
1283
  if (is_LSQ) {
1051
- iva = new IndexIVFLocalSearchQuantizer();
1284
+ iva = std::make_unique<IndexIVFLocalSearchQuantizer>();
1052
1285
  } else if (is_RQ) {
1053
- iva = new IndexIVFResidualQuantizer();
1286
+ iva = std::make_unique<IndexIVFResidualQuantizer>();
1054
1287
  } else if (is_PLSQ) {
1055
- iva = new IndexIVFProductLocalSearchQuantizer();
1288
+ iva = std::make_unique<IndexIVFProductLocalSearchQuantizer>();
1056
1289
  } else {
1057
- iva = new IndexIVFProductResidualQuantizer();
1290
+ iva = std::make_unique<IndexIVFProductResidualQuantizer>();
1058
1291
  }
1059
- read_ivf_header(iva, f);
1292
+ read_ivf_header(iva.get(), f);
1060
1293
  READ1(iva->code_size);
1061
1294
  if (is_LSQ) {
1062
- read_LocalSearchQuantizer((LocalSearchQuantizer*)iva->aq, f);
1295
+ read_LocalSearchQuantizer(*(LocalSearchQuantizer*)iva->aq, f);
1063
1296
  } else if (is_RQ) {
1064
- read_ResidualQuantizer((ResidualQuantizer*)iva->aq, f, io_flags);
1297
+ read_ResidualQuantizer(*(ResidualQuantizer*)iva->aq, f, io_flags);
1065
1298
  } else if (is_PLSQ) {
1066
1299
  read_ProductLocalSearchQuantizer(
1067
- (ProductLocalSearchQuantizer*)iva->aq, f);
1300
+ *(ProductLocalSearchQuantizer*)iva->aq, f);
1068
1301
  } else {
1069
1302
  read_ProductResidualQuantizer(
1070
- (ProductResidualQuantizer*)iva->aq, f, io_flags);
1303
+ *(ProductResidualQuantizer*)iva->aq, f, io_flags);
1071
1304
  }
1072
1305
  READ1(iva->by_residual);
1073
1306
  READ1(iva->use_precomputed_table);
1074
- read_InvertedLists(iva, f, io_flags);
1075
- idx = iva;
1307
+ read_InvertedLists(*iva, f, io_flags);
1308
+ idx = std::move(iva);
1076
1309
  } else if (h == fourcc("IwSh")) {
1077
- IndexIVFSpectralHash* ivsp = new IndexIVFSpectralHash();
1078
- read_ivf_header(ivsp, f);
1310
+ auto ivsp = std::make_unique<IndexIVFSpectralHash>();
1311
+ read_ivf_header(ivsp.get(), f);
1079
1312
  ivsp->vt = read_VectorTransform(f);
1080
1313
  ivsp->own_fields = true;
1081
1314
  READ1(ivsp->nbit);
@@ -1084,32 +1317,34 @@ Index* read_index(IOReader* f, int io_flags) {
1084
1317
  READ1(ivsp->period);
1085
1318
  READ1(ivsp->threshold_type);
1086
1319
  READVECTOR(ivsp->trained);
1087
- read_InvertedLists(ivsp, f, io_flags);
1088
- idx = ivsp;
1320
+ read_InvertedLists(*ivsp, f, io_flags);
1321
+ idx = std::move(ivsp);
1089
1322
  } else if (
1090
1323
  h == fourcc("IvPQ") || h == fourcc("IvQR") || h == fourcc("IwPQ") ||
1091
1324
  h == fourcc("IwQR")) {
1092
1325
  idx = read_ivfpq(f, h, io_flags);
1093
1326
  } else if (h == fourcc("IwIQ")) {
1094
- auto* indep = new IndexIVFIndependentQuantizer();
1327
+ auto indep = std::make_unique<IndexIVFIndependentQuantizer>();
1095
1328
  indep->own_fields = true;
1096
- read_index_header(indep, f);
1329
+ read_index_header(*indep, f);
1097
1330
  indep->quantizer = read_index(f, io_flags);
1098
1331
  bool has_vt;
1099
1332
  READ1(has_vt);
1100
1333
  if (has_vt) {
1101
1334
  indep->vt = read_VectorTransform(f);
1102
1335
  }
1103
- indep->index_ivf = dynamic_cast<IndexIVF*>(read_index(f, io_flags));
1336
+ auto ivf_idx = read_index_up(f, io_flags);
1337
+ indep->index_ivf = dynamic_cast<IndexIVF*>(ivf_idx.get());
1104
1338
  FAISS_THROW_IF_NOT(indep->index_ivf);
1339
+ ivf_idx.release();
1105
1340
  if (auto index_ivfpq = dynamic_cast<IndexIVFPQ*>(indep->index_ivf)) {
1106
1341
  READ1(index_ivfpq->use_precomputed_table);
1107
1342
  }
1108
- idx = indep;
1343
+ idx = std::move(indep);
1109
1344
  } else if (h == fourcc("IxPT")) {
1110
- IndexPreTransform* ixpt = new IndexPreTransform();
1345
+ auto ixpt = std::make_unique<IndexPreTransform>();
1111
1346
  ixpt->own_fields = true;
1112
- read_index_header(ixpt, f);
1347
+ read_index_header(*ixpt, f);
1113
1348
  int nt;
1114
1349
  if (read_old_fmt_hack == 2) {
1115
1350
  nt = 1;
@@ -1120,48 +1355,50 @@ Index* read_index(IOReader* f, int io_flags) {
1120
1355
  ixpt->chain.push_back(read_VectorTransform(f));
1121
1356
  }
1122
1357
  ixpt->index = read_index(f, io_flags);
1123
- idx = ixpt;
1358
+ idx = std::move(ixpt);
1124
1359
  } else if (h == fourcc("Imiq")) {
1125
- MultiIndexQuantizer* imiq = new MultiIndexQuantizer();
1126
- read_index_header(imiq, f);
1360
+ auto imiq = std::make_unique<MultiIndexQuantizer>();
1361
+ read_index_header(*imiq, f);
1127
1362
  read_ProductQuantizer(&imiq->pq, f);
1128
- idx = imiq;
1363
+ idx = std::move(imiq);
1129
1364
  } else if (h == fourcc("IxRF") || h == fourcc("IxRP")) {
1130
- IndexRefine* idxrf = new IndexRefine();
1131
- read_index_header(idxrf, f);
1132
- idxrf->base_index = read_index(f, io_flags);
1133
- idxrf->refine_index = read_index(f, io_flags);
1365
+ auto idxrf = std::make_unique<IndexRefine>();
1366
+ read_index_header(*idxrf, f);
1367
+ auto base = read_index_up(f, io_flags);
1368
+ auto refine = read_index_up(f, io_flags);
1134
1369
  READ1(idxrf->k_factor);
1135
1370
  if (h == fourcc("IxRP")) {
1136
1371
  // then make a RefineFlatPanorama with it
1137
- IndexRefine* idxrf_old = idxrf;
1138
- idxrf = new IndexRefinePanorama();
1139
- *idxrf = *idxrf_old;
1140
- delete idxrf_old;
1141
- } else if (dynamic_cast<IndexFlat*>(idxrf->refine_index)) {
1372
+ auto idxrf_new = std::make_unique<IndexRefinePanorama>();
1373
+ static_cast<IndexRefine&>(*idxrf_new) = *idxrf;
1374
+ idxrf = std::move(idxrf_new);
1375
+ } else if (dynamic_cast<IndexFlat*>(refine.get())) {
1142
1376
  // then make a RefineFlat with it
1143
- IndexRefine* idxrf_old = idxrf;
1144
- idxrf = new IndexRefineFlat();
1145
- *idxrf = *idxrf_old;
1146
- delete idxrf_old;
1377
+ auto idxrf_new = std::make_unique<IndexRefineFlat>();
1378
+ static_cast<IndexRefine&>(*idxrf_new) = *idxrf;
1379
+ idxrf = std::move(idxrf_new);
1147
1380
  }
1381
+ idxrf->base_index = base.release();
1382
+ idxrf->refine_index = refine.release();
1148
1383
  idxrf->own_fields = true;
1149
1384
  idxrf->own_refine_index = true;
1150
- idx = idxrf;
1385
+ idx = std::move(idxrf);
1151
1386
  } else if (h == fourcc("IxMp") || h == fourcc("IxM2")) {
1152
1387
  bool is_map2 = h == fourcc("IxM2");
1153
- IndexIDMap* idxmap = is_map2 ? new IndexIDMap2() : new IndexIDMap();
1154
- read_index_header(idxmap, f);
1388
+ std::unique_ptr<IndexIDMap> idxmap = is_map2
1389
+ ? std::make_unique<IndexIDMap2>()
1390
+ : std::make_unique<IndexIDMap>();
1391
+ read_index_header(*idxmap, f);
1155
1392
  idxmap->index = read_index(f, io_flags);
1156
1393
  idxmap->own_fields = true;
1157
1394
  READVECTOR(idxmap->id_map);
1158
1395
  if (is_map2) {
1159
- static_cast<IndexIDMap2*>(idxmap)->construct_rev_map();
1396
+ static_cast<IndexIDMap2*>(idxmap.get())->construct_rev_map();
1160
1397
  }
1161
- idx = idxmap;
1398
+ idx = std::move(idxmap);
1162
1399
  } else if (h == fourcc("Ix2L")) {
1163
- Index2Layer* idxp = new Index2Layer();
1164
- read_index_header(idxp, f);
1400
+ auto idxp = std::make_unique<Index2Layer>();
1401
+ read_index_header(*idxp, f);
1165
1402
  idxp->q1.quantizer = read_index(f, io_flags);
1166
1403
  READ1(idxp->q1.nlist);
1167
1404
  READ1(idxp->q1.quantizer_trains_alone);
@@ -1170,46 +1407,47 @@ Index* read_index(IOReader* f, int io_flags) {
1170
1407
  READ1(idxp->code_size_2);
1171
1408
  READ1(idxp->code_size);
1172
1409
  read_vector(idxp->codes, f);
1173
- idx = idxp;
1410
+ idx = std::move(idxp);
1174
1411
  } else if (
1175
1412
  h == fourcc("IHNf") || h == fourcc("IHNp") || h == fourcc("IHNs") ||
1176
1413
  h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2") ||
1177
1414
  h == fourcc("IHfP")) {
1178
- IndexHNSW* idxhnsw = nullptr;
1415
+ std::unique_ptr<IndexHNSW> idxhnsw;
1179
1416
  if (h == fourcc("IHNf")) {
1180
- idxhnsw = new IndexHNSWFlat();
1417
+ idxhnsw = std::make_unique<IndexHNSWFlat>();
1181
1418
  }
1182
1419
  if (h == fourcc("IHfP")) {
1183
- idxhnsw = new IndexHNSWFlatPanorama();
1420
+ idxhnsw = std::make_unique<IndexHNSWFlatPanorama>();
1184
1421
  }
1185
1422
  if (h == fourcc("IHNp")) {
1186
- idxhnsw = new IndexHNSWPQ();
1423
+ idxhnsw = std::make_unique<IndexHNSWPQ>();
1187
1424
  }
1188
1425
  if (h == fourcc("IHNs")) {
1189
- idxhnsw = new IndexHNSWSQ();
1426
+ idxhnsw = std::make_unique<IndexHNSWSQ>();
1190
1427
  }
1191
1428
  if (h == fourcc("IHN2")) {
1192
- idxhnsw = new IndexHNSW2Level();
1429
+ idxhnsw = std::make_unique<IndexHNSW2Level>();
1193
1430
  }
1194
1431
  if (h == fourcc("IHNc")) {
1195
- idxhnsw = new IndexHNSWCagra();
1432
+ idxhnsw = std::make_unique<IndexHNSWCagra>();
1196
1433
  }
1197
1434
  if (h == fourcc("IHc2")) {
1198
- idxhnsw = new IndexHNSWCagra();
1435
+ idxhnsw = std::make_unique<IndexHNSWCagra>();
1199
1436
  }
1200
- read_index_header(idxhnsw, f);
1437
+ read_index_header(*idxhnsw, f);
1201
1438
  if (h == fourcc("IHfP")) {
1202
- auto idx_panorama = dynamic_cast<IndexHNSWFlatPanorama*>(idxhnsw);
1439
+ auto idx_panorama =
1440
+ dynamic_cast<IndexHNSWFlatPanorama*>(idxhnsw.get());
1203
1441
  size_t nlevels;
1204
1442
  READ1(nlevels);
1205
1443
  const_cast<size_t&>(idx_panorama->num_panorama_levels) = nlevels;
1206
- const_cast<size_t&>(idx_panorama->panorama_level_width) =
1207
- (idx_panorama->d + nlevels - 1) / nlevels;
1444
+ const_cast<Panorama&>(idx_panorama->pano) =
1445
+ Panorama(idx_panorama->d * sizeof(float), nlevels, 1);
1208
1446
  READVECTOR(idx_panorama->cum_sums);
1209
1447
  }
1210
1448
  if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
1211
1449
  READ1(idxhnsw->keep_max_size_level0);
1212
- auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw);
1450
+ auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw.get());
1213
1451
  READ1(idx_hnsw_cagra->base_level_only);
1214
1452
  READ1(idx_hnsw_cagra->num_base_level_search_entrypoints);
1215
1453
  if (h == fourcc("IHc2")) {
@@ -1218,47 +1456,47 @@ Index* read_index(IOReader* f, int io_flags) {
1218
1456
  idx_hnsw_cagra->set_numeric_type(faiss::Float32);
1219
1457
  }
1220
1458
  }
1221
- read_HNSW(&idxhnsw->hnsw, f);
1459
+ read_HNSW(idxhnsw->hnsw, f);
1222
1460
  idxhnsw->hnsw.is_panorama = (h == fourcc("IHfP"));
1223
1461
  idxhnsw->storage = read_index(f, io_flags);
1224
1462
  idxhnsw->own_fields = idxhnsw->storage != nullptr;
1225
1463
  if (h == fourcc("IHNp") && !(io_flags & IO_FLAG_PQ_SKIP_SDC_TABLE)) {
1226
1464
  dynamic_cast<IndexPQ*>(idxhnsw->storage)->pq.compute_sdc_table();
1227
1465
  }
1228
- idx = idxhnsw;
1466
+ idx = std::move(idxhnsw);
1229
1467
  } else if (
1230
1468
  h == fourcc("INSf") || h == fourcc("INSp") || h == fourcc("INSs")) {
1231
- IndexNSG* idxnsg;
1469
+ std::unique_ptr<IndexNSG> idxnsg;
1232
1470
  if (h == fourcc("INSf")) {
1233
- idxnsg = new IndexNSGFlat();
1471
+ idxnsg = std::make_unique<IndexNSGFlat>();
1234
1472
  }
1235
1473
  if (h == fourcc("INSp")) {
1236
- idxnsg = new IndexNSGPQ();
1474
+ idxnsg = std::make_unique<IndexNSGPQ>();
1237
1475
  }
1238
1476
  if (h == fourcc("INSs")) {
1239
- idxnsg = new IndexNSGSQ();
1477
+ idxnsg = std::make_unique<IndexNSGSQ>();
1240
1478
  }
1241
- read_index_header(idxnsg, f);
1479
+ read_index_header(*idxnsg, f);
1242
1480
  READ1(idxnsg->GK);
1243
1481
  READ1(idxnsg->build_type);
1244
1482
  READ1(idxnsg->nndescent_S);
1245
1483
  READ1(idxnsg->nndescent_R);
1246
1484
  READ1(idxnsg->nndescent_L);
1247
1485
  READ1(idxnsg->nndescent_iter);
1248
- read_NSG(&idxnsg->nsg, f);
1486
+ read_NSG(idxnsg->nsg, f);
1249
1487
  idxnsg->storage = read_index(f, io_flags);
1250
1488
  idxnsg->own_fields = true;
1251
- idx = idxnsg;
1489
+ idx = std::move(idxnsg);
1252
1490
  } else if (h == fourcc("INNf")) {
1253
- IndexNNDescent* idxnnd = new IndexNNDescentFlat();
1254
- read_index_header(idxnnd, f);
1255
- read_NNDescent(&idxnnd->nndescent, f);
1491
+ auto idxnnd = std::make_unique<IndexNNDescentFlat>();
1492
+ read_index_header(*idxnnd, f);
1493
+ read_NNDescent(idxnnd->nndescent, f);
1256
1494
  idxnnd->storage = read_index(f, io_flags);
1257
1495
  idxnnd->own_fields = true;
1258
- idx = idxnnd;
1496
+ idx = std::move(idxnnd);
1259
1497
  } else if (h == fourcc("IPfs")) {
1260
- IndexPQFastScan* idxpqfs = new IndexPQFastScan();
1261
- read_index_header(idxpqfs, f);
1498
+ auto idxpqfs = std::make_unique<IndexPQFastScan>();
1499
+ read_index_header(*idxpqfs, f);
1262
1500
  read_ProductQuantizer(&idxpqfs->pq, f);
1263
1501
  READ1(idxpqfs->implem);
1264
1502
  READ1(idxpqfs->bbs);
@@ -1273,11 +1511,11 @@ Index* read_index(IOReader* f, int io_flags) {
1273
1511
  idxpqfs->ksub = (1 << pq.nbits);
1274
1512
  idxpqfs->code_size = pq.code_size;
1275
1513
 
1276
- idx = idxpqfs;
1514
+ idx = std::move(idxpqfs);
1277
1515
 
1278
1516
  } else if (h == fourcc("IwPf")) {
1279
- IndexIVFPQFastScan* ivpq = new IndexIVFPQFastScan();
1280
- read_ivf_header(ivpq, f);
1517
+ auto ivpq = std::make_unique<IndexIVFPQFastScan>();
1518
+ read_ivf_header(ivpq.get(), f);
1281
1519
  READ1(ivpq->by_residual);
1282
1520
  READ1(ivpq->code_size);
1283
1521
  READ1(ivpq->bbs);
@@ -1285,7 +1523,7 @@ Index* read_index(IOReader* f, int io_flags) {
1285
1523
  READ1(ivpq->implem);
1286
1524
  READ1(ivpq->qbs2);
1287
1525
  read_ProductQuantizer(&ivpq->pq, f);
1288
- read_InvertedLists(ivpq, f, io_flags);
1526
+ read_InvertedLists(*ivpq, f, io_flags);
1289
1527
  ivpq->precompute_table();
1290
1528
 
1291
1529
  const auto& pq = ivpq->pq;
@@ -1295,30 +1533,38 @@ Index* read_index(IOReader* f, int io_flags) {
1295
1533
  ivpq->code_size = pq.code_size;
1296
1534
  ivpq->init_code_packer();
1297
1535
 
1298
- idx = ivpq;
1536
+ idx = std::move(ivpq);
1299
1537
  } else if (h == fourcc("IRMf")) {
1300
- IndexRowwiseMinMax* imm = new IndexRowwiseMinMax();
1301
- read_index_header(imm, f);
1538
+ auto imm = std::make_unique<IndexRowwiseMinMax>();
1539
+ read_index_header(*imm, f);
1302
1540
 
1303
1541
  imm->index = read_index(f, io_flags);
1304
1542
  imm->own_fields = true;
1305
1543
 
1306
- idx = imm;
1544
+ idx = std::move(imm);
1307
1545
  } else if (h == fourcc("IRMh")) {
1308
- IndexRowwiseMinMaxFP16* imm = new IndexRowwiseMinMaxFP16();
1309
- read_index_header(imm, f);
1546
+ auto imm = std::make_unique<IndexRowwiseMinMaxFP16>();
1547
+ read_index_header(*imm, f);
1310
1548
 
1311
1549
  imm->index = read_index(f, io_flags);
1312
1550
  imm->own_fields = true;
1313
1551
 
1314
- idx = imm;
1315
- } else if (h == fourcc("Irfs")) {
1316
- IndexRaBitQFastScan* idxqfs = new IndexRaBitQFastScan();
1317
- read_index_header(idxqfs, f);
1318
- read_RaBitQuantizer(&idxqfs->rabitq, f, true);
1552
+ idx = std::move(imm);
1553
+ } else if (h == fourcc("Irfn") || h == fourcc("Irfs")) {
1554
+ // Irfn = new format (aux data embedded in SIMD blocks)
1555
+ // Irfs = legacy format (flat_storage separate, needs migration)
1556
+ const bool is_legacy = (h == fourcc("Irfs"));
1557
+
1558
+ auto idxqfs = std::make_unique<IndexRaBitQFastScan>();
1559
+ read_index_header(*idxqfs, f);
1560
+ read_RaBitQuantizer(idxqfs->rabitq, f, true);
1319
1561
  READVECTOR(idxqfs->center);
1320
1562
  READ1(idxqfs->qb);
1321
- READVECTOR(idxqfs->flat_storage);
1563
+
1564
+ std::vector<uint8_t> legacy_flat_storage;
1565
+ if (is_legacy) {
1566
+ READVECTOR(legacy_flat_storage);
1567
+ }
1322
1568
 
1323
1569
  READ1(idxqfs->bbs);
1324
1570
  READ1(idxqfs->ntotal2);
@@ -1332,33 +1578,55 @@ Index* read_index(IOReader* f, int io_flags) {
1332
1578
  idxqfs->ksub = (1 << nbits_fastscan);
1333
1579
 
1334
1580
  READVECTOR(idxqfs->codes);
1335
- idx = idxqfs;
1581
+
1582
+ if (is_legacy) {
1583
+ const size_t storage_size =
1584
+ rabitq_utils::compute_per_vector_storage_size(
1585
+ idxqfs->rabitq.nb_bits, idxqfs->d);
1586
+
1587
+ FAISS_THROW_IF_NOT_MSG(
1588
+ legacy_flat_storage.size() ==
1589
+ static_cast<size_t>(idxqfs->ntotal) * storage_size,
1590
+ "legacy flat_storage size mismatch during migration");
1591
+
1592
+ rabitq_utils::populate_block_aux_from_flat_storage(
1593
+ legacy_flat_storage,
1594
+ idxqfs->codes,
1595
+ static_cast<size_t>(idxqfs->ntotal),
1596
+ idxqfs->bbs,
1597
+ idxqfs->M2,
1598
+ ((idxqfs->M2 + 1) / 2) * idxqfs->bbs,
1599
+ idxqfs->get_block_stride(),
1600
+ storage_size);
1601
+ }
1602
+
1603
+ idx = std::move(idxqfs);
1336
1604
  } else if (h == fourcc("Ixrq")) {
1337
- IndexRaBitQ* idxq = new IndexRaBitQ();
1338
- read_index_header(idxq, f);
1339
- read_RaBitQuantizer(&idxq->rabitq, f, false);
1605
+ auto idxq = std::make_unique<IndexRaBitQ>();
1606
+ read_index_header(*idxq, f);
1607
+ read_RaBitQuantizer(idxq->rabitq, f, false);
1340
1608
  READVECTOR(idxq->codes);
1341
1609
  READVECTOR(idxq->center);
1342
1610
  READ1(idxq->qb);
1343
1611
 
1344
1612
  // rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
1345
1613
  idxq->code_size = idxq->rabitq.code_size;
1346
- idx = idxq;
1614
+ idx = std::move(idxq);
1347
1615
  } else if (h == fourcc("Ixrr")) {
1348
1616
  // Ixrr = multi-bit format (new)
1349
- IndexRaBitQ* idxq = new IndexRaBitQ();
1350
- read_index_header(idxq, f);
1351
- read_RaBitQuantizer(&idxq->rabitq, f, true); // Reads nb_bits from file
1617
+ auto idxq = std::make_unique<IndexRaBitQ>();
1618
+ read_index_header(*idxq, f);
1619
+ read_RaBitQuantizer(idxq->rabitq, f, true); // Reads nb_bits from file
1352
1620
  READVECTOR(idxq->codes);
1353
1621
  READVECTOR(idxq->center);
1354
1622
  READ1(idxq->qb);
1355
1623
 
1356
1624
  idxq->code_size = idxq->rabitq.code_size;
1357
- idx = idxq;
1625
+ idx = std::move(idxq);
1358
1626
  } else if (h == fourcc("Iwrq")) {
1359
- IndexIVFRaBitQ* ivrq = new IndexIVFRaBitQ();
1360
- read_ivf_header(ivrq, f);
1361
- read_RaBitQuantizer(&ivrq->rabitq, f, false);
1627
+ auto ivrq = std::make_unique<IndexIVFRaBitQ>();
1628
+ read_ivf_header(ivrq.get(), f);
1629
+ read_RaBitQuantizer(ivrq->rabitq, f, false);
1362
1630
  READ1(ivrq->code_size);
1363
1631
  READ1(ivrq->by_residual);
1364
1632
  READ1(ivrq->qb);
@@ -1368,13 +1636,13 @@ Index* read_index(IOReader* f, int io_flags) {
1368
1636
  ivrq->rabitq.code_size =
1369
1637
  ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
1370
1638
  ivrq->code_size = ivrq->rabitq.code_size;
1371
- read_InvertedLists(ivrq, f, io_flags);
1372
- idx = ivrq;
1639
+ read_InvertedLists(*ivrq, f, io_flags);
1640
+ idx = std::move(ivrq);
1373
1641
  } else if (h == fourcc("Iwrr")) {
1374
1642
  // Iwrr = multi-bit format (new)
1375
- IndexIVFRaBitQ* ivrq = new IndexIVFRaBitQ();
1376
- read_ivf_header(ivrq, f);
1377
- read_RaBitQuantizer(&ivrq->rabitq, f, true); // Reads nb_bits from file
1643
+ auto ivrq = std::make_unique<IndexIVFRaBitQ>();
1644
+ read_ivf_header(ivrq.get(), f);
1645
+ read_RaBitQuantizer(ivrq->rabitq, f, true); // Reads nb_bits from file
1378
1646
  READ1(ivrq->code_size);
1379
1647
  READ1(ivrq->by_residual);
1380
1648
  READ1(ivrq->qb);
@@ -1383,22 +1651,22 @@ Index* read_index(IOReader* f, int io_flags) {
1383
1651
  ivrq->rabitq.code_size =
1384
1652
  ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
1385
1653
  ivrq->code_size = ivrq->rabitq.code_size;
1386
- read_InvertedLists(ivrq, f, io_flags);
1387
- idx = ivrq;
1654
+ read_InvertedLists(*ivrq, f, io_flags);
1655
+ idx = std::move(ivrq);
1388
1656
  }
1389
1657
  #ifdef FAISS_ENABLE_SVS
1390
1658
  else if (
1391
1659
  h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD")) {
1392
- IndexSVSVamana* svs;
1660
+ std::unique_ptr<IndexSVSVamana> svs;
1393
1661
  if (h == fourcc("ILVQ")) {
1394
- svs = new IndexSVSVamanaLVQ();
1662
+ svs = std::make_unique<IndexSVSVamanaLVQ>();
1395
1663
  } else if (h == fourcc("ISVL")) {
1396
- svs = new IndexSVSVamanaLeanVec();
1664
+ svs = std::make_unique<IndexSVSVamanaLeanVec>();
1397
1665
  } else if (h == fourcc("ISVD")) {
1398
- svs = new IndexSVSVamana();
1666
+ svs = std::make_unique<IndexSVSVamana>();
1399
1667
  }
1400
1668
 
1401
- read_index_header(svs, f);
1669
+ read_index_header(*svs, f);
1402
1670
  READ1(svs->graph_max_degree);
1403
1671
  READ1(svs->alpha);
1404
1672
  READ1(svs->search_window_size);
@@ -1409,7 +1677,7 @@ Index* read_index(IOReader* f, int io_flags) {
1409
1677
  READ1(svs->use_full_search_history);
1410
1678
  READ1(svs->storage_kind);
1411
1679
  if (h == fourcc("ISVL")) {
1412
- READ1(dynamic_cast<IndexSVSVamanaLeanVec*>(svs)->leanvec_d);
1680
+ READ1(dynamic_cast<IndexSVSVamanaLeanVec*>(svs.get())->leanvec_d);
1413
1681
  }
1414
1682
 
1415
1683
  bool initialized;
@@ -1425,14 +1693,14 @@ Index* read_index(IOReader* f, int io_flags) {
1425
1693
  if (trained) {
1426
1694
  faiss::svs_io::ReaderStreambuf rbuf(f);
1427
1695
  std::istream is(&rbuf);
1428
- dynamic_cast<IndexSVSVamanaLeanVec*>(svs)
1696
+ dynamic_cast<IndexSVSVamanaLeanVec*>(svs.get())
1429
1697
  ->deserialize_training_data(is);
1430
1698
  }
1431
1699
  }
1432
- idx = svs;
1700
+ idx = std::move(svs);
1433
1701
  } else if (h == fourcc("ISVF")) {
1434
- IndexSVSFlat* svs = new IndexSVSFlat();
1435
- read_index_header(svs, f);
1702
+ auto svs = std::make_unique<IndexSVSFlat>();
1703
+ read_index_header(*svs, f);
1436
1704
 
1437
1705
  bool initialized;
1438
1706
  READ1(initialized);
@@ -1440,14 +1708,18 @@ Index* read_index(IOReader* f, int io_flags) {
1440
1708
  faiss::svs_io::ReaderStreambuf rbuf(f);
1441
1709
  std::istream is(&rbuf);
1442
1710
  svs->deserialize_impl(is);
1443
- idx = svs;
1444
1711
  }
1712
+ idx = std::move(svs);
1445
1713
  }
1446
1714
  #endif // FAISS_ENABLE_SVS
1447
- else if (h == fourcc("Iwrf")) {
1448
- IndexIVFRaBitQFastScan* ivrqfs = new IndexIVFRaBitQFastScan();
1449
- read_ivf_header(ivrqfs, f);
1450
- read_RaBitQuantizer(&ivrqfs->rabitq, f);
1715
+ else if (h == fourcc("Iwrn") || h == fourcc("Iwrf")) {
1716
+ // Iwrn = new format (aux data embedded in SIMD blocks)
1717
+ // Iwrf = legacy format (flat_storage separate, needs migration)
1718
+ const bool is_legacy = (h == fourcc("Iwrf"));
1719
+
1720
+ auto ivrqfs = std::make_unique<IndexIVFRaBitQFastScan>();
1721
+ read_ivf_header(ivrqfs.get(), f);
1722
+ read_RaBitQuantizer(ivrqfs->rabitq, f);
1451
1723
  READ1(ivrqfs->by_residual);
1452
1724
  READ1(ivrqfs->code_size);
1453
1725
  READ1(ivrqfs->bbs);
@@ -1456,7 +1728,11 @@ Index* read_index(IOReader* f, int io_flags) {
1456
1728
  READ1(ivrqfs->implem);
1457
1729
  READ1(ivrqfs->qb);
1458
1730
  READ1(ivrqfs->centered);
1459
- READVECTOR(ivrqfs->flat_storage);
1731
+
1732
+ std::vector<uint8_t> legacy_flat_storage;
1733
+ if (is_legacy) {
1734
+ READVECTOR(legacy_flat_storage);
1735
+ }
1460
1736
 
1461
1737
  // Initialize FastScan base class fields
1462
1738
  const size_t M_fastscan = (ivrqfs->d + 3) / 4;
@@ -1465,87 +1741,140 @@ Index* read_index(IOReader* f, int io_flags) {
1465
1741
  ivrqfs->nbits = nbits_fastscan;
1466
1742
  ivrqfs->ksub = (1 << nbits_fastscan);
1467
1743
 
1468
- read_InvertedLists(ivrqfs, f, io_flags);
1744
+ read_InvertedLists(*ivrqfs, f, io_flags);
1469
1745
  ivrqfs->init_code_packer();
1470
- idx = ivrqfs;
1746
+
1747
+ if (is_legacy) {
1748
+ auto* bil = dynamic_cast<BlockInvertedLists*>(ivrqfs->invlists);
1749
+ FAISS_THROW_IF_NOT(bil);
1750
+
1751
+ const size_t storage_size =
1752
+ rabitq_utils::compute_per_vector_storage_size(
1753
+ ivrqfs->rabitq.nb_bits, ivrqfs->d);
1754
+ const size_t new_block_stride = ivrqfs->get_block_stride();
1755
+
1756
+ for (size_t list_no = 0; list_no < ivrqfs->nlist; list_no++) {
1757
+ if (bil->list_size(list_no) == 0) {
1758
+ continue;
1759
+ }
1760
+ rabitq_utils::populate_block_aux_from_flat_storage(
1761
+ legacy_flat_storage,
1762
+ bil->codes[list_no],
1763
+ bil->list_size(list_no),
1764
+ ivrqfs->bbs,
1765
+ ivrqfs->M2,
1766
+ bil->block_size,
1767
+ new_block_stride,
1768
+ storage_size,
1769
+ bil->ids[list_no].data());
1770
+ }
1771
+
1772
+ if (bil->block_size < new_block_stride) {
1773
+ bil->block_size = new_block_stride;
1774
+ }
1775
+ }
1776
+
1777
+ idx = std::move(ivrqfs);
1471
1778
  } else {
1472
1779
  FAISS_THROW_FMT(
1473
1780
  "Index type 0x%08x (\"%s\") not recognized",
1474
1781
  h,
1475
1782
  fourcc_inv_printable(h).c_str());
1476
- idx = nullptr;
1783
+ idx.reset();
1477
1784
  }
1478
1785
  return idx;
1479
1786
  }
1480
1787
 
1481
- Index* read_index(FILE* f, int io_flags) {
1788
+ Index* read_index(IOReader* f, int io_flags) {
1789
+ return read_index_up(f, io_flags).release();
1790
+ }
1791
+
1792
+ std::unique_ptr<Index> read_index_up(FILE* f, int io_flags) {
1482
1793
  if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
1483
1794
  // enable mmap-supporting IOReader
1484
1795
  auto owner = std::make_shared<MmappedFileMappingOwner>(f);
1485
1796
  MappedFileIOReader reader(owner);
1486
- return read_index(&reader, io_flags);
1797
+ return read_index_up(&reader, io_flags);
1487
1798
  } else {
1488
1799
  FileIOReader reader(f);
1489
- return read_index(&reader, io_flags);
1800
+ return read_index_up(&reader, io_flags);
1490
1801
  }
1491
1802
  }
1492
1803
 
1493
- Index* read_index(const char* fname, int io_flags) {
1804
+ Index* read_index(FILE* f, int io_flags) {
1805
+ return read_index_up(f, io_flags).release();
1806
+ }
1807
+
1808
+ std::unique_ptr<Index> read_index_up(const char* fname, int io_flags) {
1494
1809
  if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
1495
1810
  // enable mmap-supporting IOReader
1496
1811
  auto owner = std::make_shared<MmappedFileMappingOwner>(fname);
1497
1812
  MappedFileIOReader reader(owner);
1498
- return read_index(&reader, io_flags);
1813
+ return read_index_up(&reader, io_flags);
1499
1814
  } else {
1500
1815
  FileIOReader reader(fname);
1501
- Index* idx = read_index(&reader, io_flags);
1502
- return idx;
1816
+ return read_index_up(&reader, io_flags);
1503
1817
  }
1504
1818
  }
1505
1819
 
1506
- VectorTransform* read_VectorTransform(const char* fname) {
1820
+ Index* read_index(const char* fname, int io_flags) {
1821
+ return read_index_up(fname, io_flags).release();
1822
+ }
1823
+
1824
+ std::unique_ptr<VectorTransform> read_VectorTransform_up(const char* fname) {
1507
1825
  FileIOReader reader(fname);
1508
- VectorTransform* vt = read_VectorTransform(&reader);
1509
- return vt;
1826
+ return read_VectorTransform_up(&reader);
1827
+ }
1828
+
1829
+ VectorTransform* read_VectorTransform(const char* fname) {
1830
+ return read_VectorTransform_up(fname).release();
1510
1831
  }
1511
1832
 
1512
1833
  /*************************************************************
1513
1834
  * Read binary indexes
1514
1835
  **************************************************************/
1515
1836
 
1516
- static void read_InvertedLists(IndexBinaryIVF* ivf, IOReader* f, int io_flags) {
1517
- InvertedLists* ils = read_InvertedLists(f, io_flags);
1837
+ static void read_InvertedLists(IndexBinaryIVF& ivf, IOReader* f, int io_flags) {
1838
+ auto ils = read_InvertedLists_up(f, io_flags);
1518
1839
  FAISS_THROW_IF_NOT(
1519
1840
  !ils ||
1520
- (ils->nlist == ivf->nlist && ils->code_size == ivf->code_size));
1521
- ivf->invlists = ils;
1522
- ivf->own_invlists = true;
1841
+ (ils->nlist == ivf.nlist && ils->code_size == ivf.code_size));
1842
+ ivf.invlists = ils.release();
1843
+ ivf.own_invlists = true;
1523
1844
  }
1524
1845
 
1525
- static void read_index_binary_header(IndexBinary* idx, IOReader* f) {
1526
- READ1(idx->d);
1527
- READ1(idx->code_size);
1528
- READ1(idx->ntotal);
1529
- READ1(idx->is_trained);
1530
- READ1(idx->metric_type);
1531
- idx->verbose = false;
1846
+ static void read_index_binary_header(IndexBinary& idx, IOReader* f) {
1847
+ READ1(idx.d);
1848
+ READ1(idx.code_size);
1849
+ READ1(idx.ntotal);
1850
+ READ1(idx.is_trained);
1851
+ int metric_type_int;
1852
+ READ1(metric_type_int);
1853
+ idx.metric_type = metric_type_from_int(metric_type_int);
1854
+ FAISS_THROW_IF_NOT_FMT(
1855
+ idx.d >= 0, "invalid binary index dimension %d", idx.d);
1856
+ FAISS_THROW_IF_NOT_FMT(
1857
+ idx.ntotal >= 0,
1858
+ "invalid binary index ntotal %" PRId64,
1859
+ (int64_t)idx.ntotal);
1860
+ idx.verbose = false;
1532
1861
  }
1533
1862
 
1534
1863
  static void read_binary_ivf_header(
1535
- IndexBinaryIVF* ivf,
1864
+ IndexBinaryIVF& ivf,
1536
1865
  IOReader* f,
1537
1866
  std::vector<std::vector<idx_t>>* ids = nullptr) {
1538
1867
  read_index_binary_header(ivf, f);
1539
- READ1(ivf->nlist);
1540
- READ1(ivf->nprobe);
1541
- ivf->quantizer = read_index_binary(f);
1542
- ivf->own_fields = true;
1868
+ READ1(ivf.nlist);
1869
+ READ1(ivf.nprobe);
1870
+ ivf.quantizer = read_index_binary(f);
1871
+ ivf.own_fields = true;
1543
1872
  if (ids) { // used in legacy "Iv" formats
1544
- ids->resize(ivf->nlist);
1545
- for (size_t i = 0; i < ivf->nlist; i++)
1873
+ ids->resize(ivf.nlist);
1874
+ for (size_t i = 0; i < ivf.nlist; i++)
1546
1875
  READVECTOR((*ids)[i]);
1547
1876
  }
1548
- read_direct_map(&ivf->direct_map, f);
1877
+ read_direct_map(&ivf.direct_map, f);
1549
1878
  }
1550
1879
 
1551
1880
  static void read_binary_hash_invlists(
@@ -1557,8 +1886,20 @@ static void read_binary_hash_invlists(
1557
1886
  int il_nbit = 0;
1558
1887
  READ1(il_nbit);
1559
1888
  // buffer for bitstrings
1560
- std::vector<uint8_t> buf((b + il_nbit) * sz);
1889
+ size_t bits_per_entry = (size_t)b + (size_t)il_nbit;
1890
+ size_t total_bits =
1891
+ mul_no_overflow(bits_per_entry, sz, "binary hash invlists");
1892
+ size_t needed_bytes = (total_bits + 7) / 8;
1893
+ std::vector<uint8_t> buf;
1561
1894
  READVECTOR(buf);
1895
+ FAISS_THROW_IF_NOT_FMT(
1896
+ buf.size() >= needed_bytes,
1897
+ "binary hash invlists: buffer size %zd < needed %zd bytes "
1898
+ "for %zd entries of %zd bits each",
1899
+ buf.size(),
1900
+ needed_bytes,
1901
+ sz,
1902
+ bits_per_entry);
1562
1903
  BitstringReader rd(buf.data(), buf.size());
1563
1904
  invlists.reserve(sz);
1564
1905
  for (size_t i = 0; i < sz; i++) {
@@ -1582,7 +1923,10 @@ static void read_binary_multi_hash_map(
1582
1923
  READ1(sz);
1583
1924
  std::vector<uint8_t> buf;
1584
1925
  READVECTOR(buf);
1585
- size_t nbit = (b + id_bits) * sz + ntotal * id_bits;
1926
+ size_t nbit = add_no_overflow(
1927
+ mul_no_overflow((size_t)(b + id_bits), sz, "multi hash map"),
1928
+ mul_no_overflow(ntotal, (size_t)id_bits, "multi hash map"),
1929
+ "multi hash map total bits");
1586
1930
  FAISS_THROW_IF_NOT(buf.size() == (nbit + 7) / 8);
1587
1931
  BitstringReader rd(buf.data(), buf.size());
1588
1932
  map.reserve(sz);
@@ -1596,72 +1940,74 @@ static void read_binary_multi_hash_map(
1596
1940
  }
1597
1941
  }
1598
1942
 
1599
- IndexBinary* read_index_binary(IOReader* f, int io_flags) {
1600
- IndexBinary* idx = nullptr;
1943
+ std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
1944
+ std::unique_ptr<IndexBinary> idx;
1601
1945
  uint32_t h;
1602
1946
  READ1(h);
1603
1947
  if (h == fourcc("IBxF")) {
1604
- IndexBinaryFlat* idxf = new IndexBinaryFlat();
1605
- read_index_binary_header(idxf, f);
1948
+ auto idxf = std::make_unique<IndexBinaryFlat>();
1949
+ read_index_binary_header(*idxf, f);
1606
1950
  read_vector(idxf->xb, f);
1607
1951
  FAISS_THROW_IF_NOT(idxf->xb.size() == idxf->ntotal * idxf->code_size);
1608
- // leak!
1609
- idx = idxf;
1952
+ idx = std::move(idxf);
1610
1953
  } else if (h == fourcc("IBwF")) {
1611
- IndexBinaryIVF* ivf = new IndexBinaryIVF();
1612
- read_binary_ivf_header(ivf, f);
1613
- read_InvertedLists(ivf, f, io_flags);
1614
- idx = ivf;
1954
+ auto ivf = std::make_unique<IndexBinaryIVF>();
1955
+ read_binary_ivf_header(*ivf, f);
1956
+ read_InvertedLists(*ivf, f, io_flags);
1957
+ idx = std::move(ivf);
1615
1958
  } else if (h == fourcc("IBFf")) {
1616
- IndexBinaryFromFloat* idxff = new IndexBinaryFromFloat();
1617
- read_index_binary_header(idxff, f);
1959
+ auto idxff = std::make_unique<IndexBinaryFromFloat>();
1960
+ read_index_binary_header(*idxff, f);
1618
1961
  idxff->own_fields = true;
1619
1962
  idxff->index = read_index(f, io_flags);
1620
- idx = idxff;
1963
+ idx = std::move(idxff);
1621
1964
  } else if (h == fourcc("IBHf")) {
1622
- IndexBinaryHNSW* idxhnsw = new IndexBinaryHNSW();
1623
- read_index_binary_header(idxhnsw, f);
1624
- read_HNSW(&idxhnsw->hnsw, f);
1965
+ auto idxhnsw = std::make_unique<IndexBinaryHNSW>();
1966
+ read_index_binary_header(*idxhnsw, f);
1967
+ read_HNSW(idxhnsw->hnsw, f);
1625
1968
  idxhnsw->hnsw.is_panorama = false;
1626
1969
  idxhnsw->storage = read_index_binary(f, io_flags);
1627
1970
  idxhnsw->own_fields = true;
1628
- idx = idxhnsw;
1971
+ idx = std::move(idxhnsw);
1629
1972
  } else if (h == fourcc("IBHc")) {
1630
- IndexBinaryHNSWCagra* idxhnsw = new IndexBinaryHNSWCagra();
1631
- read_index_binary_header(idxhnsw, f);
1973
+ auto idxhnsw = std::make_unique<IndexBinaryHNSWCagra>();
1974
+ read_index_binary_header(*idxhnsw, f);
1632
1975
  READ1(idxhnsw->keep_max_size_level0);
1633
1976
  READ1(idxhnsw->base_level_only);
1634
1977
  READ1(idxhnsw->num_base_level_search_entrypoints);
1635
- read_HNSW(&idxhnsw->hnsw, f);
1978
+ read_HNSW(idxhnsw->hnsw, f);
1636
1979
  idxhnsw->hnsw.is_panorama = false;
1637
1980
  idxhnsw->storage = read_index_binary(f, io_flags);
1638
1981
  idxhnsw->own_fields = true;
1639
- idx = idxhnsw;
1982
+ idx = std::move(idxhnsw);
1640
1983
  } else if (h == fourcc("IBMp") || h == fourcc("IBM2")) {
1641
1984
  bool is_map2 = h == fourcc("IBM2");
1642
- IndexBinaryIDMap* idxmap =
1643
- is_map2 ? new IndexBinaryIDMap2() : new IndexBinaryIDMap();
1644
- read_index_binary_header(idxmap, f);
1985
+ std::unique_ptr<IndexBinaryIDMap> idxmap = is_map2
1986
+ ? std::make_unique<IndexBinaryIDMap2>()
1987
+ : std::make_unique<IndexBinaryIDMap>();
1988
+ read_index_binary_header(*idxmap, f);
1645
1989
  idxmap->index = read_index_binary(f, io_flags);
1646
1990
  idxmap->own_fields = true;
1647
1991
  READVECTOR(idxmap->id_map);
1648
1992
  if (is_map2) {
1649
- static_cast<IndexBinaryIDMap2*>(idxmap)->construct_rev_map();
1993
+ static_cast<IndexBinaryIDMap2*>(idxmap.get())->construct_rev_map();
1650
1994
  }
1651
- idx = idxmap;
1995
+ idx = std::move(idxmap);
1652
1996
  } else if (h == fourcc("IBHh")) {
1653
- IndexBinaryHash* idxh = new IndexBinaryHash();
1654
- read_index_binary_header(idxh, f);
1997
+ auto idxh = std::make_unique<IndexBinaryHash>();
1998
+ read_index_binary_header(*idxh, f);
1655
1999
  READ1(idxh->b);
1656
2000
  READ1(idxh->nflip);
1657
2001
  read_binary_hash_invlists(idxh->invlists, idxh->b, f);
1658
- idx = idxh;
2002
+ idx = std::move(idxh);
1659
2003
  } else if (h == fourcc("IBHm")) {
1660
- IndexBinaryMultiHash* idxmh = new IndexBinaryMultiHash();
1661
- read_index_binary_header(idxmh, f);
1662
- idxmh->storage = dynamic_cast<IndexBinaryFlat*>(read_index_binary(f));
1663
- FAISS_THROW_IF_NOT(
1664
- idxmh->storage && idxmh->storage->ntotal == idxmh->ntotal);
2004
+ auto idxmh = std::make_unique<IndexBinaryMultiHash>();
2005
+ read_index_binary_header(*idxmh, f);
2006
+ auto storage_idx = read_index_binary_up(f);
2007
+ auto* flat_ptr = dynamic_cast<IndexBinaryFlat*>(storage_idx.get());
2008
+ FAISS_THROW_IF_NOT(flat_ptr && flat_ptr->ntotal == idxmh->ntotal);
2009
+ idxmh->storage = flat_ptr;
2010
+ storage_idx.release();
1665
2011
  idxmh->own_fields = true;
1666
2012
  READ1(idxmh->b);
1667
2013
  READ1(idxmh->nhash);
@@ -1671,40 +2017,52 @@ IndexBinary* read_index_binary(IOReader* f, int io_flags) {
1671
2017
  read_binary_multi_hash_map(
1672
2018
  idxmh->maps[i], idxmh->b, idxmh->ntotal, f);
1673
2019
  }
1674
- idx = idxmh;
2020
+ idx = std::move(idxmh);
1675
2021
  } else {
1676
2022
  FAISS_THROW_FMT(
1677
2023
  "Index type %08x (\"%s\") not recognized",
1678
2024
  h,
1679
2025
  fourcc_inv_printable(h).c_str());
1680
- idx = nullptr;
1681
2026
  }
1682
2027
  return idx;
1683
2028
  }
1684
2029
 
1685
- IndexBinary* read_index_binary(FILE* f, int io_flags) {
2030
+ IndexBinary* read_index_binary(IOReader* f, int io_flags) {
2031
+ return read_index_binary_up(f, io_flags).release();
2032
+ }
2033
+
2034
+ std::unique_ptr<IndexBinary> read_index_binary_up(FILE* f, int io_flags) {
1686
2035
  if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
1687
2036
  // enable mmap-supporting IOReader
1688
2037
  auto owner = std::make_shared<MmappedFileMappingOwner>(f);
1689
2038
  MappedFileIOReader reader(owner);
1690
- return read_index_binary(&reader, io_flags);
2039
+ return read_index_binary_up(&reader, io_flags);
1691
2040
  } else {
1692
2041
  FileIOReader reader(f);
1693
- return read_index_binary(&reader, io_flags);
2042
+ return read_index_binary_up(&reader, io_flags);
1694
2043
  }
1695
2044
  }
1696
2045
 
1697
- IndexBinary* read_index_binary(const char* fname, int io_flags) {
2046
+ IndexBinary* read_index_binary(FILE* f, int io_flags) {
2047
+ return read_index_binary_up(f, io_flags).release();
2048
+ }
2049
+
2050
+ std::unique_ptr<IndexBinary> read_index_binary_up(
2051
+ const char* fname,
2052
+ int io_flags) {
1698
2053
  if ((io_flags & IO_FLAG_MMAP_IFC) == IO_FLAG_MMAP_IFC) {
1699
2054
  // enable mmap-supporting IOReader
1700
2055
  auto owner = std::make_shared<MmappedFileMappingOwner>(fname);
1701
2056
  MappedFileIOReader reader(owner);
1702
- return read_index_binary(&reader, io_flags);
2057
+ return read_index_binary_up(&reader, io_flags);
1703
2058
  } else {
1704
2059
  FileIOReader reader(fname);
1705
- IndexBinary* idx = read_index_binary(&reader, io_flags);
1706
- return idx;
2060
+ return read_index_binary_up(&reader, io_flags);
1707
2061
  }
1708
2062
  }
1709
2063
 
2064
+ IndexBinary* read_index_binary(const char* fname, int io_flags) {
2065
+ return read_index_binary_up(fname, io_flags).release();
2066
+ }
2067
+
1710
2068
  } // namespace faiss