@ifc-lite/parser 3.15.0 → 4.0.0
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/dist/block-store.d.ts +95 -0
- package/dist/block-store.d.ts.map +1 -0
- package/dist/block-store.js +285 -0
- package/dist/block-store.js.map +1 -0
- package/dist/classification-resolver.d.ts +16 -0
- package/dist/classification-resolver.d.ts.map +1 -1
- package/dist/classification-resolver.js +35 -0
- package/dist/classification-resolver.js.map +1 -1
- package/dist/columnar-parser.d.ts +13 -2
- package/dist/columnar-parser.d.ts.map +1 -1
- package/dist/columnar-parser.js +16 -3
- package/dist/columnar-parser.js.map +1 -1
- package/dist/data-store-transport.d.ts +2 -1
- package/dist/data-store-transport.d.ts.map +1 -1
- package/dist/data-store-transport.js +2 -1
- package/dist/data-store-transport.js.map +1 -1
- package/dist/entity-extractor.d.ts +9 -2
- package/dist/entity-extractor.d.ts.map +1 -1
- package/dist/entity-extractor.js +10 -4
- package/dist/entity-extractor.js.map +1 -1
- package/dist/entity-refs-from-index.d.ts +2 -1
- package/dist/entity-refs-from-index.d.ts.map +1 -1
- package/dist/entity-refs-from-index.js +17 -11
- package/dist/entity-refs-from-index.js.map +1 -1
- package/dist/entity-source.d.ts +2 -1
- package/dist/entity-source.d.ts.map +1 -1
- package/dist/entity-source.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/on-demand-extractors.d.ts +1 -1
- package/dist/on-demand-extractors.d.ts.map +1 -1
- package/dist/on-demand-extractors.js +1 -1
- package/dist/on-demand-extractors.js.map +1 -1
- package/dist/project-units.d.ts +2 -1
- package/dist/project-units.d.ts.map +1 -1
- package/dist/project-units.js.map +1 -1
- package/dist/source-bytes.d.ts +130 -0
- package/dist/source-bytes.d.ts.map +1 -0
- package/dist/source-bytes.js +390 -0
- package/dist/source-bytes.js.map +1 -0
- package/dist/source-compress.d.ts +37 -0
- package/dist/source-compress.d.ts.map +1 -0
- package/dist/source-compress.js +104 -0
- package/dist/source-compress.js.map +1 -0
- package/dist/source-header.d.ts +12 -1
- package/dist/source-header.d.ts.map +1 -1
- package/dist/source-header.js +4 -13
- package/dist/source-header.js.map +1 -1
- package/dist/spatial-hierarchy-builder.d.ts +2 -1
- package/dist/spatial-hierarchy-builder.d.ts.map +1 -1
- package/dist/spatial-hierarchy-builder.js.map +1 -1
- package/dist/synthetic-data-store.d.ts +2 -1
- package/dist/synthetic-data-store.d.ts.map +1 -1
- package/dist/synthetic-data-store.js +2 -1
- package/dist/synthetic-data-store.js.map +1 -1
- package/dist/unit-extractor.d.ts +10 -2
- package/dist/unit-extractor.d.ts.map +1 -1
- package/dist/unit-extractor.js +40 -3
- package/dist/unit-extractor.js.map +1 -1
- package/dist/worker-parser.d.ts.map +1 -1
- package/dist/worker-parser.js +13 -5
- package/dist/worker-parser.js.map +1 -1
- package/package.json +6 -5
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* The IFC source bytes, behind an accessor instead of a bare `Uint8Array`.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists (#2183). `IfcDataStore.source` is the whole file, kept
|
|
8
|
+
* resident for the lifetime of the model because on-demand property and
|
|
9
|
+
* attribute reads slice it synchronously — during React render, in eight
|
|
10
|
+
* separate chains. On a 342 MB model that is 327 MB of the ~671 MB the
|
|
11
|
+
* viewer's main thread holds, and it is the single largest resident.
|
|
12
|
+
*
|
|
13
|
+
* The contract "here are all the bytes, contiguous, resident forever" is what
|
|
14
|
+
* blocks any cheaper representation. Replacing it with "ask for the range you
|
|
15
|
+
* need" makes every whole-file consumer visible, which is a property you
|
|
16
|
+
* cannot retrofit later, and it is the precondition for storing the source
|
|
17
|
+
* compressed in blocks.
|
|
18
|
+
*
|
|
19
|
+
* The accessor starts resident (`slice` is a `subarray`, no copy) and can
|
|
20
|
+
* switch to block-compressed storage IN PLACE via `compressInPlace`. See
|
|
21
|
+
* {@link MutableSourceBytes} for why in place rather than by replacement --
|
|
22
|
+
* that detail is the difference between a real saving and a silent no-op.
|
|
23
|
+
*
|
|
24
|
+
* Design notes that are load-bearing rather than stylistic:
|
|
25
|
+
*
|
|
26
|
+
* - `length` is an alias of `byteLength`. It exists so the ~79 existing
|
|
27
|
+
* `!store.source?.length` guards keep compiling AND keep meaning exactly
|
|
28
|
+
* what they mean today. Two of them are semantic discriminators, not
|
|
29
|
+
* defensive noise: `packages/ids/src/bridge/properties.ts` uses
|
|
30
|
+
* source-presence to mean "WASM store, not server store", and
|
|
31
|
+
* `material-resolver.ts` uses it to decide cache safety. Do not add new
|
|
32
|
+
* uses; prefer `byteLength`.
|
|
33
|
+
* - `byteLength === 0` is a real, supported state, not an error. Server
|
|
34
|
+
* parsed stores, synthetic stores, GLB and point-cloud models all have no
|
|
35
|
+
* source. {@link EMPTY_SOURCE_BYTES} models it.
|
|
36
|
+
* - `contentKey` is computed lazily and memoised. It replaces two
|
|
37
|
+
* hand-rolled hashes in the viewer, one of which walks the whole file on
|
|
38
|
+
* every call and one of which keys a cache on `byteLength` alone (and so
|
|
39
|
+
* collides across same-size models).
|
|
40
|
+
*/
|
|
41
|
+
import { safeUtf8Decode } from '@ifc-lite/data';
|
|
42
|
+
import { BlockStore } from './block-store.js';
|
|
43
|
+
/** FNV-1a over the whole buffer. Matches the viewer's existing source hash. */
|
|
44
|
+
function fnv1a(bytes) {
|
|
45
|
+
let h = 0x811c9dc5;
|
|
46
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
47
|
+
h ^= bytes[i];
|
|
48
|
+
h = Math.imul(h, 0x01000193);
|
|
49
|
+
}
|
|
50
|
+
return `${bytes.length.toString(16)}-${(h >>> 0).toString(16)}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clamp a half-open range to `[0, len]`, tolerating reversed or wild input.
|
|
54
|
+
*
|
|
55
|
+
* Only NaN falls back to 0. Infinities are truncated and clamped like any
|
|
56
|
+
* other out-of-range number, so `decodeUtf8(2, Infinity)` means "to the end"
|
|
57
|
+
* rather than the empty string — a caller expressing an open upper bound that
|
|
58
|
+
* way should get the remainder, not silence.
|
|
59
|
+
*/
|
|
60
|
+
function clampRange(start, end, len) {
|
|
61
|
+
const clamp = (v) => Number.isNaN(v) ? 0 : Math.max(0, Math.min(Math.trunc(v), len));
|
|
62
|
+
const s = clamp(start);
|
|
63
|
+
const e = clamp(end);
|
|
64
|
+
return e < s ? [s, s] : [s, e];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The source accessor. Starts resident and can switch to block-compressed
|
|
68
|
+
* storage IN PLACE.
|
|
69
|
+
*
|
|
70
|
+
* In place, not by replacement, and this is not a style choice (#2183):
|
|
71
|
+
* `attachDataStoreAccessors` captures this object in a `BufferEntitySource`
|
|
72
|
+
* whose `EntityExtractor` holds it for the store's lifetime, so `getEntity`
|
|
73
|
+
* would keep reading the old resident view after a replacement -- the original
|
|
74
|
+
* buffer would stay alive and the memory win would be exactly zero, silently.
|
|
75
|
+
* Worse, `getProperties` goes through `extractPropertiesOnDemand`, which
|
|
76
|
+
* constructs a fresh extractor from `store.source` on EVERY call, so a
|
|
77
|
+
* replacement would split-brain the store: properties served from the
|
|
78
|
+
* compressed source, entities from the resident one, both retained.
|
|
79
|
+
*
|
|
80
|
+
* Mutating the single shared instance makes every holder -- the captured
|
|
81
|
+
* extractors, the hook closures, the partial and final stores that already
|
|
82
|
+
* share one accessor by design -- observe the switch through object identity.
|
|
83
|
+
* The whole class of stale-holder bugs cannot arise.
|
|
84
|
+
*/
|
|
85
|
+
class MutableSourceBytes {
|
|
86
|
+
/** Non-null exactly while resident. */
|
|
87
|
+
#view;
|
|
88
|
+
/** Non-null exactly while compressed. Never both. */
|
|
89
|
+
#blocks = null;
|
|
90
|
+
/**
|
|
91
|
+
* Depth of open {@link withMaterialized} windows, and the buffer they share.
|
|
92
|
+
*
|
|
93
|
+
* Only meaningful while compressed: a resident `withMaterialized` hands out
|
|
94
|
+
* the real view and needs no window at all.
|
|
95
|
+
*/
|
|
96
|
+
#windowDepth = 0;
|
|
97
|
+
#window = null;
|
|
98
|
+
/**
|
|
99
|
+
* `undefined` = not computed yet, `string` = computed. Never `null` for a
|
|
100
|
+
* non-empty source: the constructor no longer accepts one, so the state
|
|
101
|
+
* "permanently keyed on nothing" -- which reads downstream as an overlay
|
|
102
|
+
* silently not rendering -- is unrepresentable rather than merely unused.
|
|
103
|
+
*/
|
|
104
|
+
#contentKey;
|
|
105
|
+
constructor(view, contentKey) {
|
|
106
|
+
this.#view = view;
|
|
107
|
+
this.#contentKey = contentKey;
|
|
108
|
+
}
|
|
109
|
+
get isResident() { return this.#view !== null; }
|
|
110
|
+
get byteLength() {
|
|
111
|
+
return this.#view !== null ? this.#view.byteLength : this.#blocks.totalLength;
|
|
112
|
+
}
|
|
113
|
+
get length() { return this.byteLength; }
|
|
114
|
+
/** Compressed-storage stats, or null while resident. For the dev counter. */
|
|
115
|
+
get blockStats() {
|
|
116
|
+
if (this.#blocks === null)
|
|
117
|
+
return null;
|
|
118
|
+
return {
|
|
119
|
+
stored: this.#blocks.storedBytes,
|
|
120
|
+
resident: this.#blocks.residentBytes,
|
|
121
|
+
counters: this.#blocks.counters,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Switch to compressed storage, dropping the resident buffer.
|
|
126
|
+
*
|
|
127
|
+
* Synchronous and single-assignment, so no read can observe a torn state: a
|
|
128
|
+
* read either precedes the switch and sees the view, or follows it and
|
|
129
|
+
* inflates. Idempotent -- a second call is a no-op rather than a re-compress.
|
|
130
|
+
*
|
|
131
|
+
* Safe to call inside a `withMaterialized` callback. That callback holds a
|
|
132
|
+
* real `Uint8Array` reference, which stays valid and correct after the
|
|
133
|
+
* switch; it simply stops being the source's storage, and the buffer becomes
|
|
134
|
+
* collectable when the callback returns. (An earlier draft deferred the
|
|
135
|
+
* switch until the window closed. That branch was unreachable: window depth
|
|
136
|
+
* only rises while compressed, and this method returns early when compressed.
|
|
137
|
+
* Unreachable safety machinery is worse than none -- it reads as a handled
|
|
138
|
+
* case.)
|
|
139
|
+
*/
|
|
140
|
+
compressInPlace(payload) {
|
|
141
|
+
// Length checked BEFORE the already-compressed short-circuit. Checking it
|
|
142
|
+
// after would skip validation exactly when a caller is confused enough to
|
|
143
|
+
// compress twice with two different payloads.
|
|
144
|
+
if (payload.totalLength !== this.byteLength) {
|
|
145
|
+
throw new Error(`compressInPlace: payload is ${payload.totalLength} bytes but the source is `
|
|
146
|
+
+ `${this.byteLength}; refusing to swap in bytes that are not this source`);
|
|
147
|
+
}
|
|
148
|
+
if (this.#view === null)
|
|
149
|
+
return;
|
|
150
|
+
if (payload.totalLength !== this.#view.byteLength) {
|
|
151
|
+
throw new Error(`compressInPlace: payload is ${payload.totalLength} bytes but the source is `
|
|
152
|
+
+ `${this.#view.byteLength}; refusing to swap in bytes that are not this source`);
|
|
153
|
+
}
|
|
154
|
+
this.#applyCompression(payload);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Adopt already-compressed blocks, for rebuilding a source that crossed a
|
|
158
|
+
* thread boundary. Distinct from {@link compressInPlace}, which starts from
|
|
159
|
+
* resident bytes and must guard that the payload describes THIS source;
|
|
160
|
+
* here there are no bytes to check against.
|
|
161
|
+
*/
|
|
162
|
+
adoptBlocks(payload) {
|
|
163
|
+
this.#contentKey ??= payload.contentKey;
|
|
164
|
+
this.#blocks = new BlockStore(payload);
|
|
165
|
+
this.#view = null;
|
|
166
|
+
}
|
|
167
|
+
#applyCompression(payload) {
|
|
168
|
+
// Take the key from the payload: it was computed while the bytes were
|
|
169
|
+
// contiguous, which is the only cheap moment. Never recompute after.
|
|
170
|
+
this.#contentKey ??= payload.contentKey;
|
|
171
|
+
this.#blocks = new BlockStore(payload);
|
|
172
|
+
this.#view = null;
|
|
173
|
+
}
|
|
174
|
+
get contentKey() {
|
|
175
|
+
// Lazy while resident: only the callers that key a cache on identity pay
|
|
176
|
+
// for it, and they pay once. Nothing on the load path needs it.
|
|
177
|
+
//
|
|
178
|
+
// Always a string, never null: `contiguousSourceBytes` collapses a
|
|
179
|
+
// zero-length view to EMPTY_SOURCE_BYTES, so this is non-empty by
|
|
180
|
+
// construction and the "no source" key has nowhere to appear.
|
|
181
|
+
//
|
|
182
|
+
// Once compressed it is always already set -- compressInPlace takes it
|
|
183
|
+
// from the payload -- so this never inflates anything to hash it.
|
|
184
|
+
this.#contentKey ??= fnv1a(this.#view);
|
|
185
|
+
return this.#contentKey;
|
|
186
|
+
}
|
|
187
|
+
slice(start, end) {
|
|
188
|
+
const [s, e] = clampRange(start, end, this.byteLength);
|
|
189
|
+
if (this.#view !== null)
|
|
190
|
+
return this.#view.subarray(s, e);
|
|
191
|
+
return this.#blocks.read(s, e);
|
|
192
|
+
}
|
|
193
|
+
decodeUtf8(start, end) {
|
|
194
|
+
const [s, e] = clampRange(start, end, this.byteLength);
|
|
195
|
+
if (e === s)
|
|
196
|
+
return '';
|
|
197
|
+
if (this.#view !== null) {
|
|
198
|
+
// SAB-safe: the source is usually SharedArrayBuffer-backed, which raw
|
|
199
|
+
// `TextDecoder.decode` rejects.
|
|
200
|
+
return safeUtf8Decode(this.#view, s, e);
|
|
201
|
+
}
|
|
202
|
+
return this.#blocks.decodeUtf8(s, e);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* While compressed this inflates the WHOLE source (~1.8 s and 343 MB on the
|
|
206
|
+
* reference model), so prefer {@link withMaterialized}, which at least
|
|
207
|
+
* scopes the buffer and shares it between nested calls.
|
|
208
|
+
*
|
|
209
|
+
* The result MAY BE SHARED: while resident it is the accessor's own view,
|
|
210
|
+
* and inside an open materialization window it is that window's buffer, so
|
|
211
|
+
* two callers can hold the same array. Treat it as read-only.
|
|
212
|
+
*/
|
|
213
|
+
materialize() {
|
|
214
|
+
if (this.#view !== null)
|
|
215
|
+
return this.#view;
|
|
216
|
+
return this.#window ?? this.#blocks.materialize();
|
|
217
|
+
}
|
|
218
|
+
withMaterialized(fn) {
|
|
219
|
+
if (this.#view !== null)
|
|
220
|
+
return fn(this.#view);
|
|
221
|
+
this.#openWindow();
|
|
222
|
+
try {
|
|
223
|
+
return fn(this.#window);
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
this.#closeWindow();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async withMaterializedAsync(fn) {
|
|
230
|
+
if (this.#view !== null)
|
|
231
|
+
return fn(this.#view);
|
|
232
|
+
this.#openWindow();
|
|
233
|
+
try {
|
|
234
|
+
return await fn(this.#window);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
this.#closeWindow();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Inflate once for the duration of a callback. Nested windows share the one
|
|
242
|
+
* buffer, so two overlapping consumers do not each allocate the whole file.
|
|
243
|
+
*/
|
|
244
|
+
#openWindow() {
|
|
245
|
+
if (this.#windowDepth === 0)
|
|
246
|
+
this.#window = this.#blocks.materialize();
|
|
247
|
+
this.#windowDepth++;
|
|
248
|
+
}
|
|
249
|
+
#closeWindow() {
|
|
250
|
+
this.#windowDepth--;
|
|
251
|
+
if (this.#windowDepth === 0)
|
|
252
|
+
this.#window = null;
|
|
253
|
+
}
|
|
254
|
+
toTransferable() {
|
|
255
|
+
// Deliberately reads the memo FIELD, not the getter. This method exists to
|
|
256
|
+
// hand a source to a worker without paying whole-file costs on the sending
|
|
257
|
+
// thread, and `contentKey` is a full-file FNV-1a walk -- forcing it here
|
|
258
|
+
// would make the documented "without materialising" path allocate and walk
|
|
259
|
+
// the whole 342 MB source on the main thread, which is what #2183 is about.
|
|
260
|
+
// Pass the key on only when something has already computed it; a receiver
|
|
261
|
+
// that needs one computes it lazily, on its own thread, to the same value.
|
|
262
|
+
if (this.#view !== null) {
|
|
263
|
+
return { kind: 'contiguous', bytes: this.#view, contentKey: this.#contentKey ?? null };
|
|
264
|
+
}
|
|
265
|
+
// Compressed: post the BLOCKS, not the inflated file -- 67 MB instead of
|
|
266
|
+
// 343 MB on the reference model, and the receiving thread does any
|
|
267
|
+
// inflating. The key is always known by now (compressInPlace took it from
|
|
268
|
+
// the payload), which is why the blocked arm declares it required.
|
|
269
|
+
return { kind: 'blocked', ...this.#blocks.toPayload(), contentKey: this.contentKey };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The no-source state, shared. Frozen and stateless, so it is safe to hand the
|
|
274
|
+
* same instance to every store that has no bytes.
|
|
275
|
+
*/
|
|
276
|
+
class EmptySourceBytes {
|
|
277
|
+
byteLength = 0;
|
|
278
|
+
length = 0;
|
|
279
|
+
isResident = true;
|
|
280
|
+
contentKey = null;
|
|
281
|
+
// A FRESH array each call: callers occasionally write into what they get
|
|
282
|
+
// back, and a shared mutable empty would couple them.
|
|
283
|
+
slice() { return new Uint8Array(0); }
|
|
284
|
+
decodeUtf8() { return ''; }
|
|
285
|
+
materialize() { return new Uint8Array(0); }
|
|
286
|
+
withMaterialized(fn) { return fn(new Uint8Array(0)); }
|
|
287
|
+
withMaterializedAsync(fn) {
|
|
288
|
+
return fn(new Uint8Array(0));
|
|
289
|
+
}
|
|
290
|
+
toTransferable() {
|
|
291
|
+
return { kind: 'contiguous', bytes: new Uint8Array(0), contentKey: null };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** The canonical "this model has no source" value. */
|
|
295
|
+
export const EMPTY_SOURCE_BYTES = Object.freeze(new EmptySourceBytes());
|
|
296
|
+
/**
|
|
297
|
+
* Wrap a resident buffer. Zero-copy: `slice` returns views into `view`.
|
|
298
|
+
*
|
|
299
|
+
* Pass `contentKey` when it is already known (e.g. carried across a worker
|
|
300
|
+
* boundary or a compression swap) so downstream caches do not invalidate.
|
|
301
|
+
*/
|
|
302
|
+
export function contiguousSourceBytes(view, contentKey) {
|
|
303
|
+
if (!view || view.byteLength === 0)
|
|
304
|
+
return EMPTY_SOURCE_BYTES;
|
|
305
|
+
return new MutableSourceBytes(view, contentKey);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Rebuild a source from {@link IfcSourceBytes.toTransferable} output.
|
|
309
|
+
*
|
|
310
|
+
* `contentKey: null` on the wire means "the sender had not computed one", NOT
|
|
311
|
+
* "the key is null" -- `toTransferable` deliberately does not force the
|
|
312
|
+
* full-file hash. So it maps to `undefined` here, which is the constructor's
|
|
313
|
+
* "not computed yet" state, and the receiver hashes lazily on its own thread if
|
|
314
|
+
* anything asks. Passing the `null` straight through would pin the key at null
|
|
315
|
+
* forever and every downstream cache would key on nothing.
|
|
316
|
+
*
|
|
317
|
+
* A non-empty source cannot legitimately have a null key: `fnv1a` always
|
|
318
|
+
* returns a string, and a zero-length view collapses to
|
|
319
|
+
* {@link EMPTY_SOURCE_BYTES} before it can reach a constructor.
|
|
320
|
+
*
|
|
321
|
+
* CONSTRAINT FOR THE BLOCKED IMPLEMENTATION. A contiguous receiver can shrug
|
|
322
|
+
* off a missing key because recomputing it is one linear pass over bytes it
|
|
323
|
+
* already holds. A blocked one cannot -- it would have to inflate every block
|
|
324
|
+
* to hash them. So the compression swap must compute and carry `contentKey`
|
|
325
|
+
* EAGERLY, at swap time, while the bytes are still contiguous. Inheriting the
|
|
326
|
+
* lazy contiguous behaviour there would turn the first cache lookup into a
|
|
327
|
+
* full decompression of the model.
|
|
328
|
+
*/
|
|
329
|
+
export function sourceBytesFromTransferable(transfer) {
|
|
330
|
+
if (transfer.kind === 'contiguous') {
|
|
331
|
+
return contiguousSourceBytes(transfer.bytes, transfer.contentKey ?? undefined);
|
|
332
|
+
}
|
|
333
|
+
// A blocked source rebuilds without inflating anything: the receiver gets
|
|
334
|
+
// the compressed blocks and pays only for the ranges it actually reads.
|
|
335
|
+
const rebuilt = new MutableSourceBytes(new Uint8Array(0), transfer.contentKey);
|
|
336
|
+
rebuilt.adoptBlocks(transfer);
|
|
337
|
+
return rebuilt;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Switch a source to block-compressed storage, in place.
|
|
341
|
+
*
|
|
342
|
+
* Returns true when the source is now compressed (or already was), false when
|
|
343
|
+
* this build cannot swap it (an empty source, or one that is not the mutable
|
|
344
|
+
* accessor). THROWS when the payload does not describe these bytes -- swapping
|
|
345
|
+
* in a different source's blocks would corrupt every subsequent read silently,
|
|
346
|
+
* which is worth a crash rather than a `false` a caller might ignore.
|
|
347
|
+
*
|
|
348
|
+
* Exported as a function rather than a method on `IfcSourceBytes`
|
|
349
|
+
* deliberately: consumers must never see a source that can change shape under
|
|
350
|
+
* them, so the capability lives with whoever owns the load, not on the read
|
|
351
|
+
* interface every hook holds.
|
|
352
|
+
*/
|
|
353
|
+
export function compressSourceInPlace(source, payload) {
|
|
354
|
+
if (!(source instanceof MutableSourceBytes))
|
|
355
|
+
return false;
|
|
356
|
+
source.compressInPlace(payload);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
/** Compressed-storage stats for the dev counter, or null while resident. */
|
|
360
|
+
export function sourceBlockStats(source) {
|
|
361
|
+
return source instanceof MutableSourceBytes ? source.blockStats : null;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Narrowing helper for the call sites that accept either shape.
|
|
365
|
+
*
|
|
366
|
+
* Checks every member EXCEPT `contentKey`, deliberately: that is a lazy getter
|
|
367
|
+
* which hashes the whole file on first read, and this predicate runs inside
|
|
368
|
+
* `asSourceBytes` — i.e. in the `EntityExtractor` constructor. Probing it here
|
|
369
|
+
* would turn a type check into a full-file scan.
|
|
370
|
+
*/
|
|
371
|
+
export function isSourceBytes(value) {
|
|
372
|
+
if (typeof value !== 'object' || value === null)
|
|
373
|
+
return false;
|
|
374
|
+
const s = value;
|
|
375
|
+
return (typeof s.byteLength === 'number'
|
|
376
|
+
&& typeof s.length === 'number'
|
|
377
|
+
&& typeof s.isResident === 'boolean'
|
|
378
|
+
&& typeof s.slice === 'function'
|
|
379
|
+
&& typeof s.decodeUtf8 === 'function'
|
|
380
|
+
&& typeof s.materialize === 'function'
|
|
381
|
+
&& typeof s.withMaterialized === 'function'
|
|
382
|
+
&& typeof s.withMaterializedAsync === 'function'
|
|
383
|
+
&& typeof s.toTransferable === 'function');
|
|
384
|
+
}
|
|
385
|
+
/** Accept either shape and normalise. Lets a helper widen without its callers
|
|
386
|
+
* changing, which is what keeps the migration diff small. */
|
|
387
|
+
export function asSourceBytes(value) {
|
|
388
|
+
return isSourceBytes(value) ? value : contiguousSourceBytes(value);
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=source-bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-bytes.js","sourceRoot":"","sources":["../src/source-bytes.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAgD,MAAM,kBAAkB,CAAC;AA0E5F,+EAA+E;AAC/E,SAAS,KAAK,CAAC,KAAiB;IAC9B,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACd,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IACzD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAClC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,kBAAkB;IACtB,uCAAuC;IACvC,KAAK,CAAoB;IACzB,qDAAqD;IACrD,OAAO,GAAsB,IAAI,CAAC;IAClC;;;;;OAKG;IACH,YAAY,GAAG,CAAC,CAAC;IACjB,OAAO,GAAsB,IAAI,CAAC;IAClC;;;;;OAKG;IACH,WAAW,CAAqB;IAEhC,YAAY,IAAgB,EAAE,UAAmB;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED,IAAI,UAAU,KAAc,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;IAEzD,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAQ,CAAC,WAAW,CAAC;IACjF,CAAC;IAED,IAAI,MAAM,KAAa,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEhD,6EAA6E;IAC7E,IAAI,UAAU;QACZ,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YAChC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa;YACpC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;SAChC,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,eAAe,CAAC,OAAyB;QACvC,0EAA0E;QAC1E,0EAA0E;QAC1E,8CAA8C;QAC9C,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,WAAW,2BAA2B;kBAC3E,GAAG,IAAI,CAAC,UAAU,sDAAsD,CAC3E,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,WAAW,2BAA2B;kBAC3E,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,sDAAsD,CACjF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,OAAgD;QAC1D,IAAI,CAAC,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,iBAAiB,CAAC,OAAyB;QACzC,sEAAsE;QACtE,qEAAqE;QACrE,IAAI,CAAC,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,IAAI,UAAU;QACZ,yEAAyE;QACzE,gEAAgE;QAChE,EAAE;QACF,mEAAmE;QACnE,kEAAkE;QAClE,8DAA8D;QAC9D,EAAE;QACF,uEAAuE;QACvE,kEAAkE;QAClE,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,KAAa,EAAE,GAAW;QAC9B,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,GAAW;QACnC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxB,sEAAsE;YACtE,gCAAgC;YAChC,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,OAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW;QACT,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC;QAC3C,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAQ,CAAC,WAAW,EAAE,CAAC;IACrD,CAAC;IAED,gBAAgB,CAAI,EAA4B;QAC9C,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAI,EAAqC;QAClE,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,OAAQ,CAAC,CAAC;QACjC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAQ,CAAC,WAAW,EAAE,CAAC;QACxE,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,YAAY;QACV,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACnD,CAAC;IAED,cAAc;QACZ,2EAA2E;QAC3E,2EAA2E;QAC3E,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;QACzF,CAAC;QACD,yEAAyE;QACzE,mEAAmE;QACnE,0EAA0E;QAC1E,mEAAmE;QACnE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,OAAQ,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACxF,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,gBAAgB;IACX,UAAU,GAAG,CAAC,CAAC;IACf,MAAM,GAAG,CAAC,CAAC;IACX,UAAU,GAAG,IAAI,CAAC;IAClB,UAAU,GAAG,IAAI,CAAC;IAE3B,yEAAyE;IACzE,sDAAsD;IACtD,KAAK,KAAiB,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,UAAU,KAAa,OAAO,EAAE,CAAC,CAAC,CAAC;IACnC,WAAW,KAAiB,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,gBAAgB,CAAI,EAA4B,IAAO,OAAO,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,qBAAqB,CAAI,EAAqC;QAC5D,OAAO,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,cAAc;QACZ,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;CACF;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAmB,MAAM,CAAC,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC,CAAC;AAExF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAmC,EACnC,UAAmB;IAEnB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAC9D,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,2BAA2B,CAAC,QAA2B;IACrE,IAAI,QAAQ,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACnC,OAAO,qBAAqB,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC;IACjF,CAAC;IACD,0EAA0E;IAC1E,wEAAwE;IACxE,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC/E,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAsB,EACtB,OAAyB;IAEzB,IAAI,CAAC,CAAC,MAAM,YAAY,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAC9B,MAAsB;IAEtB,OAAO,MAAM,YAAY,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;WAC7B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;WAC5B,OAAO,CAAC,CAAC,UAAU,KAAK,SAAS;WACjC,OAAO,CAAC,CAAC,KAAK,KAAK,UAAU;WAC7B,OAAO,CAAC,CAAC,UAAU,KAAK,UAAU;WAClC,OAAO,CAAC,CAAC,WAAW,KAAK,UAAU;WACnC,OAAO,CAAC,CAAC,gBAAgB,KAAK,UAAU;WACxC,OAAO,CAAC,CAAC,qBAAqB,KAAK,UAAU;WAC7C,OAAO,CAAC,CAAC,cAAc,KAAK,UAAU,CAC1C,CAAC;AACJ,CAAC;AAED;8DAC8D;AAC9D,MAAM,UAAU,aAAa,CAAC,KAAkC;IAC9D,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type BlockedPayload } from './block-store.js';
|
|
2
|
+
export interface CompressedSource extends BlockedPayload {
|
|
3
|
+
/**
|
|
4
|
+
* FNV-1a over the whole source.
|
|
5
|
+
*
|
|
6
|
+
* Computed HERE, during the same pass, not lazily like the contiguous
|
|
7
|
+
* accessor's. A blocked source cannot hash itself without inflating
|
|
8
|
+
* everything, so the key has to be taken while the bytes are still
|
|
9
|
+
* contiguous -- which is why the `blocked` arm of `IfcSourceTransfer`
|
|
10
|
+
* declares it required rather than nullable.
|
|
11
|
+
*/
|
|
12
|
+
contentKey: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Split `source` into fixed-size blocks and deflate each one.
|
|
16
|
+
*
|
|
17
|
+
* A block is stored verbatim when deflating it does not shrink it. STEP files
|
|
18
|
+
* embed already-compressed payloads (base64 textures, for one), and without
|
|
19
|
+
* this a pathological source could compress to LARGER than it started -- which
|
|
20
|
+
* would make the whole feature impossible to gate on size honestly.
|
|
21
|
+
*/
|
|
22
|
+
export declare function compressSource(source: Uint8Array, blockSize?: number): CompressedSource;
|
|
23
|
+
/**
|
|
24
|
+
* Whether compressing `byteLength` is worth it.
|
|
25
|
+
*
|
|
26
|
+
* The win scales with size but the risk surface (LRU, stitching, the swap,
|
|
27
|
+
* a worker) is constant, so there is a floor below which this is all cost.
|
|
28
|
+
* The threshold is deliberately the size above which the viewer already stops
|
|
29
|
+
* persisting the source to IndexedDB: above it the source is always
|
|
30
|
+
* SharedArrayBuffer-backed, so handing it to the compression worker is
|
|
31
|
+
* zero-copy. Gating lower would drag in the IndexedDB-restore path, which
|
|
32
|
+
* would hand the worker a plain ArrayBuffer and pay a full structured clone
|
|
33
|
+
* on every cache restore.
|
|
34
|
+
*/
|
|
35
|
+
export declare const COMPRESSION_MIN_BYTES: number;
|
|
36
|
+
export declare function shouldCompressSource(byteLength: number): boolean;
|
|
37
|
+
//# sourceMappingURL=source-compress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-compress.d.ts","sourceRoot":"","sources":["../src/source-compress.ts"],"names":[],"mappings":"AAcA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAY3E,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAYD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAClB,SAAS,GAAE,MAA2B,GACrC,gBAAgB,CAoDlB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,QAAoB,CAAC;AAEvD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEhE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Compress an IFC source into the blocks {@link BlockStore} reads (#2183).
|
|
6
|
+
*
|
|
7
|
+
* Kept as a plain function rather than living inside the worker so it is
|
|
8
|
+
* directly testable and so the swap can be exercised synchronously in tests
|
|
9
|
+
* without a Worker at all. The worker is a thin shell around this.
|
|
10
|
+
*/
|
|
11
|
+
import { deflateSync } from 'fflate';
|
|
12
|
+
import { DEFAULT_BLOCK_SIZE } from './block-store.js';
|
|
13
|
+
/**
|
|
14
|
+
* Deflate level.
|
|
15
|
+
*
|
|
16
|
+
* 6 rather than 4: measured on the reference model it costs ~1.2 s more of
|
|
17
|
+
* one-time worker CPU and returns 2 MB, and this runs off the main thread
|
|
18
|
+
* while the viewer is already interactive. Level 9 is not worth measuring --
|
|
19
|
+
* deflate's tail is famously poor value and the input is already at 19.7%.
|
|
20
|
+
*/
|
|
21
|
+
const DEFLATE_LEVEL = 6;
|
|
22
|
+
/** Must match `fnv1a` in source-bytes.ts, or the key changes across a swap. */
|
|
23
|
+
function fnv1a(bytes) {
|
|
24
|
+
let h = 0x811c9dc5;
|
|
25
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
26
|
+
h ^= bytes[i];
|
|
27
|
+
h = Math.imul(h, 0x01000193);
|
|
28
|
+
}
|
|
29
|
+
return `${bytes.length.toString(16)}-${(h >>> 0).toString(16)}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Split `source` into fixed-size blocks and deflate each one.
|
|
33
|
+
*
|
|
34
|
+
* A block is stored verbatim when deflating it does not shrink it. STEP files
|
|
35
|
+
* embed already-compressed payloads (base64 textures, for one), and without
|
|
36
|
+
* this a pathological source could compress to LARGER than it started -- which
|
|
37
|
+
* would make the whole feature impossible to gate on size honestly.
|
|
38
|
+
*/
|
|
39
|
+
export function compressSource(source, blockSize = DEFAULT_BLOCK_SIZE) {
|
|
40
|
+
// Integer, finite, positive. A fractional size corrupts silently rather than
|
|
41
|
+
// failing: this function slices at `i * blockSize` (truncated by subarray)
|
|
42
|
+
// while BlockStore maps offsets with Math.floor(start / blockSize), so the
|
|
43
|
+
// two disagree and reads come back wrong with no error anywhere.
|
|
44
|
+
if (!Number.isInteger(blockSize) || blockSize <= 0) {
|
|
45
|
+
throw new Error(`compressSource: blockSize must be a positive integer, got ${blockSize}`);
|
|
46
|
+
}
|
|
47
|
+
const totalLength = source.byteLength;
|
|
48
|
+
const blockCount = Math.ceil(totalLength / blockSize);
|
|
49
|
+
const index = new Uint32Array(blockCount + 1);
|
|
50
|
+
const storedMask = new Uint8Array(blockCount);
|
|
51
|
+
const parts = [];
|
|
52
|
+
let written = 0;
|
|
53
|
+
for (let i = 0; i < blockCount; i++) {
|
|
54
|
+
const start = i * blockSize;
|
|
55
|
+
const raw = source.subarray(start, Math.min(start + blockSize, totalLength));
|
|
56
|
+
const deflated = deflateSync(raw, { level: DEFLATE_LEVEL });
|
|
57
|
+
index[i] = written;
|
|
58
|
+
if (deflated.byteLength < raw.byteLength) {
|
|
59
|
+
parts.push(deflated);
|
|
60
|
+
written += deflated.byteLength;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Verbatim. Copied, because `raw` is a view into the caller's buffer and
|
|
64
|
+
// the whole point is that the caller's buffer becomes collectable.
|
|
65
|
+
const copy = new Uint8Array(raw.byteLength);
|
|
66
|
+
copy.set(raw);
|
|
67
|
+
parts.push(copy);
|
|
68
|
+
storedMask[i] = 1;
|
|
69
|
+
written += copy.byteLength;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
index[blockCount] = written;
|
|
73
|
+
const blocks = new Uint8Array(written);
|
|
74
|
+
let at = 0;
|
|
75
|
+
for (const part of parts) {
|
|
76
|
+
blocks.set(part, at);
|
|
77
|
+
at += part.byteLength;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
blocks,
|
|
81
|
+
index,
|
|
82
|
+
storedMask,
|
|
83
|
+
blockSize,
|
|
84
|
+
totalLength,
|
|
85
|
+
contentKey: fnv1a(source),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Whether compressing `byteLength` is worth it.
|
|
90
|
+
*
|
|
91
|
+
* The win scales with size but the risk surface (LRU, stitching, the swap,
|
|
92
|
+
* a worker) is constant, so there is a floor below which this is all cost.
|
|
93
|
+
* The threshold is deliberately the size above which the viewer already stops
|
|
94
|
+
* persisting the source to IndexedDB: above it the source is always
|
|
95
|
+
* SharedArrayBuffer-backed, so handing it to the compression worker is
|
|
96
|
+
* zero-copy. Gating lower would drag in the IndexedDB-restore path, which
|
|
97
|
+
* would hand the worker a plain ArrayBuffer and pay a full structured clone
|
|
98
|
+
* on every cache restore.
|
|
99
|
+
*/
|
|
100
|
+
export const COMPRESSION_MIN_BYTES = 150 * 1024 * 1024;
|
|
101
|
+
export function shouldCompressSource(byteLength) {
|
|
102
|
+
return byteLength >= COMPRESSION_MIN_BYTES;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=source-compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-compress.js","sourceRoot":"","sources":["../src/source-compress.ts"],"names":[],"mappings":"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"}
|
package/dist/source-header.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse the ISO 10303-21 HEADER section of a STEP/IFC file into the structured
|
|
3
|
+
* {@link IfcSourceHeader} the exporter uses to round-trip header fidelity.
|
|
4
|
+
*
|
|
5
|
+
* This is deliberately a small, self-contained, quote-aware reader rather than
|
|
6
|
+
* a reuse of the generic STEP value parser: `FILE_DESCRIPTION` items and
|
|
7
|
+
* `FILE_NAME` fields routinely contain commas and parentheses inside quoted
|
|
8
|
+
* strings (e.g. `'CoordinateReference [..., ProjectSite: Origin]'`), which a
|
|
9
|
+
* splitter that ignores quote state would mis-split.
|
|
10
|
+
*/
|
|
1
11
|
import type { IfcSourceHeader } from '@ifc-lite/data';
|
|
12
|
+
import { type IfcSourceBytes } from './source-bytes.js';
|
|
2
13
|
/**
|
|
3
14
|
* Parse the HEADER section of a STEP/IFC buffer into {@link IfcSourceHeader}.
|
|
4
15
|
* Returns `undefined` when no recognisable header records are present (e.g.
|
|
5
16
|
* non-STEP input). Cheap: only the first {@link MAX_HEADER_BYTES} are decoded,
|
|
6
17
|
* truncated at the first `ENDSEC` so the DATA section is never scanned.
|
|
7
18
|
*/
|
|
8
|
-
export declare function parseSourceHeader(buffer: Uint8Array): IfcSourceHeader | undefined;
|
|
19
|
+
export declare function parseSourceHeader(buffer: Uint8Array | IfcSourceBytes): IfcSourceHeader | undefined;
|
|
9
20
|
//# sourceMappingURL=source-header.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-header.d.ts","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"source-header.d.ts","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AA2LvE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,GAAG,cAAc,GAClC,eAAe,GAAG,SAAS,CA2D7B"}
|
package/dist/source-header.js
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
-
/**
|
|
5
|
-
* Parse the ISO 10303-21 HEADER section of a STEP/IFC file into the structured
|
|
6
|
-
* {@link IfcSourceHeader} the exporter uses to round-trip header fidelity.
|
|
7
|
-
*
|
|
8
|
-
* This is deliberately a small, self-contained, quote-aware reader rather than
|
|
9
|
-
* a reuse of the generic STEP value parser: `FILE_DESCRIPTION` items and
|
|
10
|
-
* `FILE_NAME` fields routinely contain commas and parentheses inside quoted
|
|
11
|
-
* strings (e.g. `'CoordinateReference [..., ProjectSite: Origin]'`), which a
|
|
12
|
-
* splitter that ignores quote state would mis-split.
|
|
13
|
-
*/
|
|
14
|
-
import { safeUtf8Decode } from '@ifc-lite/data';
|
|
15
4
|
import { decodeIfcString } from '@ifc-lite/encoding';
|
|
5
|
+
import { asSourceBytes } from './source-bytes.js';
|
|
16
6
|
/** Headers are tiny; cap the decode so a huge file's body is never scanned. */
|
|
17
7
|
const MAX_HEADER_BYTES = 64 * 1024;
|
|
18
8
|
/**
|
|
@@ -217,8 +207,9 @@ function extractRecordArgs(text, keyword, fromIndex = 0) {
|
|
|
217
207
|
* truncated at the first `ENDSEC` so the DATA section is never scanned.
|
|
218
208
|
*/
|
|
219
209
|
export function parseSourceHeader(buffer) {
|
|
220
|
-
const
|
|
221
|
-
|
|
210
|
+
const src = asSourceBytes(buffer);
|
|
211
|
+
const cap = Math.min(src.byteLength, MAX_HEADER_BYTES);
|
|
212
|
+
let text = src.decodeUtf8(0, cap);
|
|
222
213
|
const endSec = text.toUpperCase().indexOf('ENDSEC');
|
|
223
214
|
if (endSec >= 0)
|
|
224
215
|
text = text.slice(0, endSec);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-header.js","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"AAAA;;+DAE+D;
|
|
1
|
+
{"version":3,"file":"source-header.js","sourceRoot":"","sources":["../src/source-header.ts"],"names":[],"mappings":"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"}
|