minidraco 0.3.0 → 0.5.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/three.js CHANGED
@@ -271,6 +271,90 @@ var Mesh = class extends PointCloud {
271
271
  }
272
272
  };
273
273
 
274
+ // src/decoder/core/ScratchArena.ts
275
+ var MAX_CLASS = 28;
276
+ var freeInt32 = [];
277
+ var freeUint32 = [];
278
+ var freeUint8 = [];
279
+ for (let i = 0; i <= MAX_CLASS; ++i) {
280
+ freeInt32.push([]);
281
+ freeUint32.push([]);
282
+ freeUint8.push([]);
283
+ }
284
+ var borrowedInt32 = [];
285
+ var borrowedUint32 = [];
286
+ var borrowedUint8 = [];
287
+ var sizeClass = (size) => size <= 1 ? 0 : 32 - Math.clz32(size - 1);
288
+ var takeInt32 = (size) => {
289
+ const k = sizeClass(size);
290
+ if (k > MAX_CLASS) return new Int32Array(size);
291
+ const bucket = freeInt32[k];
292
+ let pooled = bucket.length > 0 ? bucket.pop() : new Int32Array(size);
293
+ if (pooled.length < size) pooled = new Int32Array(1 << k);
294
+ borrowedInt32.push(pooled);
295
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
296
+ };
297
+ var takeUint32 = (size) => {
298
+ const k = sizeClass(size);
299
+ if (k > MAX_CLASS) return new Uint32Array(size);
300
+ const bucket = freeUint32[k];
301
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint32Array(size);
302
+ if (pooled.length < size) pooled = new Uint32Array(1 << k);
303
+ borrowedUint32.push(pooled);
304
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
305
+ };
306
+ var byteCapacity = (size) => size < 8 ? 8 : size + 7 & ~7;
307
+ var takeUint8 = (size) => {
308
+ const capacity = byteCapacity(size);
309
+ const k = sizeClass(capacity);
310
+ if (k > MAX_CLASS) return new Uint8Array(capacity);
311
+ const bucket = freeUint8[k];
312
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint8Array(capacity);
313
+ if (pooled.length < size) pooled = new Uint8Array(1 << k);
314
+ borrowedUint8.push(pooled);
315
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
316
+ };
317
+ var scratchInt32 = (size) => takeInt32(size);
318
+ var scratchInt32Filled = (size, value) => {
319
+ const view = takeInt32(size);
320
+ view.fill(value);
321
+ return view;
322
+ };
323
+ var scratchUint32 = (size) => takeUint32(size);
324
+ var scratchUint32Zeroed = (size) => {
325
+ const view = takeUint32(size);
326
+ view.fill(0);
327
+ return view;
328
+ };
329
+ var scratchUint8 = (size) => takeUint8(size);
330
+ var scratchUint8Zeroed = (size) => {
331
+ const view = takeUint8(size);
332
+ view.fill(0);
333
+ return view;
334
+ };
335
+ var scratchUint8Filled = (size, value) => {
336
+ const view = takeUint8(size);
337
+ view.fill(value);
338
+ return view;
339
+ };
340
+ var releaseScratch = () => {
341
+ for (let i = 0; i < borrowedInt32.length; ++i) {
342
+ const buffer = borrowedInt32[i];
343
+ freeInt32[sizeClass(buffer.length)].push(buffer);
344
+ }
345
+ for (let i = 0; i < borrowedUint32.length; ++i) {
346
+ const buffer = borrowedUint32[i];
347
+ freeUint32[sizeClass(buffer.length)].push(buffer);
348
+ }
349
+ for (let i = 0; i < borrowedUint8.length; ++i) {
350
+ const buffer = borrowedUint8[i];
351
+ freeUint8[sizeClass(buffer.length)].push(buffer);
352
+ }
353
+ borrowedInt32.length = 0;
354
+ borrowedUint32.length = 0;
355
+ borrowedUint8.length = 0;
356
+ };
357
+
274
358
  // src/decoder/core/DataBuffer.ts
