knitting 0.1.46

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.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +632 -0
  3. package/knitting.d.ts +4 -0
  4. package/knitting.js +5 -0
  5. package/map.md +264 -0
  6. package/package.json +77 -0
  7. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  8. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  9. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  11. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  16. package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
  17. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  18. package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
  19. package/process-shared-buffer.d.ts +1 -0
  20. package/process-shared-buffer.js +1 -0
  21. package/scripts/build-native-addons.ts +295 -0
  22. package/src/api.d.ts +55 -0
  23. package/src/api.js +384 -0
  24. package/src/common/envelope.d.ts +11 -0
  25. package/src/common/envelope.js +8 -0
  26. package/src/common/module-url.d.ts +1 -0
  27. package/src/common/module-url.js +24 -0
  28. package/src/common/node-compat.d.ts +20 -0
  29. package/src/common/node-compat.js +24 -0
  30. package/src/common/path-canonical.d.ts +6 -0
  31. package/src/common/path-canonical.js +41 -0
  32. package/src/common/runtime.d.ts +15 -0
  33. package/src/common/runtime.js +91 -0
  34. package/src/common/shared-buffer-region.d.ts +11 -0
  35. package/src/common/shared-buffer-region.js +21 -0
  36. package/src/common/shared-buffer-text.d.ts +16 -0
  37. package/src/common/shared-buffer-text.js +65 -0
  38. package/src/common/task-source.d.ts +2 -0
  39. package/src/common/task-source.js +79 -0
  40. package/src/common/task-symbol.d.ts +1 -0
  41. package/src/common/task-symbol.js +1 -0
  42. package/src/common/with-resolvers.d.ts +9 -0
  43. package/src/common/with-resolvers.js +23 -0
  44. package/src/common/worker-runtime.d.ts +40 -0
  45. package/src/common/worker-runtime.js +52 -0
  46. package/src/connections/bun.d.ts +20 -0
  47. package/src/connections/bun.js +159 -0
  48. package/src/connections/deno.d.ts +20 -0
  49. package/src/connections/deno.js +150 -0
  50. package/src/connections/file-descriptor.d.ts +37 -0
  51. package/src/connections/file-descriptor.js +139 -0
  52. package/src/connections/index.d.ts +3 -0
  53. package/src/connections/index.js +3 -0
  54. package/src/connections/node-addons.d.ts +5 -0
  55. package/src/connections/node-addons.js +43 -0
  56. package/src/connections/node.d.ts +29 -0
  57. package/src/connections/node.js +59 -0
  58. package/src/connections/posix.d.ts +31 -0
  59. package/src/connections/posix.js +71 -0
  60. package/src/connections/process-shared-buffer.d.ts +67 -0
  61. package/src/connections/process-shared-buffer.js +267 -0
  62. package/src/connections/types.d.ts +48 -0
  63. package/src/connections/types.js +37 -0
  64. package/src/error.d.ts +13 -0
  65. package/src/error.js +49 -0
  66. package/src/ipc/tools/ring-queue.d.ts +33 -0
  67. package/src/ipc/tools/ring-queue.js +159 -0
  68. package/src/ipc/transport/shared-memory.d.ts +25 -0
  69. package/src/ipc/transport/shared-memory.js +35 -0
  70. package/src/knitting_shared_memory.cc +436 -0
  71. package/src/knitting_shm.cc +476 -0
  72. package/src/memory/byte-carpet.d.ts +73 -0
  73. package/src/memory/byte-carpet.js +157 -0
  74. package/src/memory/lock.d.ts +190 -0
  75. package/src/memory/lock.js +856 -0
  76. package/src/memory/payload-config.d.ts +22 -0
  77. package/src/memory/payload-config.js +67 -0
  78. package/src/memory/payloadCodec.d.ts +46 -0
  79. package/src/memory/payloadCodec.js +1157 -0
  80. package/src/memory/regionRegistry.d.ts +17 -0
  81. package/src/memory/regionRegistry.js +285 -0
  82. package/src/memory/shared-buffer-io.d.ts +53 -0
  83. package/src/memory/shared-buffer-io.js +380 -0
  84. package/src/permission/index.d.ts +2 -0
  85. package/src/permission/index.js +2 -0
  86. package/src/permission/protocol.d.ts +166 -0
  87. package/src/permission/protocol.js +640 -0
  88. package/src/runtime/balancer.d.ts +19 -0
  89. package/src/runtime/balancer.js +149 -0
  90. package/src/runtime/dispatcher.d.ts +34 -0
  91. package/src/runtime/dispatcher.js +142 -0
  92. package/src/runtime/inline-executor.d.ts +10 -0
  93. package/src/runtime/inline-executor.js +270 -0
  94. package/src/runtime/pool.d.ts +43 -0
  95. package/src/runtime/pool.js +922 -0
  96. package/src/runtime/tx-queue.d.ts +25 -0
  97. package/src/runtime/tx-queue.js +144 -0
  98. package/src/shared/abortSignal.d.ts +23 -0
  99. package/src/shared/abortSignal.js +126 -0
  100. package/src/types.d.ts +283 -0
  101. package/src/types.js +2 -0
  102. package/src/worker/composable-runners.d.ts +12 -0
  103. package/src/worker/composable-runners.js +105 -0
  104. package/src/worker/loop.d.ts +2 -0
  105. package/src/worker/loop.js +453 -0
  106. package/src/worker/rx-queue.d.ts +22 -0
  107. package/src/worker/rx-queue.js +124 -0
  108. package/src/worker/safety/index.d.ts +4 -0
  109. package/src/worker/safety/index.js +4 -0
  110. package/src/worker/safety/performance.d.ts +1 -0
  111. package/src/worker/safety/performance.js +17 -0
  112. package/src/worker/safety/process.d.ts +2 -0
  113. package/src/worker/safety/process.js +79 -0
  114. package/src/worker/safety/startup.d.ts +16 -0
  115. package/src/worker/safety/startup.js +30 -0
  116. package/src/worker/safety/worker-data.d.ts +2 -0
  117. package/src/worker/safety/worker-data.js +36 -0
  118. package/src/worker/task-loader.d.ts +26 -0
  119. package/src/worker/task-loader.js +66 -0
  120. package/src/worker/timers.d.ts +18 -0
  121. package/src/worker/timers.js +97 -0
