@topgunbuild/native 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/binding.gyp +46 -0
- package/build/Makefile +352 -0
- package/build/Release/.deps/Release/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.o.d +4 -0
- package/build/Release/.deps/Release/nothing.a.d +1 -0
- package/build/Release/.deps/Release/obj.target/topgun_hash/src/hash.o.d +19 -0
- package/build/Release/.deps/Release/topgun_hash.node.d +1 -0
- package/build/Release/node_modules/.pnpm/node-addon-api@8.5.0/node_modules/node-addon-api/nothing.o +0 -0
- package/build/Release/nothing.a +0 -0
- package/build/Release/obj.target/topgun_hash/src/hash.o +0 -0
- package/build/Release/topgun_hash.node +0 -0
- package/build/binding.Makefile +6 -0
- package/build/gyp-mac-tool +772 -0
- package/build/topgun_hash.target.mk +198 -0
- package/deps/xxhash/LICENSE +26 -0
- package/deps/xxhash/xxhash.h +7489 -0
- package/dist/hash.d.ts +83 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +314 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
- package/src/hash.cc +310 -0
package/src/hash.cc
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native xxHash64 implementation for TopGun
|
|
3
|
+
*
|
|
4
|
+
* Provides high-performance hashing for Merkle tree operations.
|
|
5
|
+
* xxHash64 is 10-20x faster than SHA-256 for small inputs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include <napi.h>
|
|
9
|
+
|
|
10
|
+
// xxHash header-only implementation
|
|
11
|
+
#define XXH_INLINE_ALL
|
|
12
|
+
#include "xxhash.h"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compute xxHash64 of a buffer.
|
|
16
|
+
* @param buffer - Input data (Buffer or Uint8Array)
|
|
17
|
+
* @param seed - Optional seed (default: 0)
|
|
18
|
+
* @returns BigInt with 64-bit hash
|
|
19
|
+
*/
|
|
20
|
+
Napi::Value XxHash64(const Napi::CallbackInfo& info) {
|
|
21
|
+
Napi::Env env = info.Env();
|
|
22
|
+
|
|
23
|
+
// Validate arguments
|
|
24
|
+
if (info.Length() < 1) {
|
|
25
|
+
Napi::TypeError::New(env, "Expected at least 1 argument")
|
|
26
|
+
.ThrowAsJavaScriptException();
|
|
27
|
+
return env.Null();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!info[0].IsBuffer() && !info[0].IsTypedArray()) {
|
|
31
|
+
Napi::TypeError::New(env, "Expected Buffer or TypedArray")
|
|
32
|
+
.ThrowAsJavaScriptException();
|
|
33
|
+
return env.Null();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Get buffer data
|
|
37
|
+
uint8_t* data;
|
|
38
|
+
size_t length;
|
|
39
|
+
|
|
40
|
+
if (info[0].IsBuffer()) {
|
|
41
|
+
auto buffer = info[0].As<Napi::Buffer<uint8_t>>();
|
|
42
|
+
data = buffer.Data();
|
|
43
|
+
length = buffer.Length();
|
|
44
|
+
} else {
|
|
45
|
+
auto typedArray = info[0].As<Napi::TypedArray>();
|
|
46
|
+
data = static_cast<uint8_t*>(typedArray.ArrayBuffer().Data()) +
|
|
47
|
+
typedArray.ByteOffset();
|
|
48
|
+
length = typedArray.ByteLength();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Get optional seed
|
|
52
|
+
uint64_t seed = 0;
|
|
53
|
+
if (info.Length() > 1 && info[1].IsBigInt()) {
|
|
54
|
+
bool lossless;
|
|
55
|
+
seed = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
56
|
+
} else if (info.Length() > 1 && info[1].IsNumber()) {
|
|
57
|
+
seed = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Compute hash
|
|
61
|
+
XXH64_hash_t hash = XXH64(data, length, seed);
|
|
62
|
+
|
|
63
|
+
// Return as BigInt for full 64-bit precision
|
|
64
|
+
return Napi::BigInt::New(env, hash);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Compute xxHash64 and return as 32-bit number (truncated).
|
|
69
|
+
* Useful when BigInt overhead is not needed.
|
|
70
|
+
*/
|
|
71
|
+
Napi::Value XxHash64AsNumber(const Napi::CallbackInfo& info) {
|
|
72
|
+
Napi::Env env = info.Env();
|
|
73
|
+
|
|
74
|
+
if (info.Length() < 1 || (!info[0].IsBuffer() && !info[0].IsTypedArray())) {
|
|
75
|
+
Napi::TypeError::New(env, "Expected Buffer or TypedArray")
|
|
76
|
+
.ThrowAsJavaScriptException();
|
|
77
|
+
return env.Null();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
uint8_t* data;
|
|
81
|
+
size_t length;
|
|
82
|
+
|
|
83
|
+
if (info[0].IsBuffer()) {
|
|
84
|
+
auto buffer = info[0].As<Napi::Buffer<uint8_t>>();
|
|
85
|
+
data = buffer.Data();
|
|
86
|
+
length = buffer.Length();
|
|
87
|
+
} else {
|
|
88
|
+
auto typedArray = info[0].As<Napi::TypedArray>();
|
|
89
|
+
data = static_cast<uint8_t*>(typedArray.ArrayBuffer().Data()) +
|
|
90
|
+
typedArray.ByteOffset();
|
|
91
|
+
length = typedArray.ByteLength();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
uint64_t seed = 0;
|
|
95
|
+
if (info.Length() > 1 && info[1].IsNumber()) {
|
|
96
|
+
seed = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
XXH64_hash_t hash = XXH64(data, length, seed);
|
|
100
|
+
|
|
101
|
+
// Truncate to 32 bits for compatibility with current FNV-1a usage
|
|
102
|
+
return Napi::Number::New(env, static_cast<uint32_t>(hash & 0xFFFFFFFF));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Compute xxHash64 for multiple buffers in batch.
|
|
107
|
+
* More efficient than calling XxHash64 repeatedly due to reduced JS<->C++ overhead.
|
|
108
|
+
* @param buffers - Array of Buffers
|
|
109
|
+
* @param seed - Optional seed
|
|
110
|
+
* @returns Array of BigInt hashes
|
|
111
|
+
*/
|
|
112
|
+
Napi::Value XxHash64Batch(const Napi::CallbackInfo& info) {
|
|
113
|
+
Napi::Env env = info.Env();
|
|
114
|
+
|
|
115
|
+
if (!info[0].IsArray()) {
|
|
116
|
+
Napi::TypeError::New(env, "Expected Array of Buffers")
|
|
117
|
+
.ThrowAsJavaScriptException();
|
|
118
|
+
return env.Null();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
auto buffers = info[0].As<Napi::Array>();
|
|
122
|
+
uint32_t count = buffers.Length();
|
|
123
|
+
|
|
124
|
+
uint64_t seed = 0;
|
|
125
|
+
if (info.Length() > 1 && info[1].IsBigInt()) {
|
|
126
|
+
bool lossless;
|
|
127
|
+
seed = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
128
|
+
} else if (info.Length() > 1 && info[1].IsNumber()) {
|
|
129
|
+
seed = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Napi::Array results = Napi::Array::New(env, count);
|
|
133
|
+
|
|
134
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
135
|
+
Napi::Value item = buffers.Get(i);
|
|
136
|
+
|
|
137
|
+
if (!item.IsBuffer() && !item.IsTypedArray()) {
|
|
138
|
+
results.Set(i, Napi::BigInt::New(env, static_cast<uint64_t>(0)));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
uint8_t* data;
|
|
143
|
+
size_t length;
|
|
144
|
+
|
|
145
|
+
if (item.IsBuffer()) {
|
|
146
|
+
auto buffer = item.As<Napi::Buffer<uint8_t>>();
|
|
147
|
+
data = buffer.Data();
|
|
148
|
+
length = buffer.Length();
|
|
149
|
+
} else {
|
|
150
|
+
auto typedArray = item.As<Napi::TypedArray>();
|
|
151
|
+
data = static_cast<uint8_t*>(typedArray.ArrayBuffer().Data()) +
|
|
152
|
+
typedArray.ByteOffset();
|
|
153
|
+
length = typedArray.ByteLength();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
XXH64_hash_t hash = XXH64(data, length, seed);
|
|
157
|
+
results.Set(i, Napi::BigInt::New(env, hash));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return results;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Batch hash returning 32-bit numbers.
|
|
165
|
+
*/
|
|
166
|
+
Napi::Value XxHash64BatchAsNumbers(const Napi::CallbackInfo& info) {
|
|
167
|
+
Napi::Env env = info.Env();
|
|
168
|
+
|
|
169
|
+
if (!info[0].IsArray()) {
|
|
170
|
+
Napi::TypeError::New(env, "Expected Array of Buffers")
|
|
171
|
+
.ThrowAsJavaScriptException();
|
|
172
|
+
return env.Null();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
auto buffers = info[0].As<Napi::Array>();
|
|
176
|
+
uint32_t count = buffers.Length();
|
|
177
|
+
|
|
178
|
+
uint64_t seed = 0;
|
|
179
|
+
if (info.Length() > 1 && info[1].IsNumber()) {
|
|
180
|
+
seed = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Napi::Array results = Napi::Array::New(env, count);
|
|
184
|
+
|
|
185
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
186
|
+
Napi::Value item = buffers.Get(i);
|
|
187
|
+
|
|
188
|
+
if (!item.IsBuffer() && !item.IsTypedArray()) {
|
|
189
|
+
results.Set(i, Napi::Number::New(env, 0));
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
uint8_t* data;
|
|
194
|
+
size_t length;
|
|
195
|
+
|
|
196
|
+
if (item.IsBuffer()) {
|
|
197
|
+
auto buffer = item.As<Napi::Buffer<uint8_t>>();
|
|
198
|
+
data = buffer.Data();
|
|
199
|
+
length = buffer.Length();
|
|
200
|
+
} else {
|
|
201
|
+
auto typedArray = item.As<Napi::TypedArray>();
|
|
202
|
+
data = static_cast<uint8_t*>(typedArray.ArrayBuffer().Data()) +
|
|
203
|
+
typedArray.ByteOffset();
|
|
204
|
+
length = typedArray.ByteLength();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
XXH64_hash_t hash = XXH64(data, length, seed);
|
|
208
|
+
results.Set(i, Napi::Number::New(env, static_cast<uint32_t>(hash & 0xFFFFFFFF)));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create streaming hash state.
|
|
216
|
+
* For hashing data incrementally.
|
|
217
|
+
*/
|
|
218
|
+
class XxHash64State : public Napi::ObjectWrap<XxHash64State> {
|
|
219
|
+
public:
|
|
220
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
221
|
+
Napi::Function func = DefineClass(env, "XxHash64State", {
|
|
222
|
+
InstanceMethod("update", &XxHash64State::Update),
|
|
223
|
+
InstanceMethod("digest", &XxHash64State::Digest),
|
|
224
|
+
InstanceMethod("digestAsNumber", &XxHash64State::DigestAsNumber),
|
|
225
|
+
InstanceMethod("reset", &XxHash64State::Reset),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
exports.Set("XxHash64State", func);
|
|
229
|
+
return exports;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
XxHash64State(const Napi::CallbackInfo& info)
|
|
233
|
+
: Napi::ObjectWrap<XxHash64State>(info) {
|
|
234
|
+
seed_ = 0;
|
|
235
|
+
if (info.Length() > 0 && info[0].IsBigInt()) {
|
|
236
|
+
bool lossless;
|
|
237
|
+
seed_ = info[0].As<Napi::BigInt>().Uint64Value(&lossless);
|
|
238
|
+
} else if (info.Length() > 0 && info[0].IsNumber()) {
|
|
239
|
+
seed_ = static_cast<uint64_t>(info[0].As<Napi::Number>().Int64Value());
|
|
240
|
+
}
|
|
241
|
+
state_ = XXH64_createState();
|
|
242
|
+
XXH64_reset(state_, seed_);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
~XxHash64State() {
|
|
246
|
+
if (state_) {
|
|
247
|
+
XXH64_freeState(state_);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
Napi::Value Update(const Napi::CallbackInfo& info) {
|
|
252
|
+
Napi::Env env = info.Env();
|
|
253
|
+
|
|
254
|
+
if (!info[0].IsBuffer() && !info[0].IsTypedArray()) {
|
|
255
|
+
Napi::TypeError::New(env, "Expected Buffer or TypedArray")
|
|
256
|
+
.ThrowAsJavaScriptException();
|
|
257
|
+
return env.Undefined();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
uint8_t* data;
|
|
261
|
+
size_t length;
|
|
262
|
+
|
|
263
|
+
if (info[0].IsBuffer()) {
|
|
264
|
+
auto buffer = info[0].As<Napi::Buffer<uint8_t>>();
|
|
265
|
+
data = buffer.Data();
|
|
266
|
+
length = buffer.Length();
|
|
267
|
+
} else {
|
|
268
|
+
auto typedArray = info[0].As<Napi::TypedArray>();
|
|
269
|
+
data = static_cast<uint8_t*>(typedArray.ArrayBuffer().Data()) +
|
|
270
|
+
typedArray.ByteOffset();
|
|
271
|
+
length = typedArray.ByteLength();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
XXH64_update(state_, data, length);
|
|
275
|
+
return info.This();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
Napi::Value Digest(const Napi::CallbackInfo& info) {
|
|
279
|
+
Napi::Env env = info.Env();
|
|
280
|
+
XXH64_hash_t hash = XXH64_digest(state_);
|
|
281
|
+
return Napi::BigInt::New(env, hash);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
Napi::Value DigestAsNumber(const Napi::CallbackInfo& info) {
|
|
285
|
+
Napi::Env env = info.Env();
|
|
286
|
+
XXH64_hash_t hash = XXH64_digest(state_);
|
|
287
|
+
return Napi::Number::New(env, static_cast<uint32_t>(hash & 0xFFFFFFFF));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
Napi::Value Reset(const Napi::CallbackInfo& info) {
|
|
291
|
+
XXH64_reset(state_, seed_);
|
|
292
|
+
return info.This();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private:
|
|
296
|
+
XXH64_state_t* state_;
|
|
297
|
+
uint64_t seed_;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Module initialization
|
|
301
|
+
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
302
|
+
exports.Set("xxhash64", Napi::Function::New(env, XxHash64));
|
|
303
|
+
exports.Set("xxhash64AsNumber", Napi::Function::New(env, XxHash64AsNumber));
|
|
304
|
+
exports.Set("xxhash64Batch", Napi::Function::New(env, XxHash64Batch));
|
|
305
|
+
exports.Set("xxhash64BatchAsNumbers", Napi::Function::New(env, XxHash64BatchAsNumbers));
|
|
306
|
+
XxHash64State::Init(env, exports);
|
|
307
|
+
return exports;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
NODE_API_MODULE(topgun_hash, Init)
|