275
359
  var DataBuffer = class {
276
360
  _data;
@@ -295,6 +379,15 @@ var DataBuffer = class {
295
379
  resize(newSize) {
296
380
  this._resize(newSize);
297
381
  }
382
+ // Replaces the contents with a decode-scoped scratch buffer of exactly
383
+ // `size` bytes; the previous contents are dropped and the new ones are
384
+ // arbitrary, so every byte must be written before it is read. Only for
385
+ // buffers that never outlive the decode (see ScratchArena) — the portable
386
+ // attributes the integer decoders build and discard per attribute are the
387
+ // one caller, and their per-primitive allocation showed up in profiles.
388
+ adoptScratch(size) {
389
+ this._data = scratchUint8(size);
390
+ }
298
391
  write(bytePos, inArray, dataSize) {
299
392
  if (inArray instanceof Uint8Array) {
300
393
  this._data.set(inArray.length === dataSize ? inArray : inArray.subarray(0, dataSize), bytePos);
@@ -384,6 +477,19 @@ var PointAttribute = class extends GeometryAttribute {
384
477
  this._numUniqueEntries = numAttributeValues;
385
478
  return true;
386
479
  }
480
+ // Like reset(), but backs the attribute with decode-scoped scratch memory.
481
+ // Only valid for attributes that never escape the decode and whose every
482
+ // byte is written before it is read (see DataBuffer.adoptScratch).
483
+ resetScratch(numAttributeValues) {
484
+ if (this._attributeBuffer === null) {
485
+ this._attributeBuffer = new DataBuffer();
486
+ }
487
+ const entrySize = dataTypeLength(this.dataType) * this.numComponents;
488
+ this._attributeBuffer.adoptScratch(numAttributeValues * entrySize);
489
+ this.resetBuffer(this._attributeBuffer, entrySize, 0);
490
+ this._numUniqueEntries = numAttributeValues;
491
+ return true;
492
+ }
387
493
  get size() {
388
494
  return this._numUniqueEntries;
389
495
  }
@@ -426,6 +532,20 @@ var PointAttribute = class extends GeometryAttribute {
426
532
  this._identityMapping = false;
427
533
  this._indicesMap = new Uint32Array(numPoints);
428
534
  }
535
+ // Adopts an already-computed map, shared with other attributes of the same
536
+ // mesh. The map must be treated as read-only from here on (reads go through
537
+ // mappedIndex/extractTo; copyFrom slices).
538
+ setExplicitMappingShared(map) {
539
+ this._identityMapping = false;
540
+ this._indicesMap = map;
541
+ }
542
+ // Like setExplicitMappingUnfilled, but from decode-scoped scratch. Only for
543
+ // attributes that never escape the decode (see ScratchArena) -- the portable
544
+ // attributes the sequential decoders build and discard.
545
+ setExplicitMappingScratch(numPoints) {
546
+ this._identityMapping = false;
547
+ this._indicesMap = scratchUint32(numPoints);
548
+ }
429
549
  setAttributeTransformData(transformData) {
430
550
  this._attributeTransformData = transformData;
431
551
  }
@@ -921,48 +1041,6 @@ var DecoderBuffer = class {
921
1041
  }
922
1042
  };
923
1043
 
924
- // src/decoder/core/ScratchArena.ts
925
- var freeInt32 = [];
926
- var freeUint8 = [];
927
- var borrowedInt32 = [];
928
- var borrowedUint8 = [];
929
- var acquire = (free, borrowed, size) => {
930
- for (let i = free.length - 1; i >= 0; --i) {
931
- const buffer = free[i];
932
- if (buffer.length >= size) {
933
- free[i] = free[free.length - 1];
934
- free.pop();
935
- borrowed.push(buffer);
936
- return buffer;
937
- }
938
- }
939
- return null;
940
- };
941
- var scratchInt32 = (size) => {
942
- const pooled = acquire(freeInt32, borrowedInt32, size);
943
- if (pooled !== null) return pooled.subarray(0, size);
944
- const fresh = new Int32Array(size);
945
- borrowedInt32.push(fresh);
946
- return fresh;
947
- };
948
- var scratchUint8Zeroed = (size) => {
949
- const pooled = acquire(freeUint8, borrowedUint8, size);
950
- if (pooled !== null) {
951
- const view = pooled.subarray(0, size);
952
- view.fill(0);
953
- return view;
954
- }
955
- const fresh = new Uint8Array(size);
956
- borrowedUint8.push(fresh);
957
- return fresh;
958
- };
959
- var releaseScratch = () => {
960
- for (const buffer of borrowedInt32) freeInt32.push(buffer);
961
- for (const buffer of borrowedUint8) freeUint8.push(buffer);
962
- borrowedInt32.length = 0;
963
- borrowedUint8.length = 0;
964
- };
965
-
966
1044
  // src/decoder/compression/config/CompressionShared.ts
967
1045
  var kDracoPointCloudBitstreamVersionMajor = 2;
968
1046
  var kDracoPointCloudBitstreamVersionMinor = 3;
@@ -1194,82 +1272,705 @@ var MetadataDecoder = class {
1194
1272
  }
1195
1273
  };
1196
1274
 
1197
- // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1198
- var PointCloudDecoder = class _PointCloudDecoder {
1199
- _pointCloud;
1200
- _buffer;
1201
- _versionMajor;
1202
- _versionMinor;
1203
- _options;
1204
- _attributesDecoders;
1205
- _attributeToDecoderMap;
1275
+ // src/decoder/compression/entropy/ANSCoding.ts
1276
+ var ANS_P8_PRECISION = 256;
1277
+ var ANS_L_BASE = 4096;
1278
+ var ANS_IO_BASE = 256;
1279
+ var COARSE_STREAM_FACTOR = 8;
1280
+ var COARSE_BUCKET_BITS = 8;
1281
+ function memGetLe16(buf, offset) {
1282
+ return buf[offset] | buf[offset + 1] << 8;
1283
+ }
1284
+ function memGetLe24(buf, offset) {
1285
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1286
+ }
1287
+ function memGetLe32(buf, offset) {
1288
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1289
+ }
1290
+ var AnsDecoder = class {
1291
+ buf;
1292
+ bufOffset;
1293
+ // First valid byte of this decoder's slice within buf: init is passed
1294
+ // absolute offsets into the source buffer to avoid a subarray allocation.
1295
+ bufStart;
1296
+ state;
1206
1297
  constructor() {
1207
- this._pointCloud = null;
1208
- this._buffer = null;
1209
- this._versionMajor = 0;
1210
- this._versionMinor = 0;
1211
- this._options = null;
1212
- this._attributesDecoders = [];
1213
- this._attributeToDecoderMap = [];
1298
+ this.buf = null;
1299
+ this.bufOffset = 0;
1300
+ this.bufStart = 0;
1301
+ this.state = 0;
1214
1302
  }
1215
- getGeometryType() {
1216
- return EncodedGeometryType.POINT_CLOUD;
1303
+ };
1304
+ function ansReadInit(ans, buf, offset, base = 0) {
1305
+ if (offset - base < 1) {
1306
+ return 1;
1217
1307
  }
1218
- // Returns a Status; on success outHeader is populated.
1219
- static decodeHeader(buffer, outHeader) {
1220
- const kIoErrorMsg = "Failed to parse Draco header.";
1221
- const bytes = buffer.decodeBytes(5);
1222
- if (bytes === void 0) {
1223
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1308
+ ans.buf = buf;
1309
+ ans.bufStart = base;
1310
+ const x = buf[offset - 1] >> 6;
1311
+ if (x === 0) {
1312
+ ans.bufOffset = offset - 1;
1313
+ ans.state = buf[offset - 1] & 63;
1314
+ } else if (x === 1) {
1315
+ if (offset - base < 2) {
1316
+ return 1;
1224
1317
  }
1225
- for (let i = 0; i < 5; i++) {
1226
- outHeader.dracoString[i] = bytes[i];
1318
+ ans.bufOffset = offset - 2;
1319
+ ans.state = memGetLe16(buf, offset - 2) & 16383;
1320
+ } else if (x === 2) {
1321
+ if (offset - base < 3) {
1322
+ return 1;
1227
1323
  }
1228
- const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1229
- if (magic !== "DRACO") {
1230
- return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1324
+ ans.bufOffset = offset - 3;
1325
+ ans.state = memGetLe24(buf, offset - 3) & 4194303;
1326
+ } else {
1327
+ return 1;
1328
+ }
1329
+ ans.state += ANS_L_BASE;
1330
+ if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1331
+ return 1;
1332
+ }
1333
+ return 0;
1334
+ }
1335
+ function ansReadEnd(ans) {
1336
+ return ans.state === ANS_L_BASE;
1337
+ }
1338
+ var tablePool = [];
1339
+ var acquirePooled = (Ctor, size) => {
1340
+ for (let i = tablePool.length - 1; i >= 0; --i) {
1341
+ const buf = tablePool[i];
1342
+ if (buf.constructor === Ctor && buf.length >= size) {
1343
+ tablePool[i] = tablePool[tablePool.length - 1];
1344
+ tablePool.pop();
1345
+ return buf;
1231
1346
  }
1232
- const versionMajor = buffer.decodeUint8();
1233
- if (versionMajor === void 0) {
1234
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1347
+ }
1348
+ return new Ctor(size);
1349
+ };
1350
+ var RAnsDecoder = class {
1351
+ ransPrecisionBits;
1352
+ ransPrecision;
1353
+ ransPrecisionMask;
1354
+ lRansBase;
1355
+ lutTable;
1356
+ probTable;
1357
+ cumProbTable;
1358
+ // Short-stream mode (see ransBuildLookUpTable): no full-precision lut; a
1359
+ // 256-entry bucket table narrows each lookup to a symbol range that a short
1360
+ // cumProb scan finishes. cumProbTable then carries one extra trailing entry
1361
+ // (== ransPrecision) so the scan needs no bounds check.
1362
+ coarse;
1363
+ bucketShift;
1364
+ bucketTable;
1365
+ buf;
1366
+ bufOffset;
1367
+ // First valid byte of this decoder's slice within buf (absolute offsets,
1368
+ // see AnsDecoder.bufStart).
1369
+ bufStart;
1370
+ state;
1371
+ constructor(ransPrecisionBits) {
1372
+ this.ransPrecisionBits = ransPrecisionBits;
1373
+ this.ransPrecision = 1 << ransPrecisionBits;
1374
+ this.ransPrecisionMask = this.ransPrecision - 1;
1375
+ this.lRansBase = this.ransPrecision * 4;
1376
+ this.lutTable = null;
1377
+ this.probTable = null;
1378
+ this.cumProbTable = null;
1379
+ this.coarse = false;
1380
+ this.bucketShift = 0;
1381
+ this.bucketTable = null;
1382
+ this.buf = null;
1383
+ this.bufOffset = 0;
1384
+ this.bufStart = 0;
1385
+ this.state = 0;
1386
+ }
1387
+ // offset is the absolute end of the encoded bytes within buf and base the
1388
+ // absolute start (offset - base = encoded length). Passing the source
1389
+ // buffer with absolute offsets avoids a subarray allocation per init.
1390
+ // Returns 0 on success, non-zero on error.
1391
+ readInit(buf, offset, base = 0) {
1392
+ if (offset - base < 1) {
1393
+ return 1;
1235
1394
  }
1236
- outHeader.versionMajor = versionMajor;
1237
- const versionMinor = buffer.decodeUint8();
1238
- if (versionMinor === void 0) {
1239
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1395
+ this.buf = buf;
1396
+ this.bufStart = base;
1397
+ const x = buf[offset - 1] >> 6;
1398
+ if (x === 0) {
1399
+ this.bufOffset = offset - 1;
1400
+ this.state = buf[offset - 1] & 63;
1401
+ } else if (x === 1) {
1402
+ if (offset - base < 2) {
1403
+ return 1;
1404
+ }
1405
+ this.bufOffset = offset - 2;
1406
+ this.state = memGetLe16(buf, offset - 2) & 16383;
1407
+ } else if (x === 2) {
1408
+ if (offset - base < 3) {
1409
+ return 1;
1410
+ }
1411
+ this.bufOffset = offset - 3;
1412
+ this.state = memGetLe24(buf, offset - 3) & 4194303;
1413
+ } else if (x === 3) {
1414
+ if (offset - base < 4) {
1415
+ return 1;
1416
+ }
1417
+ this.bufOffset = offset - 4;
1418
+ this.state = memGetLe32(buf, offset - 4) & 1073741823;
1419
+ } else {
1420
+ return 1;
1240
1421
  }
1241
- outHeader.versionMinor = versionMinor;
1242
- const encoderType = buffer.decodeUint8();
1243
- if (encoderType === void 0) {
1244
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1422
+ this.state += this.lRansBase;
1423
+ if (this.state >= this.lRansBase * ANS_IO_BASE) {
1424
+ return 1;
1245
1425
  }
1246
- outHeader.encoderType = encoderType;
1247
- const encoderMethod = buffer.decodeUint8();
1248
- if (encoderMethod === void 0) {
1249
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1426
+ return 0;
1427
+ }
1428
+ readEnd() {
1429
+ if (this.lutTable !== null) {
1430
+ tablePool.push(this.lutTable);
1431
+ this.lutTable = null;
1250
1432
  }
1251
- outHeader.encoderMethod = encoderMethod;
1252
- const flags = buffer.decodeUint16();
1253
- if (flags === void 0) {
1254
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1433
+ if (this.probTable !== null) {
1434
+ tablePool.push(this.probTable);
1435
+ this.probTable = null;
1255
1436
  }
1256
- outHeader.flags = flags;
1257
- return okStatus();
1258
- }
1259
- // Main entry point for point cloud decoding.
1260
- decode(options, inBuffer, outPointCloud) {
1261
- this._options = options;
1262
- this._buffer = inBuffer;
1263
- this._pointCloud = outPointCloud;
1264
- const header = new DracoHeader();
1265
- const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1266
- if (!headerStatus.ok()) {
1267
- return headerStatus;
1437
+ if (this.cumProbTable !== null) {
1438
+ tablePool.push(this.cumProbTable);
1439
+ this.cumProbTable = null;
1268
1440
  }
1269
- if (header.encoderType !== this.getGeometryType()) {
1270
- return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1441
+ if (this.bucketTable !== null) {
1442
+ tablePool.push(this.bucketTable);
1443
+ this.bucketTable = null;
1271
1444
  }
1272
- this._versionMajor = header.versionMajor;
1445
+ return this.state === this.lRansBase;
1446
+ }
1447
+ ransRead() {
1448
+ const buf = this.buf;
1449
+ const lRansBase = this.lRansBase;
1450
+ let state = this.state;
1451
+ let bufOffset = this.bufOffset;
1452
+ const bufStart = this.bufStart;
1453
+ while (state < lRansBase && bufOffset > bufStart) {
1454
+ state = state << 8 | buf[--bufOffset];
1455
+ }
1456
+ const quo = state >>> this.ransPrecisionBits;
1457
+ const rem = state & this.ransPrecisionMask;
1458
+ let symbol;
1459
+ if (this.coarse) {
1460
+ const cumProbTable = this.cumProbTable;
1461
+ symbol = this.bucketTable[rem >> this.bucketShift];
1462
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1463
+ } else {
1464
+ symbol = this.lutTable[rem];
1465
+ }
1466
+ this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
1467
+ this.bufOffset = bufOffset;
1468
+ return symbol;
1469
+ }
1470
+ // Batch ransRead() into out[0..count): all fields hoisted to locals, state
1471
+ // written back once. Removes per-symbol property reads and call indirection.
1472
+ // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
1473
+ // which would make the hot lutTable[rem] access site polymorphic — dispatch
1474
+ // once here so each loop body stays monomorphic on its concrete type. The
1475
+ // three bodies are intentionally identical copies.
1476
+ decodeSymbols(out, count) {
1477
+ if (this.coarse) {
1478
+ this._decodeSymbolsCoarse(out, count);
1479
+ return;
1480
+ }
1481
+ const lutTable = this.lutTable;
1482
+ if (lutTable instanceof Uint8Array) {
1483
+ this._decodeSymbolsU8(out, count, lutTable);
1484
+ } else if (lutTable instanceof Uint16Array) {
1485
+ this._decodeSymbolsU16(out, count, lutTable);
1486
+ } else {
1487
+ this._decodeSymbolsU32(out, count, lutTable);
1488
+ }
1489
+ }
1490
+ _decodeSymbolsU8(out, count, lutTable) {
1491
+ const buf = this.buf;
1492
+ const lRansBase = this.lRansBase;
1493
+ const ransPrecisionBits = this.ransPrecisionBits;
1494
+ const ransPrecisionMask = this.ransPrecisionMask;
1495
+ const probTable = this.probTable;
1496
+ const cumProbTable = this.cumProbTable;
1497
+ let state = this.state;
1498
+ let bufOffset = this.bufOffset;
1499
+ const bufStart = this.bufStart;
1500
+ for (let i = 0; i < count; ++i) {
1501
+ while (state < lRansBase && bufOffset > bufStart) {
1502
+ state = state << 8 | buf[--bufOffset];
1503
+ }
1504
+ const rem = state & ransPrecisionMask;
1505
+ const symbol = lutTable[rem];
1506
+ out[i] = symbol;
1507
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1508
+ }
1509
+ this.state = state;
1510
+ this.bufOffset = bufOffset;
1511
+ }
1512
+ _decodeSymbolsU16(out, count, lutTable) {
1513
+ const buf = this.buf;
1514
+ const lRansBase = this.lRansBase;
1515
+ const ransPrecisionBits = this.ransPrecisionBits;
1516
+ const ransPrecisionMask = this.ransPrecisionMask;
1517
+ const probTable = this.probTable;
1518
+ const cumProbTable = this.cumProbTable;
1519
+ let state = this.state;
1520
+ let bufOffset = this.bufOffset;
1521
+ const bufStart = this.bufStart;
1522
+ for (let i = 0; i < count; ++i) {
1523
+ while (state < lRansBase && bufOffset > bufStart) {
1524
+ state = state << 8 | buf[--bufOffset];
1525
+ }
1526
+ const rem = state & ransPrecisionMask;
1527
+ const symbol = lutTable[rem];
1528
+ out[i] = symbol;
1529
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1530
+ }
1531
+ this.state = state;
1532
+ this.bufOffset = bufOffset;
1533
+ }
1534
+ // Short-stream loop: bucket table + cumProb scan instead of the full lut.
1535
+ // Same state arithmetic as the lut loops, so the output is identical.
1536
+ _decodeSymbolsCoarse(out, count) {
1537
+ const buf = this.buf;
1538
+ const lRansBase = this.lRansBase;
1539
+ const ransPrecisionBits = this.ransPrecisionBits;
1540
+ const ransPrecisionMask = this.ransPrecisionMask;
1541
+ const probTable = this.probTable;
1542
+ const cumProbTable = this.cumProbTable;
1543
+ const bucketTable = this.bucketTable;
1544
+ const bucketShift = this.bucketShift;
1545
+ let state = this.state;
1546
+ let bufOffset = this.bufOffset;
1547
+ const bufStart = this.bufStart;
1548
+ for (let i = 0; i < count; ++i) {
1549
+ while (state < lRansBase && bufOffset > bufStart) {
1550
+ state = state << 8 | buf[--bufOffset];
1551
+ }
1552
+ const rem = state & ransPrecisionMask;
1553
+ let symbol = bucketTable[rem >> bucketShift];
1554
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1555
+ out[i] = symbol;
1556
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1557
+ }
1558
+ this.state = state;
1559
+ this.bufOffset = bufOffset;
1560
+ }
1561
+ _decodeSymbolsU32(out, count, lutTable) {
1562
+ const buf = this.buf;
1563
+ const lRansBase = this.lRansBase;
1564
+ const ransPrecisionBits = this.ransPrecisionBits;
1565
+ const ransPrecisionMask = this.ransPrecisionMask;
1566
+ const probTable = this.probTable;
1567
+ const cumProbTable = this.cumProbTable;
1568
+ let state = this.state;
1569
+ let bufOffset = this.bufOffset;
1570
+ const bufStart = this.bufStart;
1571
+ for (let i = 0; i < count; ++i) {
1572
+ while (state < lRansBase && bufOffset > bufStart) {
1573
+ state = state << 8 | buf[--bufOffset];
1574
+ }
1575
+ const rem = state & ransPrecisionMask;
1576
+ const symbol = lutTable[rem];
1577
+ out[i] = symbol;
1578
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1579
+ }
1580
+ this.state = state;
1581
+ this.bufOffset = bufOffset;
1582
+ }
1583
+ // Builds the decoding tables. Returns false on bad input data.
1584
+ //
1585
+ // expectedCount is how many symbols the caller will decode from this stream.
1586
+ // The full lut has ransPrecision (>= 4096) entries, and a primitive-heavy
1587
+ // file builds thousands of them to decode a few dozen symbols each -- on such
1588
+ // files the table builds cost more than the decodes. Short streams therefore
1589
+ // get a coarse 256-entry bucket table (symbol at the start of each
1590
+ // precision/256-wide bucket) and finish each lookup with a scan of the
1591
+ // cumulative probabilities; long streams keep the exact lut, whose per-symbol
1592
+ // cost is lower.
1593
+ ransBuildLookUpTable(tokenProbs, numSymbols, expectedCount = 2147483647) {
1594
+ const ransPrecision = this.ransPrecision;
1595
+ const coarse = numSymbols <= 65535 && expectedCount * COARSE_STREAM_FACTOR < ransPrecision;
1596
+ this.coarse = coarse;
1597
+ const probTable = acquirePooled(Uint32Array, numSymbols);
1598
+ const cumProbTable = acquirePooled(Uint32Array, numSymbols + 1);
1599
+ this.probTable = probTable;
1600
+ this.cumProbTable = cumProbTable;
1601
+ let cumProb = 0;
1602
+ if (coarse) {
1603
+ this.lutTable = null;
1604
+ for (let i = 0; i < numSymbols; ++i) {
1605
+ const prob = tokenProbs[i];
1606
+ probTable[i] = prob;
1607
+ cumProbTable[i] = cumProb;
1608
+ cumProb += prob;
1609
+ if (cumProb > ransPrecision) {
1610
+ return false;
1611
+ }
1612
+ }
1613
+ if (cumProb !== ransPrecision) {
1614
+ return false;
1615
+ }
1616
+ cumProbTable[numSymbols] = ransPrecision;
1617
+ const bucketShift = this.ransPrecisionBits - COARSE_BUCKET_BITS;
1618
+ const bucketTable = acquirePooled(Uint16Array, 1 << COARSE_BUCKET_BITS);
1619
+ this.bucketShift = bucketShift;
1620
+ this.bucketTable = bucketTable;
1621
+ let symbol = 0;
1622
+ for (let b = 0; b < 1 << COARSE_BUCKET_BITS; ++b) {
1623
+ const rem = b << bucketShift;
1624
+ while (cumProbTable[symbol + 1] <= rem) symbol++;
1625
+ bucketTable[b] = symbol;
1626
+ }
1627
+ return true;
1628
+ }
1629
+ const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
1630
+ const lutTable = acquirePooled(LutArray, ransPrecision);
1631
+ this.lutTable = lutTable;
1632
+ let actProb = 0;
1633
+ for (let i = 0; i < numSymbols; ++i) {
1634
+ const prob = tokenProbs[i];
1635
+ probTable[i] = prob;
1636
+ cumProbTable[i] = cumProb;
1637
+ cumProb += prob;
1638
+ if (cumProb > ransPrecision) {
1639
+ return false;
1640
+ }
1641
+ if (prob < 32) {
1642
+ for (let j = actProb; j < cumProb; ++j) {
1643
+ lutTable[j] = i;
1644
+ }
1645
+ } else {
1646
+ lutTable.fill(i, actProb, cumProb);
1647
+ }
1648
+ actProb = cumProb;
1649
+ }
1650
+ if (cumProb !== ransPrecision) {
1651
+ return false;
1652
+ }
1653
+ cumProbTable[numSymbols] = ransPrecision;
1654
+ return true;
1655
+ }
1656
+ };
1657
+ function ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB) {
1658
+ const lutA = a.lutTable;
1659
+ const lutB = b.lutTable;
1660
+ const bufA = a.buf;
1661
+ const bufB = b.buf;
1662
+ const probA = a.probTable;
1663
+ const probB = b.probTable;
1664
+ const cumA = a.cumProbTable;
1665
+ const cumB = b.cumProbTable;
1666
+ const lBaseA = a.lRansBase;
1667
+ const lBaseB = b.lRansBase;
1668
+ const bitsA = a.ransPrecisionBits;
1669
+ const bitsB = b.ransPrecisionBits;
1670
+ const maskA = a.ransPrecisionMask;
1671
+ const maskB = b.ransPrecisionMask;
1672
+ const startA = a.bufStart;
1673
+ const startB = b.bufStart;
1674
+ let stateA = a.state;
1675
+ let stateB = b.state;
1676
+ let offA = a.bufOffset;
1677
+ let offB = b.bufOffset;
1678
+ const shared = countA < countB ? countA : countB;
1679
+ for (let i = 0; i < shared; ++i) {
1680
+ while (stateA < lBaseA && offA > startA) {
1681
+ stateA = stateA << 8 | bufA[--offA];
1682
+ }
1683
+ while (stateB < lBaseB && offB > startB) {
1684
+ stateB = stateB << 8 | bufB[--offB];
1685
+ }
1686
+ const remA = stateA & maskA;
1687
+ const remB = stateB & maskB;
1688
+ const symA = lutA[remA];
1689
+ const symB = lutB[remB];
1690
+ outA[i] = symA;
1691
+ outB[i] = symB;
1692
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1693
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1694
+ }
1695
+ a.state = stateA;
1696
+ a.bufOffset = offA;
1697
+ b.state = stateB;
1698
+ b.bufOffset = offB;
1699
+ if (shared < countA) {
1700
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1701
+ }
1702
+ if (shared < countB) {
1703
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1704
+ }
1705
+ }
1706
+ function ransDecodeSymbolsPair(a, outA, countA, b, outB, countB) {
1707
+ const lutA = a.lutTable;
1708
+ const lutB = b.lutTable;
1709
+ if (lutA instanceof Uint8Array && lutB instanceof Uint8Array) {
1710
+ ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB);
1711
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint16Array) {
1712
+ ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB);
1713
+ } else if (lutA instanceof Uint8Array && lutB instanceof Uint16Array) {
1714
+ ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB);
1715
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint8Array) {
1716
+ ransDecodeSymbolsPairU8U16(b, outB, countB, a, outA, countA);
1717
+ } else {
1718
+ a.decodeSymbols(outA, countA);
1719
+ b.decodeSymbols(outB, countB);
1720
+ }
1721
+ }
1722
+ function ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB) {
1723
+ const lutA = a.lutTable;
1724
+ const lutB = b.lutTable;
1725
+ const bufA = a.buf;
1726
+ const bufB = b.buf;
1727
+ const probA = a.probTable;
1728
+ const probB = b.probTable;
1729
+ const cumA = a.cumProbTable;
1730
+ const cumB = b.cumProbTable;
1731
+ const lBaseA = a.lRansBase;
1732
+ const lBaseB = b.lRansBase;
1733
+ const bitsA = a.ransPrecisionBits;
1734
+ const bitsB = b.ransPrecisionBits;
1735
+ const maskA = a.ransPrecisionMask;
1736
+ const maskB = b.ransPrecisionMask;
1737
+ const startA = a.bufStart;
1738
+ const startB = b.bufStart;
1739
+ let stateA = a.state;
1740
+ let stateB = b.state;
1741
+ let offA = a.bufOffset;
1742
+ let offB = b.bufOffset;
1743
+ const shared = countA < countB ? countA : countB;
1744
+ for (let i = 0; i < shared; ++i) {
1745
+ while (stateA < lBaseA && offA > startA) {
1746
+ stateA = stateA << 8 | bufA[--offA];
1747
+ }
1748
+ while (stateB < lBaseB && offB > startB) {
1749
+ stateB = stateB << 8 | bufB[--offB];
1750
+ }
1751
+ const remA = stateA & maskA;
1752
+ const remB = stateB & maskB;
1753
+ const symA = lutA[remA];
1754
+ const symB = lutB[remB];
1755
+ outA[i] = symA;
1756
+ outB[i] = symB;
1757
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1758
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1759
+ }
1760
+ a.state = stateA;
1761
+ a.bufOffset = offA;
1762
+ b.state = stateB;
1763
+ b.bufOffset = offB;
1764
+ if (shared < countA) {
1765
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1766
+ }
1767
+ if (shared < countB) {
1768
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1769
+ }
1770
+ }
1771
+ function ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB) {
1772
+ const lutA = a.lutTable;
1773
+ const lutB = b.lutTable;
1774
+ const bufA = a.buf;
1775
+ const bufB = b.buf;
1776
+ const probA = a.probTable;
1777
+ const probB = b.probTable;
1778
+ const cumA = a.cumProbTable;
1779
+ const cumB = b.cumProbTable;
1780
+ const lBaseA = a.lRansBase;
1781
+ const lBaseB = b.lRansBase;
1782
+ const bitsA = a.ransPrecisionBits;
1783
+ const bitsB = b.ransPrecisionBits;
1784
+ const maskA = a.ransPrecisionMask;
1785
+ const maskB = b.ransPrecisionMask;
1786
+ const startA = a.bufStart;
1787
+ const startB = b.bufStart;
1788
+ let stateA = a.state;
1789
+ let stateB = b.state;
1790
+ let offA = a.bufOffset;
1791
+ let offB = b.bufOffset;
1792
+ const shared = countA < countB ? countA : countB;
1793
+ for (let i = 0; i < shared; ++i) {
1794
+ while (stateA < lBaseA && offA > startA) {
1795
+ stateA = stateA << 8 | bufA[--offA];
1796
+ }
1797
+ while (stateB < lBaseB && offB > startB) {
1798
+ stateB = stateB << 8 | bufB[--offB];
1799
+ }
1800
+ const remA = stateA & maskA;
1801
+ const remB = stateB & maskB;
1802
+ const symA = lutA[remA];
1803
+ const symB = lutB[remB];
1804
+ outA[i] = symA;
1805
+ outB[i] = symB;
1806
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1807
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1808
+ }
1809
+ a.state = stateA;
1810
+ a.bufOffset = offA;
1811
+ b.state = stateB;
1812
+ b.bufOffset = offB;
1813
+ if (shared < countA) {
1814
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1815
+ }
1816
+ if (shared < countB) {
1817
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1818
+ }
1819
+ }
1820
+ function ransDecodeSymbolsTrioU8(a, outA, countA, b, outB, countB, c, outC, countC) {
1821
+ const lutA = a.lutTable;
1822
+ const lutB = b.lutTable;
1823
+ const lutC = c.lutTable;
1824
+ const bufA = a.buf;
1825
+ const bufB = b.buf;
1826
+ const bufC = c.buf;
1827
+ const probA = a.probTable;
1828
+ const probB = b.probTable;
1829
+ const probC = c.probTable;
1830
+ const cumA = a.cumProbTable;
1831
+ const cumB = b.cumProbTable;
1832
+ const cumC = c.cumProbTable;
1833
+ const lBaseA = a.lRansBase;
1834
+ const lBaseB = b.lRansBase;
1835
+ const lBaseC = c.lRansBase;
1836
+ const bitsA = a.ransPrecisionBits;
1837
+ const bitsB = b.ransPrecisionBits;
1838
+ const bitsC = c.ransPrecisionBits;
1839
+ const maskA = a.ransPrecisionMask;
1840
+ const maskB = b.ransPrecisionMask;
1841
+ const maskC = c.ransPrecisionMask;
1842
+ const startA = a.bufStart;
1843
+ const startB = b.bufStart;
1844
+ const startC = c.bufStart;
1845
+ let stateA = a.state;
1846
+ let stateB = b.state;
1847
+ let stateC = c.state;
1848
+ let offA = a.bufOffset;
1849
+ let offB = b.bufOffset;
1850
+ let offC = c.bufOffset;
1851
+ let shared = countA < countB ? countA : countB;
1852
+ if (countC < shared) shared = countC;
1853
+ for (let i = 0; i < shared; ++i) {
1854
+ while (stateA < lBaseA && offA > startA) {
1855
+ stateA = stateA << 8 | bufA[--offA];
1856
+ }
1857
+ while (stateB < lBaseB && offB > startB) {
1858
+ stateB = stateB << 8 | bufB[--offB];
1859
+ }
1860
+ while (stateC < lBaseC && offC > startC) {
1861
+ stateC = stateC << 8 | bufC[--offC];
1862
+ }
1863
+ const remA = stateA & maskA;
1864
+ const remB = stateB & maskB;
1865
+ const remC = stateC & maskC;
1866
+ const symA = lutA[remA];
1867
+ const symB = lutB[remB];
1868
+ const symC = lutC[remC];
1869
+ outA[i] = symA;
1870
+ outB[i] = symB;
1871
+ outC[i] = symC;
1872
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1873
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1874
+ stateC = (stateC >>> bitsC) * probC[symC] + remC - cumC[symC];
1875
+ }
1876
+ a.state = stateA;
1877
+ a.bufOffset = offA;
1878
+ b.state = stateB;
1879
+ b.bufOffset = offB;
1880
+ c.state = stateC;
1881
+ c.bufOffset = offC;
1882
+ const restA = countA - shared;
1883
+ const restB = countB - shared;
1884
+ const restC = countC - shared;
1885
+ const tails = [];
1886
+ if (restA > 0) tails.push([a, outA.subarray(shared), restA]);
1887
+ if (restB > 0) tails.push([b, outB.subarray(shared), restB]);
1888
+ if (restC > 0) tails.push([c, outC.subarray(shared), restC]);
1889
+ if (tails.length === 2) {
1890
+ ransDecodeSymbolsPairU8(tails[0][0], tails[0][1], tails[0][2], tails[1][0], tails[1][1], tails[1][2]);
1891
+ } else {
1892
+ for (const [decoder, out, count] of tails) {
1893
+ decoder.decodeSymbols(out, count);
1894
+ }
1895
+ }
1896
+ }
1897
+
1898
+ // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1899
+ var PointCloudDecoder = class _PointCloudDecoder {
1900
+ _pointCloud;
1901
+ _buffer;
1902
+ _versionMajor;
1903
+ _versionMinor;
1904
+ _options;
1905
+ _attributesDecoders;
1906
+ _attributeToDecoderMap;
1907
+ constructor() {
1908
+ this._pointCloud = null;
1909
+ this._buffer = null;
1910
+ this._versionMajor = 0;
1911
+ this._versionMinor = 0;
1912
+ this._options = null;
1913
+ this._attributesDecoders = [];
1914
+ this._attributeToDecoderMap = [];
1915
+ }
1916
+ getGeometryType() {
1917
+ return EncodedGeometryType.POINT_CLOUD;
1918
+ }
1919
+ // Returns a Status; on success outHeader is populated.
1920
+ static decodeHeader(buffer, outHeader) {
1921
+ const kIoErrorMsg = "Failed to parse Draco header.";
1922
+ const bytes = buffer.decodeBytes(5);
1923
+ if (bytes === void 0) {
1924
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1925
+ }
1926
+ for (let i = 0; i < 5; i++) {
1927
+ outHeader.dracoString[i] = bytes[i];
1928
+ }
1929
+ const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1930
+ if (magic !== "DRACO") {
1931
+ return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1932
+ }
1933
+ const versionMajor = buffer.decodeUint8();
1934
+ if (versionMajor === void 0) {
1935
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1936
+ }
1937
+ outHeader.versionMajor = versionMajor;
1938
+ const versionMinor = buffer.decodeUint8();
1939
+ if (versionMinor === void 0) {
1940
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1941
+ }
1942
+ outHeader.versionMinor = versionMinor;
1943
+ const encoderType = buffer.decodeUint8();
1944
+ if (encoderType === void 0) {
1945
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1946
+ }
1947
+ outHeader.encoderType = encoderType;
1948
+ const encoderMethod = buffer.decodeUint8();
1949
+ if (encoderMethod === void 0) {
1950
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1951
+ }
1952
+ outHeader.encoderMethod = encoderMethod;
1953
+ const flags = buffer.decodeUint16();
1954
+ if (flags === void 0) {
1955
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1956
+ }
1957
+ outHeader.flags = flags;
1958
+ return okStatus();
1959
+ }
1960
+ // Main entry point for point cloud decoding.
1961
+ decode(options, inBuffer, outPointCloud) {
1962
+ this._options = options;
1963
+ this._buffer = inBuffer;
1964
+ this._pointCloud = outPointCloud;
1965
+ const header = new DracoHeader();
1966
+ const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1967
+ if (!headerStatus.ok()) {
1968
+ return headerStatus;
1969
+ }
1970
+ if (header.encoderType !== this.getGeometryType()) {
1971
+ return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1972
+ }
1973
+ this._versionMajor = header.versionMajor;
1273
1974
  this._versionMinor = header.versionMinor;
1274
1975
  const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1275
1976
  const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
@@ -1388,17 +2089,40 @@ var PointCloudDecoder = class _PointCloudDecoder {
1388
2089
  return true;
1389
2090
  }
1390
2091
  decodeAllAttributes() {
1391
- for (let i = 0; i < this._attributesDecoders.length; i++) {
1392
- if (!this._attributesDecoders[i].decodeAttributes(this._buffer)) {
2092
+ const decoders = this._attributesDecoders;
2093
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2094
+ if (!decoders[i2].decodeAttributesParse(this._buffer)) {
1393
2095
  return false;
1394
2096
  }
1395
2097
  }
1396
- return true;
1397
- }
1398
- onAttributesDecoded() {
1399
- return true;
1400
- }
1401
- _decodeMetadata() {
2098
+ const pending = [];
2099
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2100
+ decoders[i2].collectPendingSymbolStreams(pending);
2101
+ }
2102
+ const paired = pending.filter((stream) => !stream.ans.coarse);
2103
+ let i = 0;
2104
+ for (; i + 1 < paired.length; i += 2) {
2105
+ const a = paired[i];
2106
+ const b = paired[i + 1];
2107
+ ransDecodeSymbolsPair(a.ans, a.out, a.count, b.ans, b.out, b.count);
2108
+ }
2109
+ if (i < paired.length) {
2110
+ paired[i].ans.decodeSymbols(paired[i].out, paired[i].count);
2111
+ }
2112
+ for (const stream of pending) {
2113
+ if (stream.ans.coarse) stream.ans.decodeSymbols(stream.out, stream.count);
2114
+ }
2115
+ for (let k = 0; k < decoders.length; k++) {
2116
+ if (!decoders[k].decodeAttributesFinish()) {
2117
+ return false;
2118
+ }
2119
+ }
2120
+ return true;
2121
+ }
2122
+ onAttributesDecoded() {
2123
+ return true;
2124
+ }
2125
+ _decodeMetadata() {
1402
2126
  const metadataDecoder = new MetadataDecoder();
1403
2127
  if (!metadataDecoder.skipGeometryMetadata(this._buffer)) {
1404
2128
  return new Status(StatusCode.DRACO_ERROR, "Failed to decode metadata.");
@@ -1457,46 +2181,62 @@ var MeshAttributeCornerTable = class {
1457
2181
  no_interior_seams_;
1458
2182
  corner_to_vertex_map_;
1459
2183
  vertex_to_left_most_corner_map_;
1460
- vertex_to_attribute_entry_id_map_;
2184
+ // Attribute-vertex count. C++ keeps a vertex -> attribute-entry map here, but
2185
+ // the decoder only ever reads its size, so track the count directly instead
2186
+ // of allocating an Int32Array per attribute corner table.
2187
+ num_attribute_vertices_;
1461
2188
  corner_table_;
1462
2189
  // Lazily built; see oppositeCornerArray.
1463
2190
  _effectiveOpposite;
1464
2191
  // Every corner passed to addSeamEdge (may contain duplicates); lets
1465
2192
  // oppositeCornerArray patch seams without scanning every corner's flag.
2193
+ // Preallocated to its exact upper bound (2 per seam edge) by the caller via
2194
+ // reserveSeamEdges -- a plain array grown by push() was measurable on
2195
+ // seam-heavy files.
1466
2196
  _seamCorners;
2197
+ _numSeamCorners;
1467
2198
  constructor() {
1468
2199
  this.is_edge_on_seam_ = [];
1469
2200
  this.is_vertex_on_seam_ = [];
1470
2201
  this.no_interior_seams_ = true;
1471
2202
  this.corner_to_vertex_map_ = [];
1472
2203
  this.vertex_to_left_most_corner_map_ = [];
1473
- this.vertex_to_attribute_entry_id_map_ = [];
2204
+ this.num_attribute_vertices_ = 0;
1474
2205
  this.corner_table_ = null;
1475
2206
  this._effectiveOpposite = null;
1476
2207
  this._seamCorners = [];
2208
+ this._numSeamCorners = 0;
1477
2209
  }
1478
2210
  initEmpty(table) {
1479
2211
  if (table === null) {
1480
2212
  return false;
1481
2213
  }
1482
- this.is_edge_on_seam_ = new Uint8Array(table.numCorners());
1483
- this.is_vertex_on_seam_ = new Uint8Array(table.numVertices());
1484
- this.corner_to_vertex_map_ = new Int32Array(table.numCorners()).fill(kInvalidVertexIndex);
1485
- this.vertex_to_attribute_entry_id_map_ = [];
2214
+ this.is_edge_on_seam_ = scratchUint8Zeroed(table.numCorners());
2215
+ this.is_vertex_on_seam_ = scratchUint8Zeroed(table.numVertices());
2216
+ this.corner_to_vertex_map_ = scratchInt32Filled(table.numCorners(), kInvalidVertexIndex);
2217
+ this.num_attribute_vertices_ = 0;
1486
2218
  this.vertex_to_left_most_corner_map_ = [];
1487
2219
  this._effectiveOpposite = null;
1488
2220
  this._seamCorners = [];
2221
+ this._numSeamCorners = 0;
1489
2222
  this.corner_table_ = table;
1490
2223
  this.no_interior_seams_ = true;
1491
2224
  return true;
1492
2225
  }
2226
+ // Sizes the seam-corner list for numSeamEdges upcoming addSeamEdge calls
2227
+ // (each adds at most two corners). Decode-scoped scratch.
2228
+ reserveSeamEdges(numSeamEdges) {
2229
+ this._seamCorners = scratchInt32(numSeamEdges * 2);
2230
+ this._numSeamCorners = 0;
2231
+ }
1493
2232
  addSeamEdge(c) {
1494
2233
  const cornerToVertex = this.corner_table_.cornerToVertexArray();
1495
2234
  const oppositeCorners = this.corner_table_.oppositeCornerArray();
1496
2235
  const isEdge = this.is_edge_on_seam_;
1497
2236
  const isVert = this.is_vertex_on_seam_;
2237
+ const seamCorners = this._seamCorners;
1498
2238
  isEdge[c] = 1;
1499
- this._seamCorners.push(c);
2239
+ seamCorners[this._numSeamCorners++] = c;
1500
2240
  let rem = c - (c / 3 | 0) * 3;
1501
2241
  isVert[cornerToVertex[rem === 2 ? c - 2 : c + 1]] = 1;
1502
2242
  isVert[cornerToVertex[rem === 0 ? c + 2 : c - 1]] = 1;
@@ -1504,7 +2244,7 @@ var MeshAttributeCornerTable = class {
1504
2244
  if (oppCorner !== kInvalidCornerIndex) {
1505
2245
  this.no_interior_seams_ = false;
1506
2246
  isEdge[oppCorner] = 1;
1507
- this._seamCorners.push(oppCorner);
2247
+ seamCorners[this._numSeamCorners++] = oppCorner;
1508
2248
  rem = oppCorner - (oppCorner / 3 | 0) * 3;
1509
2249
  isVert[cornerToVertex[rem === 2 ? oppCorner - 2 : oppCorner + 1]] = 1;
1510
2250
  isVert[cornerToVertex[rem === 0 ? oppCorner + 2 : oppCorner - 1]] = 1;
@@ -1519,12 +2259,12 @@ var MeshAttributeCornerTable = class {
1519
2259
  const ct = this.corner_table_;
1520
2260
  const numCorners = ct.numCorners();
1521
2261
  const numBaseVertices = ct.numVertices();
1522
- const leftMostMap = new Int32Array(numCorners);
2262
+ const leftMostMap = scratchInt32(numCorners);
1523
2263
  const cornerToVertex = this.corner_to_vertex_map_;
1524
2264
  const isVertexOnSeam = this.is_vertex_on_seam_;
1525
2265
  const isEdgeOnSeam = this.is_edge_on_seam_;
1526
2266
  const seamOpp = this.oppositeCornerArray();
1527
- const baseOpp = ct.oppositeCornerArray();
2267
+ const swingRight = ct.swingRightArray();
1528
2268
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
1529
2269
  let numNewVertices = 0;
1530
2270
  for (let v = 0; v < numBaseVertices; ++v) {
@@ -1534,14 +2274,10 @@ var MeshAttributeCornerTable = class {
1534
2274
  const firstVertId = numNewVertices++;
1535
2275
  leftMostMap[firstVertId] = c;
1536
2276
  cornerToVertex[c] = firstVertId;
1537
- let pv = c % 3 === 0 ? c + 2 : c - 1;
1538
- let bopp = baseOpp[pv];
1539
- let actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2277
+ let actC = swingRight[c];
1540
2278
  while (actC !== kInvalidCornerIndex && actC !== c) {
1541
2279
  cornerToVertex[actC] = firstVertId;
1542
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1543
- bopp = baseOpp[pv];
1544
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2280
+ actC = swingRight[actC];
1545
2281
  }
1546
2282
  } else {
1547
2283
  let firstVertId = numNewVertices++;
@@ -1559,9 +2295,7 @@ var MeshAttributeCornerTable = class {
1559
2295
  }
1560
2296
  cornerToVertex[firstC] = firstVertId;
1561
2297
  leftMostMap[firstVertId] = firstC;
1562
- let pv = firstC % 3 === 0 ? firstC + 2 : firstC - 1;
1563
- let bopp = baseOpp[pv];
1564
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2298
+ actC = swingRight[firstC];
1565
2299
  while (actC !== kInvalidCornerIndex && actC !== firstC) {
1566
2300
  const nAct = actC % 3 === 2 ? actC - 2 : actC + 1;
1567
2301
  if (isEdgeOnSeam[nAct]) {
@@ -1569,13 +2303,11 @@ var MeshAttributeCornerTable = class {
1569
2303
  leftMostMap[firstVertId] = actC;
1570
2304
  }
1571
2305
  cornerToVertex[actC] = firstVertId;
1572
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1573
- bopp = baseOpp[pv];
1574
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2306
+ actC = swingRight[actC];
1575
2307
  }
1576
2308
  }
1577
2309
  }
1578
- this.vertex_to_attribute_entry_id_map_ = new Int32Array(numNewVertices);
2310
+ this.num_attribute_vertices_ = numNewVertices;
1579
2311
  this.vertex_to_left_most_corner_map_ = leftMostMap.subarray(0, numNewVertices);
1580
2312
  return true;
1581
2313
  }
@@ -1601,7 +2333,7 @@ var MeshAttributeCornerTable = class {
1601
2333
  return this.next(this.opposite(this.next(corner)));
1602
2334
  }
1603
2335
  numVertices() {
1604
- return this.vertex_to_attribute_entry_id_map_.length;
2336
+ return this.num_attribute_vertices_;
1605
2337
  }
1606
2338
  numFaces() {
1607
2339
  return this.corner_table_.numFaces();
@@ -1629,12 +2361,13 @@ var MeshAttributeCornerTable = class {
1629
2361
  const nc = this.corner_table_.numCorners();
1630
2362
  const base = this.corner_table_.oppositeCornerArray();
1631
2363
  const seamCorners = this._seamCorners;
1632
- if (seamCorners.length === 0) {
2364
+ const numSeamCorners = this._numSeamCorners;
2365
+ if (numSeamCorners === 0) {
1633
2366
  this._effectiveOpposite = base;
1634
2367
  } else {
1635
2368
  const eff = scratchInt32(nc);
1636
2369
  eff.set(base.length === nc ? base : base.subarray(0, nc));
1637
- for (let i = 0, l = seamCorners.length; i < l; ++i) {
2370
+ for (let i = 0; i < numSeamCorners; ++i) {
1638
2371
  eff[seamCorners[i]] = kInvalidCornerIndex;
1639
2372
  }
1640
2373
  this._effectiveOpposite = eff;
@@ -1649,23 +2382,20 @@ var MeshAttributeCornerTable = class {
1649
2382
  vertexOnSeamArray() {
1650
2383
  return this.is_vertex_on_seam_;
1651
2384
  }
1652
- hasSameSeams(other) {
1653
- if (other === null || other === void 0) return false;
1654
- const seamA = this.is_edge_on_seam_;
1655
- const seamB = other.is_edge_on_seam_;
1656
- if (seamA.length !== seamB.length) return false;
1657
- for (let i = 0, l = seamA.length; i < l; ++i) {
1658
- if (seamA[i] !== seamB[i]) return false;
1659
- }
1660
- return true;
1661
- }
1662
- adoptVertexRecompute(other) {
2385
+ // Takes over another table's state wholesale. Only valid when both tables
2386
+ // were built from the same corner table and the same seam edges, in which
2387
+ // case every one of these is identical and read-only from here on.
2388
+ adoptFrom(other) {
2389
+ this.corner_table_ = other.corner_table_;
2390
+ this.is_edge_on_seam_ = other.is_edge_on_seam_;
2391
+ this.is_vertex_on_seam_ = other.is_vertex_on_seam_;
1663
2392
  this.corner_to_vertex_map_ = other.corner_to_vertex_map_;
1664
- this.vertex_to_attribute_entry_id_map_ = other.vertex_to_attribute_entry_id_map_;
2393
+ this.num_attribute_vertices_ = other.num_attribute_vertices_;
1665
2394
  this.vertex_to_left_most_corner_map_ = other.vertex_to_left_most_corner_map_;
1666
2395
  this.no_interior_seams_ = other.no_interior_seams_;
1667
2396
  this._effectiveOpposite = other._effectiveOpposite;
1668
2397
  this._seamCorners = other._seamCorners;
2398
+ this._numSeamCorners = other._numSeamCorners;
1669
2399
  }
1670
2400
  };
1671
2401
 
@@ -1679,6 +2409,21 @@ var AttributesDecoderInterface = class {
1679
2409
  decodeAttributesDecoderData(_buffer) {
1680
2410
  return false;
1681
2411
  }
2412
+ // --- Optional two-phase decode across attributes decoders ---
2413
+ // PointCloudDecoder parses every decoder first (all buffer reads are
2414
+ // size-driven, so parsing runs ahead of the deferred rANS symbol decodes),
2415
+ // then decodes the collected streams two at a time, then finishes each
2416
+ // decoder in order (so parent attributes complete before dependents).
2417
+ // Defaults keep the original single-phase behavior for decoders that do not
2418
+ // split.
2419
+ decodeAttributesParse(buffer) {
2420
+ return this.decodeAttributes(buffer);
2421
+ }
2422
+ collectPendingSymbolStreams(_out) {
2423
+ }
2424
+ decodeAttributesFinish() {
2425
+ return true;
2426
+ }
1682
2427
  decodeAttributes(_buffer) {
1683
2428
  return false;
1684
2429
  }
@@ -1825,6 +2570,22 @@ var SequentialAttributeDecoder = class {
1825
2570
  this._attributeId = attributeId;
1826
2571
  return true;
1827
2572
  }
2573
+ // --- Optional two-phase decode ---
2574
+ // The controller calls Parse for every attribute first (headers, schemes,
2575
+ // prediction data -- all size-driven cursor movement), collects the pending
2576
+ // rANS symbol streams, decodes them in pairs (see ransDecodeSymbolsPair),
2577
+ // then calls Finish per attribute in order. Decoders without a deferrable
2578
+ // stream simply do the whole decode in Parse. Defaults preserve the
2579
+ // original single-phase behavior.
2580
+ decodePortableAttributeParse(pointIds, buffer) {
2581
+ return this.decodePortableAttribute(pointIds, buffer);
2582
+ }
2583
+ pendingSymbolStream() {
2584
+ return null;
2585
+ }
2586
+ decodePortableAttributeFinish() {
2587
+ return true;
2588
+ }
1828
2589
  decodePortableAttribute(pointIds, buffer) {
1829
2590
  if (this._attribute.numComponents <= 0) {
1830
2591
  return false;
@@ -1845,7 +2606,7 @@ var SequentialAttributeDecoder = class {
1845
2606
  getPortableAttribute() {
1846
2607
  if (!this._attribute.isMappingIdentity && this._portableAttribute && this._portableAttribute.isMappingIdentity) {
1847
2608
  const size = this._attribute.indicesMapSize;
1848
- this._portableAttribute.setExplicitMappingUnfilled(size);
2609
+ this._portableAttribute.setExplicitMappingScratch(size);
1849
2610
  const src = this._attribute.indicesMap;
1850
2611
  const dst = this._portableAttribute.indicesMap;
1851
2612
  if (src.length === size) {
@@ -1890,300 +2651,11 @@ var SequentialAttributeDecoder = class {
1890
2651
  this._attribute.buffer.write(0, valueData, totalSize);
1891
2652
  return true;
1892
2653
  }
1893
- setPortableAttribute(att) {
1894
- this._portableAttribute = att;
1895
- }
1896
- get portableAttribute() {
1897
- return this._portableAttribute;
1898
- }
1899
- };
1900
-
1901
- // src/decoder/compression/entropy/ANSCoding.ts
1902
- var ANS_P8_PRECISION = 256;
1903
- var ANS_L_BASE = 4096;
1904
- var ANS_IO_BASE = 256;
1905
- function memGetLe16(buf, offset) {
1906
- return buf[offset] | buf[offset + 1] << 8;
1907
- }
1908
- function memGetLe24(buf, offset) {
1909
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1910
- }
1911
- function memGetLe32(buf, offset) {
1912
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1913
- }
1914
- var AnsDecoder = class {
1915
- buf;
1916
- bufOffset;
1917
- // First valid byte of this decoder's slice within buf: init is passed
1918
- // absolute offsets into the source buffer to avoid a subarray allocation.
1919
- bufStart;
1920
- state;
1921
- constructor() {
1922
- this.buf = null;
1923
- this.bufOffset = 0;
1924
- this.bufStart = 0;
1925
- this.state = 0;
1926
- }
1927
- };
1928
- function ansReadInit(ans, buf, offset, base = 0) {
1929
- if (offset - base < 1) {
1930
- return 1;
1931
- }
1932
- ans.buf = buf;
1933
- ans.bufStart = base;
1934
- const x = buf[offset - 1] >> 6;
1935
- if (x === 0) {
1936
- ans.bufOffset = offset - 1;
1937
- ans.state = buf[offset - 1] & 63;
1938
- } else if (x === 1) {
1939
- if (offset - base < 2) {
1940
- return 1;
1941
- }
1942
- ans.bufOffset = offset - 2;
1943
- ans.state = memGetLe16(buf, offset - 2) & 16383;
1944
- } else if (x === 2) {
1945
- if (offset - base < 3) {
1946
- return 1;
1947
- }
1948
- ans.bufOffset = offset - 3;
1949
- ans.state = memGetLe24(buf, offset - 3) & 4194303;
1950
- } else {
1951
- return 1;
1952
- }
1953
- ans.state += ANS_L_BASE;
1954
- if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1955
- return 1;
1956
- }
1957
- return 0;
1958
- }
1959
- function ansReadEnd(ans) {
1960
- return ans.state === ANS_L_BASE;
1961
- }
1962
- var tablePool = [];
1963
- var acquirePooled = (Ctor, size) => {
1964
- for (let i = tablePool.length - 1; i >= 0; --i) {
1965
- const buf = tablePool[i];
1966
- if (buf.constructor === Ctor && buf.length >= size) {
1967
- tablePool[i] = tablePool[tablePool.length - 1];
1968
- tablePool.pop();
1969
- return buf;
1970
- }
1971
- }
1972
- return new Ctor(size);
1973
- };
1974
- var RAnsDecoder = class {
1975
- ransPrecisionBits;
1976
- ransPrecision;
1977
- ransPrecisionMask;
1978
- lRansBase;
1979
- lutTable;
1980
- probTable;
1981
- cumProbTable;
1982
- buf;
1983
- bufOffset;
1984
- // First valid byte of this decoder's slice within buf (absolute offsets,
1985
- // see AnsDecoder.bufStart).
1986
- bufStart;
1987
- state;
1988
- constructor(ransPrecisionBits) {
1989
- this.ransPrecisionBits = ransPrecisionBits;
1990
- this.ransPrecision = 1 << ransPrecisionBits;
1991
- this.ransPrecisionMask = this.ransPrecision - 1;
1992
- this.lRansBase = this.ransPrecision * 4;
1993
- this.lutTable = null;
1994
- this.probTable = null;
1995
- this.cumProbTable = null;
1996
- this.buf = null;
1997
- this.bufOffset = 0;
1998
- this.bufStart = 0;
1999
- this.state = 0;
2000
- }
2001
- // offset is the absolute end of the encoded bytes within buf and base the
2002
- // absolute start (offset - base = encoded length). Passing the source
2003
- // buffer with absolute offsets avoids a subarray allocation per init.
2004
- // Returns 0 on success, non-zero on error.
2005
- readInit(buf, offset, base = 0) {
2006
- if (offset - base < 1) {
2007
- return 1;
2008
- }
2009
- this.buf = buf;
2010
- this.bufStart = base;
2011
- const x = buf[offset - 1] >> 6;
2012
- if (x === 0) {
2013
- this.bufOffset = offset - 1;
2014
- this.state = buf[offset - 1] & 63;
2015
- } else if (x === 1) {
2016
- if (offset - base < 2) {
2017
- return 1;
2018
- }
2019
- this.bufOffset = offset - 2;
2020
- this.state = memGetLe16(buf, offset - 2) & 16383;
2021
- } else if (x === 2) {
2022
- if (offset - base < 3) {
2023
- return 1;
2024
- }
2025
- this.bufOffset = offset - 3;
2026
- this.state = memGetLe24(buf, offset - 3) & 4194303;
2027
- } else if (x === 3) {
2028
- if (offset - base < 4) {
2029
- return 1;
2030
- }
2031
- this.bufOffset = offset - 4;
2032
- this.state = memGetLe32(buf, offset - 4) & 1073741823;
2033
- } else {
2034
- return 1;
2035
- }
2036
- this.state += this.lRansBase;
2037
- if (this.state >= this.lRansBase * ANS_IO_BASE) {
2038
- return 1;
2039
- }
2040
- return 0;
2041
- }
2042
- readEnd() {
2043
- if (this.lutTable !== null) {
2044
- tablePool.push(this.lutTable);
2045
- this.lutTable = null;
2046
- }
2047
- if (this.probTable !== null) {
2048
- tablePool.push(this.probTable);
2049
- this.probTable = null;
2050
- }
2051
- if (this.cumProbTable !== null) {
2052
- tablePool.push(this.cumProbTable);
2053
- this.cumProbTable = null;
2054
- }
2055
- return this.state === this.lRansBase;
2056
- }
2057
- ransRead() {
2058
- const buf = this.buf;
2059
- const lRansBase = this.lRansBase;
2060
- let state = this.state;
2061
- let bufOffset = this.bufOffset;
2062
- const bufStart = this.bufStart;
2063
- while (state < lRansBase && bufOffset > bufStart) {
2064
- state = state << 8 | buf[--bufOffset];
2065
- }
2066
- const quo = state >>> this.ransPrecisionBits;
2067
- const rem = state & this.ransPrecisionMask;
2068
- const symbol = this.lutTable[rem];
2069
- this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
2070
- this.bufOffset = bufOffset;
2071
- return symbol;
2072
- }
2073
- // Batch ransRead() into out[0..count): all fields hoisted to locals, state
2074
- // written back once. Removes per-symbol property reads and call indirection.
2075
- // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
2076
- // which would make the hot lutTable[rem] access site polymorphic — dispatch
2077
- // once here so each loop body stays monomorphic on its concrete type. The
2078
- // three bodies are intentionally identical copies.
2079
- decodeSymbols(out, count) {
2080
- const lutTable = this.lutTable;
2081
- if (lutTable instanceof Uint8Array) {
2082
- this._decodeSymbolsU8(out, count, lutTable);
2083
- } else if (lutTable instanceof Uint16Array) {
2084
- this._decodeSymbolsU16(out, count, lutTable);
2085
- } else {
2086
- this._decodeSymbolsU32(out, count, lutTable);
2087
- }
2088
- }
2089
- _decodeSymbolsU8(out, count, lutTable) {
2090
- const buf = this.buf;
2091
- const lRansBase = this.lRansBase;
2092
- const ransPrecisionBits = this.ransPrecisionBits;
2093
- const ransPrecisionMask = this.ransPrecisionMask;
2094
- const probTable = this.probTable;
2095
- const cumProbTable = this.cumProbTable;
2096
- let state = this.state;
2097
- let bufOffset = this.bufOffset;
2098
- const bufStart = this.bufStart;
2099
- for (let i = 0; i < count; ++i) {
2100
- while (state < lRansBase && bufOffset > bufStart) {
2101
- state = state << 8 | buf[--bufOffset];
2102
- }
2103
- const rem = state & ransPrecisionMask;
2104
- const symbol = lutTable[rem];
2105
- out[i] = symbol;
2106
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2107
- }
2108
- this.state = state;
2109
- this.bufOffset = bufOffset;
2110
- }
2111
- _decodeSymbolsU16(out, count, lutTable) {
2112
- const buf = this.buf;
2113
- const lRansBase = this.lRansBase;
2114
- const ransPrecisionBits = this.ransPrecisionBits;
2115
- const ransPrecisionMask = this.ransPrecisionMask;
2116
- const probTable = this.probTable;
2117
- const cumProbTable = this.cumProbTable;
2118
- let state = this.state;
2119
- let bufOffset = this.bufOffset;
2120
- const bufStart = this.bufStart;
2121
- for (let i = 0; i < count; ++i) {
2122
- while (state < lRansBase && bufOffset > bufStart) {
2123
- state = state << 8 | buf[--bufOffset];
2124
- }
2125
- const rem = state & ransPrecisionMask;
2126
- const symbol = lutTable[rem];
2127
- out[i] = symbol;
2128
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2129
- }
2130
- this.state = state;
2131
- this.bufOffset = bufOffset;
2132
- }
2133
- _decodeSymbolsU32(out, count, lutTable) {
2134
- const buf = this.buf;
2135
- const lRansBase = this.lRansBase;
2136
- const ransPrecisionBits = this.ransPrecisionBits;
2137
- const ransPrecisionMask = this.ransPrecisionMask;
2138
- const probTable = this.probTable;
2139
- const cumProbTable = this.cumProbTable;
2140
- let state = this.state;
2141
- let bufOffset = this.bufOffset;
2142
- const bufStart = this.bufStart;
2143
- for (let i = 0; i < count; ++i) {
2144
- while (state < lRansBase && bufOffset > bufStart) {
2145
- state = state << 8 | buf[--bufOffset];
2146
- }
2147
- const rem = state & ransPrecisionMask;
2148
- const symbol = lutTable[rem];
2149
- out[i] = symbol;
2150
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2151
- }
2152
- this.state = state;
2153
- this.bufOffset = bufOffset;
2154
- }
2155
- // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
2156
- ransBuildLookUpTable(tokenProbs, numSymbols) {
2157
- const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
2158
- const lutTable = acquirePooled(LutArray, this.ransPrecision);
2159
- const probTable = acquirePooled(Uint32Array, numSymbols);
2160
- const cumProbTable = acquirePooled(Uint32Array, numSymbols);
2161
- this.lutTable = lutTable;
2162
- this.probTable = probTable;
2163
- this.cumProbTable = cumProbTable;
2164
- let cumProb = 0;
2165
- let actProb = 0;
2166
- for (let i = 0; i < numSymbols; ++i) {
2167
- const prob = tokenProbs[i];
2168
- probTable[i] = prob;
2169
- cumProbTable[i] = cumProb;
2170
- cumProb += prob;
2171
- if (cumProb > this.ransPrecision) {
2172
- return false;
2173
- }
2174
- if (prob < 32) {
2175
- for (let j = actProb; j < cumProb; ++j) {
2176
- lutTable[j] = i;
2177
- }
2178
- } else {
2179
- lutTable.fill(i, actProb, cumProb);
2180
- }
2181
- actProb = cumProb;
2182
- }
2183
- if (cumProb !== this.ransPrecision) {
2184
- return false;
2185
- }
2186
- return true;
2654
+ setPortableAttribute(att) {
2655
+ this._portableAttribute = att;
2656
+ }
2657
+ get portableAttribute() {
2658
+ return this._portableAttribute;
2187
2659
  }
2188
2660
  };
2189
2661
 
@@ -2208,8 +2680,10 @@ var RAnsSymbolDecoder = class {
2208
2680
  get numSymbols() {
2209
2681
  return this.numSymbols_;
2210
2682
  }
2211
- // Initialize the decoder and decode the probability table.
2212
- create(buffer) {
2683
+ // Initialize the decoder and decode the probability table. expectedCount is
2684
+ // the number of symbols the caller will decode (lets short streams skip the
2685
+ // full lookup table; see RAnsDecoder.ransBuildLookUpTable).
2686
+ create(buffer, expectedCount = 2147483647) {
2213
2687
  if (buffer.bitstreamVersion === 0) {
2214
2688
  return false;
2215
2689
  }
@@ -2220,7 +2694,7 @@ var RAnsSymbolDecoder = class {
2220
2694
  return false;
2221
2695
  }
2222
2696
  const numSymbols = this.numSymbols_;
2223
- const probabilityTable = new Uint32Array(numSymbols);
2697
+ const probabilityTable = scratchUint32Zeroed(numSymbols);
2224
2698
  this.probabilityTable_ = probabilityTable;
2225
2699
  if (numSymbols === 0) {
2226
2700
  return true;
@@ -2250,7 +2724,7 @@ var RAnsSymbolDecoder = class {
2250
2724
  }
2251
2725
  }
2252
2726
  buffer.advance(pos - startPos);
2253
- if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_)) {
2727
+ if (!this.ans_.ransBuildLookUpTable(this.probabilityTable_, this.numSymbols_, expectedCount)) {
2254
2728
  return false;
2255
2729
  }
2256
2730
  return true;
@@ -2292,7 +2766,7 @@ function decodeSymbols(numValues, numComponents, srcBuffer, outValues) {
2292
2766
  }
2293
2767
  function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2294
2768
  const tagDecoder = new RAnsSymbolDecoder(5);
2295
- if (!tagDecoder.create(srcBuffer)) {
2769
+ if (!tagDecoder.create(srcBuffer, Math.ceil(numValues / numComponents))) {
2296
2770
  return false;
2297
2771
  }
2298
2772
  if (!tagDecoder.startDecoding(srcBuffer)) {
@@ -2301,27 +2775,76 @@ function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2301
2775
  if (numValues > 0 && tagDecoder.numSymbols === 0) {
2302
2776
  return false;
2303
2777
  }
2778
+ const tagAns = tagDecoder.ans_;
2304
2779
  srcBuffer.startBitDecoding(false);
2305
2780
  const bd = srcBuffer._bitDecoder;
2306
- const tagAns = tagDecoder.ans_;
2781
+ const buf = bd._bitBuffer;
2782
+ const byteLength = bd._byteLength;
2783
+ let bitOffset = bd._bitOffset;
2307
2784
  let valueId = 0;
2308
2785
  for (let i = 0; i < numValues; i += numComponents) {
2309
2786
  const bitLength = tagAns.ransRead();
2310
- for (let j = 0; j < numComponents; ++j) {
2311
- const val = bd.getBits(bitLength);
2312
- if (val === void 0) {
2313
- return false;
2787
+ if (bitLength < 32) {
2788
+ const mask = (1 << bitLength) - 1;
2789
+ let j = 0;
2790
+ for (; j < numComponents; ++j) {
2791
+ const byteOffset = bitOffset >> 3;
2792
+ if (byteOffset + 4 >= byteLength) break;
2793
+ const bitShift = bitOffset & 7;
2794
+ let value = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> bitShift;
2795
+ if (bitLength > 32 - bitShift) {
2796
+ value = (value | buf[byteOffset + 4] << 32 - bitShift) >>> 0;
2797
+ }
2798
+ bitOffset += bitLength;
2799
+ outValues[valueId++] = value & mask;
2800
+ }
2801
+ if (j === numComponents) continue;
2802
+ bd._bitOffset = bitOffset;
2803
+ for (; j < numComponents; ++j) {
2804
+ const val = bd.getBits(bitLength);
2805
+ if (val === void 0) {
2806
+ return false;
2807
+ }
2808
+ outValues[valueId++] = val;
2809
+ }
2810
+ bitOffset = bd._bitOffset;
2811
+ } else {
2812
+ bd._bitOffset = bitOffset;
2813
+ for (let j = 0; j < numComponents; ++j) {
2814
+ const val = bd.getBits(bitLength);
2815
+ if (val === void 0) {
2816
+ return false;
2817
+ }
2818
+ outValues[valueId++] = val;
2314
2819
  }
2315
- outValues[valueId++] = val;
2820
+ bitOffset = bd._bitOffset;
2316
2821
  }
2317
2822
  }
2823
+ bd._bitOffset = bitOffset;
2318
2824
  tagDecoder.endDecoding();
2319
2825
  srcBuffer.endBitDecoding();
2320
2826
  return true;
2321
2827
  }
2828
+ function parseRawSymbolStream(numValues, srcBuffer) {
2829
+ const maxBitLength = srcBuffer.decodeUint8();
2830
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
2831
+ return null;
2832
+ }
2833
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
2834
+ if (!decoder.create(srcBuffer, numValues)) {
2835
+ return null;
2836
+ }
2837
+ if (numValues > 0 && decoder.numSymbols === 0) {
2838
+ return null;
2839
+ }
2840
+ if (!decoder.startDecoding(srcBuffer)) {
2841
+ return null;
2842
+ }
2843
+ return decoder;
2844
+ }
2322
2845
  function decodeRawSymbolsInternal(uniqueSymbolsBitLength, numValues, srcBuffer, outValues) {
2323
2846
  const decoder = new RAnsSymbolDecoder(uniqueSymbolsBitLength);
2324
- if (!decoder.create(srcBuffer)) {
2847
+ if (!decoder.create(srcBuffer, numValues)) {
2325
2848
  return false;
2326
2849
  }
2327
2850
  if (numValues > 0 && decoder.numSymbols === 0) {
@@ -2421,6 +2944,16 @@ var PredictionSchemeDecoderInterface = class {
2421
2944
  decodePredictionData(_buffer) {
2422
2945
  return true;
2423
2946
  }
2947
+ /**
2948
+ * Like computeOriginalValues, but inCorr still holds unsigned zigzag-coded
2949
+ * corrections; the implementation unpacks each one inline, replacing the
2950
+ * standalone convertSymbolsToSignedInts pass. Returns undefined when the
2951
+ * scheme/transform combination cannot fuse (the caller then falls back to
2952
+ * the two-pass path). Base implementation: never fusable.
2953
+ */
2954
+ computeOriginalValuesZigzag(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2955
+ return void 0;
2956
+ }
2424
2957
  /** Reverts the prediction applied during encoding, writing original values to outData. */
2425
2958
  computeOriginalValues(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2426
2959
  return false;
@@ -2756,7 +3289,7 @@ var OctahedronToolBox = class {
2756
3289
  // src/decoder/compression/attributes/prediction_schemes/MeshPredictionSchemeGeometricNormalPredictorArea.ts
2757
3290
  var UPPER_BOUND = 1 << 29;
2758
3291
  function buildInt32PositionCache(att, map, numEntries, tempPos) {
2759
- const cache = new Int32Array(numEntries * 3);
3292
+ const cache = scratchInt32(numEntries * 3);
2760
3293
  const bufData = att.buffer && att.buffer.data;
2761
3294
  if (att.dataType === DataType.INT32 && att.numComponents === 3 && bufData) {
2762
3295
  const src = new Int32Array(bufData.buffer);
@@ -2837,7 +3370,7 @@ var MeshPredictionSchemeGeometricNormalPredictorArea = class {
2837
3370
  const cornerToVertex = this._cornerToVertex;
2838
3371
  const vertexToDataMap = this._meshData.vertexToDataMap;
2839
3372
  const nc = cornerToVertex.length;
2840
- const c2o = new Int32Array(nc);
3373
+ const c2o = scratchInt32(nc);
2841
3374
  for (let c = 0; c < nc; ++c) {
2842
3375
  const v = cornerToVertex[c];
2843
3376
  c2o[c] = v < 0 ? -1 : vertexToDataMap[v] * 3;
@@ -3088,10 +3621,21 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3088
3621
  isInitialized() {
3089
3622
  return this._meshData.isInitialized();
3090
3623
  }
3624
+ // Zigzag-fused variant: decodes corrections that are still in their unsigned
3625
+ // zigzag form, unpacking each one inline. Only offered for the wrap
3626
+ // transform (whose corrections are the only zigzag-coded ones this scheme
3627
+ // sees); callers fall back to the standalone conversion pass otherwise.
3628
+ computeOriginalValuesZigzag(inCorr, outData, _size, numComponents, _entryToPointIdMap) {
3629
+ this._transform.init(numComponents);
3630
+ if (!this._transform.getType || this._transform.getType() !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3631
+ return void 0;
3632
+ }
3633
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, true);
3634
+ }
3091
3635
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3092
3636
  this._transform.init(numComponents);
3093
3637
  if (this._transform.getType && this._transform.getType() === PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3094
- return this._computeOriginalValuesWrap(inCorr, outData, numComponents);
3638
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, false);
3095
3639
  }
3096
3640
  const table = this._meshData.cornerTable;
3097
3641
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3132,12 +3676,18 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3132
3676
  }
3133
3677
  return true;
3134
3678
  }
3135
- _computeOriginalValuesWrap(inCorr, outData, numComponents) {
3679
+ // The wrap transform's corrections are zigzag-coded; with `zigzag` set the
3680
+ // decode folds the (val >>> 1) ^ -(val & 1) unpacking into each correction
3681
+ // read, replacing the standalone convertSymbolsToSignedInts pass over the
3682
+ // whole array (see computeOriginalValuesZigzag). Every correction is read
3683
+ // exactly once before its slot is overwritten, so the in-place aliasing of
3684
+ // inCorr and outData is preserved.
3685
+ _computeOriginalValuesWrap(inCorr, outData, numComponents, zigzag) {
3136
3686
  if (numComponents === 2) {
3137
- return this._computeOriginalValuesWrap2(inCorr, outData);
3687
+ return this._computeOriginalValuesWrap2(inCorr, outData, zigzag);
3138
3688
  }
3139
3689
  if (numComponents === 3) {
3140
- return this._computeOriginalValuesWrap3(inCorr, outData);
3690
+ return this._computeOriginalValuesWrap3(inCorr, outData, zigzag);
3141
3691
  }
3142
3692
  const table = this._meshData.cornerTable;
3143
3693
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3155,7 +3705,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3155
3705
  } else if (pred < minValue) {
3156
3706
  pred = minValue;
3157
3707
  }
3158
- let orig = pred + inCorr[c] | 0;
3708
+ const raw = inCorr[c];
3709
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3159
3710
  if (orig > maxValue) {
3160
3711
  orig -= maxDif;
3161
3712
  } else if (orig < minValue) {
@@ -3194,7 +3745,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3194
3745
  } else if (pred < minValue) {
3195
3746
  pred = minValue;
3196
3747
  }
3197
- let orig = pred + inCorr[dstOffset + c] | 0;
3748
+ const raw = inCorr[dstOffset + c];
3749
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3198
3750
  if (orig > maxValue) {
3199
3751
  orig -= maxDif;
3200
3752
  } else if (orig < minValue) {
@@ -3211,7 +3763,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3211
3763
  } else if (pred < minValue) {
3212
3764
  pred = minValue;
3213
3765
  }
3214
- let orig = pred + inCorr[dstOffset + c] | 0;
3766
+ const raw = inCorr[dstOffset + c];
3767
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3215
3768
  if (orig > maxValue) {
3216
3769
  orig -= maxDif;
3217
3770
  } else if (orig < minValue) {
@@ -3223,7 +3776,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3223
3776
  }
3224
3777
  return true;
3225
3778
  }
3226
- _computeOriginalValuesWrap2(inCorr, outData) {
3779
+ _computeOriginalValuesWrap2(inCorr, outData, zigzag) {
3227
3780
  const table = this._meshData.cornerTable;
3228
3781
  const vertexToDataMap = this._meshData.vertexToDataMap;
3229
3782
  const oppositeCorners = table.oppositeCornerArray();
@@ -3245,8 +3798,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3245
3798
  } else if (pred1 < minValue) {
3246
3799
  pred1 = minValue;
3247
3800
  }
3248
- let orig0 = pred0 + inCorr[0] | 0;
3249
- let orig1 = pred1 + inCorr[1] | 0;
3801
+ const raw0 = inCorr[0];
3802
+ const raw1 = inCorr[1];
3803
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3804
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3250
3805
  if (orig0 > maxValue) {
3251
3806
  orig0 -= maxDif;
3252
3807
  } else if (orig0 < minValue) {
@@ -3300,8 +3855,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3300
3855
  } else if (pred1 < minValue) {
3301
3856
  pred1 = minValue;
3302
3857
  }
3303
- orig0 = pred0 + inCorr[dstOffset] | 0;
3304
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3858
+ const rawA = inCorr[dstOffset];
3859
+ const rawB = inCorr[dstOffset + 1];
3860
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3861
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3305
3862
  if (orig0 > maxValue) {
3306
3863
  orig0 -= maxDif;
3307
3864
  } else if (orig0 < minValue) {
@@ -3317,7 +3874,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3317
3874
  }
3318
3875
  return true;
3319
3876
  }
3320
- _computeOriginalValuesWrap3(inCorr, outData) {
3877
+ _computeOriginalValuesWrap3(inCorr, outData, zigzag) {
3321
3878
  const table = this._meshData.cornerTable;
3322
3879
  const vertexToDataMap = this._meshData.vertexToDataMap;
3323
3880
  const oppositeCorners = table.oppositeCornerArray();
@@ -3345,9 +3902,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3345
3902
  } else if (pred2 < minValue) {
3346
3903
  pred2 = minValue;
3347
3904
  }
3348
- let orig0 = pred0 + inCorr[0] | 0;
3349
- let orig1 = pred1 + inCorr[1] | 0;
3350
- let orig2 = pred2 + inCorr[2] | 0;
3905
+ const raw0 = inCorr[0];
3906
+ const raw1 = inCorr[1];
3907
+ const raw2 = inCorr[2];
3908
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3909
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3910
+ let orig2 = pred2 + (zigzag ? raw2 >>> 1 ^ -(raw2 & 1) : raw2) | 0;
3351
3911
  if (orig0 > maxValue) {
3352
3912
  orig0 -= maxDif;
3353
3913
  } else if (orig0 < minValue) {
@@ -3414,9 +3974,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3414
3974
  } else if (pred2 < minValue) {
3415
3975
  pred2 = minValue;
3416
3976
  }
3417
- orig0 = pred0 + inCorr[dstOffset] | 0;
3418
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3419
- orig2 = pred2 + inCorr[dstOffset + 2] | 0;
3977
+ const rawA = inCorr[dstOffset];
3978
+ const rawB = inCorr[dstOffset + 1];
3979
+ const rawC = inCorr[dstOffset + 2];
3980
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3981
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3982
+ orig2 = pred2 + (zigzag ? rawC >>> 1 ^ -(rawC & 1) : rawC) | 0;
3420
3983
  if (orig0 > maxValue) {
3421
3984
  orig0 -= maxDif;
3422
3985
  } else if (orig0 < minValue) {
@@ -3783,11 +4346,16 @@ var PredictionSchemeDeltaDecoder = class extends PredictionSchemeDecoder {
3783
4346
  return true;
3784
4347
  }
3785
4348
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3786
- this._transform.init(numComponents);
4349
+ const transform = this._transform;
4350
+ transform.init(numComponents);
4351
+ if (transform.computeOriginalValuesDelta) {
4352
+ transform.computeOriginalValuesDelta(inCorr, outData, size, numComponents);
4353
+ return true;
4354
+ }
3787
4355
  const zeroVals = new Int32Array(numComponents);
3788
- this._transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
4356
+ transform.computeOriginalValue(zeroVals, 0, inCorr, 0, outData, 0);
3789
4357
  for (let i = numComponents; i < size; i += numComponents) {
3790
- this._transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
4358
+ transform.computeOriginalValue(outData, i - numComponents, inCorr, i, outData, i);
3791
4359
  }
3792
4360
  return true;
3793
4361
  }
@@ -3923,14 +4491,49 @@ var PredictionSchemeWrapDecodingTransform = class {
3923
4491
  // src/decoder/compression/attributes/SequentialIntegerAttributeDecoder.ts
3924
4492
  var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder {
3925
4493
  _predictionScheme;
4494
+ // Two-phase decode state (see SequentialAttributeDecoder): the parse phase
4495
+ // stashes the primed raw-symbol stream here so the controller can batch and
4496
+ // pair several attributes' decodes; the finish phase consumes it.
4497
+ _pendingSymbolDecoder;
4498
+ _pendingNumValues;
4499
+ _finishPointIds;
3926
4500
  constructor() {
3927
4501
  super();
3928
4502
  this._predictionScheme = null;
4503
+ this._pendingSymbolDecoder = null;
4504
+ this._pendingNumValues = 0;
4505
+ this._finishPointIds = null;
3929
4506
  }
3930
- transformAttributeToOriginalFormat(pointIds) {
3931
- return this._storeValues(pointIds.length);
4507
+ // --- Two-phase decode (parse headers / batch symbol decode / finish) ---
4508
+ decodePortableAttributeParse(pointIds, buffer) {
4509
+ if (this.attribute.numComponents <= 0) {
4510
+ return false;
4511
+ }
4512
+ if (!this.attribute.reset(pointIds.length)) {
4513
+ return false;
4514
+ }
4515
+ this._finishPointIds = pointIds;
4516
+ return this._decodeValuesParse(pointIds, buffer);
3932
4517
  }
3933
- decodeValues(pointIds, buffer) {
4518
+ pendingSymbolStream() {
4519
+ if (this._pendingSymbolDecoder === null) {
4520
+ return null;
4521
+ }
4522
+ const portableAttributeData = this.getPortableAttributeData();
4523
+ return {
4524
+ ans: this._pendingSymbolDecoder.ans_,
4525
+ out: new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, this._pendingNumValues),
4526
+ count: this._pendingNumValues
4527
+ };
4528
+ }
4529
+ decodePortableAttributeFinish() {
4530
+ if (this._pendingSymbolDecoder !== null) {
4531
+ this._pendingSymbolDecoder.endDecoding();
4532
+ this._pendingSymbolDecoder = null;
4533
+ }
4534
+ return this._finishIntegerValues(this._finishPointIds);
4535
+ }
4536
+ _decodeValuesParse(pointIds, buffer) {
3934
4537
  const predictionSchemeMethod = buffer.decodeInt8();
3935
4538
  if (predictionSchemeMethod === void 0) return false;
3936
4539
  if (predictionSchemeMethod < PredictionSchemeMethod.PREDICTION_NONE || predictionSchemeMethod >= PredictionSchemeMethod.NUM_PREDICTION_SCHEMES) {
@@ -3949,12 +4552,6 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3949
4552
  return false;
3950
4553
  }
3951
4554
  }
3952
- if (!this.decodeIntegerValues(pointIds, buffer)) {
3953
- return false;
3954
- }
3955
- return true;
3956
- }
3957
- decodeIntegerValues(pointIds, buffer) {
3958
4555
  const numComponents = this.getNumValueComponents();
3959
4556
  if (numComponents <= 0) {
3960
4557
  return false;
@@ -3969,9 +4566,23 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3969
4566
  const compressed = buffer.decodeUint8();
3970
4567
  if (compressed === void 0) return false;
3971
4568
  if (compressed > 0) {
3972
- const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3973
- if (!decodeSymbols(numValues, numComponents, buffer, outUint32)) {
3974
- return false;
4569
+ if (numValues > 0) {
4570
+ const scheme = buffer.decodeUint8();
4571
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_RAW) {
4572
+ const decoder = parseRawSymbolStream(numValues, buffer);
4573
+ if (decoder === null) {
4574
+ return false;
4575
+ }
4576
+ this._pendingSymbolDecoder = decoder;
4577
+ this._pendingNumValues = numValues;
4578
+ } else if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
4579
+ const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4580
+ if (!decodeTaggedSymbols(numValues, numComponents, buffer, outUint32)) {
4581
+ return false;
4582
+ }
4583
+ } else {
4584
+ return false;
4585
+ }
3975
4586
  }
3976
4587
  } else {
3977
4588
  const numBytes = buffer.decodeUint8();
@@ -4002,15 +4613,40 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4002
4613
  }
4003
4614
  }
4004
4615
  }
4005
- if (numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive())) {
4006
- const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4007
- convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4008
- }
4009
4616
  if (this._predictionScheme) {
4010
4617
  if (!this._predictionScheme.decodePredictionData(buffer)) {
4011
4618
  return false;
4012
4619
  }
4620
+ }
4621
+ return true;
4622
+ }
4623
+ // The post-symbol tail of the decode: zigzag unpacking and prediction.
4624
+ _finishIntegerValues(pointIds) {
4625
+ const numComponents = this.getNumValueComponents();
4626
+ const numValues = pointIds.length * numComponents;
4627
+ const portableAttributeData = this.getPortableAttributeData();
4628
+ if (portableAttributeData === null) {
4629
+ return false;
4630
+ }
4631
+ const needsZigzag = numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive());
4632
+ if (this._predictionScheme) {
4013
4633
  if (numValues > 0) {
4634
+ if (needsZigzag) {
4635
+ const fused = this._predictionScheme.computeOriginalValuesZigzag(
4636
+ portableAttributeData,
4637
+ portableAttributeData,
4638
+ numValues,
4639
+ numComponents,
4640
+ pointIds
4641
+ );
4642
+ if (fused !== void 0) {
4643
+ return fused;
4644
+ }
4645
+ }
4646
+ if (needsZigzag) {
4647
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4648
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4649
+ }
4014
4650
  if (!this._predictionScheme.computeOriginalValues(
4015
4651
  portableAttributeData,
4016
4652
  portableAttributeData,
@@ -4021,9 +4657,34 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4021
4657
  return false;
4022
4658
  }
4023
4659
  }
4660
+ } else if (needsZigzag) {
4661
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4662
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4024
4663
  }
4025
4664
  return true;
4026
4665
  }
4666
+ transformAttributeToOriginalFormat(pointIds) {
4667
+ return this._storeValues(pointIds.length);
4668
+ }
4669
+ // Single-phase entry (base-class decodePortableAttribute path): parse,
4670
+ // decode any deferred symbol stream immediately, finish.
4671
+ decodeValues(pointIds, buffer) {
4672
+ this._finishPointIds = pointIds;
4673
+ if (!this._decodeValuesParse(pointIds, buffer)) {
4674
+ return false;
4675
+ }
4676
+ const pending = this._pendingSymbolDecoder;
4677
+ if (pending !== null) {
4678
+ const portableAttributeData = this.getPortableAttributeData();
4679
+ const outUint32 = new Uint32Array(
4680
+ portableAttributeData.buffer,
4681
+ portableAttributeData.byteOffset,
4682
+ this._pendingNumValues
4683
+ );
4684
+ pending.ans_.decodeSymbols(outUint32, this._pendingNumValues);
4685
+ }
4686
+ return this.decodePortableAttributeFinish();
4687
+ }
4027
4688
  // Prediction scheme for decoding integer values; subclasses override for others.
4028
4689
  createIntPredictionScheme(method, transformType) {
4029
4690
  if (transformType !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
@@ -4086,7 +4747,7 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4086
4747
  );
4087
4748
  const portAtt = new PointAttribute(ga);
4088
4749
  portAtt.setIdentityMapping();
4089
- portAtt.reset(numEntries);
4750
+ portAtt.resetScratch(numEntries);
4090
4751
  portAtt.uniqueId = this.attribute.uniqueId;
4091
4752
  this.setPortableAttribute(portAtt);
4092
4753
  }
@@ -4112,12 +4773,22 @@ var AttributeTransformType = {
4112
4773
  };
4113
4774
 
4114
4775
  // src/decoder/attributes/AttributeTransformData.ts
4776
+ var INITIAL_CAPACITY = 32;
4115
4777
  var AttributeTransformData = class {
4116
4778
  _transformType;
4117
- _buffer;
4779
+ // Parameter bytes, little-endian, appended in transform-defined order.
4780
+ // Capacity grows geometrically and the DataView is cached alongside it: the
4781
+ // previous DataBuffer-backed version reallocated the buffer and built a
4782
+ // fresh DataView on *every* appended value, which on primitive-heavy files
4783
+ // cost more than the dequantization it describes.
4784
+ _bytes;
4785
+ _view;
4786
+ _size;
4118
4787
  constructor() {
4119
4788
  this._transformType = AttributeTransformType.INVALID;
4120
- this._buffer = new DataBuffer();
4789
+ this._bytes = new Uint8Array(INITIAL_CAPACITY);
4790
+ this._view = new DataView(this._bytes.buffer);
4791
+ this._size = 0;
4121
4792
  }
4122
4793
  get transformType() {
4123
4794
  return this._transformType;
@@ -4125,13 +4796,29 @@ var AttributeTransformData = class {
4125
4796
  set transformType(type) {
4126
4797
  this._transformType = type;
4127
4798
  }
4799
+ // Number of parameter bytes written so far (the next append offset).
4800
+ get dataSize() {
4801
+ return this._size;
4802
+ }
4803
+ get data() {
4804
+ return this._bytes.subarray(0, this._size);
4805
+ }
4806
+ _reserve(sizeNeeded) {
4807
+ if (sizeNeeded <= this._bytes.length) return;
4808
+ let capacity = this._bytes.length * 2;
4809
+ if (capacity < sizeNeeded) capacity = sizeNeeded;
4810
+ const grown = new Uint8Array(capacity);
4811
+ grown.set(this._bytes);
4812
+ this._bytes = grown;
4813
+ this._view = new DataView(grown.buffer);
4814
+ }
4128
4815
  setParameterValue(byteOffset, value, type) {
4129
4816
  const sizeNeeded = byteOffset + this._typeSize(type);
4130
- if (sizeNeeded > this._buffer.dataSize) {
4131
- this._buffer.resize(sizeNeeded);
4817
+ this._reserve(sizeNeeded);
4818
+ if (sizeNeeded > this._size) {
4819
+ this._size = sizeNeeded;
4132
4820
  }
4133
- const data = this._buffer.data;
4134
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
4821
+ const view = this._view;
4135
4822
  switch (type) {
4136
4823
  case "int32":
4137
4824
  view.setInt32(byteOffset, value, true);
@@ -4163,7 +4850,7 @@ var AttributeTransformData = class {
4163
4850
  }
4164
4851
  }
4165
4852
  appendParameterValue(value, type) {
4166
- this.setParameterValue(this._buffer.dataSize, value, type);
4853
+ this.setParameterValue(this._size, value, type);
4167
4854
  }
4168
4855
  _typeSize(type) {
4169
4856
  switch (type) {
@@ -4209,7 +4896,6 @@ var AttributeTransform = class {
4209
4896
  // src/decoder/attributes/AttributeOctahedronTransform.ts
4210
4897
  var AttributeOctahedronTransform = class extends AttributeTransform {
4211
4898
  _quantizationBits;
4212
- _tmpVec;
4213
4899
  constructor() {
4214
4900
  super();
4215
4901
  this._quantizationBits = -1;
@@ -4241,15 +4927,30 @@ var AttributeOctahedronTransform = class extends AttributeTransform {
4241
4927
  const srcI32 = new Int32Array(srcAddr.buffer, srcAddr.byteOffset, numPoints * 2);
4242
4928
  const dstAddr = targetAttribute.getAddress(0);
4243
4929
  const dstF32 = new Float32Array(dstAddr.buffer, dstAddr.byteOffset, numPoints * 3);
4244
- const outVec = this._tmpVec || (this._tmpVec = new Float32Array(3));
4930
+ const fround = Math.fround;
4931
+ const scale = toolBox._dequantizationScale;
4245
4932
  let si = 0;
4246
4933
  let di = 0;
4247
4934
  for (let i = 0; i < numPoints; i++) {
4248
- toolBox.quantizedOctahedralCoordsToUnitVector(srcI32[si], srcI32[si + 1], outVec);
4935
+ let y = fround(fround(fround(srcI32[si]) * scale) - 1);
4936
+ let z = fround(fround(fround(srcI32[si + 1]) * scale) - 1);
4249
4937
  si += 2;
4250
- dstF32[di] = outVec[0];
4251
- dstF32[di + 1] = outVec[1];
4252
- dstF32[di + 2] = outVec[2];
4938
+ const x = fround(fround(1 - Math.abs(y)) - Math.abs(z));
4939
+ let xOffset = -x;
4940
+ if (xOffset < 0) xOffset = 0;
4941
+ y = fround(y + (y < 0 ? xOffset : -xOffset));
4942
+ z = fround(z + (z < 0 ? xOffset : -xOffset));
4943
+ const normSquared = fround(fround(fround(x * x) + fround(y * y)) + fround(z * z));
4944
+ if (normSquared < 1e-6) {
4945
+ dstF32[di] = 0;
4946
+ dstF32[di + 1] = 0;
4947
+ dstF32[di + 2] = 0;
4948
+ } else {
4949
+ const d = fround(1 / fround(Math.sqrt(normSquared)));
4950
+ dstF32[di] = fround(x * d);
4951
+ dstF32[di + 1] = fround(y * d);
4952
+ dstF32[di + 2] = fround(z * d);
4953
+ }
4253
4954
  di += 3;
4254
4955
  }
4255
4956
  return true;
@@ -4419,6 +5120,140 @@ var PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform = class exten
4419
5120
  outOrigVals[outOffset] = origS + center;
4420
5121
  outOrigVals[outOffset + 1] = origT + center;
4421
5122
  }
5123
+ // Fused delta loop for PredictionSchemeDeltaDecoder: the whole
5124
+ // value[i] = original(value[i-1], corr[i]) chain in one call, with the
5125
+ // toolbox fields hoisted to locals and the previous output carried in
5126
+ // locals. This is the hot path for PREDICTION_DIFFERENCE normals (one call
5127
+ // per value through the interface was ~8% of decode time on the bundles).
5128
+ // The arithmetic is computeOriginalValue above verbatim -- keep them in
5129
+ // sync; only the plumbing differs.
5130
+ computeOriginalValuesDelta(inCorr, outData, size, numComponents) {
5131
+ const toolBox = this._octahedronToolBox;
5132
+ const center = toolBox._centerValue;
5133
+ const maxQuantizedValue = toolBox._maxQuantizedValue;
5134
+ let prevS = 0;
5135
+ let prevT = 0;
5136
+ for (let i = 0; i < size; i += numComponents) {
5137
+ const corrS = inCorr[i];
5138
+ const corrT = inCorr[i + 1];
5139
+ let predS = prevS - center;
5140
+ let predT = prevT - center;
5141
+ const predIsInDiamond = Math.abs(predS) + Math.abs(predT) <= center;
5142
+ if (!predIsInDiamond) {
5143
+ let signS = 0;
5144
+ let signT = 0;
5145
+ if (predS >= 0 && predT >= 0) {
5146
+ signS = 1;
5147
+ signT = 1;
5148
+ } else if (predS <= 0 && predT <= 0) {
5149
+ signS = -1;
5150
+ signT = -1;
5151
+ } else {
5152
+ signS = predS > 0 ? 1 : -1;
5153
+ signT = predT > 0 ? 1 : -1;
5154
+ }
5155
+ const cornerPointS = signS * center;
5156
+ const cornerPointT = signT * center;
5157
+ let us = predS * 2 - cornerPointS | 0;
5158
+ let ut = predT * 2 - cornerPointT | 0;
5159
+ if (signS * signT >= 0) {
5160
+ const temp = us;
5161
+ us = -ut;
5162
+ ut = -temp;
5163
+ } else {
5164
+ const temp = us;
5165
+ us = ut;
5166
+ ut = temp;
5167
+ }
5168
+ predS = (us + cornerPointS) / 2 | 0;
5169
+ predT = (ut + cornerPointT) / 2 | 0;
5170
+ }
5171
+ const predIsInBottomLeft = predS === 0 && predT === 0 || predS < 0 && predT <= 0;
5172
+ let rotationCount = 0;
5173
+ if (predS === 0) {
5174
+ if (predT > 0) rotationCount = 3;
5175
+ else if (predT < 0) rotationCount = 1;
5176
+ } else if (predS > 0) {
5177
+ if (predT >= 0) rotationCount = 2;
5178
+ else rotationCount = 1;
5179
+ } else {
5180
+ if (predT > 0) rotationCount = 3;
5181
+ }
5182
+ if (!predIsInBottomLeft) {
5183
+ const s = predS, t = predT;
5184
+ switch (rotationCount) {
5185
+ case 1:
5186
+ predS = t;
5187
+ predT = -s | 0;
5188
+ break;
5189
+ case 2:
5190
+ predS = -s | 0;
5191
+ predT = -t | 0;
5192
+ break;
5193
+ case 3:
5194
+ predS = -t | 0;
5195
+ predT = s;
5196
+ break;
5197
+ }
5198
+ }
5199
+ let origS = predS + corrS | 0;
5200
+ if (origS > center) origS -= maxQuantizedValue;
5201
+ else if (origS < -center) origS += maxQuantizedValue;
5202
+ let origT = predT + corrT | 0;
5203
+ if (origT > center) origT -= maxQuantizedValue;
5204
+ else if (origT < -center) origT += maxQuantizedValue;
5205
+ if (!predIsInBottomLeft) {
5206
+ const s = origS, t = origT;
5207
+ switch (4 - rotationCount & 3) {
5208
+ case 1:
5209
+ origS = t;
5210
+ origT = -s | 0;
5211
+ break;
5212
+ case 2:
5213
+ origS = -s | 0;
5214
+ origT = -t | 0;
5215
+ break;
5216
+ case 3:
5217
+ origS = -t | 0;
5218
+ origT = s;
5219
+ break;
5220
+ }
5221
+ }
5222
+ if (!predIsInDiamond) {
5223
+ let signS = 0;
5224
+ let signT = 0;
5225
+ if (origS >= 0 && origT >= 0) {
5226
+ signS = 1;
5227
+ signT = 1;
5228
+ } else if (origS <= 0 && origT <= 0) {
5229
+ signS = -1;
5230
+ signT = -1;
5231
+ } else {
5232
+ signS = origS > 0 ? 1 : -1;
5233
+ signT = origT > 0 ? 1 : -1;
5234
+ }
5235
+ const cornerPointS = signS * center;
5236
+ const cornerPointT = signT * center;
5237
+ let us = origS * 2 - cornerPointS | 0;
5238
+ let ut = origT * 2 - cornerPointT | 0;
5239
+ if (signS * signT >= 0) {
5240
+ const temp = us;
5241
+ us = -ut;
5242
+ ut = -temp;
5243
+ } else {
5244
+ const temp = us;
5245
+ us = ut;
5246
+ ut = temp;
5247
+ }
5248
+ origS = (us + cornerPointS) / 2 | 0;
5249
+ origT = (ut + cornerPointT) / 2 | 0;
5250
+ }
5251
+ prevS = origS + center | 0;
5252
+ prevT = origT + center | 0;
5253
+ outData[i] = prevS;
5254
+ outData[i + 1] = prevT;
5255
+ }
5256
+ }
4422
5257
  };
4423
5258
 
4424
5259
  // src/decoder/compression/attributes/prediction_schemes/PredictionSchemeNormalOctahedronDecodingTransform.ts
@@ -4733,6 +5568,12 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4733
5568
  return true;
4734
5569
  }
4735
5570
  decodeAttributes(buffer) {
5571
+ if (!this._prepareSequence()) {
5572
+ return false;
5573
+ }
5574
+ return super.decodeAttributes(buffer);
5575
+ }
5576
+ _prepareSequence() {
4736
5577
  if (!this._sequencer) {
4737
5578
  return false;
4738
5579
  }
@@ -4747,7 +5588,44 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4747
5588
  return false;
4748
5589
  }
4749
5590
  }
4750
- return super.decodeAttributes(buffer);
5591
+ return true;
5592
+ }
5593
+ // Two-phase decode (see AttributesDecoder): parse everything that reads the
5594
+ // buffer -- sequence, portable headers (deferring raw rANS symbol decodes),
5595
+ // and the transform parameters -- so the deferred streams of ALL attributes
5596
+ // decoders can then be paired, and finish (zigzag, prediction, inverse
5597
+ // transform) runs per decoder in the original order afterwards. None of the
5598
+ // finish work reads the buffer, and dependents only read parent portable
5599
+ // VALUES in finish, so ordering and output stay identical.
5600
+ decodeAttributesParse(buffer) {
5601
+ if (!this._prepareSequence()) {
5602
+ return false;
5603
+ }
5604
+ const numAttributes = this.getNumAttributes();
5605
+ for (let i = 0; i < numAttributes; i++) {
5606
+ if (!this._sequentialDecoders[i].decodePortableAttributeParse(this._pointIds, buffer)) {
5607
+ return false;
5608
+ }
5609
+ }
5610
+ return this.decodeDataNeededByPortableTransforms(buffer);
5611
+ }
5612
+ collectPendingSymbolStreams(out) {
5613
+ const numAttributes = this.getNumAttributes();
5614
+ for (let i = 0; i < numAttributes; i++) {
5615
+ const pending = this._sequentialDecoders[i].pendingSymbolStream();
5616
+ if (pending !== null) {
5617
+ out.push(pending);
5618
+ }
5619
+ }
5620
+ }
5621
+ decodeAttributesFinish() {
5622
+ const numAttributes = this.getNumAttributes();
5623
+ for (let i = 0; i < numAttributes; i++) {
5624
+ if (!this._sequentialDecoders[i].decodePortableAttributeFinish()) {
5625
+ return false;
5626
+ }
5627
+ }
5628
+ return this.transformAttributesToOriginalFormat();
4751
5629
  }
4752
5630
  getPortableAttribute(pointAttributeId) {
4753
5631
  const locId = this.getLocalIdForPointAttribute(pointAttributeId);
@@ -4874,14 +5752,13 @@ var DepthFirstTraverser = class {
4874
5752
  // Scratch buffers are set up here rather than in init() so a shared-traversal
4875
5753
  // -cache hit — where generateSequence returns before any traversal — skips
4876
5754
  // this entirely, including the visited-flag zero-fill (worth ~0.5% on the
4877
- // 488-primitive manablade-static bundle, which shares attribute corner tables across
5755
+ // ~500-primitive manablade bundle, which shares attribute corner tables across
4878
5756
  // primitives). Uint8Array (0/1) instead of Array(bool): these flags are read
4879
5757
  // and written on every corner of the hottest decode loop (traverseFromCorner).
4880
5758
  // Decode-scoped scratch: released in bulk at the end of the decode.
4881
5759
  onTraversalStart() {
4882
5760
  const cornerTable = this._cornerTable;
4883
5761
  this._isFaceVisited = scratchUint8Zeroed(cornerTable.numFaces());
4884
- this._isVertexVisited = scratchUint8Zeroed(cornerTable.numVertices());
4885
5762
  this._cornerTraversalStack = scratchInt32(this._numCorners);
4886
5763
  }
4887
5764
  onTraversalEnd() {
@@ -4891,7 +5768,6 @@ var DepthFirstTraverser = class {
4891
5768
  return true;
4892
5769
  }
4893
5770
  const isFaceVisited = this._isFaceVisited;
4894
- const isVertexVisited = this._isVertexVisited;
4895
5771
  const observer = this._observer;
4896
5772
  const cornerToVertex = this._cornerToVertex;
4897
5773
  const oppositeCorners = this._oppositeCorners;
@@ -4915,14 +5791,12 @@ var DepthFirstTraverser = class {
4915
5791
  if (nextVert === kInvalidVertexIndex2 || prevVert === kInvalidVertexIndex2) {
4916
5792
  return false;
4917
5793
  }
4918
- if (!isVertexVisited[nextVert]) {
4919
- isVertexVisited[nextVert] = 1;
5794
+ if (vertexToEncodedMap[nextVert] < 0) {
4920
5795
  outPointIds[numOutPoints++] = obsFaces[nextCorner];
4921
5796
  encodedToCornerMap[numValues] = nextCorner;
4922
5797
  vertexToEncodedMap[nextVert] = numValues++;
4923
5798
  }
4924
- if (!isVertexVisited[prevVert]) {
4925
- isVertexVisited[prevVert] = 1;
5799
+ if (vertexToEncodedMap[prevVert] < 0) {
4926
5800
  outPointIds[numOutPoints++] = obsFaces[prevCorner];
4927
5801
  encodedToCornerMap[numValues] = prevCorner;
4928
5802
  vertexToEncodedMap[prevVert] = numValues++;
@@ -4943,27 +5817,26 @@ var DepthFirstTraverser = class {
4943
5817
  encodingData.numValues = numValues;
4944
5818
  return false;
4945
5819
  }
4946
- if (!isVertexVisited[vertId]) {
5820
+ const faceBase = faceId * 3;
5821
+ const nextCornerId = cornerId === faceBase + 2 ? faceBase : cornerId + 1;
5822
+ if (vertexToEncodedMap[vertId] < 0) {
4947
5823
  const lc = vertexLeftmost[vertId];
4948
5824
  let onBoundary = true;
4949
- if (lc !== void 0 && lc >= 0) {
5825
+ if (lc >= 0) {
4950
5826
  const nextLc = lc % 3 === 2 ? lc - 2 : lc + 1;
4951
5827
  onBoundary = oppositeCorners[nextLc] < 0;
4952
5828
  }
4953
- isVertexVisited[vertId] = 1;
4954
5829
  outPointIds[numOutPoints++] = obsFaces[cornerId];
4955
5830
  encodedToCornerMap[numValues] = cornerId;
4956
5831
  vertexToEncodedMap[vertId] = numValues++;
4957
5832
  if (!onBoundary) {
4958
- const nextCornerId2 = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4959
- cornerId = oppositeCorners[nextCornerId2];
5833
+ cornerId = oppositeCorners[nextCornerId];
4960
5834
  faceId = cornerId / 3 | 0;
4961
5835
  continue;
4962
5836
  }
4963
5837
  }
4964
- const nextCornerId = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4965
5838
  const rightCornerId = oppositeCorners[nextCornerId];
4966
- const prevCornerId = cornerId % 3 === 0 ? cornerId + 2 : cornerId - 1;
5839
+ const prevCornerId = cornerId === faceBase ? faceBase + 2 : cornerId - 1;
4967
5840
  const leftCornerId = oppositeCorners[prevCornerId];
4968
5841
  const rightFaceId = rightCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : rightCornerId / 3 | 0;
4969
5842
  const leftFaceId = leftCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : leftCornerId / 3 | 0;
@@ -5193,6 +6066,9 @@ var MeshTraversalSequencer = class {
5193
6066
  _outPointIds;
5194
6067
  _numOutPoints;
5195
6068
  _traversalCache;
6069
+ // The cache entry the last generateSequence resolved to (hit or store);
6070
+ // carries the shared indicesMap.
6071
+ _cacheEntry;
5196
6072
  constructor(mesh, encodingData, traversalCache = null) {
5197
6073
  this._mesh = mesh;
5198
6074
  this._encodingData = encodingData;
@@ -5200,6 +6076,7 @@ var MeshTraversalSequencer = class {
5200
6076
  this._outPointIds = new Int32Array(0);
5201
6077
  this._numOutPoints = 0;
5202
6078
  this._traversalCache = traversalCache;
6079
+ this._cacheEntry = null;
5203
6080
  }
5204
6081
  setTraverser(traverser) {
5205
6082
  this._traverser = traverser;
@@ -5214,6 +6091,7 @@ var MeshTraversalSequencer = class {
5214
6091
  if (cached !== void 0) {
5215
6092
  this._outPointIds = cached.pointIds;
5216
6093
  this._encodingData.adoptTraversalResult(cached.vertexMap, cached.cornerMap, cached.numValues);
6094
+ this._cacheEntry = cached;
5217
6095
  return true;
5218
6096
  }
5219
6097
  }
@@ -5229,12 +6107,15 @@ var MeshTraversalSequencer = class {
5229
6107
  byMethod = /* @__PURE__ */ new Map();
5230
6108
  this._traversalCache.set(cacheKey, byMethod);
5231
6109
  }
5232
- byMethod.set(methodId, {
6110
+ const entry = {
5233
6111
  pointIds: this._outPointIds,
5234
6112
  vertexMap: this._encodingData.vertexToEncodedAttributeValueIndexMap,
5235
6113
  cornerMap: this._encodingData.encodedAttributeValueIndexToCornerMap,
5236
- numValues: this._encodingData.numValues
5237
- });
6114
+ numValues: this._encodingData.numValues,
6115
+ indicesMap: null
6116
+ };
6117
+ byMethod.set(methodId, entry);
6118
+ this._cacheEntry = entry;
5238
6119
  }
5239
6120
  return true;
5240
6121
  }
@@ -5245,6 +6126,11 @@ var MeshTraversalSequencer = class {
5245
6126
  this._outPointIds[this._numOutPoints++] = pointId;
5246
6127
  }
5247
6128
  updatePointToAttributeIndexMapping(attribute) {
6129
+ const entry = this._cacheEntry;
6130
+ if (entry !== null && entry.indicesMap !== null) {
6131
+ attribute.setExplicitMappingShared(entry.indicesMap);
6132
+ return true;
6133
+ }
5248
6134
  const cornerTable = this._traverser.cornerTable();
5249
6135
  const numFaces = this._mesh.numFaces();
5250
6136
  const numPoints = this._mesh.numPoints();
@@ -5266,11 +6152,14 @@ var MeshTraversalSequencer = class {
5266
6152
  }
5267
6153
  indicesMap[pointId] = attEntryId;
5268
6154
  }
6155
+ if (entry !== null) {
6156
+ entry.indicesMap = attribute.indicesMap;
6157
+ }
5269
6158
  return true;
5270
6159
  }
5271
6160
  _generateSequenceInternal() {
5272
6161
  this._numOutPoints = 0;
5273
- this._outPointIds = new Int32Array(this._mesh.numPoints());
6162
+ this._outPointIds = scratchInt32(this._mesh.numPoints());
5274
6163
  this._traverser.onTraversalStart();
5275
6164
  const numFaces = this._traverser.cornerTable().numFaces();
5276
6165
  for (let i = 0; i < numFaces && this._traverser._numVisitedFaces < numFaces; ++i) {
@@ -5461,14 +6350,14 @@ var MeshEdgebreakerDecoderImpl = class {
5461
6350
  this._attributeData = [];
5462
6351
  for (let i = 0; i < numAttributeData; ++i) {
5463
6352
  const ad = new AttributeData();
5464
- ad.attributeSeamCorners = new Int32Array(numFaces * 3);
6353
+ ad.attributeSeamCorners = scratchInt32(numFaces * 3);
5465
6354
  ad.numSeamCorners = 0;
5466
6355
  this._attributeData.push(ad);
5467
6356
  }
5468
6357
  if (!this._cornerTable.reset(numFaces, this._numEncodedVertices + numEncodedSplitSymbols)) {
5469
6358
  return false;
5470
6359
  }
5471
- this._isVertHole = new Uint8Array(this._numEncodedVertices + numEncodedSplitSymbols).fill(1);
6360
+ this._isVertHole = scratchUint8Filled(this._numEncodedVertices + numEncodedSplitSymbols, 1);
5472
6361
  if (this._decodeHoleAndTopologySplitEvents(this._decoder.buffer()) === -1) {
5473
6362
  return false;
5474
6363
  }
@@ -5493,20 +6382,37 @@ var MeshEdgebreakerDecoderImpl = class {
5493
6382
  }
5494
6383
  this._traversalDecoder.done();
5495
6384
  let previousConnectivityData = null;
6385
+ let previousSeamCorners = null;
6386
+ let previousSeamCount = 0;
5496
6387
  for (let i = 0; i < this._attributeData.length; ++i) {
5497
6388
  const connectivityData = this._attributeData[i].connectivityData;
5498
- connectivityData.initEmpty(this._cornerTable);
5499
6389
  const seamCorners = this._attributeData[i].attributeSeamCorners;
5500
6390
  const seamCount = this._attributeData[i].numSeamCorners;
5501
- for (let s = 0; s < seamCount; ++s) {
5502
- connectivityData.addSeamEdge(seamCorners[s]);
6391
+ let sameAsPrevious = previousConnectivityData !== null && seamCount === previousSeamCount;
6392
+ if (sameAsPrevious) {
6393
+ const previous = previousSeamCorners;
6394
+ for (let s = 0; s < seamCount; ++s) {
6395
+ if (seamCorners[s] !== previous[s]) {
6396
+ sameAsPrevious = false;
6397
+ break;
6398
+ }
6399
+ }
5503
6400
  }
5504
- if (connectivityData.hasSameSeams(previousConnectivityData)) {
5505
- connectivityData.adoptVertexRecompute(previousConnectivityData);
5506
- } else if (!connectivityData.recomputeVertices(null, null)) {
5507
- return false;
6401
+ if (sameAsPrevious) {
6402
+ connectivityData.adoptFrom(previousConnectivityData);
6403
+ } else {
6404
+ connectivityData.initEmpty(this._cornerTable);
6405
+ connectivityData.reserveSeamEdges(seamCount);
6406
+ for (let s = 0; s < seamCount; ++s) {
6407
+ connectivityData.addSeamEdge(seamCorners[s]);
6408
+ }
6409
+ if (!connectivityData.recomputeVertices(null, null)) {
6410
+ return false;
6411
+ }
5508
6412
  }
5509
6413
  previousConnectivityData = connectivityData;
6414
+ previousSeamCorners = seamCorners;
6415
+ previousSeamCount = seamCount;
5510
6416
  }
5511
6417
  this._posEncodingData.init(this._cornerTable.numVertices());
5512
6418
  for (let i = 0; i < this._attributeData.length; ++i) {
@@ -5545,39 +6451,27 @@ var MeshEdgebreakerDecoderImpl = class {
5545
6451
  const activeCornerStack = scratchInt32(numSymbols + this._topologySplitData.length + 16);
5546
6452
  let activeCornerStackSize = 0;
5547
6453
  const topologySplitActiveCorners = /* @__PURE__ */ new Map();
6454
+ const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5548
6455
  const invalidVertices = [];
5549
6456
  const removeInvalidVertices = this._attributeData.length === 0;
5550
6457
  let maxNumVertices = this._isVertHole.length;
5551
6458
  let numFacesDecoded = 0;
5552
6459
  const cornerToVertex = this._cornerTable._cornerToVertex;
5553
6460
  const oppositeCorners = this._cornerTable._oppositeCorners;
5554
- const numCorners = this._cornerTable.numCorners();
5555
- const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
5556
- const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
5557
- const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
5558
- const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
5559
- const leftMostCorner = (v) => v < 0 || v >= this._cornerTable._vertexCorners.length ? -1 : this._cornerTable._vertexCorners[v];
5560
- const swingLeft = (c) => {
5561
- const n = next(c);
5562
- const o = opposite(n);
5563
- return o < 0 ? -1 : next(o);
5564
- };
5565
- const swingRight = (c) => {
5566
- const p = prev(c);
5567
- const o = opposite(p);
5568
- return o < 0 ? -1 : prev(o);
5569
- };
5570
6461
  const vc = this._cornerTable;
6462
+ let vertexCorners = vc._vertexCorners;
6463
+ const isVertHole = this._isVertHole;
6464
+ const traversalDecoder = this._traversalDecoder;
5571
6465
  for (let symbolId = 0; symbolId < numSymbols; ++symbolId) {
5572
6466
  const faceIndex = numFacesDecoded++;
5573
6467
  let checkTopologySplit = false;
5574
- const symbol = this._traversalDecoder.decodeSymbol();
6468
+ const symbol = traversalDecoder.decodeSymbol();
5575
6469
  if (symbol === TOPOLOGY_C) {
5576
6470
  if (activeCornerStackSize === 0) return -1;
5577
6471
  const cornerA = activeCornerStack[activeCornerStackSize - 1];
5578
6472
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5579
6473
  const vertexX = cornerToVertex[nA];
5580
- const lmcX = vc._vertexCorners[vertexX];
6474
+ const lmcX = vertexCorners[vertexX];
5581
6475
  const cornerB = lmcX % 3 === 2 ? lmcX - 2 : lmcX + 1;
5582
6476
  if (cornerA === cornerB) return -1;
5583
6477
  if (oppositeCorners[cornerA] !== kInvalidCornerIndex6 || oppositeCorners[cornerB] !== kInvalidCornerIndex6) {
@@ -5596,8 +6490,8 @@ var MeshEdgebreakerDecoderImpl = class {
5596
6490
  cornerToVertex[corner] = vertexX;
5597
6491
  cornerToVertex[corner + 1] = vertBNext;
5598
6492
  cornerToVertex[corner + 2] = vertAPrev;
5599
- vc._vertexCorners[vertAPrev] = corner + 2;
5600
- this._isVertHole[vertexX] = 0;
6493
+ vertexCorners[vertAPrev] = corner + 2;
6494
+ isVertHole[vertexX] = 0;
5601
6495
  activeCornerStack[activeCornerStackSize - 1] = corner;
5602
6496
  } else if (symbol === TOPOLOGY_R || symbol === TOPOLOGY_L) {
5603
6497
  if (activeCornerStackSize === 0) return -1;
@@ -5618,14 +6512,20 @@ var MeshEdgebreakerDecoderImpl = class {
5618
6512
  }
5619
6513
  oppositeCorners[oppCorner] = cornerA;
5620
6514
  oppositeCorners[cornerA] = oppCorner;
5621
- const newVertIndex = this._cornerTable.addNewVertex();
5622
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6515
+ let newVertIndex;
6516
+ if (vc._numVertices < vertexCorners.length) {
6517
+ newVertIndex = vc._numVertices++;
6518
+ } else {
6519
+ newVertIndex = vc.addNewVertex();
6520
+ vertexCorners = vc._vertexCorners;
6521
+ }
6522
+ if (vc._numVertices > maxNumVertices) return -1;
5623
6523
  cornerToVertex[oppCorner] = newVertIndex;
5624
- vc._vertexCorners[newVertIndex] = oppCorner;
6524
+ vertexCorners[newVertIndex] = oppCorner;
5625
6525
  const pA = cornerA % 3 === 0 ? cornerA + 2 : cornerA - 1;
5626
6526
  const vertexR = cornerToVertex[pA];
5627
6527
  cornerToVertex[cornerR] = vertexR;
5628
- vc._vertexCorners[vertexR] = cornerR;
6528
+ vertexCorners[vertexR] = cornerR;
5629
6529
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5630
6530
  cornerToVertex[cornerL] = cornerToVertex[nA];
5631
6531
  activeCornerStack[activeCornerStackSize - 1] = corner;
@@ -5657,11 +6557,11 @@ var MeshEdgebreakerDecoderImpl = class {
5657
6557
  const pB = cornerB % 3 === 0 ? cornerB + 2 : cornerB - 1;
5658
6558
  const vertBPrev = cornerToVertex[pB];
5659
6559
  cornerToVertex[corner + 2] = vertBPrev;
5660
- vc._vertexCorners[vertBPrev] = corner + 2;
6560
+ vertexCorners[vertBPrev] = corner + 2;
5661
6561
  let cornerN = cornerB % 3 === 2 ? cornerB - 2 : cornerB + 1;
5662
6562
  const vertexN = cornerToVertex[cornerN];
5663
- this._traversalDecoder.mergeVertices(vertexP, vertexN);
5664
- vc._vertexCorners[vertexP] = vc._vertexCorners[vertexN];
6563
+ traversalDecoder.mergeVertices(vertexP, vertexN);
6564
+ vertexCorners[vertexP] = vertexCorners[vertexN];
5665
6565
  const firstCorner = cornerN;
5666
6566
  while (cornerN !== kInvalidCornerIndex6) {
5667
6567
  cornerToVertex[cornerN] = vertexP;
@@ -5672,32 +6572,38 @@ var MeshEdgebreakerDecoderImpl = class {
5672
6572
  return -1;
5673
6573
  }
5674
6574
  }
5675
- vc._vertexCorners[vertexN] = -1;
6575
+ vertexCorners[vertexN] = -1;
5676
6576
  if (removeInvalidVertices) {
5677
6577
  invalidVertices.push(vertexN);
5678
6578
  }
5679
6579
  activeCornerStack[activeCornerStackSize - 1] = corner;
5680
6580
  } else if (symbol === TOPOLOGY_E) {
5681
6581
  const corner = 3 * faceIndex;
5682
- const firstVertIndex = this._cornerTable.addNewVertex();
5683
- this._cornerTable.addNewVertex();
5684
- this._cornerTable.addNewVertex();
5685
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6582
+ let firstVertIndex;
6583
+ if (vc._numVertices + 3 <= vertexCorners.length) {
6584
+ firstVertIndex = vc._numVertices;
6585
+ vc._numVertices += 3;
6586
+ } else {
6587
+ firstVertIndex = vc.addNewVertex();
6588
+ vc.addNewVertex();
6589
+ vc.addNewVertex();
6590
+ vertexCorners = vc._vertexCorners;
6591
+ }
6592
+ if (vc._numVertices > maxNumVertices) return -1;
5686
6593
  cornerToVertex[corner] = firstVertIndex;
5687
6594
  cornerToVertex[corner + 1] = firstVertIndex + 1;
5688
6595
  cornerToVertex[corner + 2] = firstVertIndex + 2;
5689
- vc._vertexCorners[firstVertIndex] = corner;
5690
- vc._vertexCorners[firstVertIndex + 1] = corner + 1;
5691
- vc._vertexCorners[firstVertIndex + 2] = corner + 2;
6596
+ vertexCorners[firstVertIndex] = corner;
6597
+ vertexCorners[firstVertIndex + 1] = corner + 1;
6598
+ vertexCorners[firstVertIndex + 2] = corner + 2;
5692
6599
  activeCornerStack[activeCornerStackSize++] = corner;
5693
6600
  checkTopologySplit = true;
5694
6601
  } else {
5695
6602
  return -1;
5696
6603
  }
5697
- this._traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
5698
- if (checkTopologySplit) {
6604
+ traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
6605
+ if (checkTopologySplit && this._topologySplitData.length > 0) {
5699
6606
  const encoderSymbolId = numSymbols - symbolId - 1;
5700
- const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5701
6607
  while (this._isTopologySplit(encoderSymbolId, splitResult)) {
5702
6608
  if (splitResult.encoderSplitSymbolId < 0) return -1;
5703
6609
  const actTopCorner = activeCornerStack[activeCornerStackSize - 1];
@@ -5712,15 +6618,39 @@ var MeshEdgebreakerDecoderImpl = class {
5712
6618
  }
5713
6619
  }
5714
6620
  }
5715
- if (this._cornerTable.numVertices() > maxNumVertices) {
6621
+ if (vc._numVertices > maxNumVertices) {
6622
+ return -1;
6623
+ }
6624
+ numFacesDecoded = this._decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded);
6625
+ if (numFacesDecoded === -1) {
6626
+ return -1;
6627
+ }
6628
+ if (numFacesDecoded !== this._cornerTable.numFaces()) {
5716
6629
  return -1;
5717
6630
  }
6631
+ return this._removeInvalidVertices(invalidVertices);
6632
+ }
6633
+ // Connects the remaining active-stack corners to newly decoded start faces.
6634
+ // Returns the updated decoded-face count, or -1 on malformed input.
6635
+ _decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded) {
6636
+ const ct = this._cornerTable;
6637
+ const cornerToVertex = ct._cornerToVertex;
6638
+ const oppositeCorners = ct._oppositeCorners;
6639
+ const vertexCorners = ct._vertexCorners;
6640
+ const isVertHole = this._isVertHole;
6641
+ const traversalDecoder = this._traversalDecoder;
6642
+ const numCorners = ct.numCorners();
6643
+ const numFaces = ct.numFaces();
6644
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6645
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6646
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6647
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
5718
6648
  while (activeCornerStackSize > 0) {
5719
6649
  const corner = activeCornerStack[activeCornerStackSize - 1];
5720
6650
  activeCornerStackSize--;
5721
- const interiorFace = this._traversalDecoder.decodeStartFaceConfiguration();
6651
+ const interiorFace = traversalDecoder.decodeStartFaceConfiguration();
5722
6652
  if (interiorFace) {
5723
- if (numFacesDecoded >= this._cornerTable.numFaces()) {
6653
+ if (numFacesDecoded >= numFaces) {
5724
6654
  return -1;
5725
6655
  }
5726
6656
  const cornerA = corner;
@@ -5746,9 +6676,9 @@ var MeshEdgebreakerDecoderImpl = class {
5746
6676
  cornerToVertex[newCorner] = vertX;
5747
6677
  cornerToVertex[newCorner + 1] = vertP;
5748
6678
  cornerToVertex[newCorner + 2] = vertN;
5749
- this._isVertHole[vertX] = 0;
5750
- this._isVertHole[vertP] = 0;
5751
- this._isVertHole[vertN] = 0;
6679
+ isVertHole[vertX] = 0;
6680
+ isVertHole[vertP] = 0;
6681
+ isVertHole[vertN] = 0;
5752
6682
  this._initFaceConfigurations.push(true);
5753
6683
  this._initCorners.push(newCorner);
5754
6684
  } else {
@@ -5756,10 +6686,34 @@ var MeshEdgebreakerDecoderImpl = class {
5756
6686
  this._initCorners.push(corner);
5757
6687
  }
5758
6688
  }
5759
- if (numFacesDecoded !== this._cornerTable.numFaces()) {
5760
- return -1;
5761
- }
5762
- let numVertices = this._cornerTable.numVertices();
6689
+ return numFacesDecoded;
6690
+ }
6691
+ // Removes invalid (isolated) vertices by swapping them with the last valid
6692
+ // vertex in the table, matching C++ mesh_edgebreaker_decoder_impl.cc (the
6693
+ // forward iteration order matters). Returns the final vertex count, or -1.
6694
+ _removeInvalidVertices(invalidVertices) {
6695
+ const ct = this._cornerTable;
6696
+ const cornerToVertex = ct._cornerToVertex;
6697
+ const oppositeCorners = ct._oppositeCorners;
6698
+ const vertexCorners = ct._vertexCorners;
6699
+ const isVertHole = this._isVertHole;
6700
+ const numCorners = ct.numCorners();
6701
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6702
+ const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
6703
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6704
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6705
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
6706
+ const swingLeft = (c) => {
6707
+ const n = next(c);
6708
+ const o = opposite(n);
6709
+ return o < 0 ? -1 : next(o);
6710
+ };
6711
+ const swingRight = (c) => {
6712
+ const p = prev(c);
6713
+ const o = opposite(p);
6714
+ return o < 0 ? -1 : prev(o);
6715
+ };
6716
+ let numVertices = ct.numVertices();
5763
6717
  for (let ivIdx = 0; ivIdx < invalidVertices.length; ++ivIdx) {
5764
6718
  const invalidVert = invalidVertices[ivIdx];
5765
6719
  let srcVert = numVertices - 1;
@@ -5789,10 +6743,10 @@ var MeshEdgebreakerDecoderImpl = class {
5789
6743
  cid = swingRight(cid);
5790
6744
  }
5791
6745
  }
5792
- this._cornerTable._vertexCorners[invalidVert] = leftMostCorner(srcVert);
5793
- this._cornerTable._vertexCorners[srcVert] = -1;
5794
- this._isVertHole[invalidVert] = this._isVertHole[srcVert];
5795
- this._isVertHole[srcVert] = 0;
6746
+ vertexCorners[invalidVert] = leftMostCorner(srcVert);
6747
+ vertexCorners[srcVert] = -1;
6748
+ isVertHole[invalidVert] = isVertHole[srcVert];
6749
+ isVertHole[srcVert] = 0;
5796
6750
  numVertices--;
5797
6751
  }
5798
6752
  return numVertices;
@@ -5835,14 +6789,55 @@ var MeshEdgebreakerDecoderImpl = class {
5835
6789
  // per-corner decodeNextBit work. Within each face the three corners are
5836
6790
  // visited in encoder edge order [base, next, prev] = [c, c+1, c+2] (the
5837
6791
  // caller always starts a face at its base corner, so next/prev need no wrap).
6792
+ //
6793
+ // The face comparison the C++ makes -- floor(oppCorner/3) >= floor(cc/3) for
6794
+ // the face's base corner -- is just `oppCorner >= faceBaseCorner`, since the
6795
+ // base corner is a multiple of 3. That removes the per-corner division; the
6796
+ // invalid-corner case (-1) is still handled by the branch above it.
5838
6797
  _decodeAttributeConnectivities() {
5839
6798
  const oppositeCorners = this._cornerTable.oppositeCornerArray();
5840
6799
  const attributeData = this._attributeData;
5841
6800
  const numAttrData = attributeData.length;
5842
6801
  const connectivityDecoders = this._traversalDecoder._attributeConnectivityDecoders;
5843
6802
  const numCorners = this._cornerTable.numCorners();
6803
+ if (numAttrData === 1) {
6804
+ const ad = attributeData[0];
6805
+ const seamCorners = ad.attributeSeamCorners;
6806
+ let numSeamCorners = ad.numSeamCorners;
6807
+ const decoder = connectivityDecoders[0];
6808
+ const ans = decoder.ansDecoder_;
6809
+ const p = decoder.p_;
6810
+ const buf = ans.buf;
6811
+ const bufStart = ans.bufStart;
6812
+ let state = ans.state;
6813
+ let bufOffset = ans.bufOffset;
6814
+ for (let corner = 0; corner < numCorners; corner += 3) {
6815
+ for (let k = 0; k < 3; ++k) {
6816
+ const cc = corner + k;
6817
+ const oppCorner = oppositeCorners[cc];
6818
+ if (oppCorner === kInvalidCornerIndex6) {
6819
+ seamCorners[numSeamCorners++] = cc;
6820
+ } else if (oppCorner >= corner) {
6821
+ if (state < ANS_L_BASE && bufOffset > bufStart) {
6822
+ state = state << 8 | buf[--bufOffset];
6823
+ }
6824
+ const rem = state & 255;
6825
+ const xn = (state >>> 8) * p;
6826
+ if (rem < p) {
6827
+ state = xn + rem;
6828
+ seamCorners[numSeamCorners++] = cc;
6829
+ } else {
6830
+ state = state - xn - p;
6831
+ }
6832
+ }
6833
+ }
6834
+ }
6835
+ ans.state = state;
6836
+ ans.bufOffset = bufOffset;
6837
+ ad.numSeamCorners = numSeamCorners;
6838
+ return;
6839
+ }
5844
6840
  for (let corner = 0; corner < numCorners; corner += 3) {
5845
- const srcFaceId = corner / 3 | 0;
5846
6841
  for (let k = 0; k < 3; ++k) {
5847
6842
  const cc = corner + k;
5848
6843
  const oppCorner = oppositeCorners[cc];
@@ -5851,7 +6846,7 @@ var MeshEdgebreakerDecoderImpl = class {
5851
6846
  const ad = attributeData[i];
5852
6847
  ad.attributeSeamCorners[ad.numSeamCorners++] = cc;
5853
6848
  }
5854
- } else if ((oppCorner / 3 | 0) >= srcFaceId) {
6849
+ } else if (oppCorner >= corner) {
5855
6850
  for (let i = 0; i < numAttrData; ++i) {
5856
6851
  if (connectivityDecoders[i].decodeNextBit()) {
5857
6852
  const ad = attributeData[i];
@@ -5882,10 +6877,10 @@ var MeshEdgebreakerDecoderImpl = class {
5882
6877
  const attributeData = this._attributeData;
5883
6878
  const numAttrData = attributeData.length;
5884
6879
  let numPoints = 0;
5885
- const cornerToPointMap = new Int32Array(ct.numCorners());
6880
+ const cornerToPointMap = scratchInt32(ct.numCorners());
5886
6881
  const numVertices = ct.numVertices();
5887
6882
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
5888
- const baseOpp = ct.oppositeCornerArray();
6883
+ const swingRight = ct.swingRightArray();
5889
6884
  const _baseCornerToVertex = ct.cornerToVertexArray();
5890
6885
  const isVertHole = this._isVertHole;
5891
6886
  const attCornerToVertex = new Array(numAttrData);
@@ -5899,7 +6894,7 @@ var MeshEdgebreakerDecoderImpl = class {
5899
6894
  if (numAttrData === 1) {
5900
6895
  anyAttVertexOnSeam = attVertexOnSeam[0];
5901
6896
  } else {
5902
- anyAttVertexOnSeam = new Uint8Array(numVertices);
6897
+ anyAttVertexOnSeam = scratchUint8Zeroed(numVertices);
5903
6898
  for (let i = 0; i < numAttrData; ++i) {
5904
6899
  const attSeam = attVertexOnSeam[i];
5905
6900
  for (let v = 0; v < numVertices; ++v) {
@@ -5917,37 +6912,24 @@ var MeshEdgebreakerDecoderImpl = class {
5917
6912
  const initialC = c;
5918
6913
  const pointId = numPoints++;
5919
6914
  cornerToPointMap[initialC] = pointId;
5920
- let rem = initialC % 3;
5921
- let pv = rem === 0 ? initialC + 2 : initialC - 1;
5922
- let opp = baseOpp[pv];
5923
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6915
+ c = swingRight[initialC];
5924
6916
  while (c !== kInvalidCornerIndex6 && c !== initialC) {
5925
6917
  cornerToPointMap[c] = pointId;
5926
- rem = c % 3;
5927
- pv = rem === 0 ? c + 2 : c - 1;
5928
- opp = baseOpp[pv];
5929
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6918
+ c = swingRight[c];
5930
6919
  }
5931
6920
  } else {
5932
6921
  let deduplicationFirstCorner = c;
5933
- let rem, pv, opp;
5934
6922
  if (!isVertHole[v]) {
5935
6923
  if (numAttrData === 1) {
5936
6924
  const vertId = singleAttC2V[c];
5937
- rem = c % 3;
5938
- pv = rem === 0 ? c + 2 : c - 1;
5939
- opp = baseOpp[pv];
5940
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6925
+ let actC = swingRight[c];
5941
6926
  while (actC !== c) {
5942
6927
  if (actC === kInvalidCornerIndex6) return false;
5943
6928
  if (singleAttC2V[actC] !== vertId) {
5944
6929
  deduplicationFirstCorner = actC;
5945
6930
  break;
5946
6931
  }
5947
- rem = actC % 3;
5948
- pv = rem === 0 ? actC + 2 : actC - 1;
5949
- opp = baseOpp[pv];
5950
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6932
+ actC = swingRight[actC];
5951
6933
  }
5952
6934
  } else {
5953
6935
  for (let i = 0; i < numAttrData; ++i) {
@@ -5956,10 +6938,7 @@ var MeshEdgebreakerDecoderImpl = class {
5956
6938
  }
5957
6939
  const attC2V = attCornerToVertex[i];
5958
6940
  const vertId = attC2V[c];
5959
- rem = c % 3;
5960
- pv = rem === 0 ? c + 2 : c - 1;
5961
- opp = baseOpp[pv];
5962
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6941
+ let actC = swingRight[c];
5963
6942
  let seamFound = false;
5964
6943
  while (actC !== c) {
5965
6944
  if (actC === kInvalidCornerIndex6) return false;
@@ -5968,10 +6947,7 @@ var MeshEdgebreakerDecoderImpl = class {
5968
6947
  seamFound = true;
5969
6948
  break;
5970
6949
  }
5971
- rem = actC % 3;
5972
- pv = rem === 0 ? actC + 2 : actC - 1;
5973
- opp = baseOpp[pv];
5974
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6950
+ actC = swingRight[actC];
5975
6951
  }
5976
6952
  if (seamFound) break;
5977
6953
  }
@@ -5980,10 +6956,7 @@ var MeshEdgebreakerDecoderImpl = class {
5980
6956
  c = deduplicationFirstCorner;
5981
6957
  cornerToPointMap[c] = numPoints++;
5982
6958
  let prevC = c;
5983
- rem = c % 3;
5984
- pv = rem === 0 ? c + 2 : c - 1;
5985
- opp = baseOpp[pv];
5986
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6959
+ c = swingRight[c];
5987
6960
  while (c !== kInvalidCornerIndex6 && c !== deduplicationFirstCorner) {
5988
6961
  let attributeSeam;
5989
6962
  if (numAttrData === 1) {
@@ -6004,10 +6977,7 @@ var MeshEdgebreakerDecoderImpl = class {
6004
6977
  cornerToPointMap[c] = cornerToPointMap[prevC];
6005
6978
  }
6006
6979
  prevC = c;
6007
- rem = c % 3;
6008
- pv = rem === 0 ? c + 2 : c - 1;
6009
- opp = baseOpp[pv];
6010
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6980
+ c = swingRight[c];
6011
6981
  }
6012
6982
  }
6013
6983
  }
@@ -6033,8 +7003,8 @@ var MeshAttributeIndicesEncodingData = class {
6033
7003
  this._numValues = 0;
6034
7004
  }
6035
7005
  init(numVertices) {
6036
- this._vertexToEncodedAttributeValueIndexMap = new Int32Array(numVertices);
6037
- this._encodedAttributeValueIndexToCornerMap = new Int32Array(numVertices);
7006
+ this._vertexToEncodedAttributeValueIndexMap = scratchInt32Filled(numVertices, -1);
7007
+ this._encodedAttributeValueIndexToCornerMap = scratchInt32(numVertices);
6038
7008
  this._numValues = 0;
6039
7009
  }
6040
7010
  // Adopts a traversal result from an identical corner table, avoiding a
@@ -6084,6 +7054,8 @@ var CornerTable = class {
6084
7054
  // corner -> opposite corner
6085
7055
  _vertexCorners;
6086
7056
  // vertex -> left-most corner
7057
+ _swingRight;
7058
+ // corner -> next corner around its vertex, CW
6087
7059
  constructor() {
6088
7060
  this._numFaces = 0;
6089
7061
  this._numCorners = 0;
@@ -6091,16 +7063,42 @@ var CornerTable = class {
6091
7063
  this._cornerToVertex = null;
6092
7064
  this._oppositeCorners = null;
6093
7065
  this._vertexCorners = null;
7066
+ this._swingRight = null;
6094
7067
  }
6095
7068
  reset(numFaces, numVertices) {
6096
7069
  this._numFaces = numFaces;
6097
7070
  this._numCorners = numFaces * 3;
6098
7071
  this._numVertices = 0;
6099
- this._cornerToVertex = new Int32Array(this._numCorners).fill(-1);
6100
- this._oppositeCorners = new Int32Array(this._numCorners).fill(-1);
6101
- this._vertexCorners = new Int32Array(numVertices).fill(-1);
7072
+ this._cornerToVertex = scratchInt32Filled(this._numCorners, -1);
7073
+ this._oppositeCorners = scratchInt32Filled(this._numCorners, -1);
7074
+ this._vertexCorners = scratchInt32Filled(numVertices, -1);
7075
+ this._swingRight = null;
6102
7076
  return true;
6103
7077
  }
7078
+ // swingRight(c) = previous(opposite(previous(c))) for every corner. Walking
7079
+ // the corner ring of a vertex is the inner loop of both the attribute-vertex
7080
+ // recompute and the point assignment, and each of those passes otherwise
7081
+ // pays two modulus-by-3 chains per step on top of the opposite lookup. Built
7082
+ // lazily -- callers only reach it once connectivity is final -- from
7083
+ // decode-scoped scratch, and dropped by reset().
7084
+ swingRightArray() {
7085
+ let table = this._swingRight;
7086
+ if (table === null) {
7087
+ const numCorners = this._numCorners;
7088
+ const opposite = this._oppositeCorners;
7089
+ table = scratchInt32(numCorners);
7090
+ for (let c = 0; c < numCorners; c += 3) {
7091
+ let o = opposite[c + 2];
7092
+ table[c] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7093
+ o = opposite[c];
7094
+ table[c + 1] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7095
+ o = opposite[c + 1];
7096
+ table[c + 2] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
7097
+ }
7098
+ this._swingRight = table;
7099
+ }
7100
+ return table;
7101
+ }
6104
7102
  numFaces() {
6105
7103
  return this._numFaces;
6106
7104
  }
@@ -6144,8 +7142,7 @@ var CornerTable = class {
6144
7142
  this._numVertices++;
6145
7143
  if (newVertex >= this._vertexCorners.length) {
6146
7144
  const newCapacity = Math.max(newVertex + 1, this._vertexCorners.length * 2, 64);
6147
- const newArr = new Int32Array(newCapacity);
6148
- newArr.fill(-1);
7145
+ const newArr = scratchInt32Filled(newCapacity, -1);
6149
7146
  newArr.set(this._vertexCorners);
6150
7147
  this._vertexCorners = newArr;
6151
7148
  }
@@ -6176,6 +7173,10 @@ var MeshEdgebreakerTraversalDecoder = class {
6176
7173
  _attributeConnectivityDecoders;
6177
7174
  _numAttributeData;
6178
7175
  _decoderImpl;
7176
+ // _symbolBuffer's bit cursor, captured once bit decoding starts: decodeSymbol
7177
+ // runs per decoded face and would otherwise reach it through two property
7178
+ // loads and a method call per read.
7179
+ _symbolBits;
6179
7180
  constructor() {
6180
7181
  this._buffer = new DecoderBuffer();
6181
7182
  this._symbolBuffer = new DecoderBuffer();
@@ -6183,6 +7184,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6183
7184
  this._attributeConnectivityDecoders = null;
6184
7185
  this._numAttributeData = 0;
6185
7186
  this._decoderImpl = null;
7187
+ this._symbolBits = null;
6186
7188
  }
6187
7189
  init(decoder) {
6188
7190
  this._decoderImpl = decoder;
@@ -6217,6 +7219,21 @@ var MeshEdgebreakerTraversalDecoder = class {
6217
7219
  return this._startFaceDecoder.decodeNextBit() ? true : false;
6218
7220
  }
6219
7221
  decodeSymbol() {
7222
+ const bd = this._symbolBits;
7223
+ if (bd !== null) {
7224
+ const buf = bd._bitBuffer;
7225
+ const off = bd._bitOffset;
7226
+ const byteOffset = off >> 3;
7227
+ if (byteOffset + 4 < bd._byteLength) {
7228
+ const bits = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> (off & 7) & 7;
7229
+ if ((bits & 1) === TOPOLOGY_C) {
7230
+ bd._bitOffset = off + 1;
7231
+ return TOPOLOGY_C;
7232
+ }
7233
+ bd._bitOffset = off + 3;
7234
+ return bits;
7235
+ }
7236
+ }
6220
7237
  let symbol = this._symbolBuffer.decodeLeastSignificantBits32(1);
6221
7238
  if (symbol === TOPOLOGY_C) {
6222
7239
  return symbol;
@@ -6246,6 +7263,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6246
7263
  if (traversalSize === void 0) {
6247
7264
  return false;
6248
7265
  }
7266
+ this._symbolBits = this._symbolBuffer._bitDecoder;
6249
7267
  this._buffer.init(
6250
7268
  this._symbolBuffer.dataHead,
6251
7269
  this._symbolBuffer.remainingSize,
@@ -6398,7 +7416,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6398
7416
  _maxValence;
6399
7417
  _vertexValences;
6400
7418
  _contextSymbols;
7419
+ // Int32Array, not number[]: read and written once per decoded symbol.
6401
7420
  _contextCounters;
7421
+ // corner -> vertex of _cornerTable, cached at init(); the array is created
7422
+ // once by CornerTable.reset() before the traversal decoder is initialized and
7423
+ // never replaced, so the per-symbol hot path can read it without two property
7424
+ // loads.
7425
+ _cornerToVertex;
6402
7426
  constructor() {
6403
7427
  super();
6404
7428
  this._cornerTable = null;
@@ -6409,11 +7433,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6409
7433
  this._maxValence = 7;
6410
7434
  this._vertexValences = new Int32Array(0);
6411
7435
  this._contextSymbols = [];
6412
- this._contextCounters = [];
7436
+ this._contextCounters = new Int32Array(0);
7437
+ this._cornerToVertex = new Int32Array(0);
6413
7438
  }
6414
7439
  init(decoder) {
6415
7440
  super.init(decoder);
6416
7441
  this._cornerTable = decoder.getCornerTable();
7442
+ this._cornerToVertex = this._cornerTable._cornerToVertex;
6417
7443
  }
6418
7444
  setNumEncodedVertices(numVertices) {
6419
7445
  this._numVertices = numVertices;
@@ -6431,10 +7457,11 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6431
7457
  if (this._numVertices < 0) {
6432
7458
  return false;
6433
7459
  }
6434
- this._vertexValences = new Int32Array(this._numVertices);
7460
+ this._vertexValences = scratchInt32Filled(this._numVertices, 0);
6435
7461
  const numUniqueValences = this._maxValence - this._minValence + 1;
6436
7462
  this._contextSymbols = new Array(numUniqueValences);
6437
- this._contextCounters = new Array(numUniqueValences);
7463
+ this._contextCounters = new Int32Array(numUniqueValences);
7464
+ const pending = [];
6438
7465
  for (let i = 0; i < numUniqueValences; ++i) {
6439
7466
  const numSymbols = decodeVarint(outBuffer);
6440
7467
  if (numSymbols === void 0) {
@@ -6444,16 +7471,73 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6444
7471
  return false;
6445
7472
  }
6446
7473
  if (numSymbols > 0) {
6447
- this._contextSymbols[i] = new Uint32Array(numSymbols);
6448
- if (!decodeSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7474
+ this._contextSymbols[i] = scratchUint32(numSymbols);
7475
+ const scheme = outBuffer.decodeUint8();
7476
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
7477
+ if (!decodeTaggedSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7478
+ return false;
7479
+ }
7480
+ this._contextCounters[i] = numSymbols;
7481
+ continue;
7482
+ }
7483
+ if (scheme !== SymbolCodingMethod.SYMBOL_CODING_RAW) {
6449
7484
  return false;
6450
7485
  }
7486
+ const maxBitLength = outBuffer.decodeUint8();
7487
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
7488
+ return false;
7489
+ }
7490
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
7491
+ if (!decoder.create(outBuffer, numSymbols)) {
7492
+ return false;
7493
+ }
7494
+ if (decoder.numSymbols === 0) {
7495
+ return false;
7496
+ }
7497
+ if (!decoder.startDecoding(outBuffer)) {
7498
+ return false;
7499
+ }
7500
+ pending.push({ decoder, out: this._contextSymbols[i], count: numSymbols });
6451
7501
  this._contextCounters[i] = numSymbols;
6452
7502
  } else {
6453
7503
  this._contextSymbols[i] = new Uint32Array(0);
6454
7504
  this._contextCounters[i] = 0;
6455
7505
  }
6456
7506
  }
7507
+ const lockstep = pending.filter((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7508
+ let p = 0;
7509
+ while (lockstep.length - p >= 3) {
7510
+ const a = lockstep[p];
7511
+ const b = lockstep[p + 1];
7512
+ const c = lockstep[p + 2];
7513
+ ransDecodeSymbolsTrioU8(
7514
+ a.decoder.ans_,
7515
+ a.out,
7516
+ a.count,
7517
+ b.decoder.ans_,
7518
+ b.out,
7519
+ b.count,
7520
+ c.decoder.ans_,
7521
+ c.out,
7522
+ c.count
7523
+ );
7524
+ p += 3;
7525
+ }
7526
+ if (lockstep.length - p === 2) {
7527
+ const a = lockstep[p];
7528
+ const b = lockstep[p + 1];
7529
+ ransDecodeSymbolsPairU8(a.decoder.ans_, a.out, a.count, b.decoder.ans_, b.out, b.count);
7530
+ p += 2;
7531
+ }
7532
+ for (; p < lockstep.length; ++p) {
7533
+ lockstep[p].decoder.ans_.decodeSymbols(lockstep[p].out, lockstep[p].count);
7534
+ }
7535
+ for (const entry of pending) {
7536
+ if (!(entry.decoder.ans_.lutTable instanceof Uint8Array)) {
7537
+ entry.decoder.ans_.decodeSymbols(entry.out, entry.count);
7538
+ }
7539
+ entry.decoder.endDecoding();
7540
+ }
6457
7541
  return true;
6458
7542
  }
6459
7543
  decodeSymbol() {
@@ -6473,7 +7557,7 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6473
7557
  return this._lastSymbol;
6474
7558
  }
6475
7559
  newActiveCornerReached(corner) {
6476
- const cornerToVertex = this._cornerTable._cornerToVertex;
7560
+ const cornerToVertex = this._cornerToVertex;
6477
7561
  const valences = this._vertexValences;
6478
7562
  const next = corner % 3 === 2 ? corner - 2 : corner + 1;
6479
7563
  const prev = corner % 3 === 0 ? corner + 2 : corner - 1;