@bldrs-ai/conway 1.405.1243 → 1.408.1248

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.
@@ -30,7 +30,7 @@ var import_node_process = require("node:process");
30
30
  var readline = __toESM(require("node:readline"), 1);
31
31
 
32
32
  // compiled/src/version/version.js
33
- var versionString = "Conway v1.405.1243";
33
+ var versionString = "Conway v1.408.1248";
34
34
 
35
35
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
36
36
  var wasmType = "";
@@ -14965,7 +14965,7 @@ ${t5.join("\n")}` : "";
14965
14965
  var import_process = require("process");
14966
14966
 
14967
14967
  // compiled/src/version/version.js
14968
- var versionString = "Conway v1.405.1243";
14968
+ var versionString = "Conway v1.408.1248";
14969
14969
 
14970
14970
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
14971
14971
  function pThreadsAllowed() {
@@ -15943,7 +15943,7 @@ var ParsingBuffer = class {
15943
15943
  };
15944
15944
 
15945
15945
  // compiled/src/version/version.js
15946
- var versionString = "Conway v1.405.1243";
15946
+ var versionString = "Conway v1.408.1248";
15947
15947
 
15948
15948
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
15949
15949
  function pThreadsAllowed() {
@@ -944,7 +944,7 @@ var EntityTypesIfcCount = 909;
944
944
  var entity_types_ifc_gen_default = EntityTypesIfc;
945
945
 
946
946
  // compiled/src/version/version.js
947
- var versionString = "Conway v1.405.1243";
947
+ var versionString = "Conway v1.408.1248";
948
948
 
949
949
  // compiled/dependencies/conway-geom/interface/conway_geometry.js
950
950
  var wasmType = "";
@@ -0,0 +1,99 @@
1
+ import { GeometryTiles } from "./demand_geometry_queue.js";
2
+ import { ChunkedPool } from "./mem/chunked_pool.js";
3
+ import { SharedAssetPool } from "./mem/shared_asset_pool.js";
4
+ /**
5
+ * One shareable geometry payload an instance needs resident: the asset's
6
+ * identity and its byte size. For IFC, the asset is a representation's
7
+ * tessellated geometry and `byteSize` comes from the wasm-side extract.
8
+ */
9
+ export interface GeometryAsset<AssetID> {
10
+ assetID: AssetID;
11
+ byteSize: number;
12
+ }
13
+ /**
14
+ * The domain half the tile pool delegates to: what assets an instance is made
15
+ * of, and how to materialise / discard one asset's payload. This is the seam
16
+ * the production conway-geom surface implements (extract → tessellate into
17
+ * the asset's chunks; discard on last release); tests implement it with
18
+ * recording mocks.
19
+ */
20
+ export interface InstanceAssetSource<AssetID> {
21
+ /**
22
+ * The assets an instance requires resident, with their byte sizes. For a
23
+ * simple product this is one asset; mapped/instanced representations return
24
+ * the same assetID from many instances — that sharing is the point.
25
+ *
26
+ * @param instanceID The instance (product local ID in the geometry case).
27
+ * @return {GeometryAsset<AssetID>[]} The instance's assets.
28
+ */
29
+ assetsOf(instanceID: number): GeometryAsset<AssetID>[];
30
+ /**
31
+ * Materialise an asset's payload (called exactly once per residency, on
32
+ * the 0→1 reference). In production: run the wasm extract/tessellate and
33
+ * write the result into the asset's pool chunks.
34
+ *
35
+ * @param assetID The asset to materialise.
36
+ */
37
+ materialize(assetID: AssetID): void;
38
+ /**
39
+ * Discard an asset's payload (called exactly once per residency, on the
40
+ * 1→0 reference, after its chunks are already back in the pool).
41
+ *
42
+ * @param assetID The asset to discard.
43
+ */
44
+ discard(assetID: AssetID): void;
45
+ }
46
+ /**
47
+ * The geometry narrowing of the general memory system (`src/core/mem/`): a
48
+ * {@link GeometryTiles} backend where a product's "tile" is a set of
49
+ * refcounted, chunk-resident **assets** in a {@link SharedAssetPool}.
50
+ *
51
+ * Composed with {@link DemandGeometryQueue} this completes the M3 resident
52
+ * design: the queue owns demand ordering and the logical budget; this pool
53
+ * owns physical residency. Two accounting views, one invariant:
54
+ *
55
+ * - **Logical (queue-side):** `extract` charges an instance the full
56
+ * chunk-rounded cost of *every* asset it references, shared or not. Sharing
57
+ * is thus double-charged logically — deliberately conservative: summed
58
+ * logical charges always cover physical use, so with the queue's budget set
59
+ * to the pool's budget, an acquire can never fail mid-extract. (Heavy
60
+ * sharing under-utilises the logical budget; the production wiring can
61
+ * widen the queue budget once measured. The safe direction first.)
62
+ * - **Physical (pool-side):** `bytesInUse` counts real chunks. Shared assets
63
+ * are stored once, refcounted, and freed only on the last release — so
64
+ * evicting product A never discards the representation product B still
65
+ * renders (the mapped-item correctness rule, held structurally).
66
+ */
67
+ export declare class GeometryTilePool<AssetID> implements GeometryTiles {
68
+ private readonly assets_;
69
+ private readonly source_;
70
+ /** Assets each extracted instance holds references on. */
71
+ private readonly held_;
72
+ /**
73
+ * @param pool The chunk pool residents live in.
74
+ * @param source The domain half (asset composition + materialise/discard).
75
+ */
76
+ constructor(pool: ChunkedPool, source: InstanceAssetSource<AssetID>);
77
+ /**
78
+ * @return {SharedAssetPool<AssetID>} The refcounted asset layer (physical
79
+ * accounting lives here / on its pool).
80
+ */
81
+ get assets(): SharedAssetPool<AssetID>;
82
+ /**
83
+ * Materialise an instance's tile: take a reference on each of its assets,
84
+ * materialising the ones not yet resident.
85
+ *
86
+ * @param instanceID The instance (product local ID).
87
+ * @return {number} The instance's logical byte cost (chunk-rounded, every
88
+ * asset fully charged — see class docs for why sharing is double-charged).
89
+ */
90
+ extract(instanceID: number): number;
91
+ /**
92
+ * Release an instance's tile: drop the reference on each of its assets,
93
+ * discarding those this instance was the last holder of.
94
+ *
95
+ * @param instanceID The instance (product local ID).
96
+ */
97
+ release(instanceID: number): void;
98
+ }
99
+ //# sourceMappingURL=geometry_tile_pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry_tile_pool.d.ts","sourceRoot":"","sources":["../../../src/core/geometry_tile_pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAGzD;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,OAAO;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAGD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB,CAAC,OAAO;IAE1C;;;;;;;OAOG;IACH,QAAQ,CAAE,UAAU,EAAE,MAAM,GAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAA;IAExD;;;;;;OAMG;IACH,WAAW,CAAE,OAAO,EAAE,OAAO,GAAI,IAAI,CAAA;IAErC;;;;;OAKG;IACH,OAAO,CAAE,OAAO,EAAE,OAAO,GAAI,IAAI,CAAA;CAClC;AAGD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,gBAAgB,CAAC,OAAO,CAAE,YAAW,aAAa;IAE7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAElD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IAEtD,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8C;IAEpE;;;OAGG;gBACU,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC;IAKpE;;;OAGG;IACH,IAAW,MAAM,IAAI,eAAe,CAAC,OAAO,CAAC,CAE5C;IAED;;;;;;;OAOG;IACI,OAAO,CAAE,UAAU,EAAE,MAAM,GAAI,MAAM;IAoD5C;;;;;OAKG;IACI,OAAO,CAAE,UAAU,EAAE,MAAM,GAAI,IAAI;CAgB3C"}
@@ -0,0 +1,104 @@
1
+ import { SharedAssetPool } from "./mem/shared_asset_pool.js";
2
+ /**
3
+ * The geometry narrowing of the general memory system (`src/core/mem/`): a
4
+ * {@link GeometryTiles} backend where a product's "tile" is a set of
5
+ * refcounted, chunk-resident **assets** in a {@link SharedAssetPool}.
6
+ *
7
+ * Composed with {@link DemandGeometryQueue} this completes the M3 resident
8
+ * design: the queue owns demand ordering and the logical budget; this pool
9
+ * owns physical residency. Two accounting views, one invariant:
10
+ *
11
+ * - **Logical (queue-side):** `extract` charges an instance the full
12
+ * chunk-rounded cost of *every* asset it references, shared or not. Sharing
13
+ * is thus double-charged logically — deliberately conservative: summed
14
+ * logical charges always cover physical use, so with the queue's budget set
15
+ * to the pool's budget, an acquire can never fail mid-extract. (Heavy
16
+ * sharing under-utilises the logical budget; the production wiring can
17
+ * widen the queue budget once measured. The safe direction first.)
18
+ * - **Physical (pool-side):** `bytesInUse` counts real chunks. Shared assets
19
+ * are stored once, refcounted, and freed only on the last release — so
20
+ * evicting product A never discards the representation product B still
21
+ * renders (the mapped-item correctness rule, held structurally).
22
+ */
23
+ export class GeometryTilePool {
24
+ /**
25
+ * @param pool The chunk pool residents live in.
26
+ * @param source The domain half (asset composition + materialise/discard).
27
+ */
28
+ constructor(pool, source) {
29
+ /** Assets each extracted instance holds references on. */
30
+ this.held_ = new Map();
31
+ this.assets_ = new SharedAssetPool(pool);
32
+ this.source_ = source;
33
+ }
34
+ /**
35
+ * @return {SharedAssetPool<AssetID>} The refcounted asset layer (physical
36
+ * accounting lives here / on its pool).
37
+ */
38
+ get assets() {
39
+ return this.assets_;
40
+ }
41
+ /**
42
+ * Materialise an instance's tile: take a reference on each of its assets,
43
+ * materialising the ones not yet resident.
44
+ *
45
+ * @param instanceID The instance (product local ID).
46
+ * @return {number} The instance's logical byte cost (chunk-rounded, every
47
+ * asset fully charged — see class docs for why sharing is double-charged).
48
+ */
49
+ extract(instanceID) {
50
+ if (this.held_.has(instanceID)) {
51
+ throw new Error(`Instance ${instanceID} already extracted`);
52
+ }
53
+ const assets = this.source_.assetsOf(instanceID);
54
+ const pool = this.assets_.pool;
55
+ // Validate and price every asset BEFORE taking any references, so a bad
56
+ // byte size (a source bug) throws at zero state instead of mid-extract
57
+ // with references already taken. chunkRound throws on negatives.
58
+ let logicalBytes = 0;
59
+ for (const asset of assets) {
60
+ logicalBytes += pool.chunkRound(asset.byteSize);
61
+ }
62
+ for (const asset of assets) {
63
+ const { retained, wasAbsent } = this.assets_.retain(asset.assetID, asset.byteSize);
64
+ if (!retained) {
65
+ // Unwind references already taken so a failed extract has no effect —
66
+ // keeping the materialize/discard pairing: an asset this unwind takes
67
+ // to zero references must be discarded like any other last release.
68
+ for (const taken of assets) {
69
+ if (taken === asset) {
70
+ break;
71
+ }
72
+ if (this.assets_.release(taken.assetID)) {
73
+ this.source_.discard(taken.assetID);
74
+ }
75
+ }
76
+ throw new Error(`Geometry pool exhausted materialising instance ${instanceID} — ` +
77
+ "the scheduler budget must not exceed the pool budget");
78
+ }
79
+ if (wasAbsent) {
80
+ this.source_.materialize(asset.assetID);
81
+ }
82
+ }
83
+ this.held_.set(instanceID, assets);
84
+ return logicalBytes;
85
+ }
86
+ /**
87
+ * Release an instance's tile: drop the reference on each of its assets,
88
+ * discarding those this instance was the last holder of.
89
+ *
90
+ * @param instanceID The instance (product local ID).
91
+ */
92
+ release(instanceID) {
93
+ const assets = this.held_.get(instanceID);
94
+ if (assets === void 0) {
95
+ throw new Error(`Release of un-extracted instance ${instanceID}`);
96
+ }
97
+ this.held_.delete(instanceID);
98
+ for (const asset of assets) {
99
+ if (this.assets_.release(asset.assetID)) {
100
+ this.source_.discard(asset.assetID);
101
+ }
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=geometry_tile_pool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry_tile_pool.test.d.ts","sourceRoot":"","sources":["../../../src/core/geometry_tile_pool.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,180 @@
1
+ /* eslint-disable no-magic-numbers */
2
+ // M3 production design: the geometry narrowing of the mem system, alone and
3
+ // composed with DemandGeometryQueue. The load-bearing assertions: shared
4
+ // representations are stored once and never freed while any product still
5
+ // holds them, and the queue's logical budget always covers the pool's
6
+ // physical use (so an extract can never blow the pool mid-flight).
7
+ import { describe, expect, test } from "@jest/globals";
8
+ import { DemandGeometryQueue } from "./demand_geometry_queue.js";
9
+ import { GeometryTilePool } from "./geometry_tile_pool.js";
10
+ import { ChunkedPool } from "./mem/chunked_pool.js";
11
+ /**
12
+ * A recording mock of the domain half: instance → assets from a fixture map,
13
+ * with materialize/discard calls captured.
14
+ *
15
+ * @param composition Instance → assets fixture.
16
+ * @return {object} `{ source, materialized, discarded }`.
17
+ */
18
+ function mockSource(composition) {
19
+ const materialized = [];
20
+ const discarded = [];
21
+ const source = {
22
+ assetsOf: (instanceID) => composition[instanceID] ?? [],
23
+ materialize: (assetID) => {
24
+ materialized.push(assetID);
25
+ },
26
+ discard: (assetID) => {
27
+ discarded.push(assetID);
28
+ },
29
+ };
30
+ return { source, materialized, discarded };
31
+ }
32
+ describe("GeometryTilePool", () => {
33
+ test("a shared representation is materialised once and stored once", () => {
34
+ // Products 1 and 2 both instance rep-X (mapped item); 2 also has its own.
35
+ const { source, materialized } = mockSource({
36
+ 1: [{ assetID: "rep-X", byteSize: 100 }],
37
+ 2: [{ assetID: "rep-X", byteSize: 100 }, { assetID: "rep-Y", byteSize: 100 }],
38
+ });
39
+ const tiles = new GeometryTilePool(new ChunkedPool(1000, 100), source);
40
+ tiles.extract(1);
41
+ tiles.extract(2);
42
+ expect(materialized).toEqual(["rep-X", "rep-Y"]);
43
+ // Physically: two assets, 200 bytes — not 300.
44
+ expect(tiles.assets.pool.bytesInUse).toBe(200);
45
+ expect(tiles.assets.refCountOf("rep-X")).toBe(2);
46
+ });
47
+ test("evicting one product never discards a representation another still renders", () => {
48
+ const { source, discarded } = mockSource({
49
+ 1: [{ assetID: "rep-X", byteSize: 100 }],
50
+ 2: [{ assetID: "rep-X", byteSize: 100 }],
51
+ });
52
+ const tiles = new GeometryTilePool(new ChunkedPool(1000, 100), source);
53
+ tiles.extract(1);
54
+ tiles.extract(2);
55
+ tiles.release(1);
56
+ expect(discarded).toEqual([]);
57
+ expect(tiles.assets.isResident("rep-X")).toBe(true);
58
+ tiles.release(2);
59
+ expect(discarded).toEqual(["rep-X"]);
60
+ expect(tiles.assets.pool.bytesInUse).toBe(0);
61
+ });
62
+ test("extract charges the logical (chunk-rounded, fully-charged) cost", () => {
63
+ const { source } = mockSource({
64
+ 1: [{ assetID: "a", byteSize: 150 }, { assetID: "b", byteSize: 10 }],
65
+ 2: [{ assetID: "a", byteSize: 150 }],
66
+ });
67
+ const tiles = new GeometryTilePool(new ChunkedPool(1000, 100), source);
68
+ // 150 → 200 rounded, 10 → 100 rounded.
69
+ expect(tiles.extract(1)).toBe(300);
70
+ // Sharing is deliberately double-charged logically (conservative)...
71
+ expect(tiles.extract(2)).toBe(200);
72
+ // ...while physical stays single-stored.
73
+ expect(tiles.assets.pool.bytesInUse).toBe(300);
74
+ });
75
+ test("a failed extract unwinds cleanly, discarding what it materialised", () => {
76
+ // Asset a fits; asset b cannot (pool holds 2 chunks, b needs 3). The
77
+ // extract must throw, and the unwind must keep the materialize/discard
78
+ // pairing for a (which it took to zero references) and leave the pool
79
+ // exactly as before — a failed extract has no effect.
80
+ const { source, materialized, discarded } = mockSource({
81
+ 1: [{ assetID: "a", byteSize: 100 }, { assetID: "b", byteSize: 300 }],
82
+ });
83
+ const pool = new ChunkedPool(200, 100);
84
+ const tiles = new GeometryTilePool(pool, source);
85
+ expect(() => tiles.extract(1)).toThrow(/exhausted/);
86
+ expect(materialized).toEqual(["a"]);
87
+ expect(discarded).toEqual(["a"]);
88
+ expect(pool.bytesInUse).toBe(0);
89
+ expect(tiles.assets.isResident("a")).toBe(false);
90
+ });
91
+ test("a failed extract never discards an asset another instance still holds", () => {
92
+ // Instance 1 holds shared. Instance 2 wants shared + big; big fails.
93
+ // The unwind drops 2's reference on shared but must NOT discard it —
94
+ // instance 1 still renders it.
95
+ const { source, discarded } = mockSource({
96
+ 1: [{ assetID: "shared", byteSize: 100 }],
97
+ 2: [{ assetID: "shared", byteSize: 100 }, { assetID: "big", byteSize: 300 }],
98
+ });
99
+ const pool = new ChunkedPool(200, 100);
100
+ const tiles = new GeometryTilePool(pool, source);
101
+ tiles.extract(1);
102
+ expect(() => tiles.extract(2)).toThrow(/exhausted/);
103
+ expect(discarded).toEqual([]);
104
+ expect(tiles.assets.isResident("shared")).toBe(true);
105
+ expect(tiles.assets.refCountOf("shared")).toBe(1);
106
+ expect(pool.bytesInUse).toBe(100);
107
+ });
108
+ test("double extract and unmatched release are errors", () => {
109
+ const { source } = mockSource({ 1: [{ assetID: "a", byteSize: 10 }] });
110
+ const tiles = new GeometryTilePool(new ChunkedPool(1000, 100), source);
111
+ tiles.extract(1);
112
+ expect(() => tiles.extract(1)).toThrow(/already extracted/);
113
+ expect(() => tiles.release(2)).toThrow(/un-extracted/);
114
+ });
115
+ test("a malformed byte size throws before any state changes", () => {
116
+ // A buggy source returning a negative size must fail at zero state —
117
+ // no references taken, nothing materialised, pool untouched.
118
+ const { source, materialized } = mockSource({
119
+ 1: [{ assetID: "ok", byteSize: 100 }, { assetID: "bad", byteSize: -5 }],
120
+ });
121
+ const pool = new ChunkedPool(1000, 100);
122
+ const tiles = new GeometryTilePool(pool, source);
123
+ expect(() => tiles.extract(1)).toThrow(/Invalid byte size/);
124
+ expect(materialized).toEqual([]);
125
+ expect(pool.bytesInUse).toBe(0);
126
+ expect(tiles.assets.isResident("ok")).toBe(false);
127
+ });
128
+ test("composed with the demand queue: budget holds, shared reps survive eviction", () => {
129
+ // 20 products all sharing one heavy rep, each with a light unique rep —
130
+ // the mapped-item shape (fixtures, repeated windows).
131
+ const composition = {};
132
+ for (let id = 0; id < 20; ++id) {
133
+ composition[id] = [
134
+ { assetID: "shared-heavy", byteSize: 400 },
135
+ { assetID: `unique-${id}`, byteSize: 100 },
136
+ ];
137
+ }
138
+ const { source, materialized, discarded } = mockSource(composition);
139
+ const pool = new ChunkedPool(4000, 100);
140
+ const tiles = new GeometryTilePool(pool, source);
141
+ // Queue budget == pool budget: the safety invariant under test.
142
+ const queue = new DemandGeometryQueue(tiles, 4000);
143
+ // Stream demand across all 20 products with rising priority.
144
+ for (let id = 0; id < 20; ++id) {
145
+ queue.request(id, id);
146
+ queue.pump();
147
+ // Physical use never exceeds the logical budget the queue enforces.
148
+ expect(pool.bytesInUse).toBeLessThanOrEqual(queue.stats.residentBytes);
149
+ expect(queue.stats.residentBytes).toBeLessThanOrEqual(4000);
150
+ }
151
+ // The shared rep was materialised exactly once across all that churn...
152
+ expect(materialized.filter((a) => a === "shared-heavy").length).toBe(1);
153
+ // ...and never discarded, because some product always held it.
154
+ expect(discarded).not.toContain("shared-heavy");
155
+ // Evicting everything finally discards it and empties the pool.
156
+ queue.evictAll();
157
+ expect(discarded).toContain("shared-heavy");
158
+ expect(pool.bytesInUse).toBe(0);
159
+ });
160
+ test("composed queue at exact pool budget never exhausts the pool", () => {
161
+ // Every product unique (worst case for the invariant: no sharing slack).
162
+ const composition = {};
163
+ for (let id = 0; id < 50; ++id) {
164
+ composition[id] = [{ assetID: `u-${id}`, byteSize: 190 }];
165
+ }
166
+ const { source } = mockSource(composition);
167
+ const pool = new ChunkedPool(1000, 100);
168
+ const tiles = new GeometryTilePool(pool, source);
169
+ const queue = new DemandGeometryQueue(tiles, 1000);
170
+ // 190 rounds to 200 → 5 tiles fit. Churn all 50 through; extract must
171
+ // never throw pool-exhausted because logical charges cover physical.
172
+ for (let id = 0; id < 50; ++id) {
173
+ queue.request(id, id);
174
+ queue.pump();
175
+ }
176
+ expect(queue.stats.residentCount).toBe(5);
177
+ expect(pool.bytesInUse).toBe(1000);
178
+ expect(pool.stats.failedAcquires).toBe(0);
179
+ });
180
+ });
@@ -0,0 +1,116 @@
1
+ /**
2
+ * A fixed-chunk budget pool — the resident-memory primitive (M3 production
3
+ * design; see "Resident memory: two regimes" in the design doc).
4
+ *
5
+ * The wasm heap grows and never shrinks: `free()` returns bytes to the
6
+ * allocator's freelists, not to the browser, so the tab pays the heap's
7
+ * **high-water mark forever** and external fragmentation is a permanent leak,
8
+ * not a throughput nuisance. General-purpose allocation is therefore the
9
+ * wrong tool for large, evictable resident data. This pool is the explicit
10
+ * alternative: one region carved into fixed-size chunks with a freelist —
11
+ * acquire rounds up to whole chunks, release is O(chunks), and the region's
12
+ * high-water mark is the budget, by construction. Fragmentation is confined
13
+ * to bounded internal waste (the tail of the last chunk).
14
+ *
15
+ * This TS class is the pool's **accounting/policy half** and the executable
16
+ * spec for its C++ twin in conway-geom (which owns real bytes inside the wasm
17
+ * heap). It is also usable directly for JS-side resident budgets (property
18
+ * caches, sidecar caches) — the abstraction is deliberately not
19
+ * geometry-specific. The intended layering:
20
+ *
21
+ * ChunkedPool — chunks and bytes (this file)
22
+ * SharedAssetPool — refcounted assets living in those chunks
23
+ * GeometryTilePool — the geometry narrowing (products ⇄ assets)
24
+ *
25
+ * Lifetime regimes, for orientation: *phase-bounded* scratch (tessellation
26
+ * temporaries) stays on the AFTP bump arenas — reset, never freed piecemeal.
27
+ * *Demand-bounded* residents (data that lives until evicted) live here. The
28
+ * general allocator keeps only the small, messy-lifetime residual.
29
+ */
30
+ /**
31
+ * An acquired run of chunks. `chunks` are chunk indices into the pool's
32
+ * region (index × chunkBytes = byte offset in the C++ twin); `byteSize` is
33
+ * the caller's requested size, ≤ `chunks.length × chunkBytes`.
34
+ */
35
+ export interface ChunkSpan {
36
+ chunks: number[];
37
+ byteSize: number;
38
+ }
39
+ /** Runtime counters for a pool, for tests and telemetry. */
40
+ export interface ChunkedPoolStats {
41
+ totalChunks: number;
42
+ freeChunks: number;
43
+ bytesInUse: number;
44
+ acquires: number;
45
+ releases: number;
46
+ failedAcquires: number;
47
+ }
48
+ /**
49
+ * A budgeted pool of fixed-size chunks with a freelist. Purely accounting —
50
+ * see the module docs for how it maps onto real memory.
51
+ */
52
+ export declare class ChunkedPool {
53
+ private readonly chunkBytes_;
54
+ private readonly totalChunks_;
55
+ /** Free chunk indices (LIFO for locality in the C++ twin). */
56
+ private readonly freeList_;
57
+ private acquires_;
58
+ private releases_;
59
+ private failedAcquires_;
60
+ /**
61
+ * @param budgetBytes The pool's total byte budget (rounded down to whole
62
+ * chunks; must fit at least one chunk).
63
+ * @param chunkBytes The fixed chunk size in bytes.
64
+ */
65
+ constructor(budgetBytes: number, chunkBytes: number);
66
+ /**
67
+ * @return {number} The fixed chunk size in bytes.
68
+ */
69
+ get chunkBytes(): number;
70
+ /**
71
+ * @return {number} Total chunks in the pool.
72
+ */
73
+ get totalChunks(): number;
74
+ /**
75
+ * @return {number} Chunks currently free.
76
+ */
77
+ get freeChunks(): number;
78
+ /**
79
+ * @return {number} The pool's total byte capacity (whole chunks).
80
+ */
81
+ get totalBytes(): number;
82
+ /**
83
+ * @return {number} Physical bytes currently acquired (chunk-rounded).
84
+ */
85
+ get bytesInUse(): number;
86
+ /**
87
+ * The physical (chunk-rounded) cost of a byte size — what an acquire of
88
+ * `bytes` actually consumes. Use this for any budget accounting layered
89
+ * above the pool so logical charges cover physical use.
90
+ *
91
+ * @param bytes The byte size to round.
92
+ * @return {number} The chunk-rounded byte cost.
93
+ */
94
+ chunkRound(bytes: number): number;
95
+ /**
96
+ * Acquire chunks to hold `bytes`. All-or-nothing: returns undefined (and
97
+ * changes nothing) if the free chunks can't cover the request — the
98
+ * caller's cue to evict and retry.
99
+ *
100
+ * @param bytes The byte size to hold (0 is allowed and acquires no chunks).
101
+ * @return {ChunkSpan | undefined} The span, or undefined if it can't fit.
102
+ */
103
+ acquire(bytes: number): ChunkSpan | undefined;
104
+ /**
105
+ * Return a span's chunks to the freelist. The span must not be used (or
106
+ * released) again afterwards.
107
+ *
108
+ * @param span The span to release.
109
+ */
110
+ release(span: ChunkSpan): void;
111
+ /**
112
+ * @return {ChunkedPoolStats} A snapshot of runtime counters.
113
+ */
114
+ get stats(): ChunkedPoolStats;
115
+ }
116
+ //# sourceMappingURL=chunked_pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunked_pool.d.ts","sourceRoot":"","sources":["../../../../src/core/mem/chunked_pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAGD,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;CACvB;AAGD;;;GAGG;AACH,qBAAa,WAAW;IAEtB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IAErC,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IAEpC,OAAO,CAAC,SAAS,CAAI;IAErB,OAAO,CAAC,SAAS,CAAI;IAErB,OAAO,CAAC,eAAe,CAAI;IAE3B;;;;OAIG;gBACU,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;IAsBpD;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;OAEG;IACH,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;;;;;;OAOG;IACI,UAAU,CAAE,KAAK,EAAE,MAAM,GAAI,MAAM;IAS1C;;;;;;;OAOG;IACI,OAAO,CAAE,KAAK,EAAE,MAAM,GAAI,SAAS,GAAG,SAAS;IAoBtD;;;;;OAKG;IACI,OAAO,CAAE,IAAI,EAAE,SAAS,GAAI,IAAI;IAUvC;;OAEG;IACH,IAAW,KAAK,IAAI,gBAAgB,CASnC;CACF"}