@gjsify/sab-native 0.4.13 → 0.4.15
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/lib/esm/index.js +1 -1
- package/lib/types/index.d.ts +51 -1
- package/package.json +3 -3
package/lib/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";let e=null;const t=globalThis.imports?.gi;if(t)try{e=t.GjsifySabNative}catch{}const n=e;function hasNativeSab(){return e!==null}const r="@gjsify/sab-native prebuild is not loaded — install the package on a Linux system with the prebuild available, or build locally via `meson compile` in packages/node/sab-native/.";var i=class SharedBuffer{_native;constructor(e){this._native=e}static create(t){if(!e)throw Error(r);if(!Number.isInteger(t)||t<=0)throw TypeError(`SharedBuffer.create: size must be a positive integer`);let n=e.SharedBuffer.create(t);if(!n)throw Error(`memfd_create or mmap failed`);return new SharedBuffer(n)}static fromFd(t,n){if(!e)throw Error(r);if(!Number.isInteger(t)||t<0)throw TypeError(`SharedBuffer.fromFd: fd must be a non-negative integer`);if(!Number.isInteger(n)||n<=0)throw TypeError(`SharedBuffer.fromFd: size must be a positive integer`);let i=e.SharedBuffer.from_fd(t,n);if(!i)throw Error(`mmap failed`);return new SharedBuffer(i)}get byteLength(){return this._assertOpen().byte_length}get fd(){return this._assertOpen().fd}get closed(){return this._native===null}close(){this._native=null}getUint8(e){return this._assertOpen().get_u8(e)}setUint8(e,t){this._assertOpen().set_u8(e,t&255)}getUint32LE(e){return this._assertOpen().get_u32_le(e)}setUint32LE(e,t){this._assertOpen().set_u32_le(e,t>>>0)}getInt32LE(e){return this._assertOpen().get_i32_le(e)}setInt32LE(e,t){this._assertOpen().set_i32_le(e,t|0)}getBigUint64LE(e){return this._assertOpen().get_u64_le(e)}setBigUint64LE(e,t){this._assertOpen().set_u64_le(e,t)}readBytes(e,t){let n=this._assertOpen().read_bytes(e,t),r=globalThis.imports?.byteArray;if(r&&typeof r.fromGBytes==`function`){let e=r.fromGBytes(n);return new Uint8Array(e)}return new Uint8Array(n.toArray())}writeBytes(e,t){let n=globalThis.imports?.byteArray,r;if(n&&typeof n.toGBytes==`function`)r=n.toGBytes(t);else{let e=globalThis.imports?.gi?.GLib;r=e?.Bytes?new e.Bytes(t):t}this._assertOpen().write_bytes(e,r)}get _nativeHandle(){return this._assertOpen()}_assertOpen(){if(!this._native)throw Error(`SharedBuffer has been closed`);return this._native}};const a={add32(e,t,n){return e._nativeHandle.atomic_add_i32(t,n|0)},sub32(e,t,n){return e._nativeHandle.atomic_sub_i32(t,n|0)},load32(e,t){return e._nativeHandle.atomic_load_i32(t)},store32(e,t,n){e._nativeHandle.atomic_store_i32(t,n|0)},exchange32(e,t,n){return e._nativeHandle.atomic_xchg_i32(t,n|0)},compareExchange32(e,t,n,r){return e._nativeHandle.atomic_cmpxchg_i32(t,n|0,r|0)},wait32(e,t,n,r){let i=e._nativeHandle.futex_wait(t,n|0,r|0);if(i===0)return`ok`;if(i===-1)return`not-equal`;if(i===-2)return`timed-out`;if(i===-3)return`interrupted`;throw Error(`futex_wait returned errno ${-i}`)},notify32(e,t,n){return e._nativeHandle.futex_wake(t,n|0)}},o=e?{makePair(){let[t,n,r]=e.FdChannel.make_pair();if(!t)throw Error(`socketpair() failed`);return{parentFd:n,childFd:r}},sendFd(t,n,r){return e.FdChannel.send_fd(t,n,r>>>0)},recvFd(t){let[n,r]=e.FdChannel.recv_fd(t);if(n===0)return null;if(n<0)throw Error(`recvmsg failed`);return{fd:n,tag:r>>>0}},closeFd(t){e.FdChannel.close_fd(t)}}:null;export{i as SharedBuffer,a as atomics,o as fdChannel,hasNativeSab,n as nativeSab};
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";let e=null;const t=globalThis.imports?.gi;if(t)try{e=t.GjsifySabNative}catch{}const n=e;function hasNativeSab(){return e!==null}const r="@gjsify/sab-native prebuild is not loaded — install the package on a Linux system with the prebuild available, or build locally via `meson compile` in packages/node/sab-native/.";var i=class SharedBuffer{_native;constructor(e){this._native=e}static create(t){if(!e)throw Error(r);if(!Number.isInteger(t)||t<=0)throw TypeError(`SharedBuffer.create: size must be a positive integer`);let n=e.SharedBuffer.create(t);if(!n)throw Error(`memfd_create or mmap failed`);return new SharedBuffer(n)}static fromFd(t,n){if(!e)throw Error(r);if(!Number.isInteger(t)||t<0)throw TypeError(`SharedBuffer.fromFd: fd must be a non-negative integer`);if(!Number.isInteger(n)||n<=0)throw TypeError(`SharedBuffer.fromFd: size must be a positive integer`);let i=e.SharedBuffer.from_fd(t,n);if(!i)throw Error(`mmap failed`);return new SharedBuffer(i)}get byteLength(){return this._assertOpen().byte_length}get fd(){return this._assertOpen().fd}get closed(){return this._native===null}close(){this._native=null}getUint8(e){return this._assertOpen().get_u8(e)}setUint8(e,t){this._assertOpen().set_u8(e,t&255)}getUint32LE(e){return this._assertOpen().get_u32_le(e)}setUint32LE(e,t){this._assertOpen().set_u32_le(e,t>>>0)}getInt32LE(e){return this._assertOpen().get_i32_le(e)}setInt32LE(e,t){this._assertOpen().set_i32_le(e,t|0)}getBigUint64LE(e){return this._assertOpen().get_u64_le(e)}setBigUint64LE(e,t){this._assertOpen().set_u64_le(e,t)}readBytes(e,t){let n=this._assertOpen().read_bytes(e,t),r=globalThis.imports?.byteArray;if(r&&typeof r.fromGBytes==`function`){let e=r.fromGBytes(n);return new Uint8Array(e)}return new Uint8Array(n.toArray())}viewBytes(e,t){return this.readBytes(e,t)}toBuffer(e=0,t){let n=t??this.byteLength-e,r=this.viewBytes(e,n),i=globalThis.Buffer;if(typeof i?.from!=`function`)throw Error(`SharedBuffer.toBuffer: globalThis.Buffer is not registered. Import "@gjsify/buffer/register" or rely on --globals auto.`);return i.from(r.buffer,r.byteOffset,r.byteLength)}writeBytes(e,t){let n=globalThis.imports?.byteArray,r;if(n&&typeof n.toGBytes==`function`)r=n.toGBytes(t);else{let e=globalThis.imports?.gi?.GLib;r=e?.Bytes?new e.Bytes(t):t}this._assertOpen().write_bytes(e,r)}get _nativeHandle(){return this._assertOpen()}_assertOpen(){if(!this._native)throw Error(`SharedBuffer has been closed`);return this._native}};const a={add32(e,t,n){return e._nativeHandle.atomic_add_i32(t,n|0)},sub32(e,t,n){return e._nativeHandle.atomic_sub_i32(t,n|0)},load32(e,t){return e._nativeHandle.atomic_load_i32(t)},store32(e,t,n){e._nativeHandle.atomic_store_i32(t,n|0)},exchange32(e,t,n){return e._nativeHandle.atomic_xchg_i32(t,n|0)},compareExchange32(e,t,n,r){return e._nativeHandle.atomic_cmpxchg_i32(t,n|0,r|0)},wait32(e,t,n,r){let i=e._nativeHandle.futex_wait(t,n|0,r|0);if(i===0)return`ok`;if(i===-1)return`not-equal`;if(i===-2)return`timed-out`;if(i===-3)return`interrupted`;throw Error(`futex_wait returned errno ${-i}`)},notify32(e,t,n){return e._nativeHandle.futex_wake(t,n|0)}},o=e?{makePair(){let[t,n,r]=e.FdChannel.make_pair();if(!t)throw Error(`socketpair() failed`);return{parentFd:n,childFd:r}},sendFd(t,n,r){return e.FdChannel.send_fd(t,n,r>>>0)},recvFd(t){let[n,r]=e.FdChannel.recv_fd(t);if(n===0)return null;if(n<0)throw Error(`recvmsg failed`);return{fd:n,tag:r>>>0}},closeFd(t){e.FdChannel.close_fd(t)}}:null;export{i as SharedBuffer,a as atomics,o as fdChannel,hasNativeSab,n as nativeSab};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -105,9 +105,59 @@ export declare class SharedBuffer {
|
|
|
105
105
|
/**
|
|
106
106
|
* Read a byte range out as a Uint8Array. ONE-TIME COPY — modifications
|
|
107
107
|
* to the returned array do NOT propagate back to the region. Use
|
|
108
|
-
* `writeBytes()` to commit changes back
|
|
108
|
+
* `writeBytes()` to commit changes back, or `viewBytes()` for a
|
|
109
|
+
* zero-copy view.
|
|
109
110
|
*/
|
|
110
111
|
readBytes(offset: number, length: number): Uint8Array;
|
|
112
|
+
/**
|
|
113
|
+
* Return a fresh `Uint8Array` containing the bytes at `[offset, offset+length)`.
|
|
114
|
+
* Same semantics as `readBytes()` — kept under this name because
|
|
115
|
+
* downstream tooling (`Buffer.from`, `node:crypto`'s `Hash.update`,
|
|
116
|
+
* `fs.writeSync`) calls into this method via duck-typing and the
|
|
117
|
+
* `viewBytes` name reads more naturally there.
|
|
118
|
+
*
|
|
119
|
+
* **NOT zero-copy in current GJS.** GJS's `byteArray.fromGBytes`
|
|
120
|
+
* (`refs/gjs/gjs/byteArray.cpp::from_gbytes_func`) allocates a fresh
|
|
121
|
+
* `JS::ArrayBuffer` and memcpy's the GBytes data into it — by design,
|
|
122
|
+
* for alignment + immutability reasons.
|
|
123
|
+
*
|
|
124
|
+
* **Why no internal fix is possible**: a true zero-copy view would need
|
|
125
|
+
* `JS::NewExternalArrayBuffer` against the mmap pointer, but that JSAPI
|
|
126
|
+
* call requires a `JSContext*` which GJS does not expose to
|
|
127
|
+
* GObject-introspected `.so` plugins. The fix has to land in GJS
|
|
128
|
+
* itself (e.g. a `byteArray.fromGBytesShared` helper). Tracked under
|
|
129
|
+
* STATUS.md "Upstream GJS Patch Candidates".
|
|
130
|
+
*
|
|
131
|
+
* Modifications to the returned array therefore do NOT propagate back
|
|
132
|
+
* to the region — use `writeBytes()` to commit changes.
|
|
133
|
+
*
|
|
134
|
+
* @throws Error if `byteArray.fromGBytes` is not available (GJS < 1.66).
|
|
135
|
+
*/
|
|
136
|
+
viewBytes(offset: number, length: number): Uint8Array;
|
|
137
|
+
/**
|
|
138
|
+
* Return a `Buffer` containing the bytes at `[offset, offset+length)`.
|
|
139
|
+
* The Buffer is a fresh allocation (see `viewBytes()` for the "not
|
|
140
|
+
* zero-copy in current GJS" caveat — the limitation is in GJS'
|
|
141
|
+
* `byteArray.fromGBytes`, not bypassable from a `.so` plugin without
|
|
142
|
+
* upstream patching GJS) — `buf.writeUInt32LE(...)`, `buf.subarray(...)`,
|
|
143
|
+
* `buf.toString('hex')`, `createHash().update(buf)` all work, but
|
|
144
|
+
* writes do NOT propagate back to the shared region.
|
|
145
|
+
*
|
|
146
|
+
* Requires `globalThis.Buffer` to be registered (via
|
|
147
|
+
* `@gjsify/buffer/register`) — otherwise throws. Consumers running
|
|
148
|
+
* under the standard gjsify CLI bundle have Buffer registered via
|
|
149
|
+
* `--globals auto`; for ad-hoc scripts, add an explicit
|
|
150
|
+
* `import '@gjsify/buffer/register'` at the entry point.
|
|
151
|
+
*
|
|
152
|
+
* Return type is generic to avoid an import dependency on
|
|
153
|
+
* `@gjsify/buffer` — the concrete return is a `Buffer` (subclass of
|
|
154
|
+
* `Uint8Array`). Default `T = Uint8Array` is always safe;
|
|
155
|
+
* `toBuffer<Buffer>()` gets the full Buffer surface.
|
|
156
|
+
*
|
|
157
|
+
* @param offset byte offset into the shared region. Defaults to 0.
|
|
158
|
+
* @param length byte length. Defaults to `byteLength - offset`.
|
|
159
|
+
*/
|
|
160
|
+
toBuffer<T extends Uint8Array = Uint8Array>(offset?: number, length?: number): T;
|
|
111
161
|
/**
|
|
112
162
|
* Write a byte range into the region. memcpy on the C side.
|
|
113
163
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/sab-native",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.15",
|
|
4
4
|
"description": "Optional Vala/GObject bridge providing cross-process shared memory + atomics for @gjsify/worker_threads. Wraps Linux memfd_create(2) + mmap(MAP_SHARED) to back JS-visible SharedBuffer regions whose fds can be passed to child workers via SCM_RIGHTS over a Unix-domain socket; exposes typed accessors (getInt32LE / setUint8 / etc.) plus atomics built on __atomic_* GCC builtins and SYS_futex for wait/notify. Workaround for the stock-GJS gap where SharedArrayBuffer is unavailable (Mozilla disables the SAB constructor without COOP/COEP opts) and SAB cannot share backing stores across Gio.Subprocess workers anyway.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/esm/index.js",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"@girs/gio-2.0": "2.88.0-4.0.0-rc.15"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@gjsify/cli": "^0.4.
|
|
60
|
-
"@gjsify/unit": "^0.4.
|
|
59
|
+
"@gjsify/cli": "^0.4.15",
|
|
60
|
+
"@gjsify/unit": "^0.4.15",
|
|
61
61
|
"@ts-for-gir/cli": "^4.0.0-rc.15",
|
|
62
62
|
"@types/node": "^25.6.2",
|
|
63
63
|
"typescript": "^6.0.3"
|