minidraco 0.3.0 → 0.4.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,85 +1272,614 @@ 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
+ function memGetLe16(buf, offset) {
1280
+ return buf[offset] | buf[offset + 1] << 8;
1281
+ }
1282
+ function memGetLe24(buf, offset) {
1283
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1284
+ }
1285
+ function memGetLe32(buf, offset) {
1286
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1287
+ }
1288
+ var AnsDecoder = class {
1289
+ buf;
1290
+ bufOffset;
1291
+ // First valid byte of this decoder's slice within buf: init is passed
1292
+ // absolute offsets into the source buffer to avoid a subarray allocation.
1293
+ bufStart;
1294
+ state;
1206
1295
  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 = [];
1296
+ this.buf = null;
1297
+ this.bufOffset = 0;
1298
+ this.bufStart = 0;
1299
+ this.state = 0;
1214
1300
  }
1215
- getGeometryType() {
1216
- return EncodedGeometryType.POINT_CLOUD;
1301
+ };
1302
+ function ansReadInit(ans, buf, offset, base = 0) {
1303
+ if (offset - base < 1) {
1304
+ return 1;
1217
1305
  }
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);
1224
- }
1225
- for (let i = 0; i < 5; i++) {
1226
- outHeader.dracoString[i] = bytes[i];
1227
- }
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.");
1306
+ ans.buf = buf;
1307
+ ans.bufStart = base;
1308
+ const x = buf[offset - 1] >> 6;
1309
+ if (x === 0) {
1310
+ ans.bufOffset = offset - 1;
1311
+ ans.state = buf[offset - 1] & 63;
1312
+ } else if (x === 1) {
1313
+ if (offset - base < 2) {
1314
+ return 1;
1231
1315
  }
1232
- const versionMajor = buffer.decodeUint8();
1233
- if (versionMajor === void 0) {
1234
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1316
+ ans.bufOffset = offset - 2;
1317
+ ans.state = memGetLe16(buf, offset - 2) & 16383;
1318
+ } else if (x === 2) {
1319
+ if (offset - base < 3) {
1320
+ return 1;
1235
1321
  }
1236
- outHeader.versionMajor = versionMajor;
1237
- const versionMinor = buffer.decodeUint8();
1238
- if (versionMinor === void 0) {
1239
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1322
+ ans.bufOffset = offset - 3;
1323
+ ans.state = memGetLe24(buf, offset - 3) & 4194303;
1324
+ } else {
1325
+ return 1;
1326
+ }
1327
+ ans.state += ANS_L_BASE;
1328
+ if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1329
+ return 1;
1330
+ }
1331
+ return 0;
1332
+ }
1333
+ function ansReadEnd(ans) {
1334
+ return ans.state === ANS_L_BASE;
1335
+ }
1336
+ var tablePool = [];
1337
+ var acquirePooled = (Ctor, size) => {
1338
+ for (let i = tablePool.length - 1; i >= 0; --i) {
1339
+ const buf = tablePool[i];
1340
+ if (buf.constructor === Ctor && buf.length >= size) {
1341
+ tablePool[i] = tablePool[tablePool.length - 1];
1342
+ tablePool.pop();
1343
+ return buf;
1240
1344
  }
1241
- outHeader.versionMinor = versionMinor;
1242
- const encoderType = buffer.decodeUint8();
1243
- if (encoderType === void 0) {
1244
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1345
+ }
1346
+ return new Ctor(size);
1347
+ };
1348
+ var RAnsDecoder = class {
1349
+ ransPrecisionBits;
1350
+ ransPrecision;
1351
+ ransPrecisionMask;
1352
+ lRansBase;
1353
+ lutTable;
1354
+ probTable;
1355
+ cumProbTable;
1356
+ buf;
1357
+ bufOffset;
1358
+ // First valid byte of this decoder's slice within buf (absolute offsets,
1359
+ // see AnsDecoder.bufStart).
1360
+ bufStart;
1361
+ state;
1362
+ constructor(ransPrecisionBits) {
1363
+ this.ransPrecisionBits = ransPrecisionBits;
1364
+ this.ransPrecision = 1 << ransPrecisionBits;
1365
+ this.ransPrecisionMask = this.ransPrecision - 1;
1366
+ this.lRansBase = this.ransPrecision * 4;
1367
+ this.lutTable = null;
1368
+ this.probTable = null;
1369
+ this.cumProbTable = null;
1370
+ this.buf = null;
1371
+ this.bufOffset = 0;
1372
+ this.bufStart = 0;
1373
+ this.state = 0;
1374
+ }
1375
+ // offset is the absolute end of the encoded bytes within buf and base the
1376
+ // absolute start (offset - base = encoded length). Passing the source
1377
+ // buffer with absolute offsets avoids a subarray allocation per init.
1378
+ // Returns 0 on success, non-zero on error.
1379
+ readInit(buf, offset, base = 0) {
1380
+ if (offset - base < 1) {
1381
+ return 1;
1245
1382
  }
1246
- outHeader.encoderType = encoderType;
1247
- const encoderMethod = buffer.decodeUint8();
1248
- if (encoderMethod === void 0) {
1249
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1383
+ this.buf = buf;
1384
+ this.bufStart = base;
1385
+ const x = buf[offset - 1] >> 6;
1386
+ if (x === 0) {
1387
+ this.bufOffset = offset - 1;
1388
+ this.state = buf[offset - 1] & 63;
1389
+ } else if (x === 1) {
1390
+ if (offset - base < 2) {
1391
+ return 1;
1392
+ }
1393
+ this.bufOffset = offset - 2;
1394
+ this.state = memGetLe16(buf, offset - 2) & 16383;
1395
+ } else if (x === 2) {
1396
+ if (offset - base < 3) {
1397
+ return 1;
1398
+ }
1399
+ this.bufOffset = offset - 3;
1400
+ this.state = memGetLe24(buf, offset - 3) & 4194303;
1401
+ } else if (x === 3) {
1402
+ if (offset - base < 4) {
1403
+ return 1;
1404
+ }
1405
+ this.bufOffset = offset - 4;
1406
+ this.state = memGetLe32(buf, offset - 4) & 1073741823;
1407
+ } else {
1408
+ return 1;
1250
1409
  }
1251
- outHeader.encoderMethod = encoderMethod;
1252
- const flags = buffer.decodeUint16();
1253
- if (flags === void 0) {
1254
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1410
+ this.state += this.lRansBase;
1411
+ if (this.state >= this.lRansBase * ANS_IO_BASE) {
1412
+ return 1;
1255
1413
  }
1256
- outHeader.flags = flags;
1257
- return okStatus();
1414
+ return 0;
1258
1415
  }
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;
1416
+ readEnd() {
1417
+ if (this.lutTable !== null) {
1418
+ tablePool.push(this.lutTable);
1419
+ this.lutTable = null;
1268
1420
  }
1269
- if (header.encoderType !== this.getGeometryType()) {
1270
- return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1421
+ if (this.probTable !== null) {
1422
+ tablePool.push(this.probTable);
1423
+ this.probTable = null;
1271
1424
  }
1272
- this._versionMajor = header.versionMajor;
1273
- this._versionMinor = header.versionMinor;
1274
- const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1275
- const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
1425
+ if (this.cumProbTable !== null) {
1426
+ tablePool.push(this.cumProbTable);
1427
+ this.cumProbTable = null;
1428
+ }
1429
+ return this.state === this.lRansBase;
1430
+ }
1431
+ ransRead() {
1432
+ const buf = this.buf;
1433
+ const lRansBase = this.lRansBase;
1434
+ let state = this.state;
1435
+ let bufOffset = this.bufOffset;
1436
+ const bufStart = this.bufStart;
1437
+ while (state < lRansBase && bufOffset > bufStart) {
1438
+ state = state << 8 | buf[--bufOffset];
1439
+ }
1440
+ const quo = state >>> this.ransPrecisionBits;
1441
+ const rem = state & this.ransPrecisionMask;
1442
+ const symbol = this.lutTable[rem];
1443
+ this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
1444
+ this.bufOffset = bufOffset;
1445
+ return symbol;
1446
+ }
1447
+ // Batch ransRead() into out[0..count): all fields hoisted to locals, state
1448
+ // written back once. Removes per-symbol property reads and call indirection.
1449
+ // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
1450
+ // which would make the hot lutTable[rem] access site polymorphic — dispatch
1451
+ // once here so each loop body stays monomorphic on its concrete type. The
1452
+ // three bodies are intentionally identical copies.
1453
+ decodeSymbols(out, count) {
1454
+ const lutTable = this.lutTable;
1455
+ if (lutTable instanceof Uint8Array) {
1456
+ this._decodeSymbolsU8(out, count, lutTable);
1457
+ } else if (lutTable instanceof Uint16Array) {
1458
+ this._decodeSymbolsU16(out, count, lutTable);
1459
+ } else {
1460
+ this._decodeSymbolsU32(out, count, lutTable);
1461
+ }
1462
+ }
1463
+ _decodeSymbolsU8(out, count, lutTable) {
1464
+ const buf = this.buf;
1465
+ const lRansBase = this.lRansBase;
1466
+ const ransPrecisionBits = this.ransPrecisionBits;
1467
+ const ransPrecisionMask = this.ransPrecisionMask;
1468
+ const probTable = this.probTable;
1469
+ const cumProbTable = this.cumProbTable;
1470
+ let state = this.state;
1471
+ let bufOffset = this.bufOffset;
1472
+ const bufStart = this.bufStart;
1473
+ for (let i = 0; i < count; ++i) {
1474
+ while (state < lRansBase && bufOffset > bufStart) {
1475
+ state = state << 8 | buf[--bufOffset];
1476
+ }
1477
+ const rem = state & ransPrecisionMask;
1478
+ const symbol = lutTable[rem];
1479
+ out[i] = symbol;
1480
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1481
+ }
1482
+ this.state = state;
1483
+ this.bufOffset = bufOffset;
1484
+ }
1485
+ _decodeSymbolsU16(out, count, lutTable) {
1486
+ const buf = this.buf;
1487
+ const lRansBase = this.lRansBase;
1488
+ const ransPrecisionBits = this.ransPrecisionBits;
1489
+ const ransPrecisionMask = this.ransPrecisionMask;
1490
+ const probTable = this.probTable;
1491
+ const cumProbTable = this.cumProbTable;
1492
+ let state = this.state;
1493
+ let bufOffset = this.bufOffset;
1494
+ const bufStart = this.bufStart;
1495
+ for (let i = 0; i < count; ++i) {
1496
+ while (state < lRansBase && bufOffset > bufStart) {
1497
+ state = state << 8 | buf[--bufOffset];
1498
+ }
1499
+ const rem = state & ransPrecisionMask;
1500
+ const symbol = lutTable[rem];
1501
+ out[i] = symbol;
1502
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1503
+ }
1504
+ this.state = state;
1505
+ this.bufOffset = bufOffset;
1506
+ }
1507
+ _decodeSymbolsU32(out, count, lutTable) {
1508
+ const buf = this.buf;
1509
+ const lRansBase = this.lRansBase;
1510
+ const ransPrecisionBits = this.ransPrecisionBits;
1511
+ const ransPrecisionMask = this.ransPrecisionMask;
1512
+ const probTable = this.probTable;
1513
+ const cumProbTable = this.cumProbTable;
1514
+ let state = this.state;
1515
+ let bufOffset = this.bufOffset;
1516
+ const bufStart = this.bufStart;
1517
+ for (let i = 0; i < count; ++i) {
1518
+ while (state < lRansBase && bufOffset > bufStart) {
1519
+ state = state << 8 | buf[--bufOffset];
1520
+ }
1521
+ const rem = state & ransPrecisionMask;
1522
+ const symbol = lutTable[rem];
1523
+ out[i] = symbol;
1524
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1525
+ }
1526
+ this.state = state;
1527
+ this.bufOffset = bufOffset;
1528
+ }
1529
+ // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
1530
+ ransBuildLookUpTable(tokenProbs, numSymbols) {
1531
+ const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
1532
+ const lutTable = acquirePooled(LutArray, this.ransPrecision);
1533
+ const probTable = acquirePooled(Uint32Array, numSymbols);
1534
+ const cumProbTable = acquirePooled(Uint32Array, numSymbols);
1535
+ this.lutTable = lutTable;
1536
+ this.probTable = probTable;
1537
+ this.cumProbTable = cumProbTable;
1538
+ let cumProb = 0;
1539
+ let actProb = 0;
1540
+ for (let i = 0; i < numSymbols; ++i) {
1541
+ const prob = tokenProbs[i];
1542
+ probTable[i] = prob;
1543
+ cumProbTable[i] = cumProb;
1544
+ cumProb += prob;
1545
+ if (cumProb > this.ransPrecision) {
1546
+ return false;
1547
+ }
1548
+ if (prob < 32) {
1549
+ for (let j = actProb; j < cumProb; ++j) {
1550
+ lutTable[j] = i;
1551
+ }
1552
+ } else {
1553
+ lutTable.fill(i, actProb, cumProb);
1554
+ }
1555
+ actProb = cumProb;
1556
+ }
1557
+ if (cumProb !== this.ransPrecision) {
1558
+ return false;
1559
+ }
1560
+ return true;
1561
+ }
1562
+ };
1563
+ function ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB) {
1564
+ const lutA = a.lutTable;
1565
+ const lutB = b.lutTable;
1566
+ const bufA = a.buf;
1567
+ const bufB = b.buf;
1568
+ const probA = a.probTable;
1569
+ const probB = b.probTable;
1570
+ const cumA = a.cumProbTable;
1571
+ const cumB = b.cumProbTable;
1572
+ const lBaseA = a.lRansBase;
1573
+ const lBaseB = b.lRansBase;
1574
+ const bitsA = a.ransPrecisionBits;
1575
+ const bitsB = b.ransPrecisionBits;
1576
+ const maskA = a.ransPrecisionMask;
1577
+ const maskB = b.ransPrecisionMask;
1578
+ const startA = a.bufStart;
1579
+ const startB = b.bufStart;
1580
+ let stateA = a.state;
1581
+ let stateB = b.state;
1582
+ let offA = a.bufOffset;
1583
+ let offB = b.bufOffset;
1584
+ const shared = countA < countB ? countA : countB;
1585
+ for (let i = 0; i < shared; ++i) {
1586
+ while (stateA < lBaseA && offA > startA) {
1587
+ stateA = stateA << 8 | bufA[--offA];
1588
+ }
1589
+ while (stateB < lBaseB && offB > startB) {
1590
+ stateB = stateB << 8 | bufB[--offB];
1591
+ }
1592
+ const remA = stateA & maskA;
1593
+ const remB = stateB & maskB;
1594
+ const symA = lutA[remA];
1595
+ const symB = lutB[remB];
1596
+ outA[i] = symA;
1597
+ outB[i] = symB;
1598
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1599
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1600
+ }
1601
+ a.state = stateA;
1602
+ a.bufOffset = offA;
1603
+ b.state = stateB;
1604
+ b.bufOffset = offB;
1605
+ if (shared < countA) {
1606
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1607
+ }
1608
+ if (shared < countB) {
1609
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1610
+ }
1611
+ }
1612
+ function ransDecodeSymbolsPair(a, outA, countA, b, outB, countB) {
1613
+ const lutA = a.lutTable;
1614
+ const lutB = b.lutTable;
1615
+ if (lutA instanceof Uint8Array && lutB instanceof Uint8Array) {
1616
+ ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB);
1617
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint16Array) {
1618
+ ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB);
1619
+ } else if (lutA instanceof Uint8Array && lutB instanceof Uint16Array) {
1620
+ ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB);
1621
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint8Array) {
1622
+ ransDecodeSymbolsPairU8U16(b, outB, countB, a, outA, countA);
1623
+ } else {
1624
+ a.decodeSymbols(outA, countA);
1625
+ b.decodeSymbols(outB, countB);
1626
+ }
1627
+ }
1628
+ function ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB) {
1629
+ const lutA = a.lutTable;
1630
+ const lutB = b.lutTable;
1631
+ const bufA = a.buf;
1632
+ const bufB = b.buf;
1633
+ const probA = a.probTable;
1634
+ const probB = b.probTable;
1635
+ const cumA = a.cumProbTable;
1636
+ const cumB = b.cumProbTable;
1637
+ const lBaseA = a.lRansBase;
1638
+ const lBaseB = b.lRansBase;
1639
+ const bitsA = a.ransPrecisionBits;
1640
+ const bitsB = b.ransPrecisionBits;
1641
+ const maskA = a.ransPrecisionMask;
1642
+ const maskB = b.ransPrecisionMask;
1643
+ const startA = a.bufStart;
1644
+ const startB = b.bufStart;
1645
+ let stateA = a.state;
1646
+ let stateB = b.state;
1647
+ let offA = a.bufOffset;
1648
+ let offB = b.bufOffset;
1649
+ const shared = countA < countB ? countA : countB;
1650
+ for (let i = 0; i < shared; ++i) {
1651
+ while (stateA < lBaseA && offA > startA) {
1652
+ stateA = stateA << 8 | bufA[--offA];
1653
+ }
1654
+ while (stateB < lBaseB && offB > startB) {
1655
+ stateB = stateB << 8 | bufB[--offB];
1656
+ }
1657
+ const remA = stateA & maskA;
1658
+ const remB = stateB & maskB;
1659
+ const symA = lutA[remA];
1660
+ const symB = lutB[remB];
1661
+ outA[i] = symA;
1662
+ outB[i] = symB;
1663
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1664
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1665
+ }
1666
+ a.state = stateA;
1667
+ a.bufOffset = offA;
1668
+ b.state = stateB;
1669
+ b.bufOffset = offB;
1670
+ if (shared < countA) {
1671
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1672
+ }
1673
+ if (shared < countB) {
1674
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1675
+ }
1676
+ }
1677
+ function ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB) {
1678
+ const lutA = a.lutTable;
1679
+ const lutB = b.lutTable;
1680
+ const bufA = a.buf;
1681
+ const bufB = b.buf;
1682
+ const probA = a.probTable;
1683
+ const probB = b.probTable;
1684
+ const cumA = a.cumProbTable;
1685
+ const cumB = b.cumProbTable;
1686
+ const lBaseA = a.lRansBase;
1687
+ const lBaseB = b.lRansBase;
1688
+ const bitsA = a.ransPrecisionBits;
1689
+ const bitsB = b.ransPrecisionBits;
1690
+ const maskA = a.ransPrecisionMask;
1691
+ const maskB = b.ransPrecisionMask;
1692
+ const startA = a.bufStart;
1693
+ const startB = b.bufStart;
1694
+ let stateA = a.state;
1695
+ let stateB = b.state;
1696
+ let offA = a.bufOffset;
1697
+ let offB = b.bufOffset;
1698
+ const shared = countA < countB ? countA : countB;
1699
+ for (let i = 0; i < shared; ++i) {
1700
+ while (stateA < lBaseA && offA > startA) {
1701
+ stateA = stateA << 8 | bufA[--offA];
1702
+ }
1703
+ while (stateB < lBaseB && offB > startB) {
1704
+ stateB = stateB << 8 | bufB[--offB];
1705
+ }
1706
+ const remA = stateA & maskA;
1707
+ const remB = stateB & maskB;
1708
+ const symA = lutA[remA];
1709
+ const symB = lutB[remB];
1710
+ outA[i] = symA;
1711
+ outB[i] = symB;
1712
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1713
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1714
+ }
1715
+ a.state = stateA;
1716
+ a.bufOffset = offA;
1717
+ b.state = stateB;
1718
+ b.bufOffset = offB;
1719
+ if (shared < countA) {
1720
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1721
+ }
1722
+ if (shared < countB) {
1723
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1724
+ }
1725
+ }
1726
+ function ransDecodeSymbolsTrioU8(a, outA, countA, b, outB, countB, c, outC, countC) {
1727
+ const lutA = a.lutTable;
1728
+ const lutB = b.lutTable;
1729
+ const lutC = c.lutTable;
1730
+ const bufA = a.buf;
1731
+ const bufB = b.buf;
1732
+ const bufC = c.buf;
1733
+ const probA = a.probTable;
1734
+ const probB = b.probTable;
1735
+ const probC = c.probTable;
1736
+ const cumA = a.cumProbTable;
1737
+ const cumB = b.cumProbTable;
1738
+ const cumC = c.cumProbTable;
1739
+ const lBaseA = a.lRansBase;
1740
+ const lBaseB = b.lRansBase;
1741
+ const lBaseC = c.lRansBase;
1742
+ const bitsA = a.ransPrecisionBits;
1743
+ const bitsB = b.ransPrecisionBits;
1744
+ const bitsC = c.ransPrecisionBits;
1745
+ const maskA = a.ransPrecisionMask;
1746
+ const maskB = b.ransPrecisionMask;
1747
+ const maskC = c.ransPrecisionMask;
1748
+ const startA = a.bufStart;
1749
+ const startB = b.bufStart;
1750
+ const startC = c.bufStart;
1751
+ let stateA = a.state;
1752
+ let stateB = b.state;
1753
+ let stateC = c.state;
1754
+ let offA = a.bufOffset;
1755
+ let offB = b.bufOffset;
1756
+ let offC = c.bufOffset;
1757
+ let shared = countA < countB ? countA : countB;
1758
+ if (countC < shared) shared = countC;
1759
+ for (let i = 0; i < shared; ++i) {
1760
+ while (stateA < lBaseA && offA > startA) {
1761
+ stateA = stateA << 8 | bufA[--offA];
1762
+ }
1763
+ while (stateB < lBaseB && offB > startB) {
1764
+ stateB = stateB << 8 | bufB[--offB];
1765
+ }
1766
+ while (stateC < lBaseC && offC > startC) {
1767
+ stateC = stateC << 8 | bufC[--offC];
1768
+ }
1769
+ const remA = stateA & maskA;
1770
+ const remB = stateB & maskB;
1771
+ const remC = stateC & maskC;
1772
+ const symA = lutA[remA];
1773
+ const symB = lutB[remB];
1774
+ const symC = lutC[remC];
1775
+ outA[i] = symA;
1776
+ outB[i] = symB;
1777
+ outC[i] = symC;
1778
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1779
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1780
+ stateC = (stateC >>> bitsC) * probC[symC] + remC - cumC[symC];
1781
+ }
1782
+ a.state = stateA;
1783
+ a.bufOffset = offA;
1784
+ b.state = stateB;
1785
+ b.bufOffset = offB;
1786
+ c.state = stateC;
1787
+ c.bufOffset = offC;
1788
+ const restA = countA - shared;
1789
+ const restB = countB - shared;
1790
+ const restC = countC - shared;
1791
+ const tails = [];
1792
+ if (restA > 0) tails.push([a, outA.subarray(shared), restA]);
1793
+ if (restB > 0) tails.push([b, outB.subarray(shared), restB]);
1794
+ if (restC > 0) tails.push([c, outC.subarray(shared), restC]);
1795
+ if (tails.length === 2) {
1796
+ ransDecodeSymbolsPairU8(tails[0][0], tails[0][1], tails[0][2], tails[1][0], tails[1][1], tails[1][2]);
1797
+ } else {
1798
+ for (const [decoder, out, count] of tails) {
1799
+ decoder.decodeSymbols(out, count);
1800
+ }
1801
+ }
1802
+ }
1803
+
1804
+ // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1805
+ var PointCloudDecoder = class _PointCloudDecoder {
1806
+ _pointCloud;
1807
+ _buffer;
1808
+ _versionMajor;
1809
+ _versionMinor;
1810
+ _options;
1811
+ _attributesDecoders;
1812
+ _attributeToDecoderMap;
1813
+ constructor() {
1814
+ this._pointCloud = null;
1815
+ this._buffer = null;
1816
+ this._versionMajor = 0;
1817
+ this._versionMinor = 0;
1818
+ this._options = null;
1819
+ this._attributesDecoders = [];
1820
+ this._attributeToDecoderMap = [];
1821
+ }
1822
+ getGeometryType() {
1823
+ return EncodedGeometryType.POINT_CLOUD;
1824
+ }
1825
+ // Returns a Status; on success outHeader is populated.
1826
+ static decodeHeader(buffer, outHeader) {
1827
+ const kIoErrorMsg = "Failed to parse Draco header.";
1828
+ const bytes = buffer.decodeBytes(5);
1829
+ if (bytes === void 0) {
1830
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1831
+ }
1832
+ for (let i = 0; i < 5; i++) {
1833
+ outHeader.dracoString[i] = bytes[i];
1834
+ }
1835
+ const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1836
+ if (magic !== "DRACO") {
1837
+ return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1838
+ }
1839
+ const versionMajor = buffer.decodeUint8();
1840
+ if (versionMajor === void 0) {
1841
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1842
+ }
1843
+ outHeader.versionMajor = versionMajor;
1844
+ const versionMinor = buffer.decodeUint8();
1845
+ if (versionMinor === void 0) {
1846
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1847
+ }
1848
+ outHeader.versionMinor = versionMinor;
1849
+ const encoderType = buffer.decodeUint8();
1850
+ if (encoderType === void 0) {
1851
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1852
+ }
1853
+ outHeader.encoderType = encoderType;
1854
+ const encoderMethod = buffer.decodeUint8();
1855
+ if (encoderMethod === void 0) {
1856
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1857
+ }
1858
+ outHeader.encoderMethod = encoderMethod;
1859
+ const flags = buffer.decodeUint16();
1860
+ if (flags === void 0) {
1861
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1862
+ }
1863
+ outHeader.flags = flags;
1864
+ return okStatus();
1865
+ }
1866
+ // Main entry point for point cloud decoding.
1867
+ decode(options, inBuffer, outPointCloud) {
1868
+ this._options = options;
1869
+ this._buffer = inBuffer;
1870
+ this._pointCloud = outPointCloud;
1871
+ const header = new DracoHeader();
1872
+ const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1873
+ if (!headerStatus.ok()) {
1874
+ return headerStatus;
1875
+ }
1876
+ if (header.encoderType !== this.getGeometryType()) {
1877
+ return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1878
+ }
1879
+ this._versionMajor = header.versionMajor;
1880
+ this._versionMinor = header.versionMinor;
1881
+ const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1882
+ const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
1276
1883
  if (this._versionMajor < 1 || this._versionMajor > maxSupportedMajorVersion) {
1277
1884
  return new Status(StatusCode.UNKNOWN_VERSION, "Unknown major version.");
1278
1885
  }
@@ -1388,8 +1995,27 @@ var PointCloudDecoder = class _PointCloudDecoder {
1388
1995
  return true;
1389
1996
  }
1390
1997
  decodeAllAttributes() {
1391
- for (let i = 0; i < this._attributesDecoders.length; i++) {
1392
- if (!this._attributesDecoders[i].decodeAttributes(this._buffer)) {
1998
+ const decoders = this._attributesDecoders;
1999
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2000
+ if (!decoders[i2].decodeAttributesParse(this._buffer)) {
2001
+ return false;
2002
+ }
2003
+ }
2004
+ const pending = [];
2005
+ for (let i2 = 0; i2 < decoders.length; i2++) {
2006
+ decoders[i2].collectPendingSymbolStreams(pending);
2007
+ }
2008
+ let i = 0;
2009
+ for (; i + 1 < pending.length; i += 2) {
2010
+ const a = pending[i];
2011
+ const b = pending[i + 1];
2012
+ ransDecodeSymbolsPair(a.ans, a.out, a.count, b.ans, b.out, b.count);
2013
+ }
2014
+ if (i < pending.length) {
2015
+ pending[i].ans.decodeSymbols(pending[i].out, pending[i].count);
2016
+ }
2017
+ for (let k = 0; k < decoders.length; k++) {
2018
+ if (!decoders[k].decodeAttributesFinish()) {
1393
2019
  return false;
1394
2020
  }
1395
2021
  }
@@ -1457,46 +2083,62 @@ var MeshAttributeCornerTable = class {
1457
2083
  no_interior_seams_;
1458
2084
  corner_to_vertex_map_;
1459
2085
  vertex_to_left_most_corner_map_;
1460
- vertex_to_attribute_entry_id_map_;
2086
+ // Attribute-vertex count. C++ keeps a vertex -> attribute-entry map here, but
2087
+ // the decoder only ever reads its size, so track the count directly instead
2088
+ // of allocating an Int32Array per attribute corner table.
2089
+ num_attribute_vertices_;
1461
2090
  corner_table_;
1462
2091
  // Lazily built; see oppositeCornerArray.
1463
2092
  _effectiveOpposite;
1464
2093
  // Every corner passed to addSeamEdge (may contain duplicates); lets
1465
2094
  // oppositeCornerArray patch seams without scanning every corner's flag.
2095
+ // Preallocated to its exact upper bound (2 per seam edge) by the caller via
2096
+ // reserveSeamEdges -- a plain array grown by push() was measurable on
2097
+ // seam-heavy files.
1466
2098
  _seamCorners;
2099
+ _numSeamCorners;
1467
2100
  constructor() {
1468
2101
  this.is_edge_on_seam_ = [];
1469
2102
  this.is_vertex_on_seam_ = [];
1470
2103
  this.no_interior_seams_ = true;
1471
2104
  this.corner_to_vertex_map_ = [];
1472
2105
  this.vertex_to_left_most_corner_map_ = [];
1473
- this.vertex_to_attribute_entry_id_map_ = [];
2106
+ this.num_attribute_vertices_ = 0;
1474
2107
  this.corner_table_ = null;
1475
2108
  this._effectiveOpposite = null;
1476
2109
  this._seamCorners = [];
2110
+ this._numSeamCorners = 0;
1477
2111
  }
1478
2112
  initEmpty(table) {
1479
2113
  if (table === null) {
1480
2114
  return false;
1481
2115
  }
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_ = [];
2116
+ this.is_edge_on_seam_ = scratchUint8Zeroed(table.numCorners());
2117
+ this.is_vertex_on_seam_ = scratchUint8Zeroed(table.numVertices());
2118
+ this.corner_to_vertex_map_ = scratchInt32Filled(table.numCorners(), kInvalidVertexIndex);
2119
+ this.num_attribute_vertices_ = 0;
1486
2120
  this.vertex_to_left_most_corner_map_ = [];
1487
2121
  this._effectiveOpposite = null;
1488
2122
  this._seamCorners = [];
2123
+ this._numSeamCorners = 0;
1489
2124
  this.corner_table_ = table;
1490
2125
  this.no_interior_seams_ = true;
1491
2126
  return true;
1492
2127
  }
2128
+ // Sizes the seam-corner list for numSeamEdges upcoming addSeamEdge calls
2129
+ // (each adds at most two corners). Decode-scoped scratch.
2130
+ reserveSeamEdges(numSeamEdges) {
2131
+ this._seamCorners = scratchInt32(numSeamEdges * 2);
2132
+ this._numSeamCorners = 0;
2133
+ }
1493
2134
  addSeamEdge(c) {
1494
2135
  const cornerToVertex = this.corner_table_.cornerToVertexArray();
1495
2136
  const oppositeCorners = this.corner_table_.oppositeCornerArray();
1496
2137
  const isEdge = this.is_edge_on_seam_;
1497
2138
  const isVert = this.is_vertex_on_seam_;
2139
+ const seamCorners = this._seamCorners;
1498
2140
  isEdge[c] = 1;
1499
- this._seamCorners.push(c);
2141
+ seamCorners[this._numSeamCorners++] = c;
1500
2142
  let rem = c - (c / 3 | 0) * 3;
1501
2143
  isVert[cornerToVertex[rem === 2 ? c - 2 : c + 1]] = 1;
1502
2144
  isVert[cornerToVertex[rem === 0 ? c + 2 : c - 1]] = 1;
@@ -1504,7 +2146,7 @@ var MeshAttributeCornerTable = class {
1504
2146
  if (oppCorner !== kInvalidCornerIndex) {
1505
2147
  this.no_interior_seams_ = false;
1506
2148
  isEdge[oppCorner] = 1;
1507
- this._seamCorners.push(oppCorner);
2149
+ seamCorners[this._numSeamCorners++] = oppCorner;
1508
2150
  rem = oppCorner - (oppCorner / 3 | 0) * 3;
1509
2151
  isVert[cornerToVertex[rem === 2 ? oppCorner - 2 : oppCorner + 1]] = 1;
1510
2152
  isVert[cornerToVertex[rem === 0 ? oppCorner + 2 : oppCorner - 1]] = 1;
@@ -1519,12 +2161,12 @@ var MeshAttributeCornerTable = class {
1519
2161
  const ct = this.corner_table_;
1520
2162
  const numCorners = ct.numCorners();
1521
2163
  const numBaseVertices = ct.numVertices();
1522
- const leftMostMap = new Int32Array(numCorners);
2164
+ const leftMostMap = scratchInt32(numCorners);
1523
2165
  const cornerToVertex = this.corner_to_vertex_map_;
1524
2166
  const isVertexOnSeam = this.is_vertex_on_seam_;
1525
2167
  const isEdgeOnSeam = this.is_edge_on_seam_;
1526
2168
  const seamOpp = this.oppositeCornerArray();
1527
- const baseOpp = ct.oppositeCornerArray();
2169
+ const swingRight = ct.swingRightArray();
1528
2170
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
1529
2171
  let numNewVertices = 0;
1530
2172
  for (let v = 0; v < numBaseVertices; ++v) {
@@ -1534,14 +2176,10 @@ var MeshAttributeCornerTable = class {
1534
2176
  const firstVertId = numNewVertices++;
1535
2177
  leftMostMap[firstVertId] = c;
1536
2178
  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;
2179
+ let actC = swingRight[c];
1540
2180
  while (actC !== kInvalidCornerIndex && actC !== c) {
1541
2181
  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;
2182
+ actC = swingRight[actC];
1545
2183
  }
1546
2184
  } else {
1547
2185
  let firstVertId = numNewVertices++;
@@ -1559,9 +2197,7 @@ var MeshAttributeCornerTable = class {
1559
2197
  }
1560
2198
  cornerToVertex[firstC] = firstVertId;
1561
2199
  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;
2200
+ actC = swingRight[firstC];
1565
2201
  while (actC !== kInvalidCornerIndex && actC !== firstC) {
1566
2202
  const nAct = actC % 3 === 2 ? actC - 2 : actC + 1;
1567
2203
  if (isEdgeOnSeam[nAct]) {
@@ -1569,13 +2205,11 @@ var MeshAttributeCornerTable = class {
1569
2205
  leftMostMap[firstVertId] = actC;
1570
2206
  }
1571
2207
  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;
2208
+ actC = swingRight[actC];
1575
2209
  }
1576
2210
  }
1577
2211
  }
1578
- this.vertex_to_attribute_entry_id_map_ = new Int32Array(numNewVertices);
2212
+ this.num_attribute_vertices_ = numNewVertices;
1579
2213
  this.vertex_to_left_most_corner_map_ = leftMostMap.subarray(0, numNewVertices);
1580
2214
  return true;
1581
2215
  }
@@ -1601,7 +2235,7 @@ var MeshAttributeCornerTable = class {
1601
2235
  return this.next(this.opposite(this.next(corner)));
1602
2236
  }
1603
2237
  numVertices() {
1604
- return this.vertex_to_attribute_entry_id_map_.length;
2238
+ return this.num_attribute_vertices_;
1605
2239
  }
1606
2240
  numFaces() {
1607
2241
  return this.corner_table_.numFaces();
@@ -1629,12 +2263,13 @@ var MeshAttributeCornerTable = class {
1629
2263
  const nc = this.corner_table_.numCorners();
1630
2264
  const base = this.corner_table_.oppositeCornerArray();
1631
2265
  const seamCorners = this._seamCorners;
1632
- if (seamCorners.length === 0) {
2266
+ const numSeamCorners = this._numSeamCorners;
2267
+ if (numSeamCorners === 0) {
1633
2268
  this._effectiveOpposite = base;
1634
2269
  } else {
1635
2270
  const eff = scratchInt32(nc);
1636
2271
  eff.set(base.length === nc ? base : base.subarray(0, nc));
1637
- for (let i = 0, l = seamCorners.length; i < l; ++i) {
2272
+ for (let i = 0; i < numSeamCorners; ++i) {
1638
2273
  eff[seamCorners[i]] = kInvalidCornerIndex;
1639
2274
  }
1640
2275
  this._effectiveOpposite = eff;
@@ -1649,23 +2284,20 @@ var MeshAttributeCornerTable = class {
1649
2284
  vertexOnSeamArray() {
1650
2285
  return this.is_vertex_on_seam_;
1651
2286
  }
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) {
2287
+ // Takes over another table's state wholesale. Only valid when both tables
2288
+ // were built from the same corner table and the same seam edges, in which
2289
+ // case every one of these is identical and read-only from here on.
2290
+ adoptFrom(other) {
2291
+ this.corner_table_ = other.corner_table_;
2292
+ this.is_edge_on_seam_ = other.is_edge_on_seam_;
2293
+ this.is_vertex_on_seam_ = other.is_vertex_on_seam_;
1663
2294
  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_;
2295
+ this.num_attribute_vertices_ = other.num_attribute_vertices_;
1665
2296
  this.vertex_to_left_most_corner_map_ = other.vertex_to_left_most_corner_map_;
1666
2297
  this.no_interior_seams_ = other.no_interior_seams_;
1667
2298
  this._effectiveOpposite = other._effectiveOpposite;
1668
2299
  this._seamCorners = other._seamCorners;
2300
+ this._numSeamCorners = other._numSeamCorners;
1669
2301
  }
1670
2302
  };
1671
2303
 
@@ -1679,6 +2311,21 @@ var AttributesDecoderInterface = class {
1679
2311
  decodeAttributesDecoderData(_buffer) {
1680
2312
  return false;
1681
2313
  }
2314
+ // --- Optional two-phase decode across attributes decoders ---
2315
+ // PointCloudDecoder parses every decoder first (all buffer reads are
2316
+ // size-driven, so parsing runs ahead of the deferred rANS symbol decodes),
2317
+ // then decodes the collected streams two at a time, then finishes each
2318
+ // decoder in order (so parent attributes complete before dependents).
2319
+ // Defaults keep the original single-phase behavior for decoders that do not
2320
+ // split.
2321
+ decodeAttributesParse(buffer) {
2322
+ return this.decodeAttributes(buffer);
2323
+ }
2324
+ collectPendingSymbolStreams(_out) {
2325
+ }
2326
+ decodeAttributesFinish() {
2327
+ return true;
2328
+ }
1682
2329
  decodeAttributes(_buffer) {
1683
2330
  return false;
1684
2331
  }
@@ -1825,6 +2472,22 @@ var SequentialAttributeDecoder = class {
1825
2472
  this._attributeId = attributeId;
1826
2473
  return true;
1827
2474
  }
2475
+ // --- Optional two-phase decode ---
2476
+ // The controller calls Parse for every attribute first (headers, schemes,
2477
+ // prediction data -- all size-driven cursor movement), collects the pending
2478
+ // rANS symbol streams, decodes them in pairs (see ransDecodeSymbolsPair),
2479
+ // then calls Finish per attribute in order. Decoders without a deferrable
2480
+ // stream simply do the whole decode in Parse. Defaults preserve the
2481
+ // original single-phase behavior.
2482
+ decodePortableAttributeParse(pointIds, buffer) {
2483
+ return this.decodePortableAttribute(pointIds, buffer);
2484
+ }
2485
+ pendingSymbolStream() {
2486
+ return null;
2487
+ }
2488
+ decodePortableAttributeFinish() {
2489
+ return true;
2490
+ }
1828
2491
  decodePortableAttribute(pointIds, buffer) {
1829
2492
  if (this._attribute.numComponents <= 0) {
1830
2493
  return false;
@@ -1845,7 +2508,7 @@ var SequentialAttributeDecoder = class {
1845
2508
  getPortableAttribute() {
1846
2509
  if (!this._attribute.isMappingIdentity && this._portableAttribute && this._portableAttribute.isMappingIdentity) {
1847
2510
  const size = this._attribute.indicesMapSize;
1848
- this._portableAttribute.setExplicitMappingUnfilled(size);
2511
+ this._portableAttribute.setExplicitMappingScratch(size);
1849
2512
  const src = this._attribute.indicesMap;
1850
2513
  const dst = this._portableAttribute.indicesMap;
1851
2514
  if (src.length === size) {
@@ -1860,331 +2523,42 @@ var SequentialAttributeDecoder = class {
1860
2523
  return this._attribute;
1861
2524
  }
1862
2525
  get attributeId() {
1863
- return this._attributeId;
1864
- }
1865
- get decoder() {
1866
- return this._decoder;
1867
- }
1868
- initPredictionScheme(ps) {
1869
- for (let i = 0; i < ps.getNumParentAttributes(); i++) {
1870
- const attId = this._decoder.pointCloud().getNamedAttributeId(ps.getParentAttributeType(i));
1871
- if (attId === -1) {
1872
- return false;
1873
- }
1874
- const pa = this._decoder.getPortableAttribute(attId);
1875
- if (pa === null || !ps.setParentAttribute(pa)) {
1876
- return false;
1877
- }
1878
- }
1879
- return true;
1880
- }
1881
- // Decodes raw attribute values in their original format.
1882
- decodeValues(pointIds, buffer) {
1883
- const numValues = pointIds.length;
1884
- const entrySize = this._attribute.byteStride;
1885
- const totalSize = numValues * entrySize;
1886
- const valueData = buffer.decodeBytesView(totalSize);
1887
- if (valueData === void 0) {
1888
- return false;
1889
- }
1890
- this._attribute.buffer.write(0, valueData, totalSize);
1891
- return true;
1892
- }
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) {
2526
+ return this._attributeId;
2527
+ }
2528
+ get decoder() {
2529
+ return this._decoder;
2530
+ }
2531
+ initPredictionScheme(ps) {
2532
+ for (let i = 0; i < ps.getNumParentAttributes(); i++) {
2533
+ const attId = this._decoder.pointCloud().getNamedAttributeId(ps.getParentAttributeType(i));
2534
+ if (attId === -1) {
2172
2535
  return false;
2173
2536
  }
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);
2537
+ const pa = this._decoder.getPortableAttribute(attId);
2538
+ if (pa === null || !ps.setParentAttribute(pa)) {
2539
+ return false;
2180
2540
  }
2181
- actProb = cumProb;
2182
2541
  }
2183
- if (cumProb !== this.ransPrecision) {
2542
+ return true;
2543
+ }
2544
+ // Decodes raw attribute values in their original format.
2545
+ decodeValues(pointIds, buffer) {
2546
+ const numValues = pointIds.length;
2547
+ const entrySize = this._attribute.byteStride;
2548
+ const totalSize = numValues * entrySize;
2549
+ const valueData = buffer.decodeBytesView(totalSize);
2550
+ if (valueData === void 0) {
2184
2551
  return false;
2185
2552
  }
2553
+ this._attribute.buffer.write(0, valueData, totalSize);
2186
2554
  return true;
2187
2555
  }
2556
+ setPortableAttribute(att) {
2557
+ this._portableAttribute = att;
2558
+ }
2559
+ get portableAttribute() {
2560
+ return this._portableAttribute;
2561
+ }
2188
2562
  };
2189
2563
 
2190
2564
  // src/decoder/compression/entropy/RAnsSymbolDecoder.ts
@@ -2220,7 +2594,7 @@ var RAnsSymbolDecoder = class {
2220
2594
  return false;
2221
2595
  }
2222
2596
  const numSymbols = this.numSymbols_;
2223
- const probabilityTable = new Uint32Array(numSymbols);
2597
+ const probabilityTable = scratchUint32Zeroed(numSymbols);
2224
2598
  this.probabilityTable_ = probabilityTable;
2225
2599
  if (numSymbols === 0) {
2226
2600
  return true;
@@ -2301,24 +2675,73 @@ function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2301
2675
  if (numValues > 0 && tagDecoder.numSymbols === 0) {
2302
2676
  return false;
2303
2677
  }
2678
+ const tagAns = tagDecoder.ans_;
2304
2679
  srcBuffer.startBitDecoding(false);
2305
2680
  const bd = srcBuffer._bitDecoder;
2306
- const tagAns = tagDecoder.ans_;
2681
+ const buf = bd._bitBuffer;
2682
+ const byteLength = bd._byteLength;
2683
+ let bitOffset = bd._bitOffset;
2307
2684
  let valueId = 0;
2308
2685
  for (let i = 0; i < numValues; i += numComponents) {
2309
2686
  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;
2687
+ if (bitLength < 32) {
2688
+ const mask = (1 << bitLength) - 1;
2689
+ let j = 0;
2690
+ for (; j < numComponents; ++j) {
2691
+ const byteOffset = bitOffset >> 3;
2692
+ if (byteOffset + 4 >= byteLength) break;
2693
+ const bitShift = bitOffset & 7;
2694
+ let value = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> bitShift;
2695
+ if (bitLength > 32 - bitShift) {
2696
+ value = (value | buf[byteOffset + 4] << 32 - bitShift) >>> 0;
2697
+ }
2698
+ bitOffset += bitLength;
2699
+ outValues[valueId++] = value & mask;
2700
+ }
2701
+ if (j === numComponents) continue;
2702
+ bd._bitOffset = bitOffset;
2703
+ for (; j < numComponents; ++j) {
2704
+ const val = bd.getBits(bitLength);
2705
+ if (val === void 0) {
2706
+ return false;
2707
+ }
2708
+ outValues[valueId++] = val;
2709
+ }
2710
+ bitOffset = bd._bitOffset;
2711
+ } else {
2712
+ bd._bitOffset = bitOffset;
2713
+ for (let j = 0; j < numComponents; ++j) {
2714
+ const val = bd.getBits(bitLength);
2715
+ if (val === void 0) {
2716
+ return false;
2717
+ }
2718
+ outValues[valueId++] = val;
2314
2719
  }
2315
- outValues[valueId++] = val;
2720
+ bitOffset = bd._bitOffset;
2316
2721
  }
2317
2722
  }
2723
+ bd._bitOffset = bitOffset;
2318
2724
  tagDecoder.endDecoding();
2319
2725
  srcBuffer.endBitDecoding();
2320
2726
  return true;
2321
2727
  }
2728
+ function parseRawSymbolStream(numValues, srcBuffer) {
2729
+ const maxBitLength = srcBuffer.decodeUint8();
2730
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
2731
+ return null;
2732
+ }
2733
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
2734
+ if (!decoder.create(srcBuffer)) {
2735
+ return null;
2736
+ }
2737
+ if (numValues > 0 && decoder.numSymbols === 0) {
2738
+ return null;
2739
+ }
2740
+ if (!decoder.startDecoding(srcBuffer)) {
2741
+ return null;
2742
+ }
2743
+ return decoder;
2744
+ }
2322
2745
  function decodeRawSymbolsInternal(uniqueSymbolsBitLength, numValues, srcBuffer, outValues) {
2323
2746
  const decoder = new RAnsSymbolDecoder(uniqueSymbolsBitLength);
2324
2747
  if (!decoder.create(srcBuffer)) {
@@ -2421,6 +2844,16 @@ var PredictionSchemeDecoderInterface = class {
2421
2844
  decodePredictionData(_buffer) {
2422
2845
  return true;
2423
2846
  }
2847
+ /**
2848
+ * Like computeOriginalValues, but inCorr still holds unsigned zigzag-coded
2849
+ * corrections; the implementation unpacks each one inline, replacing the
2850
+ * standalone convertSymbolsToSignedInts pass. Returns undefined when the
2851
+ * scheme/transform combination cannot fuse (the caller then falls back to
2852
+ * the two-pass path). Base implementation: never fusable.
2853
+ */
2854
+ computeOriginalValuesZigzag(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2855
+ return void 0;
2856
+ }
2424
2857
  /** Reverts the prediction applied during encoding, writing original values to outData. */
2425
2858
  computeOriginalValues(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2426
2859
  return false;
@@ -2756,7 +3189,7 @@ var OctahedronToolBox = class {
2756
3189
  // src/decoder/compression/attributes/prediction_schemes/MeshPredictionSchemeGeometricNormalPredictorArea.ts
2757
3190
  var UPPER_BOUND = 1 << 29;
2758
3191
  function buildInt32PositionCache(att, map, numEntries, tempPos) {
2759
- const cache = new Int32Array(numEntries * 3);
3192
+ const cache = scratchInt32(numEntries * 3);
2760
3193
  const bufData = att.buffer && att.buffer.data;
2761
3194
  if (att.dataType === DataType.INT32 && att.numComponents === 3 && bufData) {
2762
3195
  const src = new Int32Array(bufData.buffer);
@@ -2837,7 +3270,7 @@ var MeshPredictionSchemeGeometricNormalPredictorArea = class {
2837
3270
  const cornerToVertex = this._cornerToVertex;
2838
3271
  const vertexToDataMap = this._meshData.vertexToDataMap;
2839
3272
  const nc = cornerToVertex.length;
2840
- const c2o = new Int32Array(nc);
3273
+ const c2o = scratchInt32(nc);
2841
3274
  for (let c = 0; c < nc; ++c) {
2842
3275
  const v = cornerToVertex[c];
2843
3276
  c2o[c] = v < 0 ? -1 : vertexToDataMap[v] * 3;
@@ -3088,10 +3521,21 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3088
3521
  isInitialized() {
3089
3522
  return this._meshData.isInitialized();
3090
3523
  }
3524
+ // Zigzag-fused variant: decodes corrections that are still in their unsigned
3525
+ // zigzag form, unpacking each one inline. Only offered for the wrap
3526
+ // transform (whose corrections are the only zigzag-coded ones this scheme
3527
+ // sees); callers fall back to the standalone conversion pass otherwise.
3528
+ computeOriginalValuesZigzag(inCorr, outData, _size, numComponents, _entryToPointIdMap) {
3529
+ this._transform.init(numComponents);
3530
+ if (!this._transform.getType || this._transform.getType() !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3531
+ return void 0;
3532
+ }
3533
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, true);
3534
+ }
3091
3535
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3092
3536
  this._transform.init(numComponents);
3093
3537
  if (this._transform.getType && this._transform.getType() === PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3094
- return this._computeOriginalValuesWrap(inCorr, outData, numComponents);
3538
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, false);
3095
3539
  }
3096
3540
  const table = this._meshData.cornerTable;
3097
3541
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3132,12 +3576,18 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3132
3576
  }
3133
3577
  return true;
3134
3578
  }
3135
- _computeOriginalValuesWrap(inCorr, outData, numComponents) {
3579
+ // The wrap transform's corrections are zigzag-coded; with `zigzag` set the
3580
+ // decode folds the (val >>> 1) ^ -(val & 1) unpacking into each correction
3581
+ // read, replacing the standalone convertSymbolsToSignedInts pass over the
3582
+ // whole array (see computeOriginalValuesZigzag). Every correction is read
3583
+ // exactly once before its slot is overwritten, so the in-place aliasing of
3584
+ // inCorr and outData is preserved.
3585
+ _computeOriginalValuesWrap(inCorr, outData, numComponents, zigzag) {
3136
3586
  if (numComponents === 2) {
3137
- return this._computeOriginalValuesWrap2(inCorr, outData);
3587
+ return this._computeOriginalValuesWrap2(inCorr, outData, zigzag);
3138
3588
  }
3139
3589
  if (numComponents === 3) {
3140
- return this._computeOriginalValuesWrap3(inCorr, outData);
3590
+ return this._computeOriginalValuesWrap3(inCorr, outData, zigzag);
3141
3591
  }
3142
3592
  const table = this._meshData.cornerTable;
3143
3593
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3155,7 +3605,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3155
3605
  } else if (pred < minValue) {
3156
3606
  pred = minValue;
3157
3607
  }
3158
- let orig = pred + inCorr[c] | 0;
3608
+ const raw = inCorr[c];
3609
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3159
3610
  if (orig > maxValue) {
3160
3611
  orig -= maxDif;
3161
3612
  } else if (orig < minValue) {
@@ -3194,7 +3645,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3194
3645
  } else if (pred < minValue) {
3195
3646
  pred = minValue;
3196
3647
  }
3197
- let orig = pred + inCorr[dstOffset + c] | 0;
3648
+ const raw = inCorr[dstOffset + c];
3649
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3198
3650
  if (orig > maxValue) {
3199
3651
  orig -= maxDif;
3200
3652
  } else if (orig < minValue) {
@@ -3211,7 +3663,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3211
3663
  } else if (pred < minValue) {
3212
3664
  pred = minValue;
3213
3665
  }
3214
- let orig = pred + inCorr[dstOffset + c] | 0;
3666
+ const raw = inCorr[dstOffset + c];
3667
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3215
3668
  if (orig > maxValue) {
3216
3669
  orig -= maxDif;
3217
3670
  } else if (orig < minValue) {
@@ -3223,7 +3676,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3223
3676
  }
3224
3677
  return true;
3225
3678
  }
3226
- _computeOriginalValuesWrap2(inCorr, outData) {
3679
+ _computeOriginalValuesWrap2(inCorr, outData, zigzag) {
3227
3680
  const table = this._meshData.cornerTable;
3228
3681
  const vertexToDataMap = this._meshData.vertexToDataMap;
3229
3682
  const oppositeCorners = table.oppositeCornerArray();
@@ -3245,8 +3698,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3245
3698
  } else if (pred1 < minValue) {
3246
3699
  pred1 = minValue;
3247
3700
  }
3248
- let orig0 = pred0 + inCorr[0] | 0;
3249
- let orig1 = pred1 + inCorr[1] | 0;
3701
+ const raw0 = inCorr[0];
3702
+ const raw1 = inCorr[1];
3703
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3704
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3250
3705
  if (orig0 > maxValue) {
3251
3706
  orig0 -= maxDif;
3252
3707
  } else if (orig0 < minValue) {
@@ -3300,8 +3755,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3300
3755
  } else if (pred1 < minValue) {
3301
3756
  pred1 = minValue;
3302
3757
  }
3303
- orig0 = pred0 + inCorr[dstOffset] | 0;
3304
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3758
+ const rawA = inCorr[dstOffset];
3759
+ const rawB = inCorr[dstOffset + 1];
3760
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3761
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3305
3762
  if (orig0 > maxValue) {
3306
3763
  orig0 -= maxDif;
3307
3764
  } else if (orig0 < minValue) {
@@ -3317,7 +3774,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3317
3774
  }
3318
3775
  return true;
3319
3776
  }
3320
- _computeOriginalValuesWrap3(inCorr, outData) {
3777
+ _computeOriginalValuesWrap3(inCorr, outData, zigzag) {
3321
3778
  const table = this._meshData.cornerTable;
3322
3779
  const vertexToDataMap = this._meshData.vertexToDataMap;
3323
3780
  const oppositeCorners = table.oppositeCornerArray();
@@ -3345,9 +3802,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3345
3802
  } else if (pred2 < minValue) {
3346
3803
  pred2 = minValue;
3347
3804
  }
3348
- let orig0 = pred0 + inCorr[0] | 0;
3349
- let orig1 = pred1 + inCorr[1] | 0;
3350
- let orig2 = pred2 + inCorr[2] | 0;
3805
+ const raw0 = inCorr[0];
3806
+ const raw1 = inCorr[1];
3807
+ const raw2 = inCorr[2];
3808
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3809
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3810
+ let orig2 = pred2 + (zigzag ? raw2 >>> 1 ^ -(raw2 & 1) : raw2) | 0;
3351
3811
  if (orig0 > maxValue) {
3352
3812
  orig0 -= maxDif;
3353
3813
  } else if (orig0 < minValue) {
@@ -3414,9 +3874,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3414
3874
  } else if (pred2 < minValue) {
3415
3875
  pred2 = minValue;
3416
3876
  }
3417
- orig0 = pred0 + inCorr[dstOffset] | 0;
3418
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3419
- orig2 = pred2 + inCorr[dstOffset + 2] | 0;
3877
+ const rawA = inCorr[dstOffset];
3878
+ const rawB = inCorr[dstOffset + 1];
3879
+ const rawC = inCorr[dstOffset + 2];
3880
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3881
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3882
+ orig2 = pred2 + (zigzag ? rawC >>> 1 ^ -(rawC & 1) : rawC) | 0;
3420
3883
  if (orig0 > maxValue) {
3421
3884
  orig0 -= maxDif;
3422
3885
  } else if (orig0 < minValue) {
@@ -3923,14 +4386,49 @@ var PredictionSchemeWrapDecodingTransform = class {
3923
4386
  // src/decoder/compression/attributes/SequentialIntegerAttributeDecoder.ts
3924
4387
  var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder {
3925
4388
  _predictionScheme;
4389
+ // Two-phase decode state (see SequentialAttributeDecoder): the parse phase
4390
+ // stashes the primed raw-symbol stream here so the controller can batch and
4391
+ // pair several attributes' decodes; the finish phase consumes it.
4392
+ _pendingSymbolDecoder;
4393
+ _pendingNumValues;
4394
+ _finishPointIds;
3926
4395
  constructor() {
3927
4396
  super();
3928
4397
  this._predictionScheme = null;
4398
+ this._pendingSymbolDecoder = null;
4399
+ this._pendingNumValues = 0;
4400
+ this._finishPointIds = null;
3929
4401
  }
3930
- transformAttributeToOriginalFormat(pointIds) {
3931
- return this._storeValues(pointIds.length);
4402
+ // --- Two-phase decode (parse headers / batch symbol decode / finish) ---
4403
+ decodePortableAttributeParse(pointIds, buffer) {
4404
+ if (this.attribute.numComponents <= 0) {
4405
+ return false;
4406
+ }
4407
+ if (!this.attribute.reset(pointIds.length)) {
4408
+ return false;
4409
+ }
4410
+ this._finishPointIds = pointIds;
4411
+ return this._decodeValuesParse(pointIds, buffer);
3932
4412
  }
3933
- decodeValues(pointIds, buffer) {
4413
+ pendingSymbolStream() {
4414
+ if (this._pendingSymbolDecoder === null) {
4415
+ return null;
4416
+ }
4417
+ const portableAttributeData = this.getPortableAttributeData();
4418
+ return {
4419
+ ans: this._pendingSymbolDecoder.ans_,
4420
+ out: new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, this._pendingNumValues),
4421
+ count: this._pendingNumValues
4422
+ };
4423
+ }
4424
+ decodePortableAttributeFinish() {
4425
+ if (this._pendingSymbolDecoder !== null) {
4426
+ this._pendingSymbolDecoder.endDecoding();
4427
+ this._pendingSymbolDecoder = null;
4428
+ }
4429
+ return this._finishIntegerValues(this._finishPointIds);
4430
+ }
4431
+ _decodeValuesParse(pointIds, buffer) {
3934
4432
  const predictionSchemeMethod = buffer.decodeInt8();
3935
4433
  if (predictionSchemeMethod === void 0) return false;
3936
4434
  if (predictionSchemeMethod < PredictionSchemeMethod.PREDICTION_NONE || predictionSchemeMethod >= PredictionSchemeMethod.NUM_PREDICTION_SCHEMES) {
@@ -3949,12 +4447,6 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3949
4447
  return false;
3950
4448
  }
3951
4449
  }
3952
- if (!this.decodeIntegerValues(pointIds, buffer)) {
3953
- return false;
3954
- }
3955
- return true;
3956
- }
3957
- decodeIntegerValues(pointIds, buffer) {
3958
4450
  const numComponents = this.getNumValueComponents();
3959
4451
  if (numComponents <= 0) {
3960
4452
  return false;
@@ -3969,9 +4461,23 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3969
4461
  const compressed = buffer.decodeUint8();
3970
4462
  if (compressed === void 0) return false;
3971
4463
  if (compressed > 0) {
3972
- const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3973
- if (!decodeSymbols(numValues, numComponents, buffer, outUint32)) {
3974
- return false;
4464
+ if (numValues > 0) {
4465
+ const scheme = buffer.decodeUint8();
4466
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_RAW) {
4467
+ const decoder = parseRawSymbolStream(numValues, buffer);
4468
+ if (decoder === null) {
4469
+ return false;
4470
+ }
4471
+ this._pendingSymbolDecoder = decoder;
4472
+ this._pendingNumValues = numValues;
4473
+ } else if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
4474
+ const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4475
+ if (!decodeTaggedSymbols(numValues, numComponents, buffer, outUint32)) {
4476
+ return false;
4477
+ }
4478
+ } else {
4479
+ return false;
4480
+ }
3975
4481
  }
3976
4482
  } else {
3977
4483
  const numBytes = buffer.decodeUint8();
@@ -4002,15 +4508,40 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4002
4508
  }
4003
4509
  }
4004
4510
  }
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
4511
  if (this._predictionScheme) {
4010
4512
  if (!this._predictionScheme.decodePredictionData(buffer)) {
4011
4513
  return false;
4012
4514
  }
4515
+ }
4516
+ return true;
4517
+ }
4518
+ // The post-symbol tail of the decode: zigzag unpacking and prediction.
4519
+ _finishIntegerValues(pointIds) {
4520
+ const numComponents = this.getNumValueComponents();
4521
+ const numValues = pointIds.length * numComponents;
4522
+ const portableAttributeData = this.getPortableAttributeData();
4523
+ if (portableAttributeData === null) {
4524
+ return false;
4525
+ }
4526
+ const needsZigzag = numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive());
4527
+ if (this._predictionScheme) {
4013
4528
  if (numValues > 0) {
4529
+ if (needsZigzag) {
4530
+ const fused = this._predictionScheme.computeOriginalValuesZigzag(
4531
+ portableAttributeData,
4532
+ portableAttributeData,
4533
+ numValues,
4534
+ numComponents,
4535
+ pointIds
4536
+ );
4537
+ if (fused !== void 0) {
4538
+ return fused;
4539
+ }
4540
+ }
4541
+ if (needsZigzag) {
4542
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4543
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4544
+ }
4014
4545
  if (!this._predictionScheme.computeOriginalValues(
4015
4546
  portableAttributeData,
4016
4547
  portableAttributeData,
@@ -4021,9 +4552,34 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4021
4552
  return false;
4022
4553
  }
4023
4554
  }
4555
+ } else if (needsZigzag) {
4556
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4557
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4024
4558
  }
4025
4559
  return true;
4026
4560
  }
4561
+ transformAttributeToOriginalFormat(pointIds) {
4562
+ return this._storeValues(pointIds.length);
4563
+ }
4564
+ // Single-phase entry (base-class decodePortableAttribute path): parse,
4565
+ // decode any deferred symbol stream immediately, finish.
4566
+ decodeValues(pointIds, buffer) {
4567
+ this._finishPointIds = pointIds;
4568
+ if (!this._decodeValuesParse(pointIds, buffer)) {
4569
+ return false;
4570
+ }
4571
+ const pending = this._pendingSymbolDecoder;
4572
+ if (pending !== null) {
4573
+ const portableAttributeData = this.getPortableAttributeData();
4574
+ const outUint32 = new Uint32Array(
4575
+ portableAttributeData.buffer,
4576
+ portableAttributeData.byteOffset,
4577
+ this._pendingNumValues
4578
+ );
4579
+ pending.ans_.decodeSymbols(outUint32, this._pendingNumValues);
4580
+ }
4581
+ return this.decodePortableAttributeFinish();
4582
+ }
4027
4583
  // Prediction scheme for decoding integer values; subclasses override for others.
4028
4584
  createIntPredictionScheme(method, transformType) {
4029
4585
  if (transformType !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
@@ -4086,7 +4642,7 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4086
4642
  );
4087
4643
  const portAtt = new PointAttribute(ga);
4088
4644
  portAtt.setIdentityMapping();
4089
- portAtt.reset(numEntries);
4645
+ portAtt.resetScratch(numEntries);
4090
4646
  portAtt.uniqueId = this.attribute.uniqueId;
4091
4647
  this.setPortableAttribute(portAtt);
4092
4648
  }
@@ -4112,12 +4668,22 @@ var AttributeTransformType = {
4112
4668
  };
4113
4669
 
4114
4670
  // src/decoder/attributes/AttributeTransformData.ts
4671
+ var INITIAL_CAPACITY = 32;
4115
4672
  var AttributeTransformData = class {
4116
4673
  _transformType;
4117
- _buffer;
4674
+ // Parameter bytes, little-endian, appended in transform-defined order.
4675
+ // Capacity grows geometrically and the DataView is cached alongside it: the
4676
+ // previous DataBuffer-backed version reallocated the buffer and built a
4677
+ // fresh DataView on *every* appended value, which on primitive-heavy files
4678
+ // cost more than the dequantization it describes.
4679
+ _bytes;
4680
+ _view;
4681
+ _size;
4118
4682
  constructor() {
4119
4683
  this._transformType = AttributeTransformType.INVALID;
4120
- this._buffer = new DataBuffer();
4684
+ this._bytes = new Uint8Array(INITIAL_CAPACITY);
4685
+ this._view = new DataView(this._bytes.buffer);
4686
+ this._size = 0;
4121
4687
  }
4122
4688
  get transformType() {
4123
4689
  return this._transformType;
@@ -4125,13 +4691,29 @@ var AttributeTransformData = class {
4125
4691
  set transformType(type) {
4126
4692
  this._transformType = type;
4127
4693
  }
4694
+ // Number of parameter bytes written so far (the next append offset).
4695
+ get dataSize() {
4696
+ return this._size;
4697
+ }
4698
+ get data() {
4699
+ return this._bytes.subarray(0, this._size);
4700
+ }
4701
+ _reserve(sizeNeeded) {
4702
+ if (sizeNeeded <= this._bytes.length) return;
4703
+ let capacity = this._bytes.length * 2;
4704
+ if (capacity < sizeNeeded) capacity = sizeNeeded;
4705
+ const grown = new Uint8Array(capacity);
4706
+ grown.set(this._bytes);
4707
+ this._bytes = grown;
4708
+ this._view = new DataView(grown.buffer);
4709
+ }
4128
4710
  setParameterValue(byteOffset, value, type) {
4129
4711
  const sizeNeeded = byteOffset + this._typeSize(type);
4130
- if (sizeNeeded > this._buffer.dataSize) {
4131
- this._buffer.resize(sizeNeeded);
4712
+ this._reserve(sizeNeeded);
4713
+ if (sizeNeeded > this._size) {
4714
+ this._size = sizeNeeded;
4132
4715
  }
4133
- const data = this._buffer.data;
4134
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
4716
+ const view = this._view;
4135
4717
  switch (type) {
4136
4718
  case "int32":
4137
4719
  view.setInt32(byteOffset, value, true);
@@ -4163,7 +4745,7 @@ var AttributeTransformData = class {
4163
4745
  }
4164
4746
  }
4165
4747
  appendParameterValue(value, type) {
4166
- this.setParameterValue(this._buffer.dataSize, value, type);
4748
+ this.setParameterValue(this._size, value, type);
4167
4749
  }
4168
4750
  _typeSize(type) {
4169
4751
  switch (type) {
@@ -4733,6 +5315,12 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4733
5315
  return true;
4734
5316
  }
4735
5317
  decodeAttributes(buffer) {
5318
+ if (!this._prepareSequence()) {
5319
+ return false;
5320
+ }
5321
+ return super.decodeAttributes(buffer);
5322
+ }
5323
+ _prepareSequence() {
4736
5324
  if (!this._sequencer) {
4737
5325
  return false;
4738
5326
  }
@@ -4747,7 +5335,44 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4747
5335
  return false;
4748
5336
  }
4749
5337
  }
4750
- return super.decodeAttributes(buffer);
5338
+ return true;
5339
+ }
5340
+ // Two-phase decode (see AttributesDecoder): parse everything that reads the
5341
+ // buffer -- sequence, portable headers (deferring raw rANS symbol decodes),
5342
+ // and the transform parameters -- so the deferred streams of ALL attributes
5343
+ // decoders can then be paired, and finish (zigzag, prediction, inverse
5344
+ // transform) runs per decoder in the original order afterwards. None of the
5345
+ // finish work reads the buffer, and dependents only read parent portable
5346
+ // VALUES in finish, so ordering and output stay identical.
5347
+ decodeAttributesParse(buffer) {
5348
+ if (!this._prepareSequence()) {
5349
+ return false;
5350
+ }
5351
+ const numAttributes = this.getNumAttributes();
5352
+ for (let i = 0; i < numAttributes; i++) {
5353
+ if (!this._sequentialDecoders[i].decodePortableAttributeParse(this._pointIds, buffer)) {
5354
+ return false;
5355
+ }
5356
+ }
5357
+ return this.decodeDataNeededByPortableTransforms(buffer);
5358
+ }
5359
+ collectPendingSymbolStreams(out) {
5360
+ const numAttributes = this.getNumAttributes();
5361
+ for (let i = 0; i < numAttributes; i++) {
5362
+ const pending = this._sequentialDecoders[i].pendingSymbolStream();
5363
+ if (pending !== null) {
5364
+ out.push(pending);
5365
+ }
5366
+ }
5367
+ }
5368
+ decodeAttributesFinish() {
5369
+ const numAttributes = this.getNumAttributes();
5370
+ for (let i = 0; i < numAttributes; i++) {
5371
+ if (!this._sequentialDecoders[i].decodePortableAttributeFinish()) {
5372
+ return false;
5373
+ }
5374
+ }
5375
+ return this.transformAttributesToOriginalFormat();
4751
5376
  }
4752
5377
  getPortableAttribute(pointAttributeId) {
4753
5378
  const locId = this.getLocalIdForPointAttribute(pointAttributeId);
@@ -4874,14 +5499,13 @@ var DepthFirstTraverser = class {
4874
5499
  // Scratch buffers are set up here rather than in init() so a shared-traversal
4875
5500
  // -cache hit — where generateSequence returns before any traversal — skips
4876
5501
  // 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
5502
+ // ~500-primitive manablade bundle, which shares attribute corner tables across
4878
5503
  // primitives). Uint8Array (0/1) instead of Array(bool): these flags are read
4879
5504
  // and written on every corner of the hottest decode loop (traverseFromCorner).
4880
5505
  // Decode-scoped scratch: released in bulk at the end of the decode.
4881
5506
  onTraversalStart() {
4882
5507
  const cornerTable = this._cornerTable;
4883
5508
  this._isFaceVisited = scratchUint8Zeroed(cornerTable.numFaces());
4884
- this._isVertexVisited = scratchUint8Zeroed(cornerTable.numVertices());
4885
5509
  this._cornerTraversalStack = scratchInt32(this._numCorners);
4886
5510
  }
4887
5511
  onTraversalEnd() {
@@ -4891,7 +5515,6 @@ var DepthFirstTraverser = class {
4891
5515
  return true;
4892
5516
  }
4893
5517
  const isFaceVisited = this._isFaceVisited;
4894
- const isVertexVisited = this._isVertexVisited;
4895
5518
  const observer = this._observer;
4896
5519
  const cornerToVertex = this._cornerToVertex;
4897
5520
  const oppositeCorners = this._oppositeCorners;
@@ -4915,14 +5538,12 @@ var DepthFirstTraverser = class {
4915
5538
  if (nextVert === kInvalidVertexIndex2 || prevVert === kInvalidVertexIndex2) {
4916
5539
  return false;
4917
5540
  }
4918
- if (!isVertexVisited[nextVert]) {
4919
- isVertexVisited[nextVert] = 1;
5541
+ if (vertexToEncodedMap[nextVert] < 0) {
4920
5542
  outPointIds[numOutPoints++] = obsFaces[nextCorner];
4921
5543
  encodedToCornerMap[numValues] = nextCorner;
4922
5544
  vertexToEncodedMap[nextVert] = numValues++;
4923
5545
  }
4924
- if (!isVertexVisited[prevVert]) {
4925
- isVertexVisited[prevVert] = 1;
5546
+ if (vertexToEncodedMap[prevVert] < 0) {
4926
5547
  outPointIds[numOutPoints++] = obsFaces[prevCorner];
4927
5548
  encodedToCornerMap[numValues] = prevCorner;
4928
5549
  vertexToEncodedMap[prevVert] = numValues++;
@@ -4943,27 +5564,26 @@ var DepthFirstTraverser = class {
4943
5564
  encodingData.numValues = numValues;
4944
5565
  return false;
4945
5566
  }
4946
- if (!isVertexVisited[vertId]) {
5567
+ const faceBase = faceId * 3;
5568
+ const nextCornerId = cornerId === faceBase + 2 ? faceBase : cornerId + 1;
5569
+ if (vertexToEncodedMap[vertId] < 0) {
4947
5570
  const lc = vertexLeftmost[vertId];
4948
5571
  let onBoundary = true;
4949
- if (lc !== void 0 && lc >= 0) {
5572
+ if (lc >= 0) {
4950
5573
  const nextLc = lc % 3 === 2 ? lc - 2 : lc + 1;
4951
5574
  onBoundary = oppositeCorners[nextLc] < 0;
4952
5575
  }
4953
- isVertexVisited[vertId] = 1;
4954
5576
  outPointIds[numOutPoints++] = obsFaces[cornerId];
4955
5577
  encodedToCornerMap[numValues] = cornerId;
4956
5578
  vertexToEncodedMap[vertId] = numValues++;
4957
5579
  if (!onBoundary) {
4958
- const nextCornerId2 = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4959
- cornerId = oppositeCorners[nextCornerId2];
5580
+ cornerId = oppositeCorners[nextCornerId];
4960
5581
  faceId = cornerId / 3 | 0;
4961
5582
  continue;
4962
5583
  }
4963
5584
  }
4964
- const nextCornerId = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4965
5585
  const rightCornerId = oppositeCorners[nextCornerId];
4966
- const prevCornerId = cornerId % 3 === 0 ? cornerId + 2 : cornerId - 1;
5586
+ const prevCornerId = cornerId === faceBase ? faceBase + 2 : cornerId - 1;
4967
5587
  const leftCornerId = oppositeCorners[prevCornerId];
4968
5588
  const rightFaceId = rightCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : rightCornerId / 3 | 0;
4969
5589
  const leftFaceId = leftCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : leftCornerId / 3 | 0;
@@ -5193,6 +5813,9 @@ var MeshTraversalSequencer = class {
5193
5813
  _outPointIds;
5194
5814
  _numOutPoints;
5195
5815
  _traversalCache;
5816
+ // The cache entry the last generateSequence resolved to (hit or store);
5817
+ // carries the shared indicesMap.
5818
+ _cacheEntry;
5196
5819
  constructor(mesh, encodingData, traversalCache = null) {
5197
5820
  this._mesh = mesh;
5198
5821
  this._encodingData = encodingData;
@@ -5200,6 +5823,7 @@ var MeshTraversalSequencer = class {
5200
5823
  this._outPointIds = new Int32Array(0);
5201
5824
  this._numOutPoints = 0;
5202
5825
  this._traversalCache = traversalCache;
5826
+ this._cacheEntry = null;
5203
5827
  }
5204
5828
  setTraverser(traverser) {
5205
5829
  this._traverser = traverser;
@@ -5214,6 +5838,7 @@ var MeshTraversalSequencer = class {
5214
5838
  if (cached !== void 0) {
5215
5839
  this._outPointIds = cached.pointIds;
5216
5840
  this._encodingData.adoptTraversalResult(cached.vertexMap, cached.cornerMap, cached.numValues);
5841
+ this._cacheEntry = cached;
5217
5842
  return true;
5218
5843
  }
5219
5844
  }
@@ -5229,12 +5854,15 @@ var MeshTraversalSequencer = class {
5229
5854
  byMethod = /* @__PURE__ */ new Map();
5230
5855
  this._traversalCache.set(cacheKey, byMethod);
5231
5856
  }
5232
- byMethod.set(methodId, {
5857
+ const entry = {
5233
5858
  pointIds: this._outPointIds,
5234
5859
  vertexMap: this._encodingData.vertexToEncodedAttributeValueIndexMap,
5235
5860
  cornerMap: this._encodingData.encodedAttributeValueIndexToCornerMap,
5236
- numValues: this._encodingData.numValues
5237
- });
5861
+ numValues: this._encodingData.numValues,
5862
+ indicesMap: null
5863
+ };
5864
+ byMethod.set(methodId, entry);
5865
+ this._cacheEntry = entry;
5238
5866
  }
5239
5867
  return true;
5240
5868
  }
@@ -5245,6 +5873,11 @@ var MeshTraversalSequencer = class {
5245
5873
  this._outPointIds[this._numOutPoints++] = pointId;
5246
5874
  }
5247
5875
  updatePointToAttributeIndexMapping(attribute) {
5876
+ const entry = this._cacheEntry;
5877
+ if (entry !== null && entry.indicesMap !== null) {
5878
+ attribute.setExplicitMappingShared(entry.indicesMap);
5879
+ return true;
5880
+ }
5248
5881
  const cornerTable = this._traverser.cornerTable();
5249
5882
  const numFaces = this._mesh.numFaces();
5250
5883
  const numPoints = this._mesh.numPoints();
@@ -5266,11 +5899,14 @@ var MeshTraversalSequencer = class {
5266
5899
  }
5267
5900
  indicesMap[pointId] = attEntryId;
5268
5901
  }
5902
+ if (entry !== null) {
5903
+ entry.indicesMap = attribute.indicesMap;
5904
+ }
5269
5905
  return true;
5270
5906
  }
5271
5907
  _generateSequenceInternal() {
5272
5908
  this._numOutPoints = 0;
5273
- this._outPointIds = new Int32Array(this._mesh.numPoints());
5909
+ this._outPointIds = scratchInt32(this._mesh.numPoints());
5274
5910
  this._traverser.onTraversalStart();
5275
5911
  const numFaces = this._traverser.cornerTable().numFaces();
5276
5912
  for (let i = 0; i < numFaces && this._traverser._numVisitedFaces < numFaces; ++i) {
@@ -5461,14 +6097,14 @@ var MeshEdgebreakerDecoderImpl = class {
5461
6097
  this._attributeData = [];
5462
6098
  for (let i = 0; i < numAttributeData; ++i) {
5463
6099
  const ad = new AttributeData();
5464
- ad.attributeSeamCorners = new Int32Array(numFaces * 3);
6100
+ ad.attributeSeamCorners = scratchInt32(numFaces * 3);
5465
6101
  ad.numSeamCorners = 0;
5466
6102
  this._attributeData.push(ad);
5467
6103
  }
5468
6104
  if (!this._cornerTable.reset(numFaces, this._numEncodedVertices + numEncodedSplitSymbols)) {
5469
6105
  return false;
5470
6106
  }
5471
- this._isVertHole = new Uint8Array(this._numEncodedVertices + numEncodedSplitSymbols).fill(1);
6107
+ this._isVertHole = scratchUint8Filled(this._numEncodedVertices + numEncodedSplitSymbols, 1);
5472
6108
  if (this._decodeHoleAndTopologySplitEvents(this._decoder.buffer()) === -1) {
5473
6109
  return false;
5474
6110
  }
@@ -5493,20 +6129,37 @@ var MeshEdgebreakerDecoderImpl = class {
5493
6129
  }
5494
6130
  this._traversalDecoder.done();
5495
6131
  let previousConnectivityData = null;
6132
+ let previousSeamCorners = null;
6133
+ let previousSeamCount = 0;
5496
6134
  for (let i = 0; i < this._attributeData.length; ++i) {
5497
6135
  const connectivityData = this._attributeData[i].connectivityData;
5498
- connectivityData.initEmpty(this._cornerTable);
5499
6136
  const seamCorners = this._attributeData[i].attributeSeamCorners;
5500
6137
  const seamCount = this._attributeData[i].numSeamCorners;
5501
- for (let s = 0; s < seamCount; ++s) {
5502
- connectivityData.addSeamEdge(seamCorners[s]);
6138
+ let sameAsPrevious = previousConnectivityData !== null && seamCount === previousSeamCount;
6139
+ if (sameAsPrevious) {
6140
+ const previous = previousSeamCorners;
6141
+ for (let s = 0; s < seamCount; ++s) {
6142
+ if (seamCorners[s] !== previous[s]) {
6143
+ sameAsPrevious = false;
6144
+ break;
6145
+ }
6146
+ }
5503
6147
  }
5504
- if (connectivityData.hasSameSeams(previousConnectivityData)) {
5505
- connectivityData.adoptVertexRecompute(previousConnectivityData);
5506
- } else if (!connectivityData.recomputeVertices(null, null)) {
5507
- return false;
6148
+ if (sameAsPrevious) {
6149
+ connectivityData.adoptFrom(previousConnectivityData);
6150
+ } else {
6151
+ connectivityData.initEmpty(this._cornerTable);
6152
+ connectivityData.reserveSeamEdges(seamCount);
6153
+ for (let s = 0; s < seamCount; ++s) {
6154
+ connectivityData.addSeamEdge(seamCorners[s]);
6155
+ }
6156
+ if (!connectivityData.recomputeVertices(null, null)) {
6157
+ return false;
6158
+ }
5508
6159
  }
5509
6160
  previousConnectivityData = connectivityData;
6161
+ previousSeamCorners = seamCorners;
6162
+ previousSeamCount = seamCount;
5510
6163
  }
5511
6164
  this._posEncodingData.init(this._cornerTable.numVertices());
5512
6165
  for (let i = 0; i < this._attributeData.length; ++i) {
@@ -5545,39 +6198,27 @@ var MeshEdgebreakerDecoderImpl = class {
5545
6198
  const activeCornerStack = scratchInt32(numSymbols + this._topologySplitData.length + 16);
5546
6199
  let activeCornerStackSize = 0;
5547
6200
  const topologySplitActiveCorners = /* @__PURE__ */ new Map();
6201
+ const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5548
6202
  const invalidVertices = [];
5549
6203
  const removeInvalidVertices = this._attributeData.length === 0;
5550
6204
  let maxNumVertices = this._isVertHole.length;
5551
6205
  let numFacesDecoded = 0;
5552
6206
  const cornerToVertex = this._cornerTable._cornerToVertex;
5553
6207
  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
6208
  const vc = this._cornerTable;
6209
+ let vertexCorners = vc._vertexCorners;
6210
+ const isVertHole = this._isVertHole;
6211
+ const traversalDecoder = this._traversalDecoder;
5571
6212
  for (let symbolId = 0; symbolId < numSymbols; ++symbolId) {
5572
6213
  const faceIndex = numFacesDecoded++;
5573
6214
  let checkTopologySplit = false;
5574
- const symbol = this._traversalDecoder.decodeSymbol();
6215
+ const symbol = traversalDecoder.decodeSymbol();
5575
6216
  if (symbol === TOPOLOGY_C) {
5576
6217
  if (activeCornerStackSize === 0) return -1;
5577
6218
  const cornerA = activeCornerStack[activeCornerStackSize - 1];
5578
6219
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5579
6220
  const vertexX = cornerToVertex[nA];
5580
- const lmcX = vc._vertexCorners[vertexX];
6221
+ const lmcX = vertexCorners[vertexX];
5581
6222
  const cornerB = lmcX % 3 === 2 ? lmcX - 2 : lmcX + 1;
5582
6223
  if (cornerA === cornerB) return -1;
5583
6224
  if (oppositeCorners[cornerA] !== kInvalidCornerIndex6 || oppositeCorners[cornerB] !== kInvalidCornerIndex6) {
@@ -5596,8 +6237,8 @@ var MeshEdgebreakerDecoderImpl = class {
5596
6237
  cornerToVertex[corner] = vertexX;
5597
6238
  cornerToVertex[corner + 1] = vertBNext;
5598
6239
  cornerToVertex[corner + 2] = vertAPrev;
5599
- vc._vertexCorners[vertAPrev] = corner + 2;
5600
- this._isVertHole[vertexX] = 0;
6240
+ vertexCorners[vertAPrev] = corner + 2;
6241
+ isVertHole[vertexX] = 0;
5601
6242
  activeCornerStack[activeCornerStackSize - 1] = corner;
5602
6243
  } else if (symbol === TOPOLOGY_R || symbol === TOPOLOGY_L) {
5603
6244
  if (activeCornerStackSize === 0) return -1;
@@ -5618,14 +6259,20 @@ var MeshEdgebreakerDecoderImpl = class {
5618
6259
  }
5619
6260
  oppositeCorners[oppCorner] = cornerA;
5620
6261
  oppositeCorners[cornerA] = oppCorner;
5621
- const newVertIndex = this._cornerTable.addNewVertex();
5622
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6262
+ let newVertIndex;
6263
+ if (vc._numVertices < vertexCorners.length) {
6264
+ newVertIndex = vc._numVertices++;
6265
+ } else {
6266
+ newVertIndex = vc.addNewVertex();
6267
+ vertexCorners = vc._vertexCorners;
6268
+ }
6269
+ if (vc._numVertices > maxNumVertices) return -1;
5623
6270
  cornerToVertex[oppCorner] = newVertIndex;
5624
- vc._vertexCorners[newVertIndex] = oppCorner;
6271
+ vertexCorners[newVertIndex] = oppCorner;
5625
6272
  const pA = cornerA % 3 === 0 ? cornerA + 2 : cornerA - 1;
5626
6273
  const vertexR = cornerToVertex[pA];
5627
6274
  cornerToVertex[cornerR] = vertexR;
5628
- vc._vertexCorners[vertexR] = cornerR;
6275
+ vertexCorners[vertexR] = cornerR;
5629
6276
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5630
6277
  cornerToVertex[cornerL] = cornerToVertex[nA];
5631
6278
  activeCornerStack[activeCornerStackSize - 1] = corner;
@@ -5657,11 +6304,11 @@ var MeshEdgebreakerDecoderImpl = class {
5657
6304
  const pB = cornerB % 3 === 0 ? cornerB + 2 : cornerB - 1;
5658
6305
  const vertBPrev = cornerToVertex[pB];
5659
6306
  cornerToVertex[corner + 2] = vertBPrev;
5660
- vc._vertexCorners[vertBPrev] = corner + 2;
6307
+ vertexCorners[vertBPrev] = corner + 2;
5661
6308
  let cornerN = cornerB % 3 === 2 ? cornerB - 2 : cornerB + 1;
5662
6309
  const vertexN = cornerToVertex[cornerN];
5663
- this._traversalDecoder.mergeVertices(vertexP, vertexN);
5664
- vc._vertexCorners[vertexP] = vc._vertexCorners[vertexN];
6310
+ traversalDecoder.mergeVertices(vertexP, vertexN);
6311
+ vertexCorners[vertexP] = vertexCorners[vertexN];
5665
6312
  const firstCorner = cornerN;
5666
6313
  while (cornerN !== kInvalidCornerIndex6) {
5667
6314
  cornerToVertex[cornerN] = vertexP;
@@ -5672,32 +6319,38 @@ var MeshEdgebreakerDecoderImpl = class {
5672
6319
  return -1;
5673
6320
  }
5674
6321
  }
5675
- vc._vertexCorners[vertexN] = -1;
6322
+ vertexCorners[vertexN] = -1;
5676
6323
  if (removeInvalidVertices) {
5677
6324
  invalidVertices.push(vertexN);
5678
6325
  }
5679
6326
  activeCornerStack[activeCornerStackSize - 1] = corner;
5680
6327
  } else if (symbol === TOPOLOGY_E) {
5681
6328
  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;
6329
+ let firstVertIndex;
6330
+ if (vc._numVertices + 3 <= vertexCorners.length) {
6331
+ firstVertIndex = vc._numVertices;
6332
+ vc._numVertices += 3;
6333
+ } else {
6334
+ firstVertIndex = vc.addNewVertex();
6335
+ vc.addNewVertex();
6336
+ vc.addNewVertex();
6337
+ vertexCorners = vc._vertexCorners;
6338
+ }
6339
+ if (vc._numVertices > maxNumVertices) return -1;
5686
6340
  cornerToVertex[corner] = firstVertIndex;
5687
6341
  cornerToVertex[corner + 1] = firstVertIndex + 1;
5688
6342
  cornerToVertex[corner + 2] = firstVertIndex + 2;
5689
- vc._vertexCorners[firstVertIndex] = corner;
5690
- vc._vertexCorners[firstVertIndex + 1] = corner + 1;
5691
- vc._vertexCorners[firstVertIndex + 2] = corner + 2;
6343
+ vertexCorners[firstVertIndex] = corner;
6344
+ vertexCorners[firstVertIndex + 1] = corner + 1;
6345
+ vertexCorners[firstVertIndex + 2] = corner + 2;
5692
6346
  activeCornerStack[activeCornerStackSize++] = corner;
5693
6347
  checkTopologySplit = true;
5694
6348
  } else {
5695
6349
  return -1;
5696
6350
  }
5697
- this._traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
5698
- if (checkTopologySplit) {
6351
+ traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
6352
+ if (checkTopologySplit && this._topologySplitData.length > 0) {
5699
6353
  const encoderSymbolId = numSymbols - symbolId - 1;
5700
- const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5701
6354
  while (this._isTopologySplit(encoderSymbolId, splitResult)) {
5702
6355
  if (splitResult.encoderSplitSymbolId < 0) return -1;
5703
6356
  const actTopCorner = activeCornerStack[activeCornerStackSize - 1];
@@ -5712,15 +6365,39 @@ var MeshEdgebreakerDecoderImpl = class {
5712
6365
  }
5713
6366
  }
5714
6367
  }
5715
- if (this._cornerTable.numVertices() > maxNumVertices) {
6368
+ if (vc._numVertices > maxNumVertices) {
6369
+ return -1;
6370
+ }
6371
+ numFacesDecoded = this._decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded);
6372
+ if (numFacesDecoded === -1) {
6373
+ return -1;
6374
+ }
6375
+ if (numFacesDecoded !== this._cornerTable.numFaces()) {
5716
6376
  return -1;
5717
6377
  }
6378
+ return this._removeInvalidVertices(invalidVertices);
6379
+ }
6380
+ // Connects the remaining active-stack corners to newly decoded start faces.
6381
+ // Returns the updated decoded-face count, or -1 on malformed input.
6382
+ _decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded) {
6383
+ const ct = this._cornerTable;
6384
+ const cornerToVertex = ct._cornerToVertex;
6385
+ const oppositeCorners = ct._oppositeCorners;
6386
+ const vertexCorners = ct._vertexCorners;
6387
+ const isVertHole = this._isVertHole;
6388
+ const traversalDecoder = this._traversalDecoder;
6389
+ const numCorners = ct.numCorners();
6390
+ const numFaces = ct.numFaces();
6391
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6392
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6393
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6394
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
5718
6395
  while (activeCornerStackSize > 0) {
5719
6396
  const corner = activeCornerStack[activeCornerStackSize - 1];
5720
6397
  activeCornerStackSize--;
5721
- const interiorFace = this._traversalDecoder.decodeStartFaceConfiguration();
6398
+ const interiorFace = traversalDecoder.decodeStartFaceConfiguration();
5722
6399
  if (interiorFace) {
5723
- if (numFacesDecoded >= this._cornerTable.numFaces()) {
6400
+ if (numFacesDecoded >= numFaces) {
5724
6401
  return -1;
5725
6402
  }
5726
6403
  const cornerA = corner;
@@ -5746,9 +6423,9 @@ var MeshEdgebreakerDecoderImpl = class {
5746
6423
  cornerToVertex[newCorner] = vertX;
5747
6424
  cornerToVertex[newCorner + 1] = vertP;
5748
6425
  cornerToVertex[newCorner + 2] = vertN;
5749
- this._isVertHole[vertX] = 0;
5750
- this._isVertHole[vertP] = 0;
5751
- this._isVertHole[vertN] = 0;
6426
+ isVertHole[vertX] = 0;
6427
+ isVertHole[vertP] = 0;
6428
+ isVertHole[vertN] = 0;
5752
6429
  this._initFaceConfigurations.push(true);
5753
6430
  this._initCorners.push(newCorner);
5754
6431
  } else {
@@ -5756,10 +6433,34 @@ var MeshEdgebreakerDecoderImpl = class {
5756
6433
  this._initCorners.push(corner);
5757
6434
  }
5758
6435
  }
5759
- if (numFacesDecoded !== this._cornerTable.numFaces()) {
5760
- return -1;
5761
- }
5762
- let numVertices = this._cornerTable.numVertices();
6436
+ return numFacesDecoded;
6437
+ }
6438
+ // Removes invalid (isolated) vertices by swapping them with the last valid
6439
+ // vertex in the table, matching C++ mesh_edgebreaker_decoder_impl.cc (the
6440
+ // forward iteration order matters). Returns the final vertex count, or -1.
6441
+ _removeInvalidVertices(invalidVertices) {
6442
+ const ct = this._cornerTable;
6443
+ const cornerToVertex = ct._cornerToVertex;
6444
+ const oppositeCorners = ct._oppositeCorners;
6445
+ const vertexCorners = ct._vertexCorners;
6446
+ const isVertHole = this._isVertHole;
6447
+ const numCorners = ct.numCorners();
6448
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6449
+ const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
6450
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6451
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6452
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
6453
+ const swingLeft = (c) => {
6454
+ const n = next(c);
6455
+ const o = opposite(n);
6456
+ return o < 0 ? -1 : next(o);
6457
+ };
6458
+ const swingRight = (c) => {
6459
+ const p = prev(c);
6460
+ const o = opposite(p);
6461
+ return o < 0 ? -1 : prev(o);
6462
+ };
6463
+ let numVertices = ct.numVertices();
5763
6464
  for (let ivIdx = 0; ivIdx < invalidVertices.length; ++ivIdx) {
5764
6465
  const invalidVert = invalidVertices[ivIdx];
5765
6466
  let srcVert = numVertices - 1;
@@ -5789,10 +6490,10 @@ var MeshEdgebreakerDecoderImpl = class {
5789
6490
  cid = swingRight(cid);
5790
6491
  }
5791
6492
  }
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;
6493
+ vertexCorners[invalidVert] = leftMostCorner(srcVert);
6494
+ vertexCorners[srcVert] = -1;
6495
+ isVertHole[invalidVert] = isVertHole[srcVert];
6496
+ isVertHole[srcVert] = 0;
5796
6497
  numVertices--;
5797
6498
  }
5798
6499
  return numVertices;
@@ -5835,14 +6536,55 @@ var MeshEdgebreakerDecoderImpl = class {
5835
6536
  // per-corner decodeNextBit work. Within each face the three corners are
5836
6537
  // visited in encoder edge order [base, next, prev] = [c, c+1, c+2] (the
5837
6538
  // caller always starts a face at its base corner, so next/prev need no wrap).
6539
+ //
6540
+ // The face comparison the C++ makes -- floor(oppCorner/3) >= floor(cc/3) for
6541
+ // the face's base corner -- is just `oppCorner >= faceBaseCorner`, since the
6542
+ // base corner is a multiple of 3. That removes the per-corner division; the
6543
+ // invalid-corner case (-1) is still handled by the branch above it.
5838
6544
  _decodeAttributeConnectivities() {
5839
6545
  const oppositeCorners = this._cornerTable.oppositeCornerArray();
5840
6546
  const attributeData = this._attributeData;
5841
6547
  const numAttrData = attributeData.length;
5842
6548
  const connectivityDecoders = this._traversalDecoder._attributeConnectivityDecoders;
5843
6549
  const numCorners = this._cornerTable.numCorners();
6550
+ if (numAttrData === 1) {
6551
+ const ad = attributeData[0];
6552
+ const seamCorners = ad.attributeSeamCorners;
6553
+ let numSeamCorners = ad.numSeamCorners;
6554
+ const decoder = connectivityDecoders[0];
6555
+ const ans = decoder.ansDecoder_;
6556
+ const p = decoder.p_;
6557
+ const buf = ans.buf;
6558
+ const bufStart = ans.bufStart;
6559
+ let state = ans.state;
6560
+ let bufOffset = ans.bufOffset;
6561
+ for (let corner = 0; corner < numCorners; corner += 3) {
6562
+ for (let k = 0; k < 3; ++k) {
6563
+ const cc = corner + k;
6564
+ const oppCorner = oppositeCorners[cc];
6565
+ if (oppCorner === kInvalidCornerIndex6) {
6566
+ seamCorners[numSeamCorners++] = cc;
6567
+ } else if (oppCorner >= corner) {
6568
+ if (state < ANS_L_BASE && bufOffset > bufStart) {
6569
+ state = state << 8 | buf[--bufOffset];
6570
+ }
6571
+ const rem = state & 255;
6572
+ const xn = (state >>> 8) * p;
6573
+ if (rem < p) {
6574
+ state = xn + rem;
6575
+ seamCorners[numSeamCorners++] = cc;
6576
+ } else {
6577
+ state = state - xn - p;
6578
+ }
6579
+ }
6580
+ }
6581
+ }
6582
+ ans.state = state;
6583
+ ans.bufOffset = bufOffset;
6584
+ ad.numSeamCorners = numSeamCorners;
6585
+ return;
6586
+ }
5844
6587
  for (let corner = 0; corner < numCorners; corner += 3) {
5845
- const srcFaceId = corner / 3 | 0;
5846
6588
  for (let k = 0; k < 3; ++k) {
5847
6589
  const cc = corner + k;
5848
6590
  const oppCorner = oppositeCorners[cc];
@@ -5851,7 +6593,7 @@ var MeshEdgebreakerDecoderImpl = class {
5851
6593
  const ad = attributeData[i];
5852
6594
  ad.attributeSeamCorners[ad.numSeamCorners++] = cc;
5853
6595
  }
5854
- } else if ((oppCorner / 3 | 0) >= srcFaceId) {
6596
+ } else if (oppCorner >= corner) {
5855
6597
  for (let i = 0; i < numAttrData; ++i) {
5856
6598
  if (connectivityDecoders[i].decodeNextBit()) {
5857
6599
  const ad = attributeData[i];
@@ -5882,10 +6624,10 @@ var MeshEdgebreakerDecoderImpl = class {
5882
6624
  const attributeData = this._attributeData;
5883
6625
  const numAttrData = attributeData.length;
5884
6626
  let numPoints = 0;
5885
- const cornerToPointMap = new Int32Array(ct.numCorners());
6627
+ const cornerToPointMap = scratchInt32(ct.numCorners());
5886
6628
  const numVertices = ct.numVertices();
5887
6629
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
5888
- const baseOpp = ct.oppositeCornerArray();
6630
+ const swingRight = ct.swingRightArray();
5889
6631
  const _baseCornerToVertex = ct.cornerToVertexArray();
5890
6632
  const isVertHole = this._isVertHole;
5891
6633
  const attCornerToVertex = new Array(numAttrData);
@@ -5899,7 +6641,7 @@ var MeshEdgebreakerDecoderImpl = class {
5899
6641
  if (numAttrData === 1) {
5900
6642
  anyAttVertexOnSeam = attVertexOnSeam[0];
5901
6643
  } else {
5902
- anyAttVertexOnSeam = new Uint8Array(numVertices);
6644
+ anyAttVertexOnSeam = scratchUint8Zeroed(numVertices);
5903
6645
  for (let i = 0; i < numAttrData; ++i) {
5904
6646
  const attSeam = attVertexOnSeam[i];
5905
6647
  for (let v = 0; v < numVertices; ++v) {
@@ -5917,37 +6659,24 @@ var MeshEdgebreakerDecoderImpl = class {
5917
6659
  const initialC = c;
5918
6660
  const pointId = numPoints++;
5919
6661
  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;
6662
+ c = swingRight[initialC];
5924
6663
  while (c !== kInvalidCornerIndex6 && c !== initialC) {
5925
6664
  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;
6665
+ c = swingRight[c];
5930
6666
  }
5931
6667
  } else {
5932
6668
  let deduplicationFirstCorner = c;
5933
- let rem, pv, opp;
5934
6669
  if (!isVertHole[v]) {
5935
6670
  if (numAttrData === 1) {
5936
6671
  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;
6672
+ let actC = swingRight[c];
5941
6673
  while (actC !== c) {
5942
6674
  if (actC === kInvalidCornerIndex6) return false;
5943
6675
  if (singleAttC2V[actC] !== vertId) {
5944
6676
  deduplicationFirstCorner = actC;
5945
6677
  break;
5946
6678
  }
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;
6679
+ actC = swingRight[actC];
5951
6680
  }
5952
6681
  } else {
5953
6682
  for (let i = 0; i < numAttrData; ++i) {
@@ -5956,10 +6685,7 @@ var MeshEdgebreakerDecoderImpl = class {
5956
6685
  }
5957
6686
  const attC2V = attCornerToVertex[i];
5958
6687
  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;
6688
+ let actC = swingRight[c];
5963
6689
  let seamFound = false;
5964
6690
  while (actC !== c) {
5965
6691
  if (actC === kInvalidCornerIndex6) return false;
@@ -5968,10 +6694,7 @@ var MeshEdgebreakerDecoderImpl = class {
5968
6694
  seamFound = true;
5969
6695
  break;
5970
6696
  }
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;
6697
+ actC = swingRight[actC];
5975
6698
  }
5976
6699
  if (seamFound) break;
5977
6700
  }
@@ -5980,10 +6703,7 @@ var MeshEdgebreakerDecoderImpl = class {
5980
6703
  c = deduplicationFirstCorner;
5981
6704
  cornerToPointMap[c] = numPoints++;
5982
6705
  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;
6706
+ c = swingRight[c];
5987
6707
  while (c !== kInvalidCornerIndex6 && c !== deduplicationFirstCorner) {
5988
6708
  let attributeSeam;
5989
6709
  if (numAttrData === 1) {
@@ -6004,10 +6724,7 @@ var MeshEdgebreakerDecoderImpl = class {
6004
6724
  cornerToPointMap[c] = cornerToPointMap[prevC];
6005
6725
  }
6006
6726
  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;
6727
+ c = swingRight[c];
6011
6728
  }
6012
6729
  }
6013
6730
  }
@@ -6033,8 +6750,8 @@ var MeshAttributeIndicesEncodingData = class {
6033
6750
  this._numValues = 0;
6034
6751
  }
6035
6752
  init(numVertices) {
6036
- this._vertexToEncodedAttributeValueIndexMap = new Int32Array(numVertices);
6037
- this._encodedAttributeValueIndexToCornerMap = new Int32Array(numVertices);
6753
+ this._vertexToEncodedAttributeValueIndexMap = scratchInt32Filled(numVertices, -1);
6754
+ this._encodedAttributeValueIndexToCornerMap = scratchInt32(numVertices);
6038
6755
  this._numValues = 0;
6039
6756
  }
6040
6757
  // Adopts a traversal result from an identical corner table, avoiding a
@@ -6084,6 +6801,8 @@ var CornerTable = class {
6084
6801
  // corner -> opposite corner
6085
6802
  _vertexCorners;
6086
6803
  // vertex -> left-most corner
6804
+ _swingRight;
6805
+ // corner -> next corner around its vertex, CW
6087
6806
  constructor() {
6088
6807
  this._numFaces = 0;
6089
6808
  this._numCorners = 0;
@@ -6091,16 +6810,42 @@ var CornerTable = class {
6091
6810
  this._cornerToVertex = null;
6092
6811
  this._oppositeCorners = null;
6093
6812
  this._vertexCorners = null;
6813
+ this._swingRight = null;
6094
6814
  }
6095
6815
  reset(numFaces, numVertices) {
6096
6816
  this._numFaces = numFaces;
6097
6817
  this._numCorners = numFaces * 3;
6098
6818
  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);
6819
+ this._cornerToVertex = scratchInt32Filled(this._numCorners, -1);
6820
+ this._oppositeCorners = scratchInt32Filled(this._numCorners, -1);
6821
+ this._vertexCorners = scratchInt32Filled(numVertices, -1);
6822
+ this._swingRight = null;
6102
6823
  return true;
6103
6824
  }
6825
+ // swingRight(c) = previous(opposite(previous(c))) for every corner. Walking
6826
+ // the corner ring of a vertex is the inner loop of both the attribute-vertex
6827
+ // recompute and the point assignment, and each of those passes otherwise
6828
+ // pays two modulus-by-3 chains per step on top of the opposite lookup. Built
6829
+ // lazily -- callers only reach it once connectivity is final -- from
6830
+ // decode-scoped scratch, and dropped by reset().
6831
+ swingRightArray() {
6832
+ let table = this._swingRight;
6833
+ if (table === null) {
6834
+ const numCorners = this._numCorners;
6835
+ const opposite = this._oppositeCorners;
6836
+ table = scratchInt32(numCorners);
6837
+ for (let c = 0; c < numCorners; c += 3) {
6838
+ let o = opposite[c + 2];
6839
+ table[c] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6840
+ o = opposite[c];
6841
+ table[c + 1] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6842
+ o = opposite[c + 1];
6843
+ table[c + 2] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6844
+ }
6845
+ this._swingRight = table;
6846
+ }
6847
+ return table;
6848
+ }
6104
6849
  numFaces() {
6105
6850
  return this._numFaces;
6106
6851
  }
@@ -6144,8 +6889,7 @@ var CornerTable = class {
6144
6889
  this._numVertices++;
6145
6890
  if (newVertex >= this._vertexCorners.length) {
6146
6891
  const newCapacity = Math.max(newVertex + 1, this._vertexCorners.length * 2, 64);
6147
- const newArr = new Int32Array(newCapacity);
6148
- newArr.fill(-1);
6892
+ const newArr = scratchInt32Filled(newCapacity, -1);
6149
6893
  newArr.set(this._vertexCorners);
6150
6894
  this._vertexCorners = newArr;
6151
6895
  }
@@ -6176,6 +6920,10 @@ var MeshEdgebreakerTraversalDecoder = class {
6176
6920
  _attributeConnectivityDecoders;
6177
6921
  _numAttributeData;
6178
6922
  _decoderImpl;
6923
+ // _symbolBuffer's bit cursor, captured once bit decoding starts: decodeSymbol
6924
+ // runs per decoded face and would otherwise reach it through two property
6925
+ // loads and a method call per read.
6926
+ _symbolBits;
6179
6927
  constructor() {
6180
6928
  this._buffer = new DecoderBuffer();
6181
6929
  this._symbolBuffer = new DecoderBuffer();
@@ -6183,6 +6931,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6183
6931
  this._attributeConnectivityDecoders = null;
6184
6932
  this._numAttributeData = 0;
6185
6933
  this._decoderImpl = null;
6934
+ this._symbolBits = null;
6186
6935
  }
6187
6936
  init(decoder) {
6188
6937
  this._decoderImpl = decoder;
@@ -6217,6 +6966,21 @@ var MeshEdgebreakerTraversalDecoder = class {
6217
6966
  return this._startFaceDecoder.decodeNextBit() ? true : false;
6218
6967
  }
6219
6968
  decodeSymbol() {
6969
+ const bd = this._symbolBits;
6970
+ if (bd !== null) {
6971
+ const buf = bd._bitBuffer;
6972
+ const off = bd._bitOffset;
6973
+ const byteOffset = off >> 3;
6974
+ if (byteOffset + 4 < bd._byteLength) {
6975
+ const bits = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> (off & 7) & 7;
6976
+ if ((bits & 1) === TOPOLOGY_C) {
6977
+ bd._bitOffset = off + 1;
6978
+ return TOPOLOGY_C;
6979
+ }
6980
+ bd._bitOffset = off + 3;
6981
+ return bits;
6982
+ }
6983
+ }
6220
6984
  let symbol = this._symbolBuffer.decodeLeastSignificantBits32(1);
6221
6985
  if (symbol === TOPOLOGY_C) {
6222
6986
  return symbol;
@@ -6246,6 +7010,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6246
7010
  if (traversalSize === void 0) {
6247
7011
  return false;
6248
7012
  }
7013
+ this._symbolBits = this._symbolBuffer._bitDecoder;
6249
7014
  this._buffer.init(
6250
7015
  this._symbolBuffer.dataHead,
6251
7016
  this._symbolBuffer.remainingSize,
@@ -6398,7 +7163,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6398
7163
  _maxValence;
6399
7164
  _vertexValences;
6400
7165
  _contextSymbols;
7166
+ // Int32Array, not number[]: read and written once per decoded symbol.
6401
7167
  _contextCounters;
7168
+ // corner -> vertex of _cornerTable, cached at init(); the array is created
7169
+ // once by CornerTable.reset() before the traversal decoder is initialized and
7170
+ // never replaced, so the per-symbol hot path can read it without two property
7171
+ // loads.
7172
+ _cornerToVertex;
6402
7173
  constructor() {
6403
7174
  super();
6404
7175
  this._cornerTable = null;
@@ -6409,11 +7180,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6409
7180
  this._maxValence = 7;
6410
7181
  this._vertexValences = new Int32Array(0);
6411
7182
  this._contextSymbols = [];
6412
- this._contextCounters = [];
7183
+ this._contextCounters = new Int32Array(0);
7184
+ this._cornerToVertex = new Int32Array(0);
6413
7185
  }
6414
7186
  init(decoder) {
6415
7187
  super.init(decoder);
6416
7188
  this._cornerTable = decoder.getCornerTable();
7189
+ this._cornerToVertex = this._cornerTable._cornerToVertex;
6417
7190
  }
6418
7191
  setNumEncodedVertices(numVertices) {
6419
7192
  this._numVertices = numVertices;
@@ -6431,10 +7204,11 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6431
7204
  if (this._numVertices < 0) {
6432
7205
  return false;
6433
7206
  }
6434
- this._vertexValences = new Int32Array(this._numVertices);
7207
+ this._vertexValences = scratchInt32Filled(this._numVertices, 0);
6435
7208
  const numUniqueValences = this._maxValence - this._minValence + 1;
6436
7209
  this._contextSymbols = new Array(numUniqueValences);
6437
- this._contextCounters = new Array(numUniqueValences);
7210
+ this._contextCounters = new Int32Array(numUniqueValences);
7211
+ const pending = [];
6438
7212
  for (let i = 0; i < numUniqueValences; ++i) {
6439
7213
  const numSymbols = decodeVarint(outBuffer);
6440
7214
  if (numSymbols === void 0) {
@@ -6444,16 +7218,72 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6444
7218
  return false;
6445
7219
  }
6446
7220
  if (numSymbols > 0) {
6447
- this._contextSymbols[i] = new Uint32Array(numSymbols);
6448
- if (!decodeSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7221
+ this._contextSymbols[i] = scratchUint32(numSymbols);
7222
+ const scheme = outBuffer.decodeUint8();
7223
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
7224
+ if (!decodeTaggedSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7225
+ return false;
7226
+ }
7227
+ this._contextCounters[i] = numSymbols;
7228
+ continue;
7229
+ }
7230
+ if (scheme !== SymbolCodingMethod.SYMBOL_CODING_RAW) {
6449
7231
  return false;
6450
7232
  }
7233
+ const maxBitLength = outBuffer.decodeUint8();
7234
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
7235
+ return false;
7236
+ }
7237
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
7238
+ if (!decoder.create(outBuffer)) {
7239
+ return false;
7240
+ }
7241
+ if (decoder.numSymbols === 0) {
7242
+ return false;
7243
+ }
7244
+ if (!decoder.startDecoding(outBuffer)) {
7245
+ return false;
7246
+ }
7247
+ pending.push({ decoder, out: this._contextSymbols[i], count: numSymbols });
6451
7248
  this._contextCounters[i] = numSymbols;
6452
7249
  } else {
6453
7250
  this._contextSymbols[i] = new Uint32Array(0);
6454
7251
  this._contextCounters[i] = 0;
6455
7252
  }
6456
7253
  }
7254
+ const allU8 = pending.every((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7255
+ let p = 0;
7256
+ if (allU8) {
7257
+ while (pending.length - p >= 3) {
7258
+ const a = pending[p];
7259
+ const b = pending[p + 1];
7260
+ const c = pending[p + 2];
7261
+ ransDecodeSymbolsTrioU8(
7262
+ a.decoder.ans_,
7263
+ a.out,
7264
+ a.count,
7265
+ b.decoder.ans_,
7266
+ b.out,
7267
+ b.count,
7268
+ c.decoder.ans_,
7269
+ c.out,
7270
+ c.count
7271
+ );
7272
+ p += 3;
7273
+ }
7274
+ if (pending.length - p === 2) {
7275
+ const a = pending[p];
7276
+ const b = pending[p + 1];
7277
+ ransDecodeSymbolsPairU8(a.decoder.ans_, a.out, a.count, b.decoder.ans_, b.out, b.count);
7278
+ p += 2;
7279
+ }
7280
+ }
7281
+ for (; p < pending.length; ++p) {
7282
+ pending[p].decoder.ans_.decodeSymbols(pending[p].out, pending[p].count);
7283
+ }
7284
+ for (const entry of pending) {
7285
+ entry.decoder.endDecoding();
7286
+ }
6457
7287
  return true;
6458
7288
  }
6459
7289
  decodeSymbol() {
@@ -6473,7 +7303,7 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6473
7303
  return this._lastSymbol;
6474
7304
  }
6475
7305
  newActiveCornerReached(corner) {
6476
- const cornerToVertex = this._cornerTable._cornerToVertex;
7306
+ const cornerToVertex = this._cornerToVertex;
6477
7307
  const valences = this._vertexValences;
6478
7308
  const next = corner % 3 === 2 ? corner - 2 : corner + 1;
6479
7309
  const prev = corner % 3 === 0 ? corner + 2 : corner - 1;