sillstone 0.2.0 → 0.2.1

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/index.js CHANGED
@@ -25,7 +25,10 @@ class FfiError extends Error {
25
25
  }
26
26
 
27
27
  let native = null;
28
+ const platformKey = `${process.platform}-${process.arch}`;
28
29
  const candidates = [
30
+ path.join(__dirname, "prebuilds", platformKey, "sillstone_napi.node"),
31
+ path.join(__dirname, "prebuilds", platformKey, process.platform === "win32" ? "sillstone_ffi.dll" : (process.platform === "darwin" ? "libsillstone_ffi.dylib" : "libsillstone_ffi.so")),
29
32
  path.join(__dirname, "sillstone_napi.node"),
30
33
  path.join(__dirname, "build", "Release", "sillstone_napi.node"),
31
34
  path.join(__dirname, "build", "Debug", "sillstone_napi.node"),
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "sillstone",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Pure-Rust zero-dependency multimedia, hashing, and array computing suite",
5
5
  "main": "index.js",
6
+ "files": [
7
+ "index.js",
8
+ "prebuilds/",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
6
12
  "scripts": {
7
13
  "test": "node --test"
8
14
  },
package/scripts/build.js DELETED
@@ -1,49 +0,0 @@
1
- const { execSync } = require("child_process");
2
- const fs = require("fs");
3
- const path = require("path");
4
-
5
- const nodeDir = path.resolve(__dirname, "..");
6
- const outDir = path.join(nodeDir, "build", "Release");
7
- fs.mkdirSync(outDir, { recursive: true });
8
-
9
- const outFile = path.join(outDir, "sillstone_napi.node");
10
- const srcFile = path.join(nodeDir, "src", "generated.c");
11
- const repoRoot = path.resolve(nodeDir, "..", "..");
12
- const ffiLibDir = path.join(repoRoot, "target", "debug");
13
-
14
- console.log("Building N-API addon...");
15
-
16
- if (process.platform === "win32") {
17
- const nodeDef = path.join(nodeDir, "src", "node.def");
18
- const libNode = path.join(outDir, "libnode.a");
19
-
20
- // 1. Generate libnode.a import library if not present
21
- try {
22
- execSync(`dlltool -d "${nodeDef}" -D node.exe -l "${libNode}"`, { stdio: "inherit" });
23
- } catch (e) {
24
- console.error("dlltool failed:", e.message);
25
- }
26
-
27
- // 2. Compile generated.c with gcc linking against libnode.a and sillstone_ffi.dll
28
- const cmd = `gcc -shared -O3 -I"${path.join(nodeDir, "src")}" -I"${path.join(repoRoot, "include")}" "${srcFile}" "${libNode}" -L"${ffiLibDir}" -lsillstone_ffi -o "${outFile}"`;
29
- console.log("Executing:", cmd);
30
- execSync(cmd, { stdio: "inherit" });
31
- } else if (process.platform === "darwin") {
32
- const cmd = `gcc -shared -O3 -fPIC -undefined dynamic_lookup -I"${path.join(nodeDir, "src")}" -I"${path.join(repoRoot, "include")}" "${srcFile}" -L"${ffiLibDir}" -lsillstone_ffi -o "${outFile}"`;
33
- execSync(cmd, { stdio: "inherit" });
34
- } else {
35
- // Linux
36
- const cmd = `gcc -shared -O3 -fPIC -I"${path.join(nodeDir, "src")}" -I"${path.join(repoRoot, "include")}" "${srcFile}" -L"${ffiLibDir}" -lsillstone_ffi -Wl,-rpath,'$ORIGIN/../../../../target/debug' -o "${outFile}"`;
37
- console.log("Executing:", cmd);
38
- execSync(cmd, { stdio: "inherit" });
39
- }
40
-
41
- if (process.platform === "win32") {
42
- const dllSrc = path.join(ffiLibDir, "sillstone_ffi.dll");
43
- const dllDst = path.join(outDir, "sillstone_ffi.dll");
44
- if (fs.existsSync(dllSrc)) {
45
- fs.copyFileSync(dllSrc, dllDst);
46
- console.log("Copied sillstone_ffi.dll to:", dllDst);
47
- }
48
- }
49
- console.log("N-API addon built successfully at:", outFile);
package/src/generated.c DELETED
@@ -1,511 +0,0 @@
1
- /* Generated by sillstone-idl-gen. DO NOT EDIT. */
2
- #include <node_api.h>
3
- #include <stdint.h>
4
- #include <stddef.h>
5
- #include <stdio.h>
6
- #include "sillstone.h"
7
-
8
- static void throw_status(napi_env env, int32_t status) {
9
- char buf[16];
10
- snprintf(buf, sizeof(buf), "%d", status);
11
- napi_throw_error(env, buf, "FFI error");
12
- }
13
-
14
- static napi_value n_sillstone_version_major(napi_env env, napi_callback_info info) {
15
- uint32_t val = sillstone_version_major();
16
- napi_value result;
17
- napi_create_uint32(env, val, &result);
18
- return result;
19
- }
20
-
21
- static napi_value n_sillstone_version_minor(napi_env env, napi_callback_info info) {
22
- uint32_t val = sillstone_version_minor();
23
- napi_value result;
24
- napi_create_uint32(env, val, &result);
25
- return result;
26
- }
27
-
28
- static napi_value n_sillstone_version_patch(napi_env env, napi_callback_info info) {
29
- uint32_t val = sillstone_version_patch();
30
- napi_value result;
31
- napi_create_uint32(env, val, &result);
32
- return result;
33
- }
34
-
35
- static napi_value n_sillstone_buffer_checksum(napi_env env, napi_callback_info info) {
36
- size_t argc = 1;
37
- napi_value args[1];
38
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
39
- void* data;
40
- size_t length;
41
- napi_get_buffer_info(env, args[0], &data, &length);
42
- uint64_t res = sillstone_buffer_checksum((const uint8_t*)data, length);
43
- napi_value result;
44
- napi_create_bigint_uint64(env, res, &result);
45
- return result;
46
- }
47
-
48
- static napi_value n_sillstone_digest_sha256(napi_env env, napi_callback_info info) {
49
- size_t argc = 1;
50
- napi_value args[1];
51
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
52
- void* data = NULL;
53
- size_t length = 0;
54
- if (argc > 0) {
55
- napi_get_buffer_info(env, args[0], &data, &length);
56
- }
57
- uint8_t out[32];
58
- sillstone_digest_sha256((const uint8_t*)data, length, out);
59
- napi_value result;
60
- napi_create_buffer_copy(env, 32, out, NULL, &result);
61
- return result;
62
- }
63
-
64
- static napi_value n_sillstone_digest_sha3_256(napi_env env, napi_callback_info info) {
65
- size_t argc = 1;
66
- napi_value args[1];
67
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
68
- void* data = NULL;
69
- size_t length = 0;
70
- if (argc > 0) {
71
- napi_get_buffer_info(env, args[0], &data, &length);
72
- }
73
- uint8_t out[32];
74
- sillstone_digest_sha3_256((const uint8_t*)data, length, out);
75
- napi_value result;
76
- napi_create_buffer_copy(env, 32, out, NULL, &result);
77
- return result;
78
- }
79
-
80
- static napi_value n_sillstone_digest_sha3_512(napi_env env, napi_callback_info info) {
81
- size_t argc = 1;
82
- napi_value args[1];
83
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
84
- void* data = NULL;
85
- size_t length = 0;
86
- if (argc > 0) {
87
- napi_get_buffer_info(env, args[0], &data, &length);
88
- }
89
- uint8_t out[64];
90
- sillstone_digest_sha3_512((const uint8_t*)data, length, out);
91
- napi_value result;
92
- napi_create_buffer_copy(env, 64, out, NULL, &result);
93
- return result;
94
- }
95
-
96
- static napi_value n_sillstone_digest_blake2b_512(napi_env env, napi_callback_info info) {
97
- size_t argc = 1;
98
- napi_value args[1];
99
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
100
- void* data = NULL;
101
- size_t length = 0;
102
- if (argc > 0) {
103
- napi_get_buffer_info(env, args[0], &data, &length);
104
- }
105
- uint8_t out[64];
106
- sillstone_digest_blake2b_512((const uint8_t*)data, length, out);
107
- napi_value result;
108
- napi_create_buffer_copy(env, 64, out, NULL, &result);
109
- return result;
110
- }
111
-
112
- static napi_value n_sillstone_digest_blake3(napi_env env, napi_callback_info info) {
113
- size_t argc = 1;
114
- napi_value args[1];
115
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
116
- void* data = NULL;
117
- size_t length = 0;
118
- if (argc > 0) {
119
- napi_get_buffer_info(env, args[0], &data, &length);
120
- }
121
- uint8_t out[32];
122
- sillstone_digest_blake3((const uint8_t*)data, length, out);
123
- napi_value result;
124
- napi_create_buffer_copy(env, 32, out, NULL, &result);
125
- return result;
126
- }
127
-
128
- static napi_value n_sillstone_math_dst1(napi_env env, napi_callback_info info) {
129
- size_t argc = 1;
130
- napi_value args[1];
131
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
132
- void* data = NULL;
133
- size_t length = 0;
134
- if (argc > 0) {
135
- napi_get_buffer_info(env, args[0], &data, &length);
136
- }
137
- size_t count = length / sizeof(double);
138
- napi_value result;
139
- void* out_data = NULL;
140
- napi_create_buffer(env, length, &out_data, &result);
141
- sillstone_math_dst1((const double*)data, count, (double*)out_data);
142
- return result;
143
- }
144
-
145
- static napi_value n_sillstone_math_dst2(napi_env env, napi_callback_info info) {
146
- size_t argc = 1;
147
- napi_value args[1];
148
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
149
- void* data = NULL;
150
- size_t length = 0;
151
- if (argc > 0) {
152
- napi_get_buffer_info(env, args[0], &data, &length);
153
- }
154
- size_t count = length / sizeof(double);
155
- napi_value result;
156
- void* out_data = NULL;
157
- napi_create_buffer(env, length, &out_data, &result);
158
- sillstone_math_dst2((const double*)data, count, (double*)out_data);
159
- return result;
160
- }
161
-
162
- static napi_value n_sillstone_math_dst3(napi_env env, napi_callback_info info) {
163
- size_t argc = 1;
164
- napi_value args[1];
165
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
166
- void* data = NULL;
167
- size_t length = 0;
168
- if (argc > 0) {
169
- napi_get_buffer_info(env, args[0], &data, &length);
170
- }
171
- size_t count = length / sizeof(double);
172
- napi_value result;
173
- void* out_data = NULL;
174
- napi_create_buffer(env, length, &out_data, &result);
175
- sillstone_math_dst3((const double*)data, count, (double*)out_data);
176
- return result;
177
- }
178
-
179
- static napi_value n_sillstone_unicode_grapheme_count(napi_env env, napi_callback_info info) {
180
- size_t argc = 1;
181
- napi_value args[1];
182
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
183
- void* data = NULL;
184
- size_t length = 0;
185
- if (argc > 0) {
186
- napi_get_buffer_info(env, args[0], &data, &length);
187
- }
188
- size_t count = 0;
189
- sillstone_unicode_grapheme_count((const uint8_t*)data, length, &count);
190
- napi_value result;
191
- napi_create_int64(env, (int64_t)count, &result);
192
- return result;
193
- }
194
-
195
- static napi_value n_sillstone_unicode_char_category(napi_env env, napi_callback_info info) {
196
- size_t argc = 1;
197
- napi_value args[1];
198
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
199
- uint32_t cp = 0;
200
- if (argc > 0) {
201
- napi_get_value_uint32(env, args[0], &cp);
202
- }
203
- uint8_t cat = 0;
204
- sillstone_unicode_char_category(cp, &cat);
205
- napi_value result;
206
- napi_create_uint32(env, cat, &result);
207
- return result;
208
- }
209
-
210
- static napi_value n_sillstone_curator_detect(napi_env env, napi_callback_info info) {
211
- size_t argc = 1;
212
- napi_value args[1];
213
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
214
- void* data = NULL; size_t length = 0;
215
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
216
- uint32_t format = 0, modality = 0, pending = 0;
217
- int32_t status = sillstone_curator_detect((const uint8_t*)data, length, &format, &modality, &pending);
218
- if (status != 0) { throw_status(env, status); return NULL; }
219
- napi_value result, v_fmt, v_mod, v_pend;
220
- napi_create_object(env, &result);
221
- napi_create_uint32(env, format, &v_fmt);
222
- napi_set_named_property(env, result, "format", v_fmt);
223
- napi_create_uint32(env, modality, &v_mod);
224
- napi_set_named_property(env, result, "modality", v_mod);
225
- napi_create_uint32(env, pending, &v_pend);
226
- napi_set_named_property(env, result, "pending", v_pend);
227
- return result;
228
- }
229
-
230
- static napi_value n_sillstone_curator_content_hash(napi_env env, napi_callback_info info) {
231
- size_t argc = 1;
232
- napi_value args[1];
233
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
234
- void* data = NULL; size_t length = 0;
235
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
236
- uint8_t out_buf[32];
237
- size_t out_len = 0;
238
- int32_t status = sillstone_curator_content_hash((const uint8_t*)data, length, out_buf, 32, &out_len);
239
- if (status != 0) { throw_status(env, status); return NULL; }
240
- napi_value result;
241
- napi_create_buffer_copy(env, 32, out_buf, NULL, &result);
242
- return result;
243
- }
244
-
245
- static napi_value n_sillstone_curator_signature(napi_env env, napi_callback_info info) {
246
- size_t argc = 1;
247
- napi_value args[1];
248
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
249
- void* data = NULL; size_t length = 0;
250
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
251
- uint8_t* out_ptr = NULL; size_t out_len = 0;
252
- int32_t status = sillstone_curator_signature((const uint8_t*)data, length, &out_ptr, &out_len);
253
- if (status != 0 || out_ptr == NULL) { throw_status(env, status != 0 ? status : -1); return NULL; }
254
- napi_value result;
255
- napi_create_buffer_copy(env, out_len, out_ptr, NULL, &result);
256
- sillstone_curator_free(out_ptr, out_len);
257
- return result;
258
- }
259
-
260
- static napi_value n_sillstone_curator_describe(napi_env env, napi_callback_info info) {
261
- size_t argc = 1;
262
- napi_value args[1];
263
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
264
- void* data = NULL; size_t length = 0;
265
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
266
- uint8_t* out_ptr = NULL; size_t out_len = 0;
267
- int32_t status = sillstone_curator_describe((const uint8_t*)data, length, &out_ptr, &out_len);
268
- if (status != 0 || out_ptr == NULL) { throw_status(env, status != 0 ? status : -1); return NULL; }
269
- napi_value result;
270
- napi_create_buffer_copy(env, out_len, out_ptr, NULL, &result);
271
- sillstone_curator_free(out_ptr, out_len);
272
- return result;
273
- }
274
-
275
- static napi_value n_sillstone_curator_match(napi_env env, napi_callback_info info) {
276
- size_t argc = 2;
277
- napi_value args[2];
278
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
279
- void* a = NULL; size_t a_len = 0;
280
- void* b = NULL; size_t b_len = 0;
281
- if (argc > 0) napi_get_buffer_info(env, args[0], &a, &a_len);
282
- if (argc > 1) napi_get_buffer_info(env, args[1], &b, &b_len);
283
- uint32_t tag = 0, matched = 0; uint64_t slots[4] = {0};
284
- int32_t status = sillstone_curator_match((const uint8_t*)a, a_len, (const uint8_t*)b, b_len, &tag, slots, 4, &matched);
285
- if (status != 0) { throw_status(env, status); return NULL; }
286
- napi_value result, v_tag, v_slots, v_matched;
287
- napi_create_object(env, &result);
288
- napi_create_uint32(env, tag, &v_tag);
289
- napi_set_named_property(env, result, "tag", v_tag);
290
- napi_create_array_with_length(env, 4, &v_slots);
291
- for (uint32_t i = 0; i < 4; i++) {
292
- napi_value sv;
293
- napi_create_bigint_uint64(env, slots[i], &sv);
294
- napi_set_element(env, v_slots, i, sv);
295
- }
296
- napi_set_named_property(env, result, "slots", v_slots);
297
- napi_create_uint32(env, matched, &v_matched);
298
- napi_set_named_property(env, result, "matched", v_matched);
299
- return result;
300
- }
301
-
302
- static napi_value n_sillstone_curator_free(napi_env env, napi_callback_info info) {
303
- napi_value result;
304
- napi_get_undefined(env, &result);
305
- return result;
306
- }
307
-
308
- static napi_value n_sillstone_image_phash(napi_env env, napi_callback_info info) {
309
- size_t argc = 4;
310
- napi_value args[4];
311
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
312
- void* data = NULL; size_t length = 0;
313
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
314
- uint32_t width = 0, height = 0, layout = 0;
315
- if (argc > 1) napi_get_value_uint32(env, args[1], &width);
316
- if (argc > 2) napi_get_value_uint32(env, args[2], &height);
317
- if (argc > 3) napi_get_value_uint32(env, args[3], &layout);
318
- uint64_t hash_val = 0;
319
- int32_t status = sillstone_image_phash((const uint8_t*)data, length, width, height, layout, &hash_val);
320
- if (status != 0) { napi_throw_error(env, NULL, "image hash failed"); return NULL; }
321
- napi_value result;
322
- napi_create_bigint_uint64(env, hash_val, &result);
323
- return result;
324
- }
325
-
326
- static napi_value n_sillstone_image_ahash(napi_env env, napi_callback_info info) {
327
- size_t argc = 4;
328
- napi_value args[4];
329
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
330
- void* data = NULL; size_t length = 0;
331
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
332
- uint32_t width = 0, height = 0, layout = 0;
333
- if (argc > 1) napi_get_value_uint32(env, args[1], &width);
334
- if (argc > 2) napi_get_value_uint32(env, args[2], &height);
335
- if (argc > 3) napi_get_value_uint32(env, args[3], &layout);
336
- uint64_t hash_val = 0;
337
- int32_t status = sillstone_image_ahash((const uint8_t*)data, length, width, height, layout, &hash_val);
338
- if (status != 0) { napi_throw_error(env, NULL, "image hash failed"); return NULL; }
339
- napi_value result;
340
- napi_create_bigint_uint64(env, hash_val, &result);
341
- return result;
342
- }
343
-
344
- static napi_value n_sillstone_image_dhash(napi_env env, napi_callback_info info) {
345
- size_t argc = 4;
346
- napi_value args[4];
347
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
348
- void* data = NULL; size_t length = 0;
349
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
350
- uint32_t width = 0, height = 0, layout = 0;
351
- if (argc > 1) napi_get_value_uint32(env, args[1], &width);
352
- if (argc > 2) napi_get_value_uint32(env, args[2], &height);
353
- if (argc > 3) napi_get_value_uint32(env, args[3], &layout);
354
- uint64_t hash_val = 0;
355
- int32_t status = sillstone_image_dhash((const uint8_t*)data, length, width, height, layout, &hash_val);
356
- if (status != 0) { napi_throw_error(env, NULL, "image hash failed"); return NULL; }
357
- napi_value result;
358
- napi_create_bigint_uint64(env, hash_val, &result);
359
- return result;
360
- }
361
-
362
- static napi_value n_sillstone_image_whash(napi_env env, napi_callback_info info) {
363
- size_t argc = 4;
364
- napi_value args[4];
365
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
366
- void* data = NULL; size_t length = 0;
367
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
368
- uint32_t width = 0, height = 0, layout = 0;
369
- if (argc > 1) napi_get_value_uint32(env, args[1], &width);
370
- if (argc > 2) napi_get_value_uint32(env, args[2], &height);
371
- if (argc > 3) napi_get_value_uint32(env, args[3], &layout);
372
- uint64_t hash_val = 0;
373
- int32_t status = sillstone_image_whash((const uint8_t*)data, length, width, height, layout, 3, &hash_val);
374
- if (status != 0) { napi_throw_error(env, NULL, "image hash failed"); return NULL; }
375
- napi_value result;
376
- napi_create_bigint_uint64(env, hash_val, &result);
377
- return result;
378
- }
379
-
380
- static napi_value n_sillstone_image_colorhash(napi_env env, napi_callback_info info) {
381
- size_t argc = 4;
382
- napi_value args[4];
383
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
384
- void* data = NULL; size_t length = 0;
385
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
386
- uint32_t width = 0, height = 0, layout = 0;
387
- if (argc > 1) napi_get_value_uint32(env, args[1], &width);
388
- if (argc > 2) napi_get_value_uint32(env, args[2], &height);
389
- if (argc > 3) napi_get_value_uint32(env, args[3], &layout);
390
- uint64_t hash_val = 0;
391
- int32_t status = sillstone_image_colorhash((const uint8_t*)data, length, width, height, layout, 4, &hash_val);
392
- if (status != 0) { napi_throw_error(env, NULL, "image hash failed"); return NULL; }
393
- napi_value result;
394
- napi_create_bigint_uint64(env, hash_val, &result);
395
- return result;
396
- }
397
-
398
- static napi_value n_sillstone_text_tlsh(napi_env env, napi_callback_info info) {
399
- size_t argc = 1;
400
- napi_value args[1];
401
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
402
- void* data = NULL; size_t length = 0;
403
- if (argc > 0) napi_get_buffer_info(env, args[0], &data, &length);
404
- uint8_t out_buf[72];
405
- int32_t status = sillstone_text_tlsh((const uint8_t*)data, length, out_buf);
406
- if (status != 0) { napi_throw_error(env, NULL, "tlsh failed"); return NULL; }
407
- napi_value result;
408
- napi_create_buffer_copy(env, 72, out_buf, NULL, &result);
409
- return result;
410
- }
411
-
412
- static napi_value n_sillstone_text_tlsh_distance(napi_env env, napi_callback_info info) {
413
- size_t argc = 2;
414
- napi_value args[2];
415
- napi_get_cb_info(env, info, &argc, args, NULL, NULL);
416
- void* a = NULL; size_t a_len = 0;
417
- void* b = NULL; size_t b_len = 0;
418
- if (argc > 0) napi_get_buffer_info(env, args[0], &a, &a_len);
419
- if (argc > 1) napi_get_buffer_info(env, args[1], &b, &b_len);
420
- uint32_t dist = 0;
421
- int32_t status = sillstone_text_tlsh_distance((const uint8_t*)a, (const uint8_t*)b, &dist);
422
- if (status != 0) { throw_status(env, status); return NULL; }
423
- napi_value result;
424
- napi_create_uint32(env, dist, &result);
425
- return result;
426
- }
427
-
428
- NAPI_MODULE_INIT() {
429
- napi_value fn_sillstone_version_major;
430
- napi_create_function(env, NULL, 0, n_sillstone_version_major, NULL, &fn_sillstone_version_major);
431
- napi_set_named_property(env, exports, "sillstone_version_major", fn_sillstone_version_major);
432
- napi_value fn_sillstone_version_minor;
433
- napi_create_function(env, NULL, 0, n_sillstone_version_minor, NULL, &fn_sillstone_version_minor);
434
- napi_set_named_property(env, exports, "sillstone_version_minor", fn_sillstone_version_minor);
435
- napi_value fn_sillstone_version_patch;
436
- napi_create_function(env, NULL, 0, n_sillstone_version_patch, NULL, &fn_sillstone_version_patch);
437
- napi_set_named_property(env, exports, "sillstone_version_patch", fn_sillstone_version_patch);
438
- napi_value fn_sillstone_buffer_checksum;
439
- napi_create_function(env, NULL, 0, n_sillstone_buffer_checksum, NULL, &fn_sillstone_buffer_checksum);
440
- napi_set_named_property(env, exports, "sillstone_buffer_checksum", fn_sillstone_buffer_checksum);
441
- napi_value fn_sillstone_digest_sha256;
442
- napi_create_function(env, NULL, 0, n_sillstone_digest_sha256, NULL, &fn_sillstone_digest_sha256);
443
- napi_set_named_property(env, exports, "sillstone_digest_sha256", fn_sillstone_digest_sha256);
444
- napi_value fn_sillstone_digest_sha3_256;
445
- napi_create_function(env, NULL, 0, n_sillstone_digest_sha3_256, NULL, &fn_sillstone_digest_sha3_256);
446
- napi_set_named_property(env, exports, "sillstone_digest_sha3_256", fn_sillstone_digest_sha3_256);
447
- napi_value fn_sillstone_digest_sha3_512;
448
- napi_create_function(env, NULL, 0, n_sillstone_digest_sha3_512, NULL, &fn_sillstone_digest_sha3_512);
449
- napi_set_named_property(env, exports, "sillstone_digest_sha3_512", fn_sillstone_digest_sha3_512);
450
- napi_value fn_sillstone_digest_blake2b_512;
451
- napi_create_function(env, NULL, 0, n_sillstone_digest_blake2b_512, NULL, &fn_sillstone_digest_blake2b_512);
452
- napi_set_named_property(env, exports, "sillstone_digest_blake2b_512", fn_sillstone_digest_blake2b_512);
453
- napi_value fn_sillstone_digest_blake3;
454
- napi_create_function(env, NULL, 0, n_sillstone_digest_blake3, NULL, &fn_sillstone_digest_blake3);
455
- napi_set_named_property(env, exports, "sillstone_digest_blake3", fn_sillstone_digest_blake3);
456
- napi_value fn_sillstone_math_dst1;
457
- napi_create_function(env, NULL, 0, n_sillstone_math_dst1, NULL, &fn_sillstone_math_dst1);
458
- napi_set_named_property(env, exports, "sillstone_math_dst1", fn_sillstone_math_dst1);
459
- napi_value fn_sillstone_math_dst2;
460
- napi_create_function(env, NULL, 0, n_sillstone_math_dst2, NULL, &fn_sillstone_math_dst2);
461
- napi_set_named_property(env, exports, "sillstone_math_dst2", fn_sillstone_math_dst2);
462
- napi_value fn_sillstone_math_dst3;
463
- napi_create_function(env, NULL, 0, n_sillstone_math_dst3, NULL, &fn_sillstone_math_dst3);
464
- napi_set_named_property(env, exports, "sillstone_math_dst3", fn_sillstone_math_dst3);
465
- napi_value fn_sillstone_unicode_grapheme_count;
466
- napi_create_function(env, NULL, 0, n_sillstone_unicode_grapheme_count, NULL, &fn_sillstone_unicode_grapheme_count);
467
- napi_set_named_property(env, exports, "sillstone_unicode_grapheme_count", fn_sillstone_unicode_grapheme_count);
468
- napi_value fn_sillstone_unicode_char_category;
469
- napi_create_function(env, NULL, 0, n_sillstone_unicode_char_category, NULL, &fn_sillstone_unicode_char_category);
470
- napi_set_named_property(env, exports, "sillstone_unicode_char_category", fn_sillstone_unicode_char_category);
471
- napi_value fn_sillstone_curator_detect;
472
- napi_create_function(env, NULL, 0, n_sillstone_curator_detect, NULL, &fn_sillstone_curator_detect);
473
- napi_set_named_property(env, exports, "sillstone_curator_detect", fn_sillstone_curator_detect);
474
- napi_value fn_sillstone_curator_content_hash;
475
- napi_create_function(env, NULL, 0, n_sillstone_curator_content_hash, NULL, &fn_sillstone_curator_content_hash);
476
- napi_set_named_property(env, exports, "sillstone_curator_content_hash", fn_sillstone_curator_content_hash);
477
- napi_value fn_sillstone_curator_signature;
478
- napi_create_function(env, NULL, 0, n_sillstone_curator_signature, NULL, &fn_sillstone_curator_signature);
479
- napi_set_named_property(env, exports, "sillstone_curator_signature", fn_sillstone_curator_signature);
480
- napi_value fn_sillstone_curator_describe;
481
- napi_create_function(env, NULL, 0, n_sillstone_curator_describe, NULL, &fn_sillstone_curator_describe);
482
- napi_set_named_property(env, exports, "sillstone_curator_describe", fn_sillstone_curator_describe);
483
- napi_value fn_sillstone_curator_match;
484
- napi_create_function(env, NULL, 0, n_sillstone_curator_match, NULL, &fn_sillstone_curator_match);
485
- napi_set_named_property(env, exports, "sillstone_curator_match", fn_sillstone_curator_match);
486
- napi_value fn_sillstone_curator_free;
487
- napi_create_function(env, NULL, 0, n_sillstone_curator_free, NULL, &fn_sillstone_curator_free);
488
- napi_set_named_property(env, exports, "sillstone_curator_free", fn_sillstone_curator_free);
489
- napi_value fn_sillstone_image_phash;
490
- napi_create_function(env, NULL, 0, n_sillstone_image_phash, NULL, &fn_sillstone_image_phash);
491
- napi_set_named_property(env, exports, "sillstone_image_phash", fn_sillstone_image_phash);
492
- napi_value fn_sillstone_image_ahash;
493
- napi_create_function(env, NULL, 0, n_sillstone_image_ahash, NULL, &fn_sillstone_image_ahash);
494
- napi_set_named_property(env, exports, "sillstone_image_ahash", fn_sillstone_image_ahash);
495
- napi_value fn_sillstone_image_dhash;
496
- napi_create_function(env, NULL, 0, n_sillstone_image_dhash, NULL, &fn_sillstone_image_dhash);
497
- napi_set_named_property(env, exports, "sillstone_image_dhash", fn_sillstone_image_dhash);
498
- napi_value fn_sillstone_image_whash;
499
- napi_create_function(env, NULL, 0, n_sillstone_image_whash, NULL, &fn_sillstone_image_whash);
500
- napi_set_named_property(env, exports, "sillstone_image_whash", fn_sillstone_image_whash);
501
- napi_value fn_sillstone_image_colorhash;
502
- napi_create_function(env, NULL, 0, n_sillstone_image_colorhash, NULL, &fn_sillstone_image_colorhash);
503
- napi_set_named_property(env, exports, "sillstone_image_colorhash", fn_sillstone_image_colorhash);
504
- napi_value fn_sillstone_text_tlsh;
505
- napi_create_function(env, NULL, 0, n_sillstone_text_tlsh, NULL, &fn_sillstone_text_tlsh);
506
- napi_set_named_property(env, exports, "sillstone_text_tlsh", fn_sillstone_text_tlsh);
507
- napi_value fn_sillstone_text_tlsh_distance;
508
- napi_create_function(env, NULL, 0, n_sillstone_text_tlsh_distance, NULL, &fn_sillstone_text_tlsh_distance);
509
- napi_set_named_property(env, exports, "sillstone_text_tlsh_distance", fn_sillstone_text_tlsh_distance);
510
- return exports;
511
- }
package/src/node.def DELETED
@@ -1,16 +0,0 @@
1
- EXPORTS
2
- napi_create_array_with_length
3
- napi_create_bigint_uint64
4
- napi_create_buffer
5
- napi_create_buffer_copy
6
- napi_create_function
7
- napi_create_int64
8
- napi_create_object
9
- napi_create_uint32
10
- napi_get_buffer_info
11
- napi_get_cb_info
12
- napi_get_undefined
13
- napi_get_value_uint32
14
- napi_set_element
15
- napi_set_named_property
16
- napi_throw_error
package/src/node_api.h DELETED
@@ -1,104 +0,0 @@
1
- #ifndef SRC_NODE_API_H_
2
- #define SRC_NODE_API_H_
3
-
4
- #include <stddef.h>
5
- #include <stdint.h>
6
-
7
- #ifdef __cplusplus
8
- extern "C" {
9
- #endif
10
-
11
- typedef struct napi_env__* napi_env;
12
- typedef struct napi_value__* napi_value;
13
- typedef struct napi_ref__* napi_ref;
14
- typedef struct napi_handle_scope__* napi_handle_scope;
15
- typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;
16
- typedef struct napi_callback_info__* napi_callback_info;
17
- typedef struct napi_async_context__* napi_async_context;
18
- typedef struct napi_async_work__* napi_async_work;
19
-
20
- typedef enum {
21
- napi_ok,
22
- napi_invalid_arg,
23
- napi_object_expected,
24
- napi_string_expected,
25
- napi_name_expected,
26
- napi_function_expected,
27
- napi_number_expected,
28
- napi_boolean_expected,
29
- napi_array_expected,
30
- napi_generic_failure,
31
- napi_pending_exception,
32
- napi_cancelled,
33
- napi_escape_called_twice,
34
- napi_handle_scope_mismatch,
35
- napi_callback_scope_mismatch,
36
- napi_queue_full,
37
- napi_closing,
38
- napi_bigint_expected,
39
- napi_date_expected,
40
- napi_arraybuffer_expected,
41
- napi_detachable_arraybuffer_expected,
42
- napi_would_deadlock
43
- } napi_status;
44
-
45
- typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info);
46
-
47
- #if defined(_WIN32)
48
- #define NAPI_MODULE_EXPORT __declspec(dllexport)
49
- #else
50
- #define NAPI_MODULE_EXPORT __attribute__((visibility("default")))
51
- #endif
52
-
53
- #define NAPI_MODULE_INIT() \
54
- NAPI_MODULE_EXPORT napi_value \
55
- napi_register_module_v1(napi_env env, napi_value exports)
56
-
57
- napi_status napi_get_cb_info(napi_env env,
58
- napi_callback_info cbinfo,
59
- size_t* argc,
60
- napi_value* argv,
61
- napi_value* this_arg,
62
- void** data);
63
-
64
- napi_status napi_get_buffer_info(napi_env env,
65
- napi_value value,
66
- void** data,
67
- size_t* length);
68
-
69
- napi_status napi_create_buffer(napi_env env,
70
- size_t size,
71
- void** data,
72
- napi_value* result);
73
-
74
- napi_status napi_create_buffer_copy(napi_env env,
75
- size_t length,
76
- const void* data,
77
- void** result_data,
78
- napi_value* result);
79
-
80
- napi_status napi_create_uint32(napi_env env, uint32_t value, napi_value* result);
81
- napi_status napi_get_value_uint32(napi_env env, napi_value value, uint32_t* result);
82
- napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result);
83
- napi_status napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result);
84
- napi_status napi_create_object(napi_env env, napi_value* result);
85
- napi_status napi_set_named_property(napi_env env,
86
- napi_value object,
87
- const char* utf8name,
88
- napi_value value);
89
- napi_status napi_create_array_with_length(napi_env env, size_t length, napi_value* result);
90
- napi_status napi_set_element(napi_env env, napi_value object, uint32_t index, napi_value value);
91
- napi_status napi_get_undefined(napi_env env, napi_value* result);
92
- napi_status napi_throw_error(napi_env env, const char* code, const char* msg);
93
- napi_status napi_create_function(napi_env env,
94
- const char* utf8name,
95
- size_t length,
96
- napi_callback cb,
97
- void* data,
98
- napi_value* result);
99
-
100
- #ifdef __cplusplus
101
- }
102
- #endif
103
-
104
- #endif // SRC_NODE_API_H_
@@ -1,65 +0,0 @@
1
- const test = require("node:test");
2
- const assert = require("node:assert");
3
- const crypto = require("node:crypto");
4
- const sillstone = require("../index.js");
5
-
6
- test("version matches expected", () => {
7
- assert.strictEqual(sillstone.version(), "0.2.0");
8
- });
9
-
10
- test("checksum produces deterministic golden output", () => {
11
- const data = Buffer.from("sillstone pure rust zero dep");
12
- const sum = sillstone.bufferChecksum(data);
13
- let h = 0xcbf29ce484222325n;
14
- const prime = 0x0100000001b3n;
15
- for (let i = 0; i < data.length; i++) {
16
- h ^= BigInt(data[i]);
17
- h = (h * prime) & 0xffffffffffffffffn;
18
- }
19
- assert.strictEqual(sum, h);
20
- });
21
-
22
- test("digests produce correct outputs", () => {
23
- const data = Buffer.from("abc");
24
- assert.strictEqual(
25
- sillstone.sha256(data).toString("hex"),
26
- crypto.createHash("sha256").update(data).digest("hex")
27
- );
28
- assert.strictEqual(
29
- sillstone.sha3_256(data).toString("hex"),
30
- crypto.createHash("sha3-256").update(data).digest("hex")
31
- );
32
- assert.strictEqual(
33
- sillstone.sha3_512(data).toString("hex"),
34
- crypto.createHash("sha3-512").update(data).digest("hex")
35
- );
36
- assert.strictEqual(
37
- sillstone.blake3(Buffer.alloc(0)).toString("hex"),
38
- "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"
39
- );
40
- });
41
-
42
- test("dst functions satisfy mathematical inversions", () => {
43
- const x = [1.0, 2.0, 3.0, 4.0];
44
- const d1 = sillstone.dst1(x);
45
- assert.strictEqual(d1.length, 4);
46
- const rec1 = sillstone.dst1(d1);
47
- for (let i = 0; i < x.length; i++) {
48
- assert.ok(Math.abs(x[i] - rec1[i]) < 1e-10);
49
- }
50
-
51
- const d2 = sillstone.dst2(x);
52
- const rec2 = sillstone.dst3(d2);
53
- for (let i = 0; i < x.length; i++) {
54
- assert.ok(Math.abs(x[i] - rec2[i]) < 1e-10);
55
- }
56
- });
57
-
58
- test("unicode grapheme clustering and character categories", () => {
59
- const fam = "👨‍👩‍👧‍👦";
60
- assert.strictEqual(sillstone.graphemeCount(fam), 1);
61
- assert.strictEqual(sillstone.graphemeCount("Hello"), 5);
62
- assert.strictEqual(sillstone.graphemeCount(""), 0);
63
- const cat = sillstone.charCategory("A");
64
- assert.ok(cat >= 0);
65
- });
@@ -1,447 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- // Copyright (c) 2026 sillstone
3
- "use strict";
4
-
5
- // Hex-exact conformance: the committed reference vectors through koffi.
6
- // Every one of the 45 vectors in the repository-root reference.json is
7
- // replayed through the cdylib and compared against every recorded
8
- // field — detect codes, tier-2 sub-digests (recomputed SDK-side with
9
- // the same folds the generator uses), tier-1 digests, facts and match
10
- // slots. The same vectors the Rust `gen-reference verify` gate and the
11
- // Python/Go SDKs check.
12
-
13
- const test = require("node:test");
14
- const assert = require("node:assert/strict");
15
- const crypto = require("node:crypto");
16
- const fs = require("node:fs");
17
- const path = require("node:path");
18
-
19
- const {
20
- FfiError,
21
- MODALITIES,
22
- FORMATS,
23
- contentHash,
24
- describe,
25
- detect,
26
- fnv1a64,
27
- findCdylib,
28
- matchSignatures,
29
- signature,
30
- } = require("../index.js");
31
-
32
- const REPO_ROOT = path.resolve(__dirname, "..", "..", "..");
33
- const REFERENCE = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, "reference.json"), "utf8")).vectors;
34
- const BY_NAME = new Map(REFERENCE.map((v) => [v.name, v]));
35
-
36
- const MASK64 = (1n << 64n) - 1n;
37
-
38
- /** SplitMix64 next_u64 (BigInt, wrapping). @param {{s: bigint}} st */
39
- function splitmixNext(st) {
40
- st.s = (st.s + 0x9e3779b97f4a7c15n) & MASK64;
41
- let z = st.s;
42
- z = ((z ^ (z >> 30n)) * 0xbf58476d1ce4e5b9n) & MASK64;
43
- z = ((z ^ (z >> 27n)) * 0x94d049bb133111ebn) & MASK64;
44
- return z ^ (z >> 31n);
45
- }
46
-
47
- /** The upstream conformance byte recipe: one next_u64 per byte, low byte. */
48
- function splitmixBytes(seed, length) {
49
- const st = { s: BigInt(seed) };
50
- const out = Buffer.alloc(length);
51
- for (let i = 0; i < length; i++) out[i] = Number(splitmixNext(st) & 0xffn);
52
- return out;
53
- }
54
-
55
- /** The 48 KiB pseudo-file recipe of the upstream facade suite. */
56
- function blob48(seed) {
57
- return splitmixBytes(seed, 48 * 1024);
58
- }
59
-
60
- const VOCAB = [
61
- "hash", "image", "audio", "signal", "codec", "block", "frame", "sample", "stream",
62
- "vector", "median", "kernel", "window", "raster", "pixel", "tone", "hash", "luma",
63
- "delta", "chunk", "radix", "filter", "table", "edge",
64
- ];
65
-
66
- /** Deterministic ~bytes-sized prose from the seeded 24-word vocabulary. */
67
- function prose(seed, bytes) {
68
- const st = { s: BigInt(seed) };
69
- let out = "";
70
- while (out.length < bytes) {
71
- out += VOCAB[Number(splitmixNext(st) % BigInt(VOCAB.length))];
72
- out += " ";
73
- }
74
- return out.slice(0, bytes);
75
- }
76
-
77
- /** Smooth periodic RGB content with real low-frequency energy. */
78
- function patternedPixels(seed, w, h) {
79
- const st = { s: BigInt(seed) };
80
- const fx = Number(splitmixNext(st) % 5n) + 1;
81
- const fy = Number(splitmixNext(st) % 5n) + 1;
82
- const p1 = Number(splitmixNext(st) % 628n) / 100.0;
83
- const p2 = Number(splitmixNext(st) % 628n) / 100.0;
84
- const TAU = 6.283185307179586;
85
- const px = Buffer.alloc(w * h * 3);
86
- let i = 0;
87
- for (let y = 0; y < h; y++) {
88
- for (let x = 0; x < w; x++) {
89
- const u = x / w;
90
- const v = y / h;
91
- const r = 127.5 + 120.0 * Math.sin(u * fx * TAU + p1);
92
- const g = 127.5 + 120.0 * Math.sin(v * fy * TAU + p2);
93
- const b = 127.5 + 120.0 * Math.sin((u + v) * (fx + fy) + p1 + p2);
94
- px[i++] = Math.trunc(r);
95
- px[i++] = Math.trunc(g);
96
- px[i++] = Math.trunc(b);
97
- }
98
- }
99
- return px;
100
- }
101
-
102
- /** Encodes RGB triples as a bottom-up 24-bit BMP. */
103
- function bmp24(w, h, rgb) {
104
- const row = Math.ceil((w * 3) / 4) * 4;
105
- const img = row * h;
106
- const f = [Buffer.from("BM", "ascii")];
107
- const head = Buffer.alloc(52);
108
- head.writeUInt32LE(54 + img, 0); // file size
109
- head.writeUInt32LE(54, 8); // pixel offset (4 reserved bytes at 4..8)
110
- head.writeUInt32LE(40, 12); // DIB header
111
- head.writeInt32LE(w, 16);
112
- head.writeInt32LE(h, 20);
113
- head.writeUInt16LE(1, 24); // planes
114
- head.writeUInt16LE(24, 26); // bpp
115
- f.push(head);
116
- const pad = Buffer.alloc(row - w * 3);
117
- for (let y = h - 1; y >= 0; y--) {
118
- const rowBuf = Buffer.alloc(w * 3);
119
- for (let x = 0; x < w; x++) {
120
- const i = (y * w + x) * 3;
121
- rowBuf[x * 3] = rgb[i + 2];
122
- rowBuf[x * 3 + 1] = rgb[i + 1];
123
- rowBuf[x * 3 + 2] = rgb[i];
124
- }
125
- f.push(rowBuf, pad);
126
- }
127
- return Buffer.concat(f);
128
- }
129
-
130
- function fixture(name) {
131
- return fs.readFileSync(path.join(REPO_ROOT, "tests", "fixtures", name));
132
- }
133
-
134
- // The gen-reference corpus, name -> input bytes (cached).
135
- const inputCache = new Map();
136
- function input(name) {
137
- if (!inputCache.has(name)) {
138
- inputCache.set(name, makeInput(name));
139
- }
140
- return inputCache.get(name);
141
- }
142
-
143
- /** The exact name->input mapping tools/gen-reference defines. */
144
- function makeInput(name) {
145
- switch (name) {
146
- case "detect-png":
147
- case "sig-image-phash_rgb8_48x40":
148
- case "describe-image-phash_rgb8_48x40":
149
- return fixture("phash_rgb8_48x40.png");
150
- case "detect-jpeg":
151
- case "sig-image-base_444-jpeg":
152
- return fixture("base_444.jpg");
153
- case "detect-bmp":
154
- case "sig-image-bmp-patterned-48x40":
155
- case "describe-image-bmp-patterned-48x40":
156
- return bmp24(48, 40, patternedPixels(0xbeef0001, 48, 40));
157
- case "detect-wav":
158
- case "sig-audio-tone-wav":
159
- case "describe-audio-tone-wav":
160
- return fixture("tone.wav");
161
- case "detect-flac":
162
- case "sig-audio-tone-flac":
163
- return fixture("tone.flac");
164
- case "detect-mp3":
165
- case "sig-audio-l3_short-mp3":
166
- return fixture("l3_short.mp3");
167
- case "detect-mp4":
168
- case "sig-video-a_64x48-mp4":
169
- case "describe-video-a_64x48-mp4":
170
- return fixture("a_64x48.mp4");
171
- case "detect-mov":
172
- return fixture("a_64x48.mov");
173
- case "detect-pdf":
174
- case "sig-pdf-text_page":
175
- case "describe-pdf-text_page":
176
- return fixture("text_page.pdf");
177
- case "detect-zip":
178
- return Buffer.from([0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00]);
179
- case "detect-gif":
180
- return Buffer.from("GIF89a\x01\x00\x01\x00", "binary");
181
- case "detect-text":
182
- return Buffer.from("just some words", "binary");
183
- case "detect-binary":
184
- return Buffer.from([0x00, 0xff, 0x02, 0xfe]);
185
- case "sig-image-phash_gray8_40x32":
186
- return fixture("phash_gray8_40x32.png");
187
- case "sig-image-base_444-png":
188
- return fixture("base_444.png");
189
- case "sig-text-prose-4kib":
190
- case "describe-text-prose-4kib":
191
- return Buffer.from(prose(0xd1, 4096), "binary");
192
- case "sig-binary-splitmix64-48kib":
193
- case "describe-binary-splitmix64-48kib":
194
- return blob48(0xd15e5eed);
195
- case "sig-binary-splitmix64-48kib-prefix-insert": {
196
- const out = Buffer.alloc(1 + 48 * 1024);
197
- out[0] = 0xaa;
198
- blob48(0xd15e5eed).copy(out, 1);
199
- return out;
200
- }
201
- case "sig-binary-splitmix64-48kib-unrelated":
202
- return blob48(0xbadc0ffe);
203
- case "match-image-self":
204
- case "match-image-distinct":
205
- case "match-error-cross-modality":
206
- return fixture("phash_rgb8_48x40.png");
207
- case "match-image-png-vs-jpeg-same-pixels":
208
- return fixture("base_444.png");
209
- case "match-audio-self":
210
- return fixture("tone.wav");
211
- case "match-text-self":
212
- case "match-text-distinct":
213
- return Buffer.from(prose(0xd1, 4096), "binary");
214
- case "match-binary-near-duplicate":
215
- case "match-binary-unrelated":
216
- return blob48(0xd15e5eed);
217
- case "match-video-mp4-vs-mov":
218
- return fixture("a_64x48.mp4");
219
- default:
220
- throw new Error(`no input recipe for ${name}`);
221
- }
222
- }
223
-
224
- /** The right-hand input of each match vector. */
225
- function matchRight(name) {
226
- switch (name) {
227
- case "match-image-distinct":
228
- case "match-image-png-vs-jpeg-same-pixels":
229
- return name === "match-image-distinct"
230
- ? fixture("phash_gray8_40x32.png")
231
- : fixture("base_444.jpg");
232
- case "match-text-distinct":
233
- return Buffer.from(prose(0xd2, 4096), "binary");
234
- case "match-binary-near-duplicate": {
235
- const out = Buffer.alloc(1 + 48 * 1024);
236
- out[0] = 0xaa;
237
- blob48(0xd15e5eed).copy(out, 1);
238
- return out;
239
- }
240
- case "match-binary-unrelated":
241
- return blob48(0xbadc0ffe);
242
- case "match-video-mp4-vs-mov":
243
- return fixture("a_64x48.mov");
244
- default:
245
- return input(name); // self-matches
246
- }
247
- }
248
-
249
- /** f64 bit pattern from a recorded 16-hex-digit string. @param {string} hex */
250
- const bits = (hex) => BigInt(`0x${hex}`);
251
- const fnvHex = (bytes) => fnv1a64(bytes).toString(16).padStart(16, "0");
252
- const shaHex = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex");
253
-
254
- /** Words folded little-endian, the canonical payload behind the digests. */
255
- function wordsLe(words) {
256
- const out = Buffer.alloc(words.length * 8);
257
- words.forEach((w, i) => out.writeBigUInt64LE(w, i * 8));
258
- return out;
259
- }
260
-
261
- test("cdylib is discoverable", () => {
262
- assert.ok(fs.statSync(findCdylib()).isFile());
263
- });
264
-
265
- // --- detect: 13 verdict vectors --------------------------------------
266
- for (const [name, v] of BY_NAME) {
267
- if (!name.startsWith("detect-")) continue;
268
- test(`${name} verdict is reproduced`, () => {
269
- const det = detect(input(name));
270
- assert.equal(det.format, v.format, name);
271
- assert.equal(det.modality, v.modality, name);
272
- assert.equal(det.pending, v.pending, name);
273
- assert.ok(FORMATS.includes(det.format) && MODALITIES.includes(det.modality), name);
274
- });
275
- }
276
-
277
- // --- signature: 14 fold vectors --------------------------------------
278
- for (const [name, v] of BY_NAME) {
279
- if (!name.startsWith("sig-")) continue;
280
- test(`${name} signature folds are reproduced hex-exact`, () => {
281
- const sig = signature(input(name));
282
- assert.equal(sig.modality, v.modality, name);
283
- if (sig.modality === "image") {
284
- assert.equal(sig.phash, BigInt(v.phash), name);
285
- } else if (sig.modality === "audio") {
286
- assert.equal(sig.peakCount, v.peak_count, name);
287
- assert.equal(sig.peakCount, sig.peaksBytes.length, name);
288
- assert.equal(sig.peakCount % 6, 0, name);
289
- assert.equal(sig.frames, v.frames, name);
290
- assert.equal(fnvHex(sig.peaksBytes), v.peaks_fnv1a64, name);
291
- assert.equal(shaHex(sig.peaksBytes), v.peaks_sha256, name);
292
- } else if (sig.modality === "text") {
293
- assert.equal(sig.wordCount, v.minhash_words, name);
294
- assert.equal(sig.words.length * 8, v.minhash_words, name);
295
- assert.equal(fnvHex(wordsLe(sig.words)), v.minhash_fnv1a64, name);
296
- assert.equal(shaHex(wordsLe(sig.words)), v.minhash_sha256, name);
297
- } else if (sig.modality === "binary") {
298
- assert.equal(sig.chunkCount, v.chunk_count, name);
299
- assert.equal(sig.chunkCount, sig.digestBytes.length, name);
300
- assert.equal(sig.chunkCount % 32, 0, name);
301
- assert.equal(fnvHex(sig.digestBytes), v.chunks_fnv1a64, name);
302
- assert.equal(shaHex(sig.digestBytes), v.chunks_sha256, name);
303
- } else {
304
- const frames = wordsLe(sig.frameHashes);
305
- const minhash = wordsLe(sig.minhash);
306
- assert.equal(sig.frameCount, v.frame_count, name);
307
- assert.equal(sig.frameCount, frames.length, name);
308
- assert.equal(fnvHex(frames), v.frames_fnv1a64, name);
309
- assert.equal(shaHex(frames), v.frames_sha256, name);
310
- assert.equal(minhash.length, v.minhash_words, name);
311
- assert.equal(fnvHex(minhash), v.minhash_fnv1a64, name);
312
- assert.equal(sig.width, v.width, name);
313
- assert.equal(sig.height, v.height, name);
314
- assert.equal(sig.durationBits, bits(v.duration_bits), name);
315
- }
316
- });
317
- }
318
-
319
- // --- describe: 7 both-tiers vectors ----------------------------------
320
- for (const [name, v] of BY_NAME) {
321
- if (!name.startsWith("describe-")) continue;
322
- test(`${name} description is reproduced hex-exact`, () => {
323
- const d = describe(input(name));
324
- assert.equal(d.format, v.format, name);
325
- assert.equal(d.modality, v.modality, name);
326
- assert.equal(d.tier1, v.tier1, name);
327
- const s = d.signature;
328
- if (s.modality === "image") {
329
- assert.equal(s.phash, BigInt(v.phash), name);
330
- assert.deepEqual(d.facts, { width: v.facts.width, height: v.facts.height, keypoints: v.facts.keypoints }, name);
331
- } else if (s.modality === "audio") {
332
- assert.equal(s.peakCount, v.peak_count, name);
333
- assert.equal(fnvHex(s.peaksBytes), v.peaks_fnv1a64, name);
334
- assert.equal(shaHex(s.peaksBytes), v.peaks_sha256, name);
335
- assert.equal(s.frames, v.frames, name);
336
- assert.deepEqual(
337
- d.facts,
338
- { sampleRate: v.facts.sample_rate, channels: v.facts.channels, frames: v.facts.frames, peaks: v.facts.peaks },
339
- name,
340
- );
341
- } else if (s.modality === "text") {
342
- assert.equal(s.wordCount, v.minhash_words, name);
343
- assert.equal(fnvHex(wordsLe(s.words)), v.minhash_fnv1a64, name);
344
- assert.equal(shaHex(wordsLe(s.words)), v.minhash_sha256, name);
345
- assert.deepEqual(d.facts, { words: v.facts.words, canonicalLen: v.facts.canonical_len }, name);
346
- } else if (s.modality === "binary") {
347
- assert.equal(s.chunkCount, v.chunk_count, name);
348
- assert.equal(fnvHex(s.digestBytes), v.chunks_fnv1a64, name);
349
- assert.equal(shaHex(s.digestBytes), v.chunks_sha256, name);
350
- assert.deepEqual(d.facts, { len: v.facts.len, chunks: v.facts.chunks }, name);
351
- } else {
352
- const frames = wordsLe(s.frameHashes);
353
- assert.equal(s.frameCount, v.frame_count, name);
354
- assert.equal(fnvHex(frames), v.frames_fnv1a64, name);
355
- assert.equal(shaHex(frames), v.frames_sha256, name);
356
- assert.equal(wordsLe(s.minhash).length, v.minhash_words, name);
357
- assert.equal(fnvHex(wordsLe(s.minhash)), v.minhash_fnv1a64, name);
358
- assert.equal(s.width, v.width, name);
359
- assert.equal(s.height, v.height, name);
360
- assert.equal(s.durationBits, bits(v.duration_bits), name);
361
- assert.deepEqual(
362
- d.facts,
363
- {
364
- width: v.facts.width,
365
- height: v.facts.height,
366
- durationBits: bits(v.facts.duration_bits),
367
- framesSampled: v.facts.frames_sampled,
368
- },
369
- name,
370
- );
371
- }
372
- });
373
- }
374
-
375
- // --- match: 10 outcome vectors ---------------------------------------
376
- /** Recorded decimal delta_t -> the u64 two's-complement slot. */
377
- const deltaSlot = (dt) => BigInt(dt < 0 ? dt + 0x10000000000000000 : dt);
378
-
379
- for (const [name, v] of BY_NAME) {
380
- if (!name.startsWith("match-")) continue;
381
- if (v.outcome === "error") {
382
- test(`${name} is refused with status -2`, () => {
383
- assert.throws(() => matchSignatures(input(name), input("sig-text-prose-4kib")), (err) => {
384
- assert.ok(err instanceof FfiError);
385
- assert.equal(err.status, -2);
386
- return true;
387
- });
388
- });
389
- continue;
390
- }
391
- test(`${name} match slots are reproduced exactly`, () => {
392
- const m = matchSignatures(input(name), matchRight(name));
393
- assert.equal(m.matched, v.matched, name);
394
- if (v.outcome === "image") {
395
- assert.equal(m.tag, "image", name);
396
- assert.equal(m.slots[0], BigInt(v.hamming), name);
397
- } else if (v.outcome === "audio") {
398
- assert.equal(m.tag, "audio", name);
399
- assert.equal(m.slots[0], BigInt(v.votes), name);
400
- assert.equal(m.slots[1], deltaSlot(v.delta_t), name);
401
- } else if (v.outcome === "jaccard") {
402
- assert.equal(m.tag, name.startsWith("match-text") ? "text" : "binary", name);
403
- assert.equal(m.slots[0], bits(v.jaccard_bits), name);
404
- } else {
405
- assert.equal(m.tag, "video", name);
406
- assert.equal(m.slots[0], bits(v.score_bits), name);
407
- assert.equal(m.slots[1], bits(v.minhash_jaccard_bits), name);
408
- }
409
- });
410
- }
411
-
412
- // --- error vector + refusals -----------------------------------------
413
- test("non-AVC mp4 is refused with status -2", () => {
414
- assert.throws(() => signature(fixture("e_mp4v.mp4")), (err) => {
415
- assert.ok(err instanceof FfiError);
416
- assert.equal(err.status, -2);
417
- return true;
418
- });
419
- });
420
-
421
- test("corrupt container is refused, not crashing", () => {
422
- const corrupt = Buffer.concat([Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), Buffer.from("garbage")]);
423
- assert.throws(() => signature(corrupt), (err) => {
424
- assert.ok(err instanceof FfiError);
425
- assert.equal(err.status, -2);
426
- return true;
427
- });
428
- assert.throws(() => describe(corrupt), FfiError);
429
- assert.throws(() => contentHash(corrupt), (err) => {
430
- assert.ok(err instanceof FfiError);
431
- assert.equal(err.status, -2);
432
- return true;
433
- });
434
- });
435
-
436
- test("content hash matches a rust-derived pinned value", () => {
437
- // text_page.pdf's tier-1 digest, derived once from the built cdylib
438
- // and pinned here so a wrongly regenerated reference.json cannot
439
- // mask drift. The same pin lives in the Python and Go harnesses and
440
- // the Rust ffi tests.
441
- const digest = contentHash(fixture("text_page.pdf"));
442
- assert.equal(digest.length, 32);
443
- assert.equal(
444
- digest.toString("hex"),
445
- "849978a1682ba75372094611c8fc290b9c39b84d75641bf0b6129cdbaa880fa9",
446
- );
447
- });