@@ -0,0 +1,476 @@
1
+ #if defined(__linux__) && !defined(_GNU_SOURCE)
2
+ #define _GNU_SOURCE
3
+ #endif
4
+
5
+ #if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
6
+ #error "knitting_shm.cc currently supports Linux, macOS, and Windows best-effort wait/wake."
7
+ #endif
8
+
9
+ #include <node.h>
10
+ #include <v8.h>
11
+
12
+ #include <atomic>
13
+ #include <cerrno>
14
+ #include <chrono>
15
+ #include <climits>
16
+ #include <cstdint>
17
+ #include <cstring>
18
+ #include <memory>
19
+ #include <string>
20
+ #include <thread>
21
+
22
+ #ifdef __linux__
23
+ #include <linux/futex.h>
24
+ #include <sys/syscall.h>
25
+ #endif
26
+ #include <time.h>
27
+ #ifndef _WIN32
28
+ #include <unistd.h>
29
+ #endif
30
+
31
+ namespace knitting_shm {
32
+
33
+ #ifdef __APPLE__
34
+ // These are provided by libSystem. We declare them directly so the addon can
35
+ // still compile on SDKs where <sys/ulock.h> is not exposed as a public header.
36
+ extern "C" int __ulock_wait(
37
+ uint32_t operation,
38
+ void* addr,
39
+ uint64_t value,
40
+ uint32_t timeout_us
41
+ );
42
+ extern "C" int __ulock_wake(
43
+ uint32_t operation,
44
+ void* addr,
45
+ uint64_t wake_value
46
+ );
47
+
48
+ #ifndef UL_COMPARE_AND_WAIT_SHARED
49
+ #define UL_COMPARE_AND_WAIT_SHARED 3
50
+ #endif
51
+
52
+ #ifndef ULF_WAKE_ALL
53
+ #define ULF_WAKE_ALL 0x00000100
54
+ #endif
55
+ #endif
56
+
57
+ #ifdef _WIN32
58
+ uint32_t AtomicLoadU32(uint32_t* addr) {
59
+ return std::atomic_ref<uint32_t>(*addr).load(std::memory_order_acquire);
60
+ }
61
+
62
+ uint64_t TimeoutToMillis(const struct timespec* timeout) {
63
+ if (timeout == nullptr) return UINT64_MAX;
64
+
65
+ uint64_t millis = static_cast<uint64_t>(timeout->tv_sec) * 1000ULL;
66
+ millis += static_cast<uint64_t>(timeout->tv_nsec) / 1000000ULL;
67
+ if ((timeout->tv_nsec % 1000000L) != 0) millis += 1;
68
+ return millis;
69
+ }
70
+ #endif
71
+
72
+ int FutexWait(uint32_t* addr, uint32_t expected, const struct timespec* timeout) {
73
+ #ifdef __linux__
74
+ return static_cast<int>(syscall(
75
+ SYS_futex,
76
+ reinterpret_cast<int*>(addr),
77
+ FUTEX_WAIT,
78
+ static_cast<int>(expected),
79
+ timeout,
80
+ nullptr,
81
+ 0
82
+ ));
83
+ #elif defined(_WIN32)
84
+ if (AtomicLoadU32(addr) != expected) {
85
+ errno = EAGAIN;
86
+ return -1;
87
+ }
88
+
89
+ uint64_t timeout_ms = TimeoutToMillis(timeout);
90
+ const bool infinite = timeout_ms == UINT64_MAX;
91
+ const auto started = std::chrono::steady_clock::now();
92
+ const auto deadline = infinite
93
+ ? std::chrono::steady_clock::time_point::max()
94
+ : started + std::chrono::milliseconds(timeout_ms);
95
+
96
+ while (AtomicLoadU32(addr) == expected) {
97
+ if (!infinite && std::chrono::steady_clock::now() >= deadline) {
98
+ errno = ETIMEDOUT;
99
+ return -1;
100
+ }
101
+
102
+ if (infinite) {
103
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
104
+ continue;
105
+ }
106
+
107
+ auto now = std::chrono::steady_clock::now();
108
+ if (now >= deadline) {
109
+ errno = ETIMEDOUT;
110
+ return -1;
111
+ }
112
+
113
+ auto remaining = deadline - now;
114
+ auto one_ms = std::chrono::milliseconds(1);
115
+ std::this_thread::sleep_for(remaining < one_ms ? remaining : one_ms);
116
+ }
117
+
118
+ return 0;
119
+ #else
120
+ uint32_t timeout_us = 0;
121
+ if (timeout != nullptr) {
122
+ uint64_t micros =
123
+ (static_cast<uint64_t>(timeout->tv_sec) * 1000000ULL) +
124
+ (static_cast<uint64_t>(timeout->tv_nsec) / 1000ULL);
125
+ if (micros == 0) micros = 1;
126
+ timeout_us = micros > UINT32_MAX
127
+ ? UINT32_MAX
128
+ : static_cast<uint32_t>(micros);
129
+ }
130
+
131
+ return __ulock_wait(
132
+ UL_COMPARE_AND_WAIT_SHARED,
133
+ addr,
134
+ static_cast<uint64_t>(expected),
135
+ timeout_us
136
+ );
137
+ #endif
138
+ }
139
+
140
+ int FutexWake(uint32_t* addr, int count) {
141
+ #ifdef __linux__
142
+ return static_cast<int>(syscall(
143
+ SYS_futex,
144
+ reinterpret_cast<int*>(addr),
145
+ FUTEX_WAKE,
146
+ count,
147
+ nullptr,
148
+ nullptr,
149
+ 0
150
+ ));
151
+ #elif defined(_WIN32)
152
+ (void)addr;
153
+ return count <= 0 ? 0 : 1;
154
+ #else
155
+ uint32_t operation = UL_COMPARE_AND_WAIT_SHARED;
156
+ if (count <= 0 || count == INT_MAX) {
157
+ operation |= ULF_WAKE_ALL;
158
+ }
159
+
160
+ int rc = __ulock_wake(operation, addr, 0);
161
+ if (rc == 0) {
162
+ return 1;
163
+ }
164
+
165
+ if (errno == ENOENT) {
166
+ return 0;
167
+ }
168
+
169
+ return -1;
170
+ #endif
171
+ }
172
+
173
+ void ThrowErrno(v8::Isolate* isolate, const char* message, int err = errno) {
174
+ std::string full = std::string(message) + ": " + std::strerror(err);
175
+ isolate->ThrowException(v8::Exception::Error(
176
+ v8::String::NewFromUtf8(isolate, full.c_str()).ToLocalChecked()
177
+ ));
178
+ }
179
+
180
+ void ThrowType(v8::Isolate* isolate, const char* message) {
181
+ isolate->ThrowException(v8::Exception::TypeError(
182
+ v8::String::NewFromUtf8(isolate, message).ToLocalChecked()
183
+ ));
184
+ }
185
+
186
+ void ThrowRange(v8::Isolate* isolate, const char* message) {
187
+ isolate->ThrowException(v8::Exception::RangeError(
188
+ v8::String::NewFromUtf8(isolate, message).ToLocalChecked()
189
+ ));
190
+ }
191
+
192
+ bool ReadU32Argument(
193
+ const v8::FunctionCallbackInfo<v8::Value>& args,
194
+ int index,
195
+ uint32_t* out
196
+ ) {
197
+ v8::Isolate* isolate = args.GetIsolate();
198
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
199
+
200
+ if (args.Length() <= index || !args[index]->IsNumber()) {
201
+ ThrowType(isolate, "expected a number argument");
202
+ return false;
203
+ }
204
+
205
+ v8::Maybe<uint32_t> maybe = args[index]->Uint32Value(context);
206
+ if (maybe.IsNothing()) {
207
+ ThrowType(isolate, "expected a valid uint32 argument");
208
+ return false;
209
+ }
210
+
211
+ *out = maybe.FromJust();
212
+ return true;
213
+ }
214
+
215
+ bool ReadSizeArgument(
216
+ const v8::FunctionCallbackInfo<v8::Value>& args,
217
+ int index,
218
+ size_t* out
219
+ ) {
220
+ v8::Isolate* isolate = args.GetIsolate();
221
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
222
+
223
+ if (args.Length() <= index || !args[index]->IsNumber()) {
224
+ ThrowType(isolate, "expected a number argument");
225
+ return false;
226
+ }
227
+
228
+ v8::Maybe<int64_t> maybe = args[index]->IntegerValue(context);
229
+ if (maybe.IsNothing() || maybe.FromJust() < 0) {
230
+ ThrowRange(isolate, "offset must be non-negative");
231
+ return false;
232
+ }
233
+
234
+ *out = static_cast<size_t>(maybe.FromJust());
235
+ return true;
236
+ }
237
+
238
+ bool ReadOptionalMillis(
239
+ const v8::FunctionCallbackInfo<v8::Value>& args,
240
+ int index,
241
+ double fallback,
242
+ double* out
243
+ ) {
244
+ *out = fallback;
245
+
246
+ if (args.Length() <= index || args[index]->IsUndefined() || args[index]->IsNull()) {
247
+ return true;
248
+ }
249
+
250
+ v8::Isolate* isolate = args.GetIsolate();
251
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
252
+
253
+ if (!args[index]->IsNumber()) {
254
+ ThrowType(isolate, "milliseconds must be a number");
255
+ return false;
256
+ }
257
+
258
+ v8::Maybe<double> maybe = args[index]->NumberValue(context);
259
+ if (maybe.IsNothing()) {
260
+ ThrowType(isolate, "milliseconds must be a valid number");
261
+ return false;
262
+ }
263
+
264
+ double value = maybe.FromJust();
265
+ if (value != value) {
266
+ ThrowRange(isolate, "milliseconds must not be NaN");
267
+ return false;
268
+ }
269
+
270
+ *out = value < 0 ? 0 : value;
271
+ return true;
272
+ }
273
+
274
+ bool GetBackingStore(
275
+ v8::Isolate* isolate,
276
+ v8::Local<v8::Value> value,
277
+ std::shared_ptr<v8::BackingStore>* out
278
+ ) {
279
+ if (value->IsSharedArrayBuffer()) {
280
+ *out = value.As<v8::SharedArrayBuffer>()->GetBackingStore();
281
+ return true;
282
+ }
283
+
284
+ if (value->IsArrayBuffer()) {
285
+ *out = value.As<v8::ArrayBuffer>()->GetBackingStore();
286
+ return true;
287
+ }
288
+
289
+ ThrowType(isolate, "first argument must be an ArrayBuffer or SharedArrayBuffer");
290
+ return false;
291
+ }
292
+
293
+ bool GetU32Pointer(
294
+ const v8::FunctionCallbackInfo<v8::Value>& args,
295
+ uint32_t** out
296
+ ) {
297
+ v8::Isolate* isolate = args.GetIsolate();
298
+
299
+ if (args.Length() < 2) {
300
+ ThrowType(isolate, "expected buffer and byteOffset");
301
+ return false;
302
+ }
303
+
304
+ std::shared_ptr<v8::BackingStore> backing;
305
+ if (!GetBackingStore(isolate, args[0], &backing)) {
306
+ return false;
307
+ }
308
+
309
+ size_t offset = 0;
310
+ if (!ReadSizeArgument(args, 1, &offset)) {
311
+ return false;
312
+ }
313
+
314
+ if ((offset % alignof(uint32_t)) != 0) {
315
+ ThrowRange(isolate, "byteOffset must be uint32-aligned");
316
+ return false;
317
+ }
318
+
319
+ if (backing->ByteLength() < sizeof(uint32_t) || offset > backing->ByteLength() - sizeof(uint32_t)) {
320
+ ThrowRange(isolate, "byteOffset out of bounds");
321
+ return false;
322
+ }
323
+
324
+ *out = reinterpret_cast<uint32_t*>(
325
+ static_cast<uint8_t*>(backing->Data()) + offset
326
+ );
327
+ return true;
328
+ }
329
+
330
+ bool ReadTimeout(
331
+ const v8::FunctionCallbackInfo<v8::Value>& args,
332
+ int index,
333
+ struct timespec* timeout,
334
+ struct timespec** out
335
+ ) {
336
+ *out = nullptr;
337
+
338
+ if (args.Length() <= index || args[index]->IsUndefined() || args[index]->IsNull()) {
339
+ return true;
340
+ }
341
+
342
+ v8::Isolate* isolate = args.GetIsolate();
343
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
344
+
345
+ if (!args[index]->IsNumber()) {
346
+ ThrowType(isolate, "timeoutMs must be a number");
347
+ return false;
348
+ }
349
+
350
+ v8::Maybe<double> maybe = args[index]->NumberValue(context);
351
+ if (maybe.IsNothing()) {
352
+ ThrowType(isolate, "timeoutMs must be a valid number");
353
+ return false;
354
+ }
355
+
356
+ double timeoutMs = maybe.FromJust();
357
+ if (timeoutMs < 0) {
358
+ return true;
359
+ }
360
+
361
+ time_t sec = static_cast<time_t>(timeoutMs / 1000.0);
362
+ long nsec = static_cast<long>(
363
+ (timeoutMs - (static_cast<double>(sec) * 1000.0)) * 1000000.0
364
+ );
365
+
366
+ if (nsec < 0) nsec = 0;
367
+ if (nsec > 999999999L) nsec = 999999999L;
368
+
369
+ timeout->tv_sec = sec;
370
+ timeout->tv_nsec = nsec;
371
+ *out = timeout;
372
+ return true;
373
+ }
374
+
375
+ void WaitU32(const v8::FunctionCallbackInfo<v8::Value>& args) {
376
+ v8::Isolate* isolate = args.GetIsolate();
377
+
378
+ uint32_t* ptr = nullptr;
379
+ if (!GetU32Pointer(args, &ptr)) {
380
+ return;
381
+ }
382
+
383
+ uint32_t expected = 0;
384
+ if (!ReadU32Argument(args, 2, &expected)) {
385
+ return;
386
+ }
387
+
388
+ struct timespec timeout;
389
+ struct timespec* timeout_ptr = nullptr;
390
+ if (!ReadTimeout(args, 3, &timeout, &timeout_ptr)) {
391
+ return;
392
+ }
393
+
394
+ int rc = FutexWait(ptr, expected, timeout_ptr);
395
+ if (rc == 0) {
396
+ args.GetReturnValue().Set(
397
+ v8::String::NewFromUtf8Literal(isolate, "woken")
398
+ );
399
+ return;
400
+ }
401
+
402
+ int saved = errno;
403
+ if (saved == EAGAIN) {
404
+ args.GetReturnValue().Set(
405
+ v8::String::NewFromUtf8Literal(isolate, "changed")
406
+ );
407
+ return;
408
+ }
409
+ if (saved == EINTR) {
410
+ args.GetReturnValue().Set(
411
+ v8::String::NewFromUtf8Literal(isolate, "interrupted")
412
+ );
413
+ return;
414
+ }
415
+ if (saved == ETIMEDOUT) {
416
+ args.GetReturnValue().Set(
417
+ v8::String::NewFromUtf8Literal(isolate, "timed-out")
418
+ );
419
+ return;
420
+ }
421
+
422
+ ThrowErrno(isolate, "futex wait failed", saved);
423
+ }
424
+
425
+ void WakeU32(const v8::FunctionCallbackInfo<v8::Value>& args) {
426
+ v8::Isolate* isolate = args.GetIsolate();
427
+
428
+ uint32_t* ptr = nullptr;
429
+ if (!GetU32Pointer(args, &ptr)) {
430
+ return;
431
+ }
432
+
433
+ uint32_t raw_count = 1;
434
+ if (args.Length() >= 3 && !args[2]->IsUndefined()) {
435
+ if (!ReadU32Argument(args, 2, &raw_count)) {
436
+ return;
437
+ }
438
+ }
439
+
440
+ int count = raw_count == 0 ? INT_MAX : static_cast<int>(raw_count);
441
+ int woken = FutexWake(ptr, count);
442
+ if (woken == -1) {
443
+ ThrowErrno(isolate, "futex wake failed");
444
+ return;
445
+ }
446
+
447
+ args.GetReturnValue().Set(v8::Integer::New(isolate, woken));
448
+ }
449
+
450
+ void Sleep(const v8::FunctionCallbackInfo<v8::Value>& args) {
451
+ double milliseconds = 1;
452
+ if (!ReadOptionalMillis(args, 0, 1, &milliseconds)) {
453
+ return;
454
+ }
455
+
456
+ std::this_thread::sleep_for(
457
+ std::chrono::duration<double, std::milli>(milliseconds)
458
+ );
459
+ }
460
+
461
+ void Yield(const v8::FunctionCallbackInfo<v8::Value>& args) {
462
+ (void)args;
463
+ std::this_thread::yield();
464
+ }
465
+
466
+ void Initialize(v8::Local<v8::Object> exports) {
467
+ NODE_SET_METHOD(exports, "waitU32", WaitU32);
468
+ NODE_SET_METHOD(exports, "wakeU32", WakeU32);
469
+ NODE_SET_METHOD(exports, "notifyU32", WakeU32);
470
+ NODE_SET_METHOD(exports, "sleep", Sleep);
471
+ NODE_SET_METHOD(exports, "yield", Yield);
472
+ }
473
+
474
+ NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
475
+
476
+ } // namespace knitting_shm
@@ -0,0 +1,73 @@
1
+ import type { SharedBuffer, SharedBufferRegion } from "../common/shared-buffer-region.js";
2
+ export declare const BYTE_CARPET_ALIGN_BYTES = 64;
3
+ export declare const alignBytes: (value: number, alignment?: number) => number;
4
+ export declare const makeSharedBufferRegion: (sab: SharedBuffer, byteOffset: number, byteLength: number) => SharedBufferRegion;
5
+ export type ByteCarpetSlice = {
6
+ name: string;
7
+ byteOffset: number;
8
+ byteLength: number;
9
+ reservedByteLength: number;
10
+ };
11
+ export declare const createByteCarpet: ({ alignTo, startByteOffset, }?: {
12
+ alignTo?: number;
13
+ startByteOffset?: number;
14
+ }) => {
15
+ slices: ByteCarpetSlice[];
16
+ take: (name: string, byteLength: number, { alignTo: sliceAlignment, reserveByteLength, }?: {
17
+ alignTo?: number;
18
+ reserveByteLength?: number;
19
+ }) => ByteCarpetSlice;
20
+ byteLength: () => number;
21
+ bind: (sab: SharedBuffer, slice: ByteCarpetSlice) => SharedBufferRegion;
22
+ };
23
+ export declare const getStridedSlotOffsetU32: ({ slotIndex, slotStrideU32, baseU32, extraU32, }: {
24
+ slotIndex: number;
25
+ slotStrideU32: number;
26
+ baseU32?: number;
27
+ extraU32?: number;
28
+ }) => number;
29
+ export declare const getStridedSlotByteOffset: ({ slotIndex, slotStrideU32, baseByteOffset, baseU32, extraU32, }: {
30
+ slotIndex: number;
31
+ slotStrideU32: number;
32
+ baseByteOffset?: number;
33
+ baseU32?: number;
34
+ extraU32?: number;
35
+ }) => number;
36
+ export declare const getStridedRegionSpanBytes: ({ slotCount, slotStrideU32, slotLengthU32, baseU32, }: {
37
+ slotCount: number;
38
+ slotStrideU32: number;
39
+ slotLengthU32: number;
40
+ baseU32?: number;
41
+ }) => number;
42
+ export declare const getInterleavedSlotStrideU32: (slotStrideU32: number) => number;
43
+ export declare const getHeaderBlockByteLength: ({ slotCount, slotStrideU32, queues, alignTo, }: {
44
+ slotCount: number;
45
+ slotStrideU32: number;
46
+ queues?: number;
47
+ alignTo?: number;
48
+ }) => number;
49
+ export type HeaderLayoutMode = "split" | "interleaved";
50
+ export type QueueControlByteLayout = {
51
+ headers: SharedBufferRegion;
52
+ headerSlotStrideU32: number;
53
+ lockSector: SharedBufferRegion;
54
+ payloadSector: SharedBufferRegion;
55
+ };
56
+ export type LockControlCarpet = {
57
+ controlSAB: SharedBuffer;
58
+ signals: SharedBufferRegion;
59
+ abortSignals: SharedBufferRegion;
60
+ lock: QueueControlByteLayout;
61
+ returnLock: QueueControlByteLayout;
62
+ slices: readonly ByteCarpetSlice[];
63
+ };
64
+ export declare const createLockControlCarpet: ({ signalBytes, abortBytes, lockSectorBytes, headerSlotStrideU32, slotCount, headerLayout, alignTo, createBuffer, }: {
65
+ signalBytes: number;
66
+ abortBytes: number;
67
+ lockSectorBytes: number;
68
+ headerSlotStrideU32: number;
69
+ slotCount: number;
70
+ headerLayout?: HeaderLayoutMode;
71
+ alignTo?: number;
72
+ createBuffer?: (byteLength: number) => SharedBuffer;
73
+ }) => LockControlCarpet;
@@ -0,0 +1,157 @@
1
+ export const BYTE_CARPET_ALIGN_BYTES = 64;
2
+ const U32_BYTES = Uint32Array.BYTES_PER_ELEMENT;
3
+ const toNonNegativeInteger = (value, label) => {
4
+ if (!Number.isInteger(value) || value < 0) {
5
+ throw new RangeError(`${label} must be a non-negative integer`);
6
+ }
7
+ return value;
8
+ };
9
+ export const alignBytes = (value, alignment = BYTE_CARPET_ALIGN_BYTES) => {
10
+ const safeValue = toNonNegativeInteger(value, "value");
11
+ const safeAlignment = toNonNegativeInteger(alignment, "alignment");
12
+ if (safeAlignment === 0) {
13
+ throw new RangeError("alignment must be greater than zero");
14
+ }
15
+ return Math.ceil(safeValue / safeAlignment) * safeAlignment;
16
+ };
17
+ export const makeSharedBufferRegion = (sab, byteOffset, byteLength) => ({
18
+ sab,
19
+ byteOffset: toNonNegativeInteger(byteOffset, "byteOffset"),
20
+ byteLength: toNonNegativeInteger(byteLength, "byteLength"),
21
+ });
22
+ export const createByteCarpet = ({ alignTo = BYTE_CARPET_ALIGN_BYTES, startByteOffset = 0, } = {}) => {
23
+ const defaultAlignment = toNonNegativeInteger(alignTo, "alignTo");
24
+ if (defaultAlignment === 0) {
25
+ throw new RangeError("alignTo must be greater than zero");
26
+ }
27
+ let cursor = toNonNegativeInteger(startByteOffset, "startByteOffset");
28
+ const slices = [];
29
+ const take = (name, byteLength, { alignTo: sliceAlignment = defaultAlignment, reserveByteLength, } = {}) => {
30
+ const logicalByteLength = toNonNegativeInteger(byteLength, `${name} byteLength`);
31
+ const safeSliceAlignment = toNonNegativeInteger(sliceAlignment, `${name} alignTo`);
32
+ if (safeSliceAlignment === 0) {
33
+ throw new RangeError(`${name} alignTo must be greater than zero`);
34
+ }
35
+ const reserved = reserveByteLength == null
36
+ ? alignBytes(logicalByteLength, safeSliceAlignment)
37
+ : toNonNegativeInteger(reserveByteLength, `${name} reserveByteLength`);
38
+ if (reserved < logicalByteLength) {
39
+ throw new RangeError(`${name} reserveByteLength must cover byteLength`);
40
+ }
41
+ const byteOffset = alignBytes(cursor, safeSliceAlignment);
42
+ const slice = {
43
+ name,
44
+ byteOffset,
45
+ byteLength: logicalByteLength,
46
+ reservedByteLength: reserved,
47
+ };
48
+ slices.push(slice);
49
+ cursor = byteOffset + reserved;
50
+ return slice;
51
+ };
52
+ return {
53
+ slices,
54
+ take,
55
+ byteLength: () => cursor,
56
+ bind: (sab, slice) => makeSharedBufferRegion(sab, slice.byteOffset, slice.byteLength),
57
+ };
58
+ };
59
+ export const getStridedSlotOffsetU32 = ({ slotIndex, slotStrideU32, baseU32 = 0, extraU32 = 0, }) => (slotIndex * slotStrideU32) + baseU32 + extraU32;
60
+ export const getStridedSlotByteOffset = ({ slotIndex, slotStrideU32, baseByteOffset = 0, baseU32 = 0, extraU32 = 0, }) => baseByteOffset +
61
+ (getStridedSlotOffsetU32({
62
+ slotIndex,
63
+ slotStrideU32,
64
+ baseU32,
65
+ extraU32,
66
+ }) * U32_BYTES);
67
+ export const getStridedRegionSpanBytes = ({ slotCount, slotStrideU32, slotLengthU32, baseU32 = 0, }) => {
68
+ const safeSlotCount = toNonNegativeInteger(slotCount, "slotCount");
69
+ if (safeSlotCount === 0)
70
+ return 0;
71
+ return (getStridedSlotOffsetU32({
72
+ slotIndex: safeSlotCount - 1,
73
+ slotStrideU32,
74
+ baseU32,
75
+ }) + slotLengthU32) * U32_BYTES;
76
+ };
77
+ export const getInterleavedSlotStrideU32 = (slotStrideU32) => slotStrideU32 * 2;
78
+ export const getHeaderBlockByteLength = ({ slotCount, slotStrideU32, queues = 1, alignTo = BYTE_CARPET_ALIGN_BYTES, }) => alignBytes(slotCount * slotStrideU32 * U32_BYTES * queues, alignTo);
79
+ const createInterleavedHeaderPair = ({ sab, byteOffset, slotCount, slotStrideU32, }) => {
80
+ const headerSlotStrideU32 = getInterleavedSlotStrideU32(slotStrideU32);
81
+ const slotBytes = slotStrideU32 * U32_BYTES;
82
+ const spanBytes = getStridedRegionSpanBytes({
83
+ slotCount,
84
+ slotStrideU32: headerSlotStrideU32,
85
+ slotLengthU32: slotStrideU32,
86
+ });
87
+ return {
88
+ headerSlotStrideU32,
89
+ requestHeaders: makeSharedBufferRegion(sab, byteOffset, spanBytes),
90
+ returnHeaders: makeSharedBufferRegion(sab, byteOffset + slotBytes, spanBytes),
91
+ };
92
+ };
93
+ export const createLockControlCarpet = ({ signalBytes, abortBytes, lockSectorBytes, headerSlotStrideU32, slotCount, headerLayout = "interleaved", alignTo = BYTE_CARPET_ALIGN_BYTES, createBuffer = (byteLength) => new SharedArrayBuffer(byteLength), }) => {
94
+ const carpet = createByteCarpet({ alignTo });
95
+ const signalsSlice = carpet.take("signals", signalBytes);
96
+ const requestLockSlice = carpet.take("requestLockSector", lockSectorBytes);
97
+ const returnLockSlice = carpet.take("returnLockSector", lockSectorBytes);
98
+ let requestHeadersSlice;
99
+ let returnHeadersSlice;
100
+ let interleavedHeadersSlice;
101
+ if (headerLayout === "interleaved") {
102
+ interleavedHeadersSlice = carpet.take("interleavedHeaders", getHeaderBlockByteLength({
103
+ slotCount,
104
+ slotStrideU32: headerSlotStrideU32,
105
+ queues: 2,
106
+ alignTo,
107
+ }));
108
+ }
109
+ else {
110
+ requestHeadersSlice = carpet.take("requestHeaders", getHeaderBlockByteLength({
111
+ slotCount,
112
+ slotStrideU32: headerSlotStrideU32,
113
+ alignTo,
114
+ }));
115
+ returnHeadersSlice = carpet.take("returnHeaders", getHeaderBlockByteLength({
116
+ slotCount,
117
+ slotStrideU32: headerSlotStrideU32,
118
+ alignTo,
119
+ }));
120
+ }
121
+ const abortSignalsSlice = carpet.take("abortSignals", abortBytes);
122
+ const controlSAB = createBuffer(carpet.byteLength());
123
+ const signals = carpet.bind(controlSAB, signalsSlice);
124
+ const abortSignals = carpet.bind(controlSAB, abortSignalsSlice);
125
+ const requestLockSector = carpet.bind(controlSAB, requestLockSlice);
126
+ const returnLockSector = carpet.bind(controlSAB, returnLockSlice);
127
+ const headerPair = headerLayout === "interleaved"
128
+ ? createInterleavedHeaderPair({
129
+ sab: controlSAB,
130
+ byteOffset: interleavedHeadersSlice.byteOffset,
131
+ slotCount,
132
+ slotStrideU32: headerSlotStrideU32,
133
+ })
134
+ : {
135
+ headerSlotStrideU32,
136
+ requestHeaders: carpet.bind(controlSAB, requestHeadersSlice),
137
+ returnHeaders: carpet.bind(controlSAB, returnHeadersSlice),
138
+ };
139
+ return {
140
+ controlSAB,
141
+ signals,
142
+ abortSignals,
143
+ lock: {
144
+ headers: headerPair.requestHeaders,
145
+ headerSlotStrideU32: headerPair.headerSlotStrideU32,
146
+ lockSector: requestLockSector,
147
+ payloadSector: requestLockSector,
148
+ },
149
+ returnLock: {
150
+ headers: headerPair.returnHeaders,
151
+ headerSlotStrideU32: headerPair.headerSlotStrideU32,
152
+ lockSector: returnLockSector,
153
+ payloadSector: returnLockSector,
154
+ },
155
+ slices: carpet.slices,
156
+ };
157
+ };