From a572eaaebd109555bf3594d06dd43c7e766b1cf1 Mon Sep 17 00:00:00 2001 From: Abe Tomoaki Date: Sun, 8 Sep 2024 12:26:08 +0900 Subject: [PATCH 1/2] Fix: JavaScript Fix error when searching after load() and view() I got an error when I loaded and searched with load() or view(). Code Example: ```js // Saved with `index.save('index.usearch');` in another script. index.load('index.usearch'); const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10); ``` Error: ``` /build/javascript/dist/cjs/usearch.js:314 const result = __classPrivateFieldGet(this, _Index_compiledIndex, "f").search(normalizedVectors, k); ^ TypeError: Search failed at Index.search (/build/javascript/dist/cjs/usearch.js:314:80) at Object. (/build/example.js:9:21) at Module._compile (node:internal/modules/cjs/loader:1469:14) at Module._extensions..js (node:internal/modules/cjs/loader:1548:10) at Module.load (node:internal/modules/cjs/loader:1288:32) at Module._load (node:internal/modules/cjs/loader:1104:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12) at node:internal/main/run_main_module:28:49 Node.js v20.17.0 ``` This bug fixed. --- javascript/lib.cpp | 2 ++ javascript/usearch.test.js | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/javascript/lib.cpp b/javascript/lib.cpp index 38856b3f..34ef4cf6 100644 --- a/javascript/lib.cpp +++ b/javascript/lib.cpp @@ -129,6 +129,7 @@ void CompiledIndex::Load(Napi::CallbackInfo const& ctx) { auto result = native_->load(path.c_str()); if (!result) Napi::TypeError::New(ctx.Env(), result.error.release()).ThrowAsJavaScriptException(); + native_->reserve(ceil2(native_->size())); } catch (...) { Napi::TypeError::New(ctx.Env(), "Loading failed").ThrowAsJavaScriptException(); @@ -142,6 +143,7 @@ void CompiledIndex::View(Napi::CallbackInfo const& ctx) { auto result = native_->view(path.c_str()); if (!result) Napi::TypeError::New(ctx.Env(), result.error.release()).ThrowAsJavaScriptException(); + native_->reserve(ceil2(native_->size())); } catch (...) { Napi::TypeError::New(ctx.Env(), "Memory-mapping failed").ThrowAsJavaScriptException(); diff --git a/javascript/usearch.test.js b/javascript/usearch.test.js index 688a5847..2ee98d25 100644 --- a/javascript/usearch.test.js +++ b/javascript/usearch.test.js @@ -1,5 +1,8 @@ const test = require('node:test'); const assert = require('node:assert'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); const usearch = require('./dist/cjs/usearch.js'); function assertAlmostEqual(actual, expected, tolerance = 1e-6) { @@ -82,3 +85,50 @@ test('Operations with invalid values', () => { assert.equal(err.message, 'Vectors must be a TypedArray or an array of arrays.'); } }); + + +test('Serialization', async (t) => { + const indexPath = path.join(os.tmpdir(), 'usearch.test.index') + + t.beforeEach(() => { + const index = new usearch.Index({ + metric: "l2sq", + connectivity: 16, + dimensions: 3, + }); + index.add(42n, new Float32Array([0.2, 0.6, 0.4])); + index.save(indexPath); + }); + + t.afterEach(() => { + fs.unlinkSync(indexPath); + }); + + await t.test('load', () => { + const index = new usearch.Index({ + metric: "l2sq", + connectivity: 16, + dimensions: 3, + }); + index.load(indexPath); + const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10); + + assert.equal(index.size(), 1); + assert.deepEqual(results.keys, new BigUint64Array([42n])); + assertAlmostEqual(results.distances[0], new Float32Array([0])); + }); + + await t.test('view', () => { + const index = new usearch.Index({ + metric: "l2sq", + connectivity: 16, + dimensions: 3, + }); + index.view(indexPath); + const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10); + + assert.equal(index.size(), 1); + assert.deepEqual(results.keys, new BigUint64Array([42n])); + assertAlmostEqual(results.distances[0], new Float32Array([0])); + }); +}); From 18939c75f25c38c914956758ac5ef170c69cd3c6 Mon Sep 17 00:00:00 2001 From: Abe Tomoaki Date: Fri, 11 Oct 2024 08:01:14 +0900 Subject: [PATCH 2/2] Use try_reserve() instead of reserve() --- javascript/lib.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/javascript/lib.cpp b/javascript/lib.cpp index 34ef4cf6..32258a44 100644 --- a/javascript/lib.cpp +++ b/javascript/lib.cpp @@ -129,7 +129,8 @@ void CompiledIndex::Load(Napi::CallbackInfo const& ctx) { auto result = native_->load(path.c_str()); if (!result) Napi::TypeError::New(ctx.Env(), result.error.release()).ThrowAsJavaScriptException(); - native_->reserve(ceil2(native_->size())); + if (!native_->try_reserve(ceil2(native_->size()))) + Napi::Error::New(ctx.Env(), "Failed to reserve memory").ThrowAsJavaScriptException(); } catch (...) { Napi::TypeError::New(ctx.Env(), "Loading failed").ThrowAsJavaScriptException(); @@ -143,7 +144,8 @@ void CompiledIndex::View(Napi::CallbackInfo const& ctx) { auto result = native_->view(path.c_str()); if (!result) Napi::TypeError::New(ctx.Env(), result.error.release()).ThrowAsJavaScriptException(); - native_->reserve(ceil2(native_->size())); + if (!native_->try_reserve(ceil2(native_->size()))) + Napi::Error::New(ctx.Env(), "Failed to reserve memory").ThrowAsJavaScriptException(); } catch (...) { Napi::TypeError::New(ctx.Env(), "Memory-mapping failed").ThrowAsJavaScriptException();