usearch 0.10.0 → 0.11.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.
- package/.github/workflows/release.yml +1 -1
- package/.gitmodules +3 -0
- package/.vscode/settings.json +2 -0
- package/Cargo.toml +1 -1
- package/Package.swift +1 -0
- package/README.md +1 -1
- package/VERSION +1 -1
- package/binding.gyp +4 -3
- package/build.gradle +1 -1
- package/build.rs +1 -0
- package/conanfile.py +1 -1
- package/docs/compilation.md +6 -3
- package/include/usearch/usearch.hpp +133 -43
- package/package.json +1 -1
- package/python/lib.cpp +36 -24
- package/python/scripts/bench.py +2 -2
- package/python/scripts/test.py +1 -1
- package/python/usearch/index.py +298 -0
- package/robin-map/.codecov.yml +5 -0
- package/robin-map/.github/workflows/ci.yml +136 -0
- package/robin-map/CMakeLists.txt +86 -0
- package/robin-map/LICENSE +21 -0
- package/robin-map/README.md +483 -0
- package/robin-map/cmake/tsl-robin-mapConfig.cmake.in +9 -0
- package/robin-map/doxygen.conf +2483 -0
- package/robin-map/include/tsl/robin_growth_policy.h +406 -0
- package/robin-map/include/tsl/robin_hash.h +1639 -0
- package/robin-map/include/tsl/robin_map.h +807 -0
- package/robin-map/include/tsl/robin_set.h +660 -0
- package/robin-map/tests/CMakeLists.txt +26 -0
- package/robin-map/tests/custom_allocator_tests.cpp +138 -0
- package/robin-map/tests/main.cpp +26 -0
- package/robin-map/tests/policy_tests.cpp +97 -0
- package/robin-map/tests/robin_map_tests.cpp +1451 -0
- package/robin-map/tests/robin_set_tests.cpp +164 -0
- package/robin-map/tests/utils.h +443 -0
- package/robin-map/tsl-robin-map.natvis +78 -0
- package/setup.py +4 -2
- package/src/CMakeLists.txt +1 -0
- package/src/punned.hpp +50 -6
- package/src/test.cpp +29 -8
package/.gitmodules
CHANGED
package/.vscode/settings.json
CHANGED
|
@@ -125,10 +125,12 @@
|
|
|
125
125
|
"OPENMP",
|
|
126
126
|
"preprocess",
|
|
127
127
|
"Quickstart",
|
|
128
|
+
"rtype",
|
|
128
129
|
"simsimd",
|
|
129
130
|
"SLOC",
|
|
130
131
|
"usearch",
|
|
131
132
|
"usecases"
|
|
132
133
|
],
|
|
134
|
+
"autoDocstring.docstringFormat": "sphinx",
|
|
133
135
|
"java.configuration.updateBuildConfiguration": "interactive"
|
|
134
136
|
}
|
package/Cargo.toml
CHANGED
package/Package.swift
CHANGED
package/README.md
CHANGED
|
@@ -308,7 +308,7 @@ def python_dot(a, b, n, m):
|
|
|
308
308
|
c += a_array[i] * b_array[i]
|
|
309
309
|
return c
|
|
310
310
|
|
|
311
|
-
index = Index(ndim=ndim,
|
|
311
|
+
index = Index(ndim=ndim, metric=python_dot.address)
|
|
312
312
|
```
|
|
313
313
|
|
|
314
314
|
[numba]: https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.11.0
|
package/binding.gyp
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"include",
|
|
11
11
|
"src",
|
|
12
12
|
"fp16/include",
|
|
13
|
+
"robin-map/include",
|
|
13
14
|
"simsimd/include"
|
|
14
15
|
],
|
|
15
16
|
"dependencies": [
|
|
@@ -23,9 +24,9 @@
|
|
|
23
24
|
"MACOSX_DEPLOYMENT_TARGET": "10.15"
|
|
24
25
|
},
|
|
25
26
|
"msvs_settings": {
|
|
26
|
-
"VCCLCompilerTool": {
|
|
27
|
-
"ExceptionHandling": 1,
|
|
28
|
-
"AdditionalOptions": [
|
|
27
|
+
"VCCLCompilerTool": {
|
|
28
|
+
"ExceptionHandling": 1,
|
|
29
|
+
"AdditionalOptions": ["-std:c++11"]
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
}
|
package/build.gradle
CHANGED
|
@@ -45,7 +45,7 @@ model {
|
|
|
45
45
|
include "**/*.cpp"
|
|
46
46
|
}
|
|
47
47
|
exportedHeaders {
|
|
48
|
-
srcDirs "include", "src", "fp16/include", "simsimd/include", "${Jvm.current().javaHome}/include"
|
|
48
|
+
srcDirs "include", "src", "fp16/include", "robin-map/include", "simsimd/include", "${Jvm.current().javaHome}/include"
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
package/build.rs
CHANGED
package/conanfile.py
CHANGED
|
@@ -4,7 +4,7 @@ from conans import ConanFile
|
|
|
4
4
|
class USearchConan(ConanFile):
|
|
5
5
|
|
|
6
6
|
name = 'USearch'
|
|
7
|
-
version = '0.
|
|
7
|
+
version = '0.11.0'
|
|
8
8
|
license = 'Apache 2.0'
|
|
9
9
|
url = 'https://github.com/unum-cloud/usearch'
|
|
10
10
|
description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
|
package/docs/compilation.md
CHANGED
|
@@ -63,8 +63,7 @@ Benchmarking:
|
|
|
63
63
|
## Python
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
|
-
pip install -e .
|
|
67
|
-
pytest python/scripts/test.py
|
|
66
|
+
pip install -e . && pytest python/scripts/test.py
|
|
68
67
|
```
|
|
69
68
|
|
|
70
69
|
Linting:
|
|
@@ -131,7 +130,11 @@ g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
|
|
|
131
130
|
|
|
132
131
|
# MacOS
|
|
133
132
|
g++ -std=c++11 -c -fPIC \
|
|
134
|
-
-I../../../../include
|
|
133
|
+
-I../../../../include \
|
|
134
|
+
-I../../../../src \
|
|
135
|
+
-I../../../../fp16/include \
|
|
136
|
+
-I../../../../robin-map/include \
|
|
137
|
+
-I../../../../simsimd/include \
|
|
135
138
|
-I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
|
|
136
139
|
g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
|
|
137
140
|
# Run linking to that directory
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
#ifndef UNUM_USEARCH_H
|
|
10
10
|
#define UNUM_USEARCH_H
|
|
11
11
|
|
|
12
|
+
#if !defined(USEARCH_VERSION_MAJOR)
|
|
13
|
+
#define USEARCH_VERSION_MAJOR 0
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#if !defined(USEARCH_VERSION_MINOR)
|
|
17
|
+
#define USEARCH_VERSION_MINOR 0
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
#if !defined(USEARCH_VERSION_PATCH)
|
|
21
|
+
#define USEARCH_VERSION_PATCH 0
|
|
22
|
+
#endif
|
|
23
|
+
|
|
12
24
|
// Inferring C++ version
|
|
13
25
|
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
|
|
14
26
|
#define USEARCH_IS_CPP17
|
|
@@ -61,7 +73,7 @@
|
|
|
61
73
|
#include <mutex> // `std::unique_lock` - replacement candidate
|
|
62
74
|
#include <random> // `std::default_random_engine` - replacement candidate
|
|
63
75
|
#include <stdexcept> // `std::runtime_exception`
|
|
64
|
-
#include <utility> // `std::
|
|
76
|
+
#include <utility> // `std::pair`
|
|
65
77
|
#include <vector> // `std::vector`
|
|
66
78
|
|
|
67
79
|
// Prefetching
|
|
@@ -125,12 +137,20 @@ inline std::size_t ceil2(std::size_t v) noexcept {
|
|
|
125
137
|
|
|
126
138
|
template <typename at> void misaligned_store(void* ptr, at v) noexcept { std::memcpy(ptr, &v, sizeof(v)); }
|
|
127
139
|
|
|
140
|
+
/// @brief Simply dereferencing misaligned pointers can be dangerous.
|
|
128
141
|
template <typename at> at misaligned_load(void* ptr) noexcept {
|
|
129
142
|
at v;
|
|
130
143
|
std::memcpy(&v, ptr, sizeof(v));
|
|
131
144
|
return v;
|
|
132
145
|
}
|
|
133
146
|
|
|
147
|
+
/// @brief The `std::exchange` alternative for C++11.
|
|
148
|
+
template <typename at, typename other_at = at> at exchange(at& obj, other_at&& new_value) {
|
|
149
|
+
at old_value = std::move(obj);
|
|
150
|
+
obj = std::forward<other_at>(new_value);
|
|
151
|
+
return old_value;
|
|
152
|
+
}
|
|
153
|
+
|
|
134
154
|
template <typename at> class misaligned_ref_gt {
|
|
135
155
|
byte_t* ptr_;
|
|
136
156
|
|
|
@@ -141,10 +161,8 @@ template <typename at> class misaligned_ref_gt {
|
|
|
141
161
|
misaligned_store<at>(ptr_, v);
|
|
142
162
|
return *this;
|
|
143
163
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return *this;
|
|
147
|
-
}
|
|
164
|
+
|
|
165
|
+
void reset(byte_t* ptr) noexcept { ptr_ = ptr; }
|
|
148
166
|
};
|
|
149
167
|
|
|
150
168
|
template <typename at> class misaligned_ptr_gt {
|
|
@@ -783,6 +801,12 @@ constexpr std::size_t default_expansion_add() { return 128; }
|
|
|
783
801
|
constexpr std::size_t default_expansion_search() { return 64; }
|
|
784
802
|
constexpr std::size_t default_allocator_entry_bytes() { return 64; }
|
|
785
803
|
|
|
804
|
+
/**
|
|
805
|
+
* @brief The "magic" sequence helps infer the type of the file.
|
|
806
|
+
* USearch indexes start with the "unumusearch" string.
|
|
807
|
+
*/
|
|
808
|
+
static constexpr char const* default_magic() { return "unumusearch"; }
|
|
809
|
+
|
|
786
810
|
/**
|
|
787
811
|
* @brief Configuration settings for the index construction.
|
|
788
812
|
* Includes the main `::connectivity` parameter (`M` in the paper)
|
|
@@ -809,6 +833,7 @@ struct config_t {
|
|
|
809
833
|
std::size_t max_elements = 0;
|
|
810
834
|
std::size_t max_threads_add = 1;
|
|
811
835
|
std::size_t max_threads_search = 1;
|
|
836
|
+
std::size_t vector_alignment = 1;
|
|
812
837
|
|
|
813
838
|
std::size_t max_threads() const noexcept { return (std::max)(max_threads_add, max_threads_search); }
|
|
814
839
|
std::size_t concurrency() const noexcept { return (std::min)(max_threads_add, max_threads_search); }
|
|
@@ -826,6 +851,67 @@ struct search_config_t {
|
|
|
826
851
|
bool exact{};
|
|
827
852
|
};
|
|
828
853
|
|
|
854
|
+
using file_header_t = byte_t[64];
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* @brief Serialized binary representations of the USearch index start with metadata.
|
|
858
|
+
* Metadata is parsed into a `file_head_t`, containing the USearch package version,
|
|
859
|
+
* and the properties of the index.
|
|
860
|
+
*/
|
|
861
|
+
struct file_head_t {
|
|
862
|
+
|
|
863
|
+
using magic_t = char[11];
|
|
864
|
+
using version_major_t = std::uint16_t;
|
|
865
|
+
using version_minor_t = std::uint16_t;
|
|
866
|
+
using version_patch_t = std::uint16_t;
|
|
867
|
+
|
|
868
|
+
enum metric_t : std::uint8_t {
|
|
869
|
+
unknown_k = 0,
|
|
870
|
+
ip_k = 'i',
|
|
871
|
+
cos_k = 'c',
|
|
872
|
+
l2sq_k = 'e',
|
|
873
|
+
jaccard_k = 'j',
|
|
874
|
+
hamming_k = 'h',
|
|
875
|
+
pearson_k = 'p',
|
|
876
|
+
haversine_k = 'h',
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
using connectivity_t = std::uint8_t;
|
|
880
|
+
using max_level_t = std::uint8_t;
|
|
881
|
+
using vector_alignment_t = std::uint8_t;
|
|
882
|
+
using bytes_per_label_t = std::uint16_t;
|
|
883
|
+
using bytes_per_id_t = std::uint16_t;
|
|
884
|
+
using size_t = std::uint64_t;
|
|
885
|
+
using entry_idx_t = std::uint64_t;
|
|
886
|
+
|
|
887
|
+
char const* magic;
|
|
888
|
+
misaligned_ref_gt<version_major_t> version_major;
|
|
889
|
+
misaligned_ref_gt<version_minor_t> version_minor;
|
|
890
|
+
misaligned_ref_gt<version_patch_t> version_patch;
|
|
891
|
+
misaligned_ref_gt<metric_t> metric;
|
|
892
|
+
misaligned_ref_gt<connectivity_t> connectivity;
|
|
893
|
+
misaligned_ref_gt<max_level_t> max_level;
|
|
894
|
+
misaligned_ref_gt<vector_alignment_t> vector_alignment;
|
|
895
|
+
misaligned_ref_gt<bytes_per_label_t> bytes_per_label;
|
|
896
|
+
misaligned_ref_gt<bytes_per_id_t> bytes_per_id;
|
|
897
|
+
misaligned_ref_gt<size_t> size;
|
|
898
|
+
misaligned_ref_gt<entry_idx_t> entry_idx;
|
|
899
|
+
|
|
900
|
+
file_head_t(byte_t* ptr) noexcept
|
|
901
|
+
: magic((char const*)exchange(ptr, ptr + sizeof(magic_t))),
|
|
902
|
+
version_major(exchange(ptr, ptr + sizeof(version_major_t))),
|
|
903
|
+
version_minor(exchange(ptr, ptr + sizeof(version_minor_t))),
|
|
904
|
+
version_patch(exchange(ptr, ptr + sizeof(version_patch_t))), metric(exchange(ptr, ptr + sizeof(metric_t))),
|
|
905
|
+
connectivity(exchange(ptr, ptr + sizeof(connectivity_t))),
|
|
906
|
+
max_level(exchange(ptr, ptr + sizeof(max_level_t))),
|
|
907
|
+
vector_alignment(exchange(ptr, ptr + sizeof(vector_alignment_t))),
|
|
908
|
+
bytes_per_label(exchange(ptr, ptr + sizeof(bytes_per_label_t))),
|
|
909
|
+
bytes_per_id(exchange(ptr, ptr + sizeof(bytes_per_id_t))), size(exchange(ptr, ptr + sizeof(size_t))),
|
|
910
|
+
entry_idx(exchange(ptr, ptr + sizeof(entry_idx_t))) {}
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
static_assert(sizeof(file_header_t) == 64, "File header should be exactly 64 bytes");
|
|
914
|
+
|
|
829
915
|
/**
|
|
830
916
|
* @brief Approximate Nearest Neighbors Search index using the
|
|
831
917
|
* Hierarchical Navigable Small World graph algorithm.
|
|
@@ -917,14 +1003,15 @@ class index_gt {
|
|
|
917
1003
|
vector_view_t vector;
|
|
918
1004
|
};
|
|
919
1005
|
|
|
920
|
-
template <typename ref_at> class member_iterator_gt {
|
|
1006
|
+
template <typename ref_at, typename index_at> class member_iterator_gt {
|
|
921
1007
|
using ref_t = ref_at;
|
|
922
|
-
|
|
1008
|
+
using index_t = index_at;
|
|
1009
|
+
index_t* index_{};
|
|
923
1010
|
std::size_t offset_{};
|
|
924
1011
|
|
|
925
1012
|
friend class index_gt;
|
|
926
1013
|
member_iterator_gt() noexcept {}
|
|
927
|
-
member_iterator_gt(
|
|
1014
|
+
member_iterator_gt(index_t* index, std::size_t offset) noexcept : index_(index), offset_(offset) {}
|
|
928
1015
|
|
|
929
1016
|
public:
|
|
930
1017
|
using iterator_category = std::random_access_iterator_tag;
|
|
@@ -933,7 +1020,7 @@ class index_gt {
|
|
|
933
1020
|
using pointer = void;
|
|
934
1021
|
using reference = ref_t;
|
|
935
1022
|
|
|
936
|
-
reference operator*() const noexcept { return index_->node_with_id_(offset_)
|
|
1023
|
+
reference operator*() const noexcept { return ref_t(index_->node_with_id_(offset_)); }
|
|
937
1024
|
member_iterator_gt operator++(int) noexcept { return member_iterator_gt(index_, offset_ + 1); }
|
|
938
1025
|
member_iterator_gt operator--(int) noexcept { return member_iterator_gt(index_, offset_ - 1); }
|
|
939
1026
|
member_iterator_gt operator+(difference_type d) noexcept { return member_iterator_gt(index_, offset_ + d); }
|
|
@@ -963,8 +1050,8 @@ class index_gt {
|
|
|
963
1050
|
}
|
|
964
1051
|
};
|
|
965
1052
|
|
|
966
|
-
using member_iterator_t = member_iterator_gt<member_ref_t>;
|
|
967
|
-
using member_citerator_t = member_iterator_gt<member_cref_t>;
|
|
1053
|
+
using member_iterator_t = member_iterator_gt<member_ref_t, index_gt>;
|
|
1054
|
+
using member_citerator_t = member_iterator_gt<member_cref_t, index_gt const>;
|
|
968
1055
|
|
|
969
1056
|
// STL compatibility:
|
|
970
1057
|
using value_type = std::pair<label_t, distance_t>;
|
|
@@ -1051,8 +1138,8 @@ class index_gt {
|
|
|
1051
1138
|
|
|
1052
1139
|
operator vector_view_t() const noexcept { return {vector(), dim()}; }
|
|
1053
1140
|
vector_view_t vector_view() const noexcept { return {vector(), dim()}; }
|
|
1054
|
-
member_ref_t
|
|
1055
|
-
member_cref_t
|
|
1141
|
+
explicit operator member_ref_t() noexcept { return {{tape_}, vector_view()}; }
|
|
1142
|
+
explicit operator member_cref_t() const noexcept { return {label(), vector_view()}; }
|
|
1056
1143
|
};
|
|
1057
1144
|
|
|
1058
1145
|
/**
|
|
@@ -1274,7 +1361,7 @@ class index_gt {
|
|
|
1274
1361
|
inline std::size_t size() const noexcept { return count; }
|
|
1275
1362
|
inline search_result_t operator[](std::size_t i) const noexcept {
|
|
1276
1363
|
candidate_t const* top_ordered = top_.data();
|
|
1277
|
-
return {index_.node_with_id_(top_ordered[i].id)
|
|
1364
|
+
return {member_cref_t(index_.node_with_id_(top_ordered[i].id)), top_ordered[i].distance};
|
|
1278
1365
|
}
|
|
1279
1366
|
inline std::size_t dump_to(label_t* labels, distance_t* distances) const noexcept {
|
|
1280
1367
|
for (std::size_t i = 0; i != count; ++i) {
|
|
@@ -1479,25 +1566,16 @@ class index_gt {
|
|
|
1479
1566
|
|
|
1480
1567
|
void change_expansion_add(std::size_t n) noexcept { config_.expansion_add = n; }
|
|
1481
1568
|
void change_expansion_search(std::size_t n) noexcept { config_.expansion_search = n; }
|
|
1569
|
+
void change_metric(metric_t const& m) noexcept(false) {
|
|
1570
|
+
metric_ = m;
|
|
1571
|
+
for (thread_context_t& context : thread_contexts_)
|
|
1572
|
+
context.metric = metric_;
|
|
1573
|
+
}
|
|
1482
1574
|
|
|
1483
1575
|
#pragma endregion
|
|
1484
1576
|
|
|
1485
1577
|
#pragma region Serialization
|
|
1486
1578
|
|
|
1487
|
-
private:
|
|
1488
|
-
struct serialized_state_t {
|
|
1489
|
-
// Check compatibility
|
|
1490
|
-
std::uint64_t bytes_per_label{};
|
|
1491
|
-
std::uint64_t bytes_per_id{};
|
|
1492
|
-
|
|
1493
|
-
// Describe state
|
|
1494
|
-
std::uint64_t connectivity{};
|
|
1495
|
-
std::uint64_t size{};
|
|
1496
|
-
std::uint64_t entry_id{};
|
|
1497
|
-
std::uint64_t max_level{};
|
|
1498
|
-
};
|
|
1499
|
-
|
|
1500
|
-
public:
|
|
1501
1579
|
/**
|
|
1502
1580
|
* @brief Saves serialized binary index representation to disk,
|
|
1503
1581
|
* co-locating vectors and neighbors lists.
|
|
@@ -1505,15 +1583,25 @@ class index_gt {
|
|
|
1505
1583
|
*/
|
|
1506
1584
|
void save(char const* file_path) const noexcept(false) {
|
|
1507
1585
|
|
|
1508
|
-
|
|
1586
|
+
file_header_t state_buffer{};
|
|
1587
|
+
std::memset(state_buffer, 0, sizeof(file_header_t));
|
|
1588
|
+
file_head_t state{state_buffer};
|
|
1589
|
+
std::memcpy(state_buffer, default_magic(), std::strlen(default_magic()));
|
|
1590
|
+
|
|
1509
1591
|
// Check compatibility
|
|
1510
|
-
state.
|
|
1511
|
-
state.
|
|
1592
|
+
state.version_major = USEARCH_VERSION_MAJOR;
|
|
1593
|
+
state.version_minor = USEARCH_VERSION_MINOR;
|
|
1594
|
+
state.version_patch = USEARCH_VERSION_PATCH;
|
|
1595
|
+
state.metric = file_head_t::unknown_k;
|
|
1596
|
+
|
|
1512
1597
|
// Describe state
|
|
1513
1598
|
state.connectivity = config_.connectivity;
|
|
1514
|
-
state.size = size_;
|
|
1515
|
-
state.entry_id = entry_id_;
|
|
1516
1599
|
state.max_level = max_level_;
|
|
1600
|
+
state.vector_alignment = config_.vector_alignment;
|
|
1601
|
+
state.bytes_per_label = sizeof(label_t);
|
|
1602
|
+
state.bytes_per_id = sizeof(id_t);
|
|
1603
|
+
state.size = size_;
|
|
1604
|
+
state.entry_idx = entry_id_;
|
|
1517
1605
|
|
|
1518
1606
|
std::FILE* file = std::fopen(file_path, "w");
|
|
1519
1607
|
if (!file)
|
|
@@ -1528,7 +1616,7 @@ class index_gt {
|
|
|
1528
1616
|
};
|
|
1529
1617
|
|
|
1530
1618
|
// Write the header
|
|
1531
|
-
write_chunk(&
|
|
1619
|
+
write_chunk(&state_buffer[0], sizeof(file_header_t));
|
|
1532
1620
|
|
|
1533
1621
|
// Serialize nodes one by one
|
|
1534
1622
|
for (std::size_t i = 0; i != state.size; ++i) {
|
|
@@ -1549,7 +1637,7 @@ class index_gt {
|
|
|
1549
1637
|
* Available on Linux, MacOS, Windows.
|
|
1550
1638
|
*/
|
|
1551
1639
|
void load(char const* file_path) noexcept(false) {
|
|
1552
|
-
|
|
1640
|
+
file_header_t state_buffer{};
|
|
1553
1641
|
std::FILE* file = std::fopen(file_path, "r");
|
|
1554
1642
|
if (!file)
|
|
1555
1643
|
throw std::runtime_error(std::strerror(errno));
|
|
@@ -1564,7 +1652,8 @@ class index_gt {
|
|
|
1564
1652
|
|
|
1565
1653
|
// Read the header
|
|
1566
1654
|
{
|
|
1567
|
-
read_chunk(&
|
|
1655
|
+
read_chunk(&state_buffer[0], sizeof(file_header_t));
|
|
1656
|
+
file_head_t state{state_buffer};
|
|
1568
1657
|
if (state.bytes_per_label != sizeof(label_t)) {
|
|
1569
1658
|
std::fclose(file);
|
|
1570
1659
|
throw std::runtime_error("Incompatible label type!");
|
|
@@ -1576,15 +1665,16 @@ class index_gt {
|
|
|
1576
1665
|
|
|
1577
1666
|
config_.connectivity = state.connectivity;
|
|
1578
1667
|
config_.max_elements = state.size;
|
|
1668
|
+
config_.vector_alignment = state.vector_alignment;
|
|
1579
1669
|
pre_ = precompute_(config_);
|
|
1580
1670
|
reserve(state.size);
|
|
1581
1671
|
size_ = state.size;
|
|
1582
1672
|
max_level_ = static_cast<level_t>(state.max_level);
|
|
1583
|
-
entry_id_ = static_cast<id_t>(state.
|
|
1673
|
+
entry_id_ = static_cast<id_t>(state.entry_idx);
|
|
1584
1674
|
}
|
|
1585
1675
|
|
|
1586
1676
|
// Load nodes one by one
|
|
1587
|
-
for (std::size_t i = 0; i !=
|
|
1677
|
+
for (std::size_t i = 0; i != size_; ++i) {
|
|
1588
1678
|
label_t label;
|
|
1589
1679
|
dim_t dim;
|
|
1590
1680
|
level_t level;
|
|
@@ -1611,7 +1701,6 @@ class index_gt {
|
|
|
1611
1701
|
#if defined(USEARCH_IS_WINDOWS)
|
|
1612
1702
|
throw std::logic_error("Memory-mapping is not yet available for Windows");
|
|
1613
1703
|
#else
|
|
1614
|
-
serialized_state_t state;
|
|
1615
1704
|
int open_flags = O_RDONLY;
|
|
1616
1705
|
#if __linux__
|
|
1617
1706
|
open_flags |= O_NOATIME;
|
|
@@ -1635,7 +1724,7 @@ class index_gt {
|
|
|
1635
1724
|
|
|
1636
1725
|
// Read the header
|
|
1637
1726
|
{
|
|
1638
|
-
|
|
1727
|
+
file_head_t state{file};
|
|
1639
1728
|
if (state.bytes_per_label != sizeof(label_t)) {
|
|
1640
1729
|
close(descriptor);
|
|
1641
1730
|
throw std::runtime_error("Incompatible label type!");
|
|
@@ -1647,17 +1736,18 @@ class index_gt {
|
|
|
1647
1736
|
|
|
1648
1737
|
config_.connectivity = state.connectivity;
|
|
1649
1738
|
config_.max_elements = state.size;
|
|
1739
|
+
config_.vector_alignment = state.vector_alignment;
|
|
1650
1740
|
config_.max_threads_add = 0;
|
|
1651
1741
|
pre_ = precompute_(config_);
|
|
1652
1742
|
reserve(state.size);
|
|
1653
1743
|
size_ = state.size;
|
|
1654
1744
|
max_level_ = static_cast<level_t>(state.max_level);
|
|
1655
|
-
entry_id_ = static_cast<id_t>(state.
|
|
1745
|
+
entry_id_ = static_cast<id_t>(state.entry_idx);
|
|
1656
1746
|
}
|
|
1657
1747
|
|
|
1658
1748
|
// Locate every node packed into file
|
|
1659
|
-
std::size_t progress = sizeof(
|
|
1660
|
-
for (std::size_t i = 0; i !=
|
|
1749
|
+
std::size_t progress = sizeof(file_header_t);
|
|
1750
|
+
for (std::size_t i = 0; i != size_; ++i) {
|
|
1661
1751
|
byte_t* tape = (byte_t*)(file + progress);
|
|
1662
1752
|
dim_t dim = misaligned_load<dim_t>(tape + sizeof(label_t));
|
|
1663
1753
|
level_t level = misaligned_load<level_t>(tape + sizeof(label_t) + sizeof(dim_t));
|