@camstack/shm-ring 0.1.2

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 ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "shm_ring",
5
+ "sources": [ "src/shm.cc" ],
6
+ "include_dirs": [
7
+ "<!@(node -p \"require('node-addon-api').include\")"
8
+ ],
9
+ "defines": [
10
+ "NAPI_DISABLE_CPP_EXCEPTIONS",
11
+ "NAPI_VERSION=8"
12
+ ],
13
+ "cflags!": [ "-fno-exceptions" ],
14
+ "cflags_cc!": [ "-fno-exceptions" ],
15
+ "conditions": [
16
+ [ "OS=='linux'", {
17
+ "libraries": [ "-lrt" ]
18
+ } ],
19
+ [ "OS=='mac'", {
20
+ "xcode_settings": {
21
+ "CLANG_CXX_LANGUAGE_STANDARD": "c++17",
22
+ "MACOSX_DEPLOYMENT_TARGET": "11.0"
23
+ }
24
+ } ],
25
+ [ "OS=='win'", {
26
+ "msvs_settings": {
27
+ "VCCLCompilerTool": {
28
+ "ExceptionHandling": 1
29
+ }
30
+ }
31
+ } ]
32
+ ]
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,22 @@
1
+ import { DecodedFrame, FrameHandle, IScopedLogger } from '@camstack/types';
2
+ /**
3
+ * Opens (and caches) a `FrameRingReader` per `shmId` and reads the pixels a
4
+ * `FrameHandle` refers to. Single-consumer — one cache per frame subscription.
5
+ */
6
+ export declare class FrameRingReaderCache {
7
+ private readonly rings;
8
+ private readonly logger;
9
+ private closed;
10
+ constructor(logger?: IScopedLogger);
11
+ /**
12
+ * Read the pixels a `FrameHandle` refers to and return them as a
13
+ * `DecodedFrame`. Returns `null` when the ring slot was recycled before the
14
+ * read (a dropped frame) or the segment could not be opened.
15
+ */
16
+ read(handle: FrameHandle): DecodedFrame | null;
17
+ /** Close every cached segment. Idempotent. */
18
+ close(): void;
19
+ /** Get the cached reader for a handle's segment, opening it on first use. */
20
+ private ringFor;
21
+ }
22
+ //# sourceMappingURL=frame-ring-reader-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-ring-reader-cache.d.ts","sourceRoot":"","sources":["../src/frame-ring-reader-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAiB/E;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8B;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,MAAM,CAAQ;gBAEV,MAAM,CAAC,EAAE,aAAa;IAIlC;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,IAAI;IAyB9C,8CAA8C;IAC9C,KAAK,IAAI,IAAI;IAiBb,6EAA6E;IAC7E,OAAO,CAAC,OAAO;CAkChB"}
@@ -0,0 +1,379 @@
1
+ import { FrameFormat, FrameHandle } from '@camstack/types';
2
+ /** Per-frame metadata published alongside the pixels of a slot. */
3
+ export interface FrameMeta {
4
+ readonly width: number;
5
+ readonly height: number;
6
+ readonly format: FrameFormat;
7
+ readonly pts: number;
8
+ /** Valid pixel bytes — must be `<= slotByteLength`. */
9
+ readonly byteLength: number;
10
+ }
11
+ /**
12
+ * A scatter-write slot reservation returned by {@link FrameRingWriter.beginFrame}.
13
+ *
14
+ * ## Scatter-write seqlock contract (Phase 5 / D9 Task 7c — READ THIS)
15
+ *
16
+ * `beginFrame()` has already bumped this slot's `seq` to **odd** — a write is in
17
+ * progress, so any reader hitting this slot skips it (returns `null` / a failed
18
+ * `validate()`). The caller now owns an exclusive write window over `buffer`:
19
+ *
20
+ * 1. `buffer` is a writable `subarray` **directly over the slot's pixel
21
+ * region in the mapped segment** — there is no intermediate copy. A
22
+ * producer (e.g. the node-av scaler) fills it in place.
23
+ * 2. The slot stays odd for the **entire fill duration** — not just a memcpy.
24
+ * A scatter producer that takes longer to fill the slot (a whole scale
25
+ * pass) holds the slot odd for that whole pass; this is correct, readers
26
+ * simply latest-wins-drop that slot until {@link FrameRingWriter.commitFrame}.
27
+ * 3. The caller MUST call {@link FrameRingWriter.commitFrame} with this exact
28
+ * `slot` once the fill is done — that writes the metadata, bumps `seq` back
29
+ * to **even** (committed) and advances `writeIndex` so readers can see it.
30
+ *
31
+ * Exactly one `beginFrame` may be open at a time per writer (there is a single
32
+ * writer per ring). `buffer.byteLength` is the slot's full capacity
33
+ * (`slotByteLength`); the caller writes at most `commitFrame`'s `meta.byteLength`
34
+ * valid bytes into it.
35
+ */
36
+ export interface FrameSlotWrite {
37
+ /** The ring slot index this write targets — pass it back to `commitFrame`. */
38
+ readonly slot: number;
39
+ /**
40
+ * A writable view **directly over the slot's pixel region** in the shared
41
+ * mapping. Fill it in place, then `commitFrame(slot, meta)`. Valid only
42
+ * between the `beginFrame` that produced it and its matching `commitFrame`.
43
+ */
44
+ readonly buffer: Buffer;
45
+ }
46
+ /**
47
+ * The result of a successful seqlock read.
48
+ *
49
+ * ## Buffer-lifetime contract (Phase 5 / D9 Task 7b — READ THIS)
50
+ *
51
+ * `pixels` is a view onto the **reader's reusable scratch buffer**, NOT a
52
+ * freshly allocated copy. The seqlock-validated pixel bytes are memcpy'd into
53
+ * that scratch buffer (the copy is the seqlock safety copy — the bytes survive
54
+ * a post-read slot recycle), but the buffer itself is reused on this reader's
55
+ * **next** `readHandle` / `readLatest` call.
56
+ *
57
+ * Therefore `pixels` is **valid only until this same reader's next read**:
58
+ *
59
+ * - A consumer that processes the frame **synchronously**, fully, before it
60
+ * issues another read on this reader may use `pixels` directly (borrow).
61
+ * - A consumer that **retains** the frame past its next read — queues it for
62
+ * async work, stores it, hands it to another tick — MUST `Buffer.from(...)`
63
+ * it into its own storage at the point of retention. Holding the borrowed
64
+ * `pixels` past the next read is silent corruption: the next read overwrites
65
+ * the bytes in place.
66
+ *
67
+ * `meta` and `handle` are plain immutable values — safe to retain freely.
68
+ */
69
+ export interface FrameRead {
70
+ /**
71
+ * The slot's pixel bytes copied into the reader's reusable scratch buffer.
72
+ *
73
+ * **Borrowed, not owned** — valid only until this reader's next
74
+ * `readHandle` / `readLatest`. Retain past that point ⇒ copy first
75
+ * (`Buffer.from(pixels)`). See the `FrameRead` doc comment.
76
+ */
77
+ readonly pixels: Buffer;
78
+ readonly meta: FrameMeta;
79
+ /** A serialisable handle for this exact frame. */
80
+ readonly handle: FrameHandle;
81
+ }
82
+ /**
83
+ * A **zero-copy** read: `pixels` is a `subarray` directly over the shared
84
+ * mapping — no memcpy at all (Phase 5 / D9 Task 7b Step 3).
85
+ *
86
+ * ## Validation contract (READ THIS — concurrency-sensitive)
87
+ *
88
+ * Because `pixels` aliases the live ring slot, a concurrent writer can recycle
89
+ * that slot at any moment, tearing the bytes a consumer is reading. A
90
+ * `FrameView` is therefore an **optimistic** read: the seqlock's *opening*
91
+ * check already passed (the slot was committed and, for a handle read, the seq
92
+ * matched), but the closing re-check is deferred to the consumer.
93
+ *
94
+ * A consumer MUST:
95
+ * 1. read the slot **synchronously and fast** — no `await`, no yielding;
96
+ * 2. call `validate()` **immediately after** finishing with `pixels`;
97
+ * 3. if `validate()` returns `false`, **discard** whatever it computed from
98
+ * `pixels` — the bytes were (or may have been) overwritten mid-read, i.e.
99
+ * a torn frame, the latest-wins drop.
100
+ *
101
+ * `validate()` returning `true` means the slot's `seq` is unchanged since the
102
+ * read opened — the bytes the consumer just processed were a single committed
103
+ * frame, not torn. This is the same seqlock guarantee `readHandle` gives, but
104
+ * the closing half is the consumer's responsibility instead of being baked
105
+ * into a memcpy + recheck. Use this ONLY for tight synchronous consumers
106
+ * (e.g. motion's frame scan, the perf bench's compute stage); any consumer
107
+ * that retains or processes asynchronously MUST use the copying `readHandle` /
108
+ * `readLatest` instead.
109
+ */
110
+ export interface FrameView {
111
+ /**
112
+ * A `subarray` view directly over the shared-memory slot — **zero-copy**.
113
+ * Valid for a synchronous read only; `validate()` confirms it was not torn.
114
+ */
115
+ readonly pixels: Buffer;
116
+ readonly meta: FrameMeta;
117
+ readonly handle: FrameHandle;
118
+ /**
119
+ * Re-check the slot's seqlock. `true` ⇒ the slot was not recycled while the
120
+ * consumer read `pixels` — the frame is intact. `false` ⇒ a torn read,
121
+ * discard everything derived from `pixels`. Call once, right after the
122
+ * synchronous read of `pixels` completes.
123
+ */
124
+ validate(): boolean;
125
+ }
126
+ /** Internal geometry of a sized segment. */
127
+ interface RingGeometry {
128
+ readonly slotCount: number;
129
+ readonly slotByteLength: number;
130
+ /** Byte length of the `Int32Array` header. */
131
+ readonly headerBytes: number;
132
+ /** Byte offset of the metadata region. */
133
+ readonly metaOffset: number;
134
+ /** Byte offset of the pixel region. */
135
+ readonly pixelsOffset: number;
136
+ /** Total segment byte length. */
137
+ readonly totalBytes: number;
138
+ }
139
+ /**
140
+ * Total byte length a segment must have to hold a `slotCount`-slot ring with
141
+ * `slotByteLength`-byte pixel slots. The decoder uses this to size the
142
+ * shared-memory segment before constructing a `FrameRingWriter` over it.
143
+ */
144
+ export declare function computeSegmentSize(slotCount: number, slotByteLength: number): number;
145
+ /**
146
+ * Slot-count bounds for a per-resolution ring. {@link deriveSlotCount} clamps
147
+ * the budget-derived count into `[MIN_RING_SLOTS, MAX_RING_SLOTS]`:
148
+ * - `MIN` keeps a tiny floor even when one frame nearly exhausts the budget
149
+ * (a 4K RGB frame is ~24.9 MB — a 128 MB budget yields only ~5 raw slots);
150
+ * - `MAX` caps the ring for small frames so a 360p stream does not allocate
151
+ * hundreds of slots' worth of shared memory it will never use latest-wins.
152
+ */
153
+ export declare const MIN_RING_SLOTS = 6;
154
+ export declare const MAX_RING_SLOTS = 64;
155
+ /**
156
+ * Slot count for a ring, derived from a per-ring shared-memory budget.
157
+ *
158
+ * `budgetBytes` is the shared memory a single stream's ring may consume; the
159
+ * raw count is `floor(budgetBytes / slotByteLength)`, clamped into
160
+ * `[MIN_RING_SLOTS, MAX_RING_SLOTS]`. Live video is latest-wins, so the slot
161
+ * count only needs to absorb the writer-commit / reader-read window — the
162
+ * budget trades shared-memory footprint against that window per resolution.
163
+ */
164
+ export declare function deriveSlotCount(budgetBytes: number, slotByteLength: number): number;
165
+ /**
166
+ * Bytes per pixel for each packed decode format a frame ring slot can hold.
167
+ *
168
+ * This is the single source of truth shared by the decoder write side
169
+ * (`DecoderFrameRingSink`) and every consumer read side
170
+ * (`FrameRingReaderCache`): if the two computed different values, a reader
171
+ * would mis-map the shared-memory segment and read corrupt pixels.
172
+ *
173
+ * `jpeg` has no fixed bytes-per-pixel — `computeSlotByteLength` short-circuits
174
+ * it to the equivalent RGB24 raster before this is ever consulted, so the `3`
175
+ * here is only a safe placeholder for exhaustiveness over `FrameFormat`.
176
+ */
177
+ export declare function bytesPerPixel(format: FrameFormat): number;
178
+ /**
179
+ * Per-slot byte capacity for a frame of the given geometry — the single source
180
+ * of truth for both the decoder write side and the consumer read side.
181
+ *
182
+ * For raw formats this is exactly `width × height × bpp`. For `jpeg` (variable
183
+ * length) the slot is sized to the equivalent RGB24 raster — a JPEG is always
184
+ * far smaller than its source raster, so an RGB24-sized slot is a safe upper
185
+ * bound. Note: this upper bound holds only while the JPEG encoder is pinned to
186
+ * its current quality (~80). A near-lossless quality setting can produce a
187
+ * JPEG that exceeds the RGB24 byte count; if quality is raised significantly,
188
+ * this slot size must be revisited.
189
+ */
190
+ export declare function computeSlotByteLength(width: number, height: number, format: FrameFormat): number;
191
+ /**
192
+ * Shared geometry + typed views over a segment buffer. The header is an
193
+ * `Int32Array` so `Atomics` ops apply directly; metadata and pixels are read
194
+ * and written through `DataView` / `Buffer` slices.
195
+ */
196
+ declare abstract class FrameRingBase {
197
+ protected readonly segment: Buffer;
198
+ protected readonly shmId: string;
199
+ protected readonly geometry: RingGeometry;
200
+ /** `Int32Array` over the header — slotCount, slotByteLength, writeIndex, seq[]. */
201
+ protected readonly header: Int32Array;
202
+ protected readonly view: DataView;
203
+ protected constructor(segment: Buffer, shmId: string, slotCount: number, slotByteLength: number);
204
+ /** Header index of `seq[slot]`. */
205
+ protected seqIndex(slot: number): number;
206
+ /** Byte offset of slot `slot`'s metadata struct. */
207
+ protected metaOffsetOf(slot: number): number;
208
+ /** Byte offset of slot `slot`'s pixel region. */
209
+ protected pixelOffsetOf(slot: number): number;
210
+ }
211
+ /**
212
+ * The single writer for a ring. Constructed by whoever owns the segment (the
213
+ * decoder). On construction it stamps `slotCount` / `slotByteLength` into the
214
+ * header so a reader opening the same segment can self-describe.
215
+ */
216
+ export declare class FrameRingWriter extends FrameRingBase {
217
+ /** Slot index of an in-flight `beginWrite()`, or `null` when idle. */
218
+ private pendingSlot;
219
+ /** Cluster node id stamped into every `FrameHandle` this writer produces. */
220
+ private readonly nodeId;
221
+ constructor(segment: Buffer, shmId: string, slotCount: number, slotByteLength: number, nodeId: string);
222
+ /** The number of slots in this ring — derived per-resolution from the budget. */
223
+ get slotCount(): number;
224
+ /** The slot the next `writeFrame` / `beginFrame` will target. */
225
+ private targetSlot;
226
+ /**
227
+ * Open the seqlock on the next slot and hand the caller a writable view
228
+ * directly over that slot's pixel region — the **scatter-write** entry point.
229
+ *
230
+ * The seqlock contract (see {@link FrameSlotWrite}): this bumps the slot's
231
+ * `seq` to **odd** (write in progress), so a concurrent reader skips the slot
232
+ * for the entire fill window. The caller fills `buffer` in place — there is
233
+ * NO intermediate copy: a producer (the node-av scaler) writes its packed
234
+ * output straight into the mapped segment — then MUST call
235
+ * {@link commitFrame} with the returned `slot` to publish the frame.
236
+ *
237
+ * Exactly one `beginFrame` may be open per writer at a time.
238
+ */
239
+ beginFrame(): FrameSlotWrite;
240
+ /**
241
+ * Close the seqlock opened by {@link beginFrame}: write the metadata, bump
242
+ * `seq` back to **even** (committed) and advance `writeIndex` so readers see
243
+ * this slot as the latest. Returns the published frame's `FrameHandle`.
244
+ *
245
+ * `slot` must be the value from the matching `beginFrame`'s
246
+ * {@link FrameSlotWrite}. The pixels are assumed already filled in place by
247
+ * the caller (the scatter-write contract) — `commitFrame` copies nothing.
248
+ */
249
+ commitFrame(slot: number, meta: FrameMeta): FrameHandle;
250
+ /**
251
+ * Abandon the seqlock opened by {@link beginFrame} **without publishing the
252
+ * slot** — the degenerate-path counterpart of {@link commitFrame}.
253
+ *
254
+ * `beginFrame` has bumped the slot's `seq` to **odd**. A caller that opened a
255
+ * slot but then could not fill it with valid pixels (the producer failed, or
256
+ * there were no source planes) MUST NOT `commitFrame` — that would close the
257
+ * seqlock over uninitialised / stale shared-memory bytes and advance
258
+ * `writeIndex`, so a reader would see those garbage bytes as a real, latest
259
+ * frame. `abortFrame` instead:
260
+ *
261
+ * 1. bumps `seq` from odd back to **even** — the slot is not left stuck
262
+ * odd, so the writer can reuse it on a later cycle;
263
+ * 2. does **NOT** advance `writeIndex` — `readLatest` therefore never
264
+ * surfaces this slot as the latest (the prior latest stays latest);
265
+ * 3. returns no `FrameHandle` — nothing is handed downstream.
266
+ *
267
+ * The slot's pixel bytes after `abortFrame` are **undefined** (whatever was
268
+ * there before, or a half-written producer output) — but that is harmless
269
+ * because no consumer ever sees the slot as committed: `readLatest` skips it
270
+ * (writeIndex did not move) and any handle that happened to point at this
271
+ * slot from an earlier commit sees the changed `seq` and is correctly
272
+ * rejected by `readHandle`'s seq check.
273
+ *
274
+ * `slot` must be the value from the matching `beginFrame` — validated
275
+ * exactly as {@link commitFrame} does.
276
+ */
277
+ abortFrame(slot: number): void;
278
+ /**
279
+ * Publish one frame from a caller-provided pixel `Buffer` — the convenience
280
+ * wrapper over the scatter-write {@link beginFrame} / {@link commitFrame}
281
+ * pair: it opens the slot, copies `pixels` into it, then commits.
282
+ *
283
+ * Prefer the `beginFrame` / `commitFrame` pair when the pixels can be
284
+ * produced directly into the slot (the node-av scaler scattering its packed
285
+ * output into the mapped segment) — that path has zero write-side copy.
286
+ * `writeFrame` keeps the single-call form for callers that already hold a
287
+ * detached pixel `Buffer`.
288
+ */
289
+ writeFrame(pixels: Buffer, meta: FrameMeta): FrameHandle;
290
+ }
291
+ /**
292
+ * A reader for a ring. Many readers may share a segment concurrently with the
293
+ * single writer. Every read is a seqlock read — a torn or recycled slot is
294
+ * dropped (returns `null`), never returned as garbage pixels.
295
+ */
296
+ export declare class FrameRingReader extends FrameRingBase {
297
+ /**
298
+ * The reusable per-reader scratch buffer the seqlock-validated pixels are
299
+ * copied into — see the `FrameRead` doc comment for the borrow contract.
300
+ *
301
+ * Sized to `slotByteLength` on construction (the segment's stamped slot
302
+ * capacity) and lazily grown if a slot ever reports a larger `byteLength`.
303
+ * Reusing one buffer per reader replaces the per-read `Buffer.allocUnsafe` —
304
+ * the D9 perf gate's regression (Task 7b): a fresh ~5.93 MB allocation per
305
+ * 1080p frame churned ~1.39 GB/s and stalled on GC. The single memcpy stays
306
+ * (it IS the seqlock safety copy); only the allocation is removed.
307
+ */
308
+ private scratch;
309
+ /**
310
+ * Cluster node id of the node that OWNS this segment (i.e. the writer's
311
+ * node — the node whose shared memory physically holds the ring), stamped
312
+ * into every `FrameHandle` this reader produces. `FrameHandle.nodeId` always
313
+ * identifies the segment-owning node, never the reading node, so callers
314
+ * pass the writer's node id here (e.g. `FrameRingReaderCache` passes
315
+ * `handle.nodeId` straight through).
316
+ */
317
+ private readonly nodeId;
318
+ constructor(segment: Buffer, shmId: string, slotCount: number, slotByteLength: number,
319
+ /**
320
+ * Node id of the segment-owning (writer's) node — see the `nodeId` field
321
+ * doc. NOT this reader's own node.
322
+ */
323
+ nodeId: string);
324
+ /**
325
+ * Return the reader's scratch buffer as a view of exactly `byteLength` bytes,
326
+ * growing the backing buffer first if a slot ever exceeds the stamped slot
327
+ * capacity (defensive — `slotByteLength` should always bound `byteLength`).
328
+ */
329
+ private scratchFor;
330
+ /**
331
+ * Read the latest committed frame, latest-wins. Returns `null` when the ring
332
+ * is empty (no frame ever published) or the latest slot is mid-write /
333
+ * recycled (a slow reader simply drops that frame and retries next tick).
334
+ *
335
+ * The returned `FrameRead.pixels` is **borrowed** from this reader's reusable
336
+ * scratch buffer — valid only until this reader's next read. A consumer that
337
+ * retains it past the next read MUST copy. See the `FrameRead` doc comment.
338
+ */
339
+ readLatest(): FrameRead | null;
340
+ /**
341
+ * Read the exact frame a `FrameHandle` refers to. Returns `null` when the
342
+ * slot has been recycled (its committed `seq` no longer matches `handle.seq`)
343
+ * or is mid-write — i.e. the frame is gone, not torn.
344
+ *
345
+ * The returned `FrameRead.pixels` is **borrowed** from this reader's reusable
346
+ * scratch buffer — valid only until this reader's next read. A consumer that
347
+ * retains it past the next read MUST copy. See the `FrameRead` doc comment.
348
+ */
349
+ readHandle(handle: FrameHandle): FrameRead | null;
350
+ /**
351
+ * The seqlock read of a single slot.
352
+ *
353
+ * `expectedSeq` is the committed seq a `readHandle` caller demands; pass
354
+ * `null` for `readLatest`, which accepts whatever even seq it finds.
355
+ */
356
+ private seqlockRead;
357
+ /**
358
+ * Read the latest committed frame **zero-copy** — `FrameView.pixels` is a
359
+ * `subarray` over the shared mapping, no memcpy. The caller MUST process it
360
+ * synchronously and then call `validate()`; see the `FrameView` contract.
361
+ * Returns `null` when the ring is empty or the latest slot is mid-write.
362
+ */
363
+ readLatestView(): FrameView | null;
364
+ /**
365
+ * Read the frame a `FrameHandle` refers to **zero-copy** — `FrameView.pixels`
366
+ * is a `subarray` over the shared mapping, no memcpy. The caller MUST process
367
+ * it synchronously and then call `validate()`; see the `FrameView` contract.
368
+ * Returns `null` when the slot is recycled (seq mismatch) or mid-write.
369
+ */
370
+ readHandleView(handle: FrameHandle): FrameView | null;
371
+ /**
372
+ * The zero-copy half of the seqlock read: validate the opening seqlock, then
373
+ * return a `subarray` over the slot plus a `validate()` closure that re-reads
374
+ * the seqlock. No pixel copy — the consumer owns the closing recheck.
375
+ */
376
+ private seqlockReadView;
377
+ }
378
+ export {};
379
+ //# sourceMappingURL=frame-ring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-ring.d.ts","sourceRoot":"","sources":["../src/frame-ring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@camstack/shm-ring` — cross-platform shared-memory frame plane.
3
+ *
4
+ * Public surface:
5
+ * - the native segment wrapper (`createSegment` / `openSegment` / `unlinkSegment`);
6
+ * - `FrameRing` (the seqlock ring) — added in Task 2.
7
+ */
8
+ export { createSegment, openSegment, unlinkSegment, } from './native.js';
9
+ export type { ShmSegment, NativeSegmentHandle } from './native.js';
10
+ export { FrameRingWriter, FrameRingReader, computeSegmentSize, bytesPerPixel, computeSlotByteLength, deriveSlotCount, MIN_RING_SLOTS, MAX_RING_SLOTS, } from './frame-ring.js';
11
+ export type { FrameMeta, FrameRead, FrameView, FrameSlotWrite } from './frame-ring.js';
12
+ export { FrameRingReaderCache } from './frame-ring-reader-cache.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EACL,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA"}