bare-usearch 0.1.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/CMakeLists.txt +70 -0
- package/README.md +133 -0
- package/binding.c +529 -0
- package/binding.js +2 -0
- package/index.js +84 -0
- package/package.json +78 -0
- package/prebuilds/android-arm/bare-usearch.bare +0 -0
- package/prebuilds/android-arm/bare-usearch.node +0 -0
- package/prebuilds/android-arm64/bare-usearch.bare +0 -0
- package/prebuilds/android-arm64/bare-usearch.node +0 -0
- package/prebuilds/android-ia32/bare-usearch.bare +0 -0
- package/prebuilds/android-ia32/bare-usearch.node +0 -0
- package/prebuilds/android-x64/bare-usearch.bare +0 -0
- package/prebuilds/android-x64/bare-usearch.node +0 -0
- package/prebuilds/darwin-arm64/bare-usearch.bare +0 -0
- package/prebuilds/darwin-arm64/bare-usearch.node +0 -0
- package/prebuilds/darwin-x64/bare-usearch.bare +0 -0
- package/prebuilds/darwin-x64/bare-usearch.node +0 -0
- package/prebuilds/ios-arm64/bare-usearch.bare +0 -0
- package/prebuilds/ios-arm64-simulator/bare-usearch.bare +0 -0
- package/prebuilds/ios-x64-simulator/bare-usearch.bare +0 -0
- package/prebuilds/linux-arm64/bare-usearch.bare +0 -0
- package/prebuilds/linux-arm64/bare-usearch.node +0 -0
- package/prebuilds/linux-x64/bare-usearch.bare +0 -0
- package/prebuilds/linux-x64/bare-usearch.node +0 -0
- package/prebuilds/win32-arm64/bare-usearch.bare +0 -0
- package/prebuilds/win32-arm64/bare-usearch.node +0 -0
- package/prebuilds/win32-x64/bare-usearch.bare +0 -0
- package/prebuilds/win32-x64/bare-usearch.node +0 -0
- package/usearch_wrapper.cpp +164 -0
package/CMakeLists.txt
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.25)
|
|
2
|
+
|
|
3
|
+
find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare)
|
|
4
|
+
find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi)
|
|
5
|
+
|
|
6
|
+
project(bare_usearch C CXX)
|
|
7
|
+
|
|
8
|
+
# usearch requires C++ exceptions - remove -fno-exceptions if present
|
|
9
|
+
string(REPLACE "-fno-exceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
10
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
|
11
|
+
|
|
12
|
+
# Remove NDEBUG to avoid noexcept declarations that conflict with exception handling
|
|
13
|
+
string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
|
14
|
+
|
|
15
|
+
set(usearch_sources
|
|
16
|
+
binding.c
|
|
17
|
+
vendor/usearch/c/lib.cpp
|
|
18
|
+
usearch_wrapper.cpp
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
set(usearch_includes
|
|
22
|
+
vendor/usearch/include
|
|
23
|
+
vendor/usearch/c
|
|
24
|
+
vendor/usearch/fp16/include
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
set(usearch_defines
|
|
28
|
+
USEARCH_USE_OPENMP=0
|
|
29
|
+
USEARCH_USE_SIMSIMD=0
|
|
30
|
+
USEARCH_USE_FP16LIB=1
|
|
31
|
+
USEARCH_CAN_COMPILE_FP16=0
|
|
32
|
+
USEARCH_CAN_COMPILE_FLOAT16=0
|
|
33
|
+
USEARCH_CAN_COMPILE_BF16=0
|
|
34
|
+
USEARCH_CAN_COMPILE_BFLOAT16=0
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Create the Bare addon
|
|
38
|
+
add_bare_module(bare_usearch_bare)
|
|
39
|
+
|
|
40
|
+
target_sources(${bare_usearch_bare} PRIVATE ${usearch_sources})
|
|
41
|
+
target_include_directories(${bare_usearch_bare} PRIVATE ${usearch_includes})
|
|
42
|
+
target_compile_definitions(${bare_usearch_bare} PRIVATE ${usearch_defines})
|
|
43
|
+
set_target_properties(${bare_usearch_bare} PROPERTIES
|
|
44
|
+
CXX_STANDARD 11
|
|
45
|
+
CXX_STANDARD_REQUIRED ON
|
|
46
|
+
)
|
|
47
|
+
target_compile_options(${bare_usearch_bare} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fexceptions>)
|
|
48
|
+
|
|
49
|
+
# Create the Node.js NAPI addon
|
|
50
|
+
add_napi_module(bare_usearch_node)
|
|
51
|
+
|
|
52
|
+
target_sources(${bare_usearch_node} PRIVATE ${usearch_sources})
|
|
53
|
+
target_compile_definitions(${bare_usearch_node} PRIVATE
|
|
54
|
+
${usearch_defines}
|
|
55
|
+
NAPI_VERSION=9
|
|
56
|
+
)
|
|
57
|
+
target_include_directories(${bare_usearch_node} PRIVATE ${usearch_includes})
|
|
58
|
+
set_target_properties(${bare_usearch_node} PROPERTIES
|
|
59
|
+
CXX_STANDARD 11
|
|
60
|
+
CXX_STANDARD_REQUIRED ON
|
|
61
|
+
)
|
|
62
|
+
target_compile_options(${bare_usearch_node} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fexceptions>)
|
|
63
|
+
|
|
64
|
+
resolve_node_module(bare-compat-napi compat)
|
|
65
|
+
|
|
66
|
+
target_include_directories(
|
|
67
|
+
${bare_usearch_node}
|
|
68
|
+
PRIVATE
|
|
69
|
+
"${compat}/include"
|
|
70
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# bare-usearch
|
|
2
|
+
|
|
3
|
+
Native [USearch](https://github.com/unum-cloud/usearch) bindings for [Bare](https://github.com/holepunchto/bare).
|
|
4
|
+
|
|
5
|
+
Fast approximate nearest neighbor search for high-dimensional vectors.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- CMake 3.25+
|
|
10
|
+
- C/C++ compiler (clang, gcc, or MSVC)
|
|
11
|
+
- Node.js (for npm/cmake-bare)
|
|
12
|
+
- Bare runtime
|
|
13
|
+
|
|
14
|
+
## Building
|
|
15
|
+
|
|
16
|
+
Clone with submodules:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone --recursive https://github.com/CameronTofer/bare-usearch
|
|
20
|
+
cd bare-usearch
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or if already cloned:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git submodule update --init --recursive
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Install dependencies and build:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install
|
|
33
|
+
npm run build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
const { Index } = require('bare-usearch')
|
|
40
|
+
|
|
41
|
+
// Create index with 128-dimensional vectors
|
|
42
|
+
const index = new Index({
|
|
43
|
+
dimensions: 128,
|
|
44
|
+
metric: 'cos' // cosine similarity
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Add vectors (key, vector)
|
|
48
|
+
index.add(1, new Float32Array([0.1, 0.2, ...]))
|
|
49
|
+
index.add(2, new Float32Array([0.3, 0.4, ...]))
|
|
50
|
+
|
|
51
|
+
// Search for k nearest neighbors
|
|
52
|
+
const results = index.search(queryVector, 10)
|
|
53
|
+
console.log(results.keys) // [2, 1, ...]
|
|
54
|
+
console.log(results.distances) // Float32Array
|
|
55
|
+
|
|
56
|
+
// Persistence
|
|
57
|
+
index.save('index.usearch')
|
|
58
|
+
index.load('index.usearch')
|
|
59
|
+
|
|
60
|
+
// Cleanup
|
|
61
|
+
index.free()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
See `examples/` for more:
|
|
65
|
+
- `basic.js` - Basic usage with add/search/remove
|
|
66
|
+
- `persistence.js` - Saving and loading indexes
|
|
67
|
+
- `topics.js` - Topic extraction using vector similarity
|
|
68
|
+
|
|
69
|
+
Run examples with:
|
|
70
|
+
```bash
|
|
71
|
+
bare examples/basic.js
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Testing
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm test # bare
|
|
78
|
+
npm run test:node # node
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API Reference
|
|
82
|
+
|
|
83
|
+
### Index
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
new Index(options?)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Description |
|
|
90
|
+
|--------|------|---------|-------------|
|
|
91
|
+
| `dimensions` | number | required | Vector dimensionality |
|
|
92
|
+
| `metric` | string | `'cos'` | Distance metric |
|
|
93
|
+
| `quantization` | string | `'f32'` | Scalar quantization |
|
|
94
|
+
| `connectivity` | number | auto | Graph connectivity (M) |
|
|
95
|
+
| `multi` | boolean | `false` | Allow multiple vectors per key |
|
|
96
|
+
|
|
97
|
+
**Metrics:** `'cos'`, `'ip'` (inner product), `'l2'` (L2 squared), `'hamming'`, `'jaccard'`
|
|
98
|
+
|
|
99
|
+
**Quantization:** `'f32'`, `'f16'`, `'i8'`
|
|
100
|
+
|
|
101
|
+
**Properties:**
|
|
102
|
+
|
|
103
|
+
- `size` - Number of vectors in index
|
|
104
|
+
- `dimensions` - Vector dimensionality
|
|
105
|
+
- `capacity` - Current capacity
|
|
106
|
+
|
|
107
|
+
**Methods:**
|
|
108
|
+
|
|
109
|
+
- `add(key, vector)` - Add a vector with numeric key
|
|
110
|
+
- `search(query, k)` - Find k nearest neighbors, returns `{ keys, distances, count }`
|
|
111
|
+
- `remove(key)` - Remove vector by key
|
|
112
|
+
- `contains(key)` - Check if key exists
|
|
113
|
+
- `get(key)` - Retrieve vector by key (returns Float32Array or null)
|
|
114
|
+
- `reserve(capacity)` - Pre-allocate space for vectors
|
|
115
|
+
- `save(path)` - Save index to file
|
|
116
|
+
- `load(path)` - Load index from file
|
|
117
|
+
- `view(path)` - Memory-map index from file (read-only)
|
|
118
|
+
- `clear()` - Remove all vectors
|
|
119
|
+
- `free()` - Release resources
|
|
120
|
+
|
|
121
|
+
## Platform Support
|
|
122
|
+
|
|
123
|
+
| Platform | Architecture |
|
|
124
|
+
|----------|--------------|
|
|
125
|
+
| macOS | arm64, x64 |
|
|
126
|
+
| Linux | x64, arm64 |
|
|
127
|
+
| Windows | x64, arm64 |
|
|
128
|
+
| Android | arm64, arm, x64, ia32 |
|
|
129
|
+
| iOS | arm64, x64 (simulator) |
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
package/binding.c
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
#include <js.h>
|
|
2
|
+
#include <bare.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include "usearch.h"
|
|
6
|
+
|
|
7
|
+
// Exception-safe wrappers (defined in usearch_wrapper.cpp)
|
|
8
|
+
extern usearch_index_t usearch_init_safe(usearch_init_options_t* options, usearch_error_t* error);
|
|
9
|
+
extern void usearch_free_safe(usearch_index_t index, usearch_error_t* error);
|
|
10
|
+
extern size_t usearch_size_safe(usearch_index_t index, usearch_error_t* error);
|
|
11
|
+
extern size_t usearch_capacity_safe(usearch_index_t index, usearch_error_t* error);
|
|
12
|
+
extern size_t usearch_dimensions_safe(usearch_index_t index, usearch_error_t* error);
|
|
13
|
+
extern void usearch_reserve_safe(usearch_index_t index, size_t capacity, usearch_error_t* error);
|
|
14
|
+
extern void usearch_add_safe(usearch_index_t index, usearch_key_t key, void const* vector,
|
|
15
|
+
usearch_scalar_kind_t kind, usearch_error_t* error);
|
|
16
|
+
extern size_t usearch_search_safe(usearch_index_t index, void const* query, usearch_scalar_kind_t kind,
|
|
17
|
+
size_t count, usearch_key_t* keys, usearch_distance_t* distances,
|
|
18
|
+
usearch_error_t* error);
|
|
19
|
+
extern size_t usearch_remove_safe(usearch_index_t index, usearch_key_t key, usearch_error_t* error);
|
|
20
|
+
extern bool usearch_contains_safe(usearch_index_t index, usearch_key_t key, usearch_error_t* error);
|
|
21
|
+
extern size_t usearch_get_safe(usearch_index_t index, usearch_key_t key, size_t count,
|
|
22
|
+
void* vector, usearch_scalar_kind_t kind, usearch_error_t* error);
|
|
23
|
+
extern void usearch_save_safe(usearch_index_t index, char const* path, usearch_error_t* error);
|
|
24
|
+
extern void usearch_load_safe(usearch_index_t index, char const* path, usearch_error_t* error);
|
|
25
|
+
extern void usearch_view_safe(usearch_index_t index, char const* path, usearch_error_t* error);
|
|
26
|
+
extern void usearch_clear_safe(usearch_index_t index, usearch_error_t* error);
|
|
27
|
+
|
|
28
|
+
// Helper to throw JS error from usearch error
|
|
29
|
+
static void throw_if_error(js_env_t *env, usearch_error_t error) {
|
|
30
|
+
if (error) {
|
|
31
|
+
js_throw_error(env, NULL, error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create a new index
|
|
36
|
+
static js_value_t *fn_init(js_env_t *env, js_callback_info_t *info) {
|
|
37
|
+
size_t argc = 1;
|
|
38
|
+
js_value_t *argv[1];
|
|
39
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
40
|
+
|
|
41
|
+
usearch_init_options_t opts;
|
|
42
|
+
memset(&opts, 0, sizeof(opts));
|
|
43
|
+
opts.metric_kind = usearch_metric_cos_k;
|
|
44
|
+
opts.quantization = usearch_scalar_f32_k;
|
|
45
|
+
opts.dimensions = 0;
|
|
46
|
+
opts.connectivity = 16; // default M value for HNSW
|
|
47
|
+
opts.expansion_add = 128; // ef_construction
|
|
48
|
+
opts.expansion_search = 64; // ef
|
|
49
|
+
opts.multi = false;
|
|
50
|
+
|
|
51
|
+
if (argc > 0) {
|
|
52
|
+
js_value_t *val;
|
|
53
|
+
js_value_type_t val_type;
|
|
54
|
+
|
|
55
|
+
// dimensions (required) - check type
|
|
56
|
+
if (js_get_named_property(env, argv[0], "dimensions", &val) == 0) {
|
|
57
|
+
js_typeof(env, val, &val_type);
|
|
58
|
+
if (val_type == js_number) {
|
|
59
|
+
uint32_t dim;
|
|
60
|
+
js_get_value_uint32(env, val, &dim);
|
|
61
|
+
opts.dimensions = dim;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// metric - check if property exists AND is a string
|
|
66
|
+
if (js_get_named_property(env, argv[0], "metric", &val) == 0) {
|
|
67
|
+
js_typeof(env, val, &val_type);
|
|
68
|
+
if (val_type == js_string) {
|
|
69
|
+
utf8_t metric[32];
|
|
70
|
+
size_t len;
|
|
71
|
+
js_get_value_string_utf8(env, val, metric, sizeof(metric), &len);
|
|
72
|
+
if (strcmp((char *)metric, "cos") == 0 || strcmp((char *)metric, "cosine") == 0) {
|
|
73
|
+
opts.metric_kind = usearch_metric_cos_k;
|
|
74
|
+
} else if (strcmp((char *)metric, "ip") == 0 || strcmp((char *)metric, "inner") == 0) {
|
|
75
|
+
opts.metric_kind = usearch_metric_ip_k;
|
|
76
|
+
} else if (strcmp((char *)metric, "l2sq") == 0 || strcmp((char *)metric, "l2") == 0) {
|
|
77
|
+
opts.metric_kind = usearch_metric_l2sq_k;
|
|
78
|
+
} else if (strcmp((char *)metric, "hamming") == 0) {
|
|
79
|
+
opts.metric_kind = usearch_metric_hamming_k;
|
|
80
|
+
} else if (strcmp((char *)metric, "jaccard") == 0) {
|
|
81
|
+
opts.metric_kind = usearch_metric_jaccard_k;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// quantization - check if property exists AND is a string
|
|
87
|
+
if (js_get_named_property(env, argv[0], "quantization", &val) == 0) {
|
|
88
|
+
js_typeof(env, val, &val_type);
|
|
89
|
+
if (val_type == js_string) {
|
|
90
|
+
utf8_t quant[16];
|
|
91
|
+
size_t len;
|
|
92
|
+
js_get_value_string_utf8(env, val, quant, sizeof(quant), &len);
|
|
93
|
+
if (strcmp((char *)quant, "f32") == 0) {
|
|
94
|
+
opts.quantization = usearch_scalar_f32_k;
|
|
95
|
+
} else if (strcmp((char *)quant, "f16") == 0) {
|
|
96
|
+
opts.quantization = usearch_scalar_f16_k;
|
|
97
|
+
} else if (strcmp((char *)quant, "i8") == 0) {
|
|
98
|
+
opts.quantization = usearch_scalar_i8_k;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// connectivity - check type
|
|
104
|
+
if (js_get_named_property(env, argv[0], "connectivity", &val) == 0) {
|
|
105
|
+
js_typeof(env, val, &val_type);
|
|
106
|
+
if (val_type == js_number) {
|
|
107
|
+
uint32_t conn;
|
|
108
|
+
js_get_value_uint32(env, val, &conn);
|
|
109
|
+
opts.connectivity = conn;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// multi - check type
|
|
114
|
+
if (js_get_named_property(env, argv[0], "multi", &val) == 0) {
|
|
115
|
+
js_typeof(env, val, &val_type);
|
|
116
|
+
if (val_type == js_boolean) {
|
|
117
|
+
bool multi;
|
|
118
|
+
js_get_value_bool(env, val, &multi);
|
|
119
|
+
opts.multi = multi;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
usearch_error_t error = NULL;
|
|
124
|
+
usearch_index_t index = usearch_init_safe(&opts, &error);
|
|
125
|
+
if (error) {
|
|
126
|
+
throw_if_error(env, error);
|
|
127
|
+
js_value_t *undef;
|
|
128
|
+
js_get_undefined(env, &undef);
|
|
129
|
+
return undef;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Reserve initial capacity (required before adding vectors)
|
|
133
|
+
usearch_reserve_safe(index, 1024, &error);
|
|
134
|
+
if (error) {
|
|
135
|
+
usearch_free_safe(index, &error);
|
|
136
|
+
throw_if_error(env, "Failed to reserve initial capacity");
|
|
137
|
+
js_value_t *undef;
|
|
138
|
+
js_get_undefined(env, &undef);
|
|
139
|
+
return undef;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
js_value_t *result;
|
|
143
|
+
js_create_external(env, index, NULL, NULL, &result);
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Free an index
|
|
148
|
+
static js_value_t *fn_free(js_env_t *env, js_callback_info_t *info) {
|
|
149
|
+
size_t argc = 1;
|
|
150
|
+
js_value_t *argv[1];
|
|
151
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
152
|
+
|
|
153
|
+
usearch_index_t index;
|
|
154
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
155
|
+
|
|
156
|
+
usearch_error_t error = NULL;
|
|
157
|
+
usearch_free_safe(index, &error);
|
|
158
|
+
throw_if_error(env, error);
|
|
159
|
+
|
|
160
|
+
js_value_t *undef;
|
|
161
|
+
js_get_undefined(env, &undef);
|
|
162
|
+
return undef;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Get index size
|
|
166
|
+
static js_value_t *fn_size(js_env_t *env, js_callback_info_t *info) {
|
|
167
|
+
size_t argc = 1;
|
|
168
|
+
js_value_t *argv[1];
|
|
169
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
170
|
+
|
|
171
|
+
usearch_index_t index;
|
|
172
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
173
|
+
|
|
174
|
+
usearch_error_t error = NULL;
|
|
175
|
+
size_t size = usearch_size_safe(index, &error);
|
|
176
|
+
throw_if_error(env, error);
|
|
177
|
+
|
|
178
|
+
js_value_t *result;
|
|
179
|
+
js_create_uint32(env, (uint32_t)size, &result);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Get index dimensions
|
|
184
|
+
static js_value_t *fn_dimensions(js_env_t *env, js_callback_info_t *info) {
|
|
185
|
+
size_t argc = 1;
|
|
186
|
+
js_value_t *argv[1];
|
|
187
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
188
|
+
|
|
189
|
+
usearch_index_t index;
|
|
190
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
191
|
+
|
|
192
|
+
usearch_error_t error = NULL;
|
|
193
|
+
size_t dims = usearch_dimensions_safe(index, &error);
|
|
194
|
+
throw_if_error(env, error);
|
|
195
|
+
|
|
196
|
+
js_value_t *result;
|
|
197
|
+
js_create_uint32(env, (uint32_t)dims, &result);
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Get index capacity
|
|
202
|
+
static js_value_t *fn_capacity(js_env_t *env, js_callback_info_t *info) {
|
|
203
|
+
size_t argc = 1;
|
|
204
|
+
js_value_t *argv[1];
|
|
205
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
206
|
+
|
|
207
|
+
usearch_index_t index;
|
|
208
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
209
|
+
|
|
210
|
+
usearch_error_t error = NULL;
|
|
211
|
+
size_t cap = usearch_capacity_safe(index, &error);
|
|
212
|
+
throw_if_error(env, error);
|
|
213
|
+
|
|
214
|
+
js_value_t *result;
|
|
215
|
+
js_create_uint32(env, (uint32_t)cap, &result);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Reserve capacity
|
|
220
|
+
static js_value_t *fn_reserve(js_env_t *env, js_callback_info_t *info) {
|
|
221
|
+
size_t argc = 2;
|
|
222
|
+
js_value_t *argv[2];
|
|
223
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
224
|
+
|
|
225
|
+
usearch_index_t index;
|
|
226
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
227
|
+
|
|
228
|
+
uint32_t capacity;
|
|
229
|
+
js_get_value_uint32(env, argv[1], &capacity);
|
|
230
|
+
|
|
231
|
+
usearch_error_t error = NULL;
|
|
232
|
+
usearch_reserve_safe(index, capacity, &error);
|
|
233
|
+
throw_if_error(env, error);
|
|
234
|
+
|
|
235
|
+
js_value_t *undef;
|
|
236
|
+
js_get_undefined(env, &undef);
|
|
237
|
+
return undef;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Add a vector
|
|
241
|
+
static js_value_t *fn_add(js_env_t *env, js_callback_info_t *info) {
|
|
242
|
+
size_t argc = 3;
|
|
243
|
+
js_value_t *argv[3];
|
|
244
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
245
|
+
|
|
246
|
+
usearch_index_t index;
|
|
247
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
248
|
+
|
|
249
|
+
int64_t key;
|
|
250
|
+
js_get_value_int64(env, argv[1], &key);
|
|
251
|
+
|
|
252
|
+
// Get Float32Array data
|
|
253
|
+
void *data;
|
|
254
|
+
size_t length;
|
|
255
|
+
js_typedarray_type_t type;
|
|
256
|
+
js_get_typedarray_info(env, argv[2], &type, &data, &length, NULL, NULL);
|
|
257
|
+
|
|
258
|
+
usearch_error_t error = NULL;
|
|
259
|
+
usearch_add_safe(index, (usearch_key_t)key, data, usearch_scalar_f32_k, &error);
|
|
260
|
+
throw_if_error(env, error);
|
|
261
|
+
|
|
262
|
+
js_value_t *undef;
|
|
263
|
+
js_get_undefined(env, &undef);
|
|
264
|
+
return undef;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Search for nearest neighbors
|
|
268
|
+
static js_value_t *fn_search(js_env_t *env, js_callback_info_t *info) {
|
|
269
|
+
size_t argc = 3;
|
|
270
|
+
js_value_t *argv[3];
|
|
271
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
272
|
+
|
|
273
|
+
usearch_index_t index;
|
|
274
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
275
|
+
|
|
276
|
+
// Query vector
|
|
277
|
+
void *query_data;
|
|
278
|
+
size_t query_length;
|
|
279
|
+
js_typedarray_type_t type;
|
|
280
|
+
js_get_typedarray_info(env, argv[1], &type, &query_data, &query_length, NULL, NULL);
|
|
281
|
+
|
|
282
|
+
// Count (k)
|
|
283
|
+
uint32_t count;
|
|
284
|
+
js_get_value_uint32(env, argv[2], &count);
|
|
285
|
+
|
|
286
|
+
// Allocate output buffers
|
|
287
|
+
usearch_key_t *keys = (usearch_key_t *)malloc(count * sizeof(usearch_key_t));
|
|
288
|
+
usearch_distance_t *distances = (usearch_distance_t *)malloc(count * sizeof(usearch_distance_t));
|
|
289
|
+
|
|
290
|
+
usearch_error_t error = NULL;
|
|
291
|
+
size_t found = usearch_search_safe(index, query_data, usearch_scalar_f32_k, count, keys, distances, &error);
|
|
292
|
+
|
|
293
|
+
if (error) {
|
|
294
|
+
free(keys);
|
|
295
|
+
free(distances);
|
|
296
|
+
throw_if_error(env, error);
|
|
297
|
+
js_value_t *undef;
|
|
298
|
+
js_get_undefined(env, &undef);
|
|
299
|
+
return undef;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Create result object { keys: BigUint64Array, distances: Float32Array, count: number }
|
|
303
|
+
js_value_t *result;
|
|
304
|
+
js_create_object(env, &result);
|
|
305
|
+
|
|
306
|
+
// Keys array (BigUint64Array)
|
|
307
|
+
js_value_t *keys_ab, *keys_arr;
|
|
308
|
+
void *keys_buf;
|
|
309
|
+
js_create_arraybuffer(env, found * sizeof(uint64_t), &keys_buf, &keys_ab);
|
|
310
|
+
memcpy(keys_buf, keys, found * sizeof(uint64_t));
|
|
311
|
+
js_create_typedarray(env, js_biguint64array, found, keys_ab, 0, &keys_arr);
|
|
312
|
+
js_set_named_property(env, result, "keys", keys_arr);
|
|
313
|
+
|
|
314
|
+
// Distances array (Float32Array)
|
|
315
|
+
js_value_t *dist_ab, *dist_arr;
|
|
316
|
+
void *dist_buf;
|
|
317
|
+
js_create_arraybuffer(env, found * sizeof(float), &dist_buf, &dist_ab);
|
|
318
|
+
memcpy(dist_buf, distances, found * sizeof(float));
|
|
319
|
+
js_create_typedarray(env, js_float32array, found, dist_ab, 0, &dist_arr);
|
|
320
|
+
js_set_named_property(env, result, "distances", dist_arr);
|
|
321
|
+
|
|
322
|
+
// Count
|
|
323
|
+
js_value_t *count_val;
|
|
324
|
+
js_create_uint32(env, (uint32_t)found, &count_val);
|
|
325
|
+
js_set_named_property(env, result, "count", count_val);
|
|
326
|
+
|
|
327
|
+
free(keys);
|
|
328
|
+
free(distances);
|
|
329
|
+
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Remove a vector
|
|
334
|
+
static js_value_t *fn_remove(js_env_t *env, js_callback_info_t *info) {
|
|
335
|
+
size_t argc = 2;
|
|
336
|
+
js_value_t *argv[2];
|
|
337
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
338
|
+
|
|
339
|
+
usearch_index_t index;
|
|
340
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
341
|
+
|
|
342
|
+
int64_t key;
|
|
343
|
+
js_get_value_int64(env, argv[1], &key);
|
|
344
|
+
|
|
345
|
+
usearch_error_t error = NULL;
|
|
346
|
+
size_t removed = usearch_remove_safe(index, (usearch_key_t)key, &error);
|
|
347
|
+
throw_if_error(env, error);
|
|
348
|
+
|
|
349
|
+
js_value_t *result;
|
|
350
|
+
js_create_uint32(env, (uint32_t)removed, &result);
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check if index contains a key
|
|
355
|
+
static js_value_t *fn_contains(js_env_t *env, js_callback_info_t *info) {
|
|
356
|
+
size_t argc = 2;
|
|
357
|
+
js_value_t *argv[2];
|
|
358
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
359
|
+
|
|
360
|
+
usearch_index_t index;
|
|
361
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
362
|
+
|
|
363
|
+
int64_t key;
|
|
364
|
+
js_get_value_int64(env, argv[1], &key);
|
|
365
|
+
|
|
366
|
+
usearch_error_t error = NULL;
|
|
367
|
+
bool exists = usearch_contains_safe(index, (usearch_key_t)key, &error);
|
|
368
|
+
throw_if_error(env, error);
|
|
369
|
+
|
|
370
|
+
js_value_t *result;
|
|
371
|
+
js_get_boolean(env, exists, &result);
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Save index to file
|
|
376
|
+
static js_value_t *fn_save(js_env_t *env, js_callback_info_t *info) {
|
|
377
|
+
size_t argc = 2;
|
|
378
|
+
js_value_t *argv[2];
|
|
379
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
380
|
+
|
|
381
|
+
usearch_index_t index;
|
|
382
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
383
|
+
|
|
384
|
+
utf8_t path[4096];
|
|
385
|
+
size_t path_len;
|
|
386
|
+
js_get_value_string_utf8(env, argv[1], path, sizeof(path), &path_len);
|
|
387
|
+
|
|
388
|
+
usearch_error_t error = NULL;
|
|
389
|
+
usearch_save_safe(index, (char *)path, &error);
|
|
390
|
+
throw_if_error(env, error);
|
|
391
|
+
|
|
392
|
+
js_value_t *undef;
|
|
393
|
+
js_get_undefined(env, &undef);
|
|
394
|
+
return undef;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Load index from file
|
|
398
|
+
static js_value_t *fn_load(js_env_t *env, js_callback_info_t *info) {
|
|
399
|
+
size_t argc = 2;
|
|
400
|
+
js_value_t *argv[2];
|
|
401
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
402
|
+
|
|
403
|
+
usearch_index_t index;
|
|
404
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
405
|
+
|
|
406
|
+
utf8_t path[4096];
|
|
407
|
+
size_t path_len;
|
|
408
|
+
js_get_value_string_utf8(env, argv[1], path, sizeof(path), &path_len);
|
|
409
|
+
|
|
410
|
+
usearch_error_t error = NULL;
|
|
411
|
+
usearch_load_safe(index, (char *)path, &error);
|
|
412
|
+
throw_if_error(env, error);
|
|
413
|
+
|
|
414
|
+
js_value_t *undef;
|
|
415
|
+
js_get_undefined(env, &undef);
|
|
416
|
+
return undef;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// View index from file (memory-mapped)
|
|
420
|
+
static js_value_t *fn_view(js_env_t *env, js_callback_info_t *info) {
|
|
421
|
+
size_t argc = 2;
|
|
422
|
+
js_value_t *argv[2];
|
|
423
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
424
|
+
|
|
425
|
+
usearch_index_t index;
|
|
426
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
427
|
+
|
|
428
|
+
utf8_t path[4096];
|
|
429
|
+
size_t path_len;
|
|
430
|
+
js_get_value_string_utf8(env, argv[1], path, sizeof(path), &path_len);
|
|
431
|
+
|
|
432
|
+
usearch_error_t error = NULL;
|
|
433
|
+
usearch_view_safe(index, (char *)path, &error);
|
|
434
|
+
throw_if_error(env, error);
|
|
435
|
+
|
|
436
|
+
js_value_t *undef;
|
|
437
|
+
js_get_undefined(env, &undef);
|
|
438
|
+
return undef;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Get vector by key
|
|
442
|
+
static js_value_t *fn_get(js_env_t *env, js_callback_info_t *info) {
|
|
443
|
+
size_t argc = 2;
|
|
444
|
+
js_value_t *argv[2];
|
|
445
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
446
|
+
|
|
447
|
+
usearch_index_t index;
|
|
448
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
449
|
+
|
|
450
|
+
int64_t key;
|
|
451
|
+
js_get_value_int64(env, argv[1], &key);
|
|
452
|
+
|
|
453
|
+
// Get dimensions to allocate buffer
|
|
454
|
+
usearch_error_t error = NULL;
|
|
455
|
+
size_t dims = usearch_dimensions_safe(index, &error);
|
|
456
|
+
if (error) {
|
|
457
|
+
throw_if_error(env, error);
|
|
458
|
+
js_value_t *undef;
|
|
459
|
+
js_get_undefined(env, &undef);
|
|
460
|
+
return undef;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Create output Float32Array
|
|
464
|
+
js_value_t *ab, *arr;
|
|
465
|
+
void *data;
|
|
466
|
+
js_create_arraybuffer(env, dims * sizeof(float), &data, &ab);
|
|
467
|
+
|
|
468
|
+
error = NULL;
|
|
469
|
+
size_t found = usearch_get_safe(index, (usearch_key_t)key, 1, data, usearch_scalar_f32_k, &error);
|
|
470
|
+
|
|
471
|
+
if (error || found == 0) {
|
|
472
|
+
if (error) throw_if_error(env, error);
|
|
473
|
+
js_value_t *null_val;
|
|
474
|
+
js_get_null(env, &null_val);
|
|
475
|
+
return null_val;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
js_create_typedarray(env, js_float32array, dims, ab, 0, &arr);
|
|
479
|
+
return arr;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Clear index
|
|
483
|
+
static js_value_t *fn_clear(js_env_t *env, js_callback_info_t *info) {
|
|
484
|
+
size_t argc = 1;
|
|
485
|
+
js_value_t *argv[1];
|
|
486
|
+
js_get_callback_info(env, info, &argc, argv, NULL, NULL);
|
|
487
|
+
|
|
488
|
+
usearch_index_t index;
|
|
489
|
+
js_get_value_external(env, argv[0], (void **)&index);
|
|
490
|
+
|
|
491
|
+
usearch_error_t error = NULL;
|
|
492
|
+
usearch_clear_safe(index, &error);
|
|
493
|
+
throw_if_error(env, error);
|
|
494
|
+
|
|
495
|
+
js_value_t *undef;
|
|
496
|
+
js_get_undefined(env, &undef);
|
|
497
|
+
return undef;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
static js_value_t *init(js_env_t *env, js_value_t *exports) {
|
|
501
|
+
// Define all exports
|
|
502
|
+
#define EXPORT_FN(name, fn) { \
|
|
503
|
+
js_value_t *val; \
|
|
504
|
+
js_create_function(env, name, -1, fn, NULL, &val); \
|
|
505
|
+
js_set_named_property(env, exports, name, val); \
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
EXPORT_FN("init", fn_init)
|
|
509
|
+
EXPORT_FN("free", fn_free)
|
|
510
|
+
EXPORT_FN("size", fn_size)
|
|
511
|
+
EXPORT_FN("dimensions", fn_dimensions)
|
|
512
|
+
EXPORT_FN("capacity", fn_capacity)
|
|
513
|
+
EXPORT_FN("reserve", fn_reserve)
|
|
514
|
+
EXPORT_FN("add", fn_add)
|
|
515
|
+
EXPORT_FN("search", fn_search)
|
|
516
|
+
EXPORT_FN("remove", fn_remove)
|
|
517
|
+
EXPORT_FN("contains", fn_contains)
|
|
518
|
+
EXPORT_FN("get", fn_get)
|
|
519
|
+
EXPORT_FN("save", fn_save)
|
|
520
|
+
EXPORT_FN("load", fn_load)
|
|
521
|
+
EXPORT_FN("view", fn_view)
|
|
522
|
+
EXPORT_FN("clear", fn_clear)
|
|
523
|
+
|
|
524
|
+
#undef EXPORT_FN
|
|
525
|
+
|
|
526
|
+
return exports;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
BARE_MODULE(usearch, init)
|
package/binding.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const binding = require('./binding')
|
|
2
|
+
|
|
3
|
+
class Index {
|
|
4
|
+
constructor (opts = {}) {
|
|
5
|
+
this._handle = binding.init(opts)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get size () {
|
|
9
|
+
return binding.size(this._handle)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get dimensions () {
|
|
13
|
+
return binding.dimensions(this._handle)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get capacity () {
|
|
17
|
+
return binding.capacity(this._handle)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
reserve (capacity) {
|
|
21
|
+
binding.reserve(this._handle, capacity)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
add (key, vector) {
|
|
25
|
+
if (!(vector instanceof Float32Array)) {
|
|
26
|
+
vector = new Float32Array(vector)
|
|
27
|
+
}
|
|
28
|
+
binding.add(this._handle, key, vector)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
search (query, k = 10) {
|
|
32
|
+
if (!(query instanceof Float32Array)) {
|
|
33
|
+
query = new Float32Array(query)
|
|
34
|
+
}
|
|
35
|
+
const result = binding.search(this._handle, query, k)
|
|
36
|
+
// Convert BigUint64Array keys to regular numbers for convenience
|
|
37
|
+
const keys = new Array(result.count)
|
|
38
|
+
for (let i = 0; i < result.count; i++) {
|
|
39
|
+
keys[i] = Number(result.keys[i])
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
keys,
|
|
43
|
+
distances: result.distances,
|
|
44
|
+
count: result.count
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
remove (key) {
|
|
49
|
+
return binding.remove(this._handle, key)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
contains (key) {
|
|
53
|
+
return binding.contains(this._handle, key)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get (key) {
|
|
57
|
+
return binding.get(this._handle, key)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
save (path) {
|
|
61
|
+
binding.save(this._handle, path)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
load (path) {
|
|
65
|
+
binding.load(this._handle, path)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
view (path) {
|
|
69
|
+
binding.view(this._handle, path)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
clear () {
|
|
73
|
+
binding.clear(this._handle)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
free () {
|
|
77
|
+
if (this._handle) {
|
|
78
|
+
binding.free(this._handle)
|
|
79
|
+
this._handle = null
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { Index, binding }
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bare-usearch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "USearch vector similarity search bindings for Bare",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"addon": true,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/CameronTofer/bare-usearch.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/CameronTofer/bare-usearch/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/CameronTofer/bare-usearch",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"usearch",
|
|
18
|
+
"vector",
|
|
19
|
+
"similarity",
|
|
20
|
+
"search",
|
|
21
|
+
"hnsw",
|
|
22
|
+
"bare",
|
|
23
|
+
"native"
|
|
24
|
+
],
|
|
25
|
+
"imports": {
|
|
26
|
+
"fs": {
|
|
27
|
+
"bare": "bare-fs",
|
|
28
|
+
"default": "fs"
|
|
29
|
+
},
|
|
30
|
+
"fs/*": {
|
|
31
|
+
"bare": "bare-fs/*",
|
|
32
|
+
"default": "fs/*"
|
|
33
|
+
},
|
|
34
|
+
"path": {
|
|
35
|
+
"bare": "bare-path",
|
|
36
|
+
"default": "path"
|
|
37
|
+
},
|
|
38
|
+
"os": {
|
|
39
|
+
"bare": "bare-os",
|
|
40
|
+
"default": "os"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"bare": ">=1.16.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"index.js",
|
|
48
|
+
"binding.js",
|
|
49
|
+
"binding.c",
|
|
50
|
+
"usearch_wrapper.cpp",
|
|
51
|
+
"prebuilds",
|
|
52
|
+
"CMakeLists.txt",
|
|
53
|
+
"vendor/usearch/c",
|
|
54
|
+
"vendor/usearch/include",
|
|
55
|
+
"vendor/usearch/fp16/include",
|
|
56
|
+
"vendor/usearch/VERSION"
|
|
57
|
+
],
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "bare-make generate && bare-make build && bare-make install",
|
|
60
|
+
"test": "npm run test:bare",
|
|
61
|
+
"test:bare": "brittle-bare test/*.js",
|
|
62
|
+
"test:node": "brittle test/*.js",
|
|
63
|
+
"clean": "rm -rf build prebuilds",
|
|
64
|
+
"update-usearch": "git submodule update --remote --recursive vendor/usearch"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"require-addon": "^1.0.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"bare-compat-napi": "^1.3.5",
|
|
71
|
+
"bare-fs": "^4.5.3",
|
|
72
|
+
"bare-os": "^3.6.2",
|
|
73
|
+
"bare-path": "^3.0.0",
|
|
74
|
+
"brittle": "^3.19.1",
|
|
75
|
+
"cmake-bare": "^1.1.2",
|
|
76
|
+
"cmake-napi": "^1.2.1"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Wrapper that catches C++ exceptions and converts to C error strings
|
|
2
|
+
// This prevents exceptions from escaping into the Bare runtime (compiled with -fno-exceptions)
|
|
3
|
+
|
|
4
|
+
#include <cstring>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
#include <exception>
|
|
7
|
+
|
|
8
|
+
extern "C" {
|
|
9
|
+
#include "usearch.h"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Thread-local buffer for error messages
|
|
13
|
+
static thread_local char error_buffer[256];
|
|
14
|
+
|
|
15
|
+
static const char* capture_exception() {
|
|
16
|
+
try {
|
|
17
|
+
throw; // rethrow current exception
|
|
18
|
+
} catch (const std::exception& e) {
|
|
19
|
+
std::strncpy(error_buffer, e.what(), sizeof(error_buffer) - 1);
|
|
20
|
+
error_buffer[sizeof(error_buffer) - 1] = '\0';
|
|
21
|
+
return error_buffer;
|
|
22
|
+
} catch (...) {
|
|
23
|
+
return "Unknown C++ exception";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Wrapped versions of usearch functions that catch exceptions
|
|
28
|
+
|
|
29
|
+
extern "C" {
|
|
30
|
+
|
|
31
|
+
USEARCH_EXPORT usearch_index_t usearch_init_safe(usearch_init_options_t* options, usearch_error_t* error) {
|
|
32
|
+
try {
|
|
33
|
+
return usearch_init(options, error);
|
|
34
|
+
} catch (...) {
|
|
35
|
+
*error = capture_exception();
|
|
36
|
+
return nullptr;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
USEARCH_EXPORT void usearch_free_safe(usearch_index_t index, usearch_error_t* error) {
|
|
41
|
+
try {
|
|
42
|
+
usearch_free(index, error);
|
|
43
|
+
} catch (...) {
|
|
44
|
+
*error = capture_exception();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
USEARCH_EXPORT size_t usearch_size_safe(usearch_index_t index, usearch_error_t* error) {
|
|
49
|
+
try {
|
|
50
|
+
return usearch_size(index, error);
|
|
51
|
+
} catch (...) {
|
|
52
|
+
*error = capture_exception();
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
USEARCH_EXPORT size_t usearch_capacity_safe(usearch_index_t index, usearch_error_t* error) {
|
|
58
|
+
try {
|
|
59
|
+
return usearch_capacity(index, error);
|
|
60
|
+
} catch (...) {
|
|
61
|
+
*error = capture_exception();
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
USEARCH_EXPORT size_t usearch_dimensions_safe(usearch_index_t index, usearch_error_t* error) {
|
|
67
|
+
try {
|
|
68
|
+
return usearch_dimensions(index, error);
|
|
69
|
+
} catch (...) {
|
|
70
|
+
*error = capture_exception();
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
USEARCH_EXPORT void usearch_reserve_safe(usearch_index_t index, size_t capacity, usearch_error_t* error) {
|
|
76
|
+
try {
|
|
77
|
+
usearch_reserve(index, capacity, error);
|
|
78
|
+
} catch (...) {
|
|
79
|
+
*error = capture_exception();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
USEARCH_EXPORT void usearch_add_safe(usearch_index_t index, usearch_key_t key, void const* vector,
|
|
84
|
+
usearch_scalar_kind_t kind, usearch_error_t* error) {
|
|
85
|
+
try {
|
|
86
|
+
usearch_add(index, key, vector, kind, error);
|
|
87
|
+
} catch (...) {
|
|
88
|
+
*error = capture_exception();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
USEARCH_EXPORT size_t usearch_search_safe(usearch_index_t index, void const* query, usearch_scalar_kind_t kind,
|
|
93
|
+
size_t count, usearch_key_t* keys, usearch_distance_t* distances,
|
|
94
|
+
usearch_error_t* error) {
|
|
95
|
+
try {
|
|
96
|
+
return usearch_search(index, query, kind, count, keys, distances, error);
|
|
97
|
+
} catch (...) {
|
|
98
|
+
*error = capture_exception();
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
USEARCH_EXPORT size_t usearch_remove_safe(usearch_index_t index, usearch_key_t key, usearch_error_t* error) {
|
|
104
|
+
try {
|
|
105
|
+
return usearch_remove(index, key, error);
|
|
106
|
+
} catch (...) {
|
|
107
|
+
*error = capture_exception();
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
USEARCH_EXPORT bool usearch_contains_safe(usearch_index_t index, usearch_key_t key, usearch_error_t* error) {
|
|
113
|
+
try {
|
|
114
|
+
return usearch_contains(index, key, error);
|
|
115
|
+
} catch (...) {
|
|
116
|
+
*error = capture_exception();
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
USEARCH_EXPORT size_t usearch_get_safe(usearch_index_t index, usearch_key_t key, size_t count,
|
|
122
|
+
void* vector, usearch_scalar_kind_t kind, usearch_error_t* error) {
|
|
123
|
+
try {
|
|
124
|
+
return usearch_get(index, key, count, vector, kind, error);
|
|
125
|
+
} catch (...) {
|
|
126
|
+
*error = capture_exception();
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
USEARCH_EXPORT void usearch_save_safe(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
132
|
+
try {
|
|
133
|
+
usearch_save(index, path, error);
|
|
134
|
+
} catch (...) {
|
|
135
|
+
*error = capture_exception();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
USEARCH_EXPORT void usearch_load_safe(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
140
|
+
try {
|
|
141
|
+
usearch_load(index, path, error);
|
|
142
|
+
} catch (...) {
|
|
143
|
+
*error = capture_exception();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
USEARCH_EXPORT void usearch_view_safe(usearch_index_t index, char const* path, usearch_error_t* error) {
|
|
148
|
+
try {
|
|
149
|
+
usearch_view(index, path, error);
|
|
150
|
+
} catch (...) {
|
|
151
|
+
*error = capture_exception();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
USEARCH_EXPORT void usearch_clear_safe(usearch_index_t index, usearch_error_t* error) {
|
|
156
|
+
try {
|
|
157
|
+
usearch_clear(index, error);
|
|
158
|
+
} catch (...) {
|
|
159
|
+
*error = capture_exception();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} // extern "C"
|
|
164
|
+
|