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/index.js CHANGED
@@ -259,6 +259,90 @@ var Mesh = class extends PointCloud {
259
259
  }
260
260
  };
261
261
 
262
+ // src/decoder/core/ScratchArena.ts
263
+ var MAX_CLASS = 28;
264
+ var freeInt32 = [];
265
+ var freeUint32 = [];
266
+ var freeUint8 = [];
267
+ for (let i = 0; i <= MAX_CLASS; ++i) {
268
+ freeInt32.push([]);
269
+ freeUint32.push([]);
270
+ freeUint8.push([]);
271
+ }
272
+ var borrowedInt32 = [];
273
+ var borrowedUint32 = [];
274
+ var borrowedUint8 = [];
275
+ var sizeClass = (size) => size <= 1 ? 0 : 32 - Math.clz32(size - 1);
276
+ var takeInt32 = (size) => {
277
+ const k = sizeClass(size);
278
+ if (k > MAX_CLASS) return new Int32Array(size);
279
+ const bucket = freeInt32[k];
280
+ let pooled = bucket.length > 0 ? bucket.pop() : new Int32Array(size);
281
+ if (pooled.length < size) pooled = new Int32Array(1 << k);
282
+ borrowedInt32.push(pooled);
283
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
284
+ };
285
+ var takeUint32 = (size) => {
286
+ const k = sizeClass(size);
287
+ if (k > MAX_CLASS) return new Uint32Array(size);
288
+ const bucket = freeUint32[k];
289
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint32Array(size);
290
+ if (pooled.length < size) pooled = new Uint32Array(1 << k);
291
+ borrowedUint32.push(pooled);
292
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
293
+ };
294
+ var byteCapacity = (size) => size < 8 ? 8 : size + 7 & ~7;
295
+ var takeUint8 = (size) => {
296
+ const capacity = byteCapacity(size);
297
+ const k = sizeClass(capacity);
298
+ if (k > MAX_CLASS) return new Uint8Array(capacity);
299
+ const bucket = freeUint8[k];
300
+ let pooled = bucket.length > 0 ? bucket.pop() : new Uint8Array(capacity);
301
+ if (pooled.length < size) pooled = new Uint8Array(1 << k);
302
+ borrowedUint8.push(pooled);
303
+ return pooled.length === size ? pooled : pooled.subarray(0, size);
304
+ };
305
+ var scratchInt32 = (size) => takeInt32(size);
306
+ var scratchInt32Filled = (size, value) => {
307
+ const view = takeInt32(size);
308
+ view.fill(value);
309
+ return view;
310
+ };
311
+ var scratchUint32 = (size) => takeUint32(size);
312
+ var scratchUint32Zeroed = (size) => {
313
+ const view = takeUint32(size);
314
+ view.fill(0);
315
+ return view;
316
+ };
317
+ var scratchUint8 = (size) => takeUint8(size);
318
+ var scratchUint8Zeroed = (size) => {
319
+ const view = takeUint8(size);
320
+ view.fill(0);
321
+ return view;
322
+ };
323
+ var scratchUint8Filled = (size, value) => {
324
+ const view = takeUint8(size);
325
+ view.fill(value);
326
+ return view;
327
+ };
328
+ var releaseScratch = () => {
329
+ for (let i = 0; i < borrowedInt32.length; ++i) {
330
+ const buffer = borrowedInt32[i];
331
+ freeInt32[sizeClass(buffer.length)].push(buffer);
332
+ }
333
+ for (let i = 0; i < borrowedUint32.length; ++i) {
334
+ const buffer = borrowedUint32[i];
335
+ freeUint32[sizeClass(buffer.length)].push(buffer);
336
+ }
337
+ for (let i = 0; i < borrowedUint8.length; ++i) {
338
+ const buffer = borrowedUint8[i];
339
+ freeUint8[sizeClass(buffer.length)].push(buffer);
340
+ }
341
+ borrowedInt32.length = 0;
342
+ borrowedUint32.length = 0;
343
+ borrowedUint8.length = 0;
344
+ };
345
+
262
346
  // src/decoder/core/DataBuffer.ts
263
347
  var DataBuffer = class {
264
348
  _data;
@@ -283,6 +367,15 @@ var DataBuffer = class {
283
367
  resize(newSize) {
284
368
  this._resize(newSize);
285
369
  }
370
+ // Replaces the contents with a decode-scoped scratch buffer of exactly
371
+ // `size` bytes; the previous contents are dropped and the new ones are
372
+ // arbitrary, so every byte must be written before it is read. Only for
373
+ // buffers that never outlive the decode (see ScratchArena) — the portable
374
+ // attributes the integer decoders build and discard per attribute are the
375
+ // one caller, and their per-primitive allocation showed up in profiles.
376
+ adoptScratch(size) {
377
+ this._data = scratchUint8(size);
378
+ }
286
379
  write(bytePos, inArray, dataSize) {
287
380
  if (inArray instanceof Uint8Array) {
288
381
  this._data.set(inArray.length === dataSize ? inArray : inArray.subarray(0, dataSize), bytePos);
@@ -372,6 +465,19 @@ var PointAttribute = class extends GeometryAttribute {
372
465
  this._numUniqueEntries = numAttributeValues;
373
466
  return true;
374
467
  }
468
+ // Like reset(), but backs the attribute with decode-scoped scratch memory.
469
+ // Only valid for attributes that never escape the decode and whose every
470
+ // byte is written before it is read (see DataBuffer.adoptScratch).
471
+ resetScratch(numAttributeValues) {
472
+ if (this._attributeBuffer === null) {
473
+ this._attributeBuffer = new DataBuffer();
474
+ }
475
+ const entrySize = dataTypeLength(this.dataType) * this.numComponents;
476
+ this._attributeBuffer.adoptScratch(numAttributeValues * entrySize);
477
+ this.resetBuffer(this._attributeBuffer, entrySize, 0);
478
+ this._numUniqueEntries = numAttributeValues;
479
+ return true;
480
+ }
375
481
  get size() {
376
482
  return this._numUniqueEntries;
377
483
  }
@@ -414,6 +520,20 @@ var PointAttribute = class extends GeometryAttribute {
414
520
  this._identityMapping = false;
415
521
  this._indicesMap = new Uint32Array(numPoints);
416
522
  }
523
+ // Adopts an already-computed map, shared with other attributes of the same
524
+ // mesh. The map must be treated as read-only from here on (reads go through
525
+ // mappedIndex/extractTo; copyFrom slices).
526
+ setExplicitMappingShared(map) {
527
+ this._identityMapping = false;
528
+ this._indicesMap = map;
529
+ }
530
+ // Like setExplicitMappingUnfilled, but from decode-scoped scratch. Only for
531
+ // attributes that never escape the decode (see ScratchArena) -- the portable
532
+ // attributes the sequential decoders build and discard.
533
+ setExplicitMappingScratch(numPoints) {
534
+ this._identityMapping = false;
535
+ this._indicesMap = scratchUint32(numPoints);
536
+ }
417
537
  setAttributeTransformData(transformData) {
418
538
  this._attributeTransformData = transformData;
419
539
  }
@@ -909,48 +1029,6 @@ var DecoderBuffer = class {
909
1029
  }
910
1030
  };
911
1031
 
912
- // src/decoder/core/ScratchArena.ts
913
- var freeInt32 = [];
914
- var freeUint8 = [];
915
- var borrowedInt32 = [];
916
- var borrowedUint8 = [];
917
- var acquire = (free, borrowed, size) => {
918
- for (let i = free.length - 1; i >= 0; --i) {
919
- const buffer = free[i];
920
- if (buffer.length >= size) {
921
- free[i] = free[free.length - 1];
922
- free.pop();
923
- borrowed.push(buffer);
924
- return buffer;
925
- }
926
- }
927
- return null;
928
- };
929
- var scratchInt32 = (size) => {
930
- const pooled = acquire(freeInt32, borrowedInt32, size);
931
- if (pooled !== null) return pooled.subarray(0, size);
932
- const fresh = new Int32Array(size);
933
- borrowedInt32.push(fresh);
934
- return fresh;
935
- };
936
- var scratchUint8Zeroed = (size) => {
937
- const pooled = acquire(freeUint8, borrowedUint8, size);
938
- if (pooled !== null) {
939
- const view = pooled.subarray(0, size);
940
- view.fill(0);
941
- return view;
942
- }
943
- const fresh = new Uint8Array(size);
944
- borrowedUint8.push(fresh);
945
- return fresh;
946
- };
947
- var releaseScratch = () => {
948
- for (const buffer of borrowedInt32) freeInt32.push(buffer);
949
- for (const buffer of borrowedUint8) freeUint8.push(buffer);
950
- borrowedInt32.length = 0;
951
- borrowedUint8.length = 0;
952
- };
953
-
954
1032
  // src/decoder/compression/config/CompressionShared.ts
955
1033
  var kDracoPointCloudBitstreamVersionMajor = 2;
956
1034
  var kDracoPointCloudBitstreamVersionMinor = 3;
@@ -1182,85 +1260,614 @@ var MetadataDecoder = class {
1182
1260
  }
1183
1261
  };
1184
1262
 
1185
- // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1186
- var PointCloudDecoder = class _PointCloudDecoder {
1187
- _pointCloud;
1188
- _buffer;
1189
- _versionMajor;
1190
- _versionMinor;
1191
- _options;
1192
- _attributesDecoders;
1193
- _attributeToDecoderMap;
1263
+ // src/decoder/compression/entropy/ANSCoding.ts
1264
+ var ANS_P8_PRECISION = 256;
1265
+ var ANS_L_BASE = 4096;
1266
+ var ANS_IO_BASE = 256;
1267
+ function memGetLe16(buf, offset) {
1268
+ return buf[offset] | buf[offset + 1] << 8;
1269
+ }
1270
+ function memGetLe24(buf, offset) {
1271
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1272
+ }
1273
+ function memGetLe32(buf, offset) {
1274
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1275
+ }
1276
+ var AnsDecoder = class {
1277
+ buf;
1278
+ bufOffset;
1279
+ // First valid byte of this decoder's slice within buf: init is passed
1280
+ // absolute offsets into the source buffer to avoid a subarray allocation.
1281
+ bufStart;
1282
+ state;
1194
1283
  constructor() {
1195
- this._pointCloud = null;
1196
- this._buffer = null;
1197
- this._versionMajor = 0;
1198
- this._versionMinor = 0;
1199
- this._options = null;
1200
- this._attributesDecoders = [];
1201
- this._attributeToDecoderMap = [];
1284
+ this.buf = null;
1285
+ this.bufOffset = 0;
1286
+ this.bufStart = 0;
1287
+ this.state = 0;
1202
1288
  }
1203
- getGeometryType() {
1204
- return EncodedGeometryType.POINT_CLOUD;
1289
+ };
1290
+ function ansReadInit(ans, buf, offset, base = 0) {
1291
+ if (offset - base < 1) {
1292
+ return 1;
1205
1293
  }
1206
- // Returns a Status; on success outHeader is populated.
1207
- static decodeHeader(buffer, outHeader) {
1208
- const kIoErrorMsg = "Failed to parse Draco header.";
1209
- const bytes = buffer.decodeBytes(5);
1210
- if (bytes === void 0) {
1211
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1212
- }
1213
- for (let i = 0; i < 5; i++) {
1214
- outHeader.dracoString[i] = bytes[i];
1215
- }
1216
- const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1217
- if (magic !== "DRACO") {
1218
- return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1294
+ ans.buf = buf;
1295
+ ans.bufStart = base;
1296
+ const x = buf[offset - 1] >> 6;
1297
+ if (x === 0) {
1298
+ ans.bufOffset = offset - 1;
1299
+ ans.state = buf[offset - 1] & 63;
1300
+ } else if (x === 1) {
1301
+ if (offset - base < 2) {
1302
+ return 1;
1219
1303
  }
1220
- const versionMajor = buffer.decodeUint8();
1221
- if (versionMajor === void 0) {
1222
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1304
+ ans.bufOffset = offset - 2;
1305
+ ans.state = memGetLe16(buf, offset - 2) & 16383;
1306
+ } else if (x === 2) {
1307
+ if (offset - base < 3) {
1308
+ return 1;
1223
1309
  }
1224
- outHeader.versionMajor = versionMajor;
1225
- const versionMinor = buffer.decodeUint8();
1226
- if (versionMinor === void 0) {
1227
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1310
+ ans.bufOffset = offset - 3;
1311
+ ans.state = memGetLe24(buf, offset - 3) & 4194303;
1312
+ } else {
1313
+ return 1;
1314
+ }
1315
+ ans.state += ANS_L_BASE;
1316
+ if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1317
+ return 1;
1318
+ }
1319
+ return 0;
1320
+ }
1321
+ function ansReadEnd(ans) {
1322
+ return ans.state === ANS_L_BASE;
1323
+ }
1324
+ var tablePool = [];
1325
+ var acquirePooled = (Ctor, size) => {
1326
+ for (let i = tablePool.length - 1; i >= 0; --i) {
1327
+ const buf = tablePool[i];
1328
+ if (buf.constructor === Ctor && buf.length >= size) {
1329
+ tablePool[i] = tablePool[tablePool.length - 1];
1330
+ tablePool.pop();
1331
+ return buf;
1228
1332
  }
1229
- outHeader.versionMinor = versionMinor;
1230
- const encoderType = buffer.decodeUint8();
1231
- if (encoderType === void 0) {
1232
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1333
+ }
1334
+ return new Ctor(size);
1335
+ };
1336
+ var RAnsDecoder = class {
1337
+ ransPrecisionBits;
1338
+ ransPrecision;
1339
+ ransPrecisionMask;
1340
+ lRansBase;
1341
+ lutTable;
1342
+ probTable;
1343
+ cumProbTable;
1344
+ buf;
1345
+ bufOffset;
1346
+ // First valid byte of this decoder's slice within buf (absolute offsets,
1347
+ // see AnsDecoder.bufStart).
1348
+ bufStart;
1349
+ state;
1350
+ constructor(ransPrecisionBits) {
1351
+ this.ransPrecisionBits = ransPrecisionBits;
1352
+ this.ransPrecision = 1 << ransPrecisionBits;
1353
+ this.ransPrecisionMask = this.ransPrecision - 1;
1354
+ this.lRansBase = this.ransPrecision * 4;
1355
+ this.lutTable = null;
1356
+ this.probTable = null;
1357
+ this.cumProbTable = null;
1358
+ this.buf = null;
1359
+ this.bufOffset = 0;
1360
+ this.bufStart = 0;
1361
+ this.state = 0;
1362
+ }
1363
+ // offset is the absolute end of the encoded bytes within buf and base the
1364
+ // absolute start (offset - base = encoded length). Passing the source
1365
+ // buffer with absolute offsets avoids a subarray allocation per init.
1366
+ // Returns 0 on success, non-zero on error.
1367
+ readInit(buf, offset, base = 0) {
1368
+ if (offset - base < 1) {
1369
+ return 1;
1233
1370
  }
1234
- outHeader.encoderType = encoderType;
1235
- const encoderMethod = buffer.decodeUint8();
1236
- if (encoderMethod === void 0) {
1237
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1371
+ this.buf = buf;
1372
+ this.bufStart = base;
1373
+ const x = buf[offset - 1] >> 6;
1374
+ if (x === 0) {
1375
+ this.bufOffset = offset - 1;
1376
+ this.state = buf[offset - 1] & 63;
1377
+ } else if (x === 1) {
1378
+ if (offset - base < 2) {
1379
+ return 1;
1380
+ }
1381
+ this.bufOffset = offset - 2;
1382
+ this.state = memGetLe16(buf, offset - 2) & 16383;
1383
+ } else if (x === 2) {
1384
+ if (offset - base < 3) {
1385
+ return 1;
1386
+ }
1387
+ this.bufOffset = offset - 3;
1388
+ this.state = memGetLe24(buf, offset - 3) & 4194303;
1389
+ } else if (x === 3) {
1390
+ if (offset - base < 4) {
1391
+ return 1;
1392
+ }
1393
+ this.bufOffset = offset - 4;
1394
+ this.state = memGetLe32(buf, offset - 4) & 1073741823;
1395
+ } else {
1396
+ return 1;
1238
1397
  }
1239
- outHeader.encoderMethod = encoderMethod;
1240
- const flags = buffer.decodeUint16();
1241
- if (flags === void 0) {
1242
- return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1398
+ this.state += this.lRansBase;
1399
+ if (this.state >= this.lRansBase * ANS_IO_BASE) {
1400
+ return 1;
1243
1401
  }
1244
- outHeader.flags = flags;
1245
- return okStatus();
1402
+ return 0;
1246
1403
  }
1247
- // Main entry point for point cloud decoding.
1248
- decode(options, inBuffer, outPointCloud) {
1249
- this._options = options;
1250
- this._buffer = inBuffer;
1251
- this._pointCloud = outPointCloud;
1252
- const header = new DracoHeader();
1253
- const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1254
- if (!headerStatus.ok()) {
1255
- return headerStatus;
1404
+ readEnd() {
1405
+ if (this.lutTable !== null) {
1406
+ tablePool.push(this.lutTable);
1407
+ this.lutTable = null;
1256
1408
  }
1257
- if (header.encoderType !== this.getGeometryType()) {
1258
- return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1409
+ if (this.probTable !== null) {
1410
+ tablePool.push(this.probTable);
1411
+ this.probTable = null;
1259
1412
  }
1260
- this._versionMajor = header.versionMajor;
1261
- this._versionMinor = header.versionMinor;
1262
- const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1263
- const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
1413
+ if (this.cumProbTable !== null) {
1414
+ tablePool.push(this.cumProbTable);
1415
+ this.cumProbTable = null;
1416
+ }
1417
+ return this.state === this.lRansBase;
1418
+ }
1419
+ ransRead() {
1420
+ const buf = this.buf;
1421
+ const lRansBase = this.lRansBase;
1422
+ let state = this.state;
1423
+ let bufOffset = this.bufOffset;
1424
+ const bufStart = this.bufStart;
1425
+ while (state < lRansBase && bufOffset > bufStart) {
1426
+ state = state << 8 | buf[--bufOffset];
1427
+ }
1428
+ const quo = state >>> this.ransPrecisionBits;
1429
+ const rem = state & this.ransPrecisionMask;
1430
+ const symbol = this.lutTable[rem];
1431
+ this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
1432
+ this.bufOffset = bufOffset;
1433
+ return symbol;
1434
+ }
1435
+ // Batch ransRead() into out[0..count): all fields hoisted to locals, state
1436
+ // written back once. Removes per-symbol property reads and call indirection.
1437
+ // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
1438
+ // which would make the hot lutTable[rem] access site polymorphic — dispatch
1439
+ // once here so each loop body stays monomorphic on its concrete type. The
1440
+ // three bodies are intentionally identical copies.
1441
+ decodeSymbols(out, count) {
1442
+ const lutTable = this.lutTable;
1443
+ if (lutTable instanceof Uint8Array) {
1444
+ this._decodeSymbolsU8(out, count, lutTable);
1445
+ } else if (lutTable instanceof Uint16Array) {
1446
+ this._decodeSymbolsU16(out, count, lutTable);
1447
+ } else {
1448
+ this._decodeSymbolsU32(out, count, lutTable);
1449
+ }
1450
+ }
1451
+ _decodeSymbolsU8(out, count, lutTable) {
1452
+ const buf = this.buf;
1453
+ const lRansBase = this.lRansBase;
1454
+ const ransPrecisionBits = this.ransPrecisionBits;
1455
+ const ransPrecisionMask = this.ransPrecisionMask;
1456
+ const probTable = this.probTable;
1457
+ const cumProbTable = this.cumProbTable;
1458
+ let state = this.state;
1459
+ let bufOffset = this.bufOffset;
1460
+ const bufStart = this.bufStart;
1461
+ for (let i = 0; i < count; ++i) {
1462
+ while (state < lRansBase && bufOffset > bufStart) {
1463
+ state = state << 8 | buf[--bufOffset];
1464
+ }
1465
+ const rem = state & ransPrecisionMask;
1466
+ const symbol = lutTable[rem];
1467
+ out[i] = symbol;
1468
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1469
+ }
1470
+ this.state = state;
1471
+ this.bufOffset = bufOffset;
1472
+ }
1473
+ _decodeSymbolsU16(out, count, lutTable) {
1474
+ const buf = this.buf;
1475
+ const lRansBase = this.lRansBase;
1476
+ const ransPrecisionBits = this.ransPrecisionBits;
1477
+ const ransPrecisionMask = this.ransPrecisionMask;
1478
+ const probTable = this.probTable;
1479
+ const cumProbTable = this.cumProbTable;
1480
+ let state = this.state;
1481
+ let bufOffset = this.bufOffset;
1482
+ const bufStart = this.bufStart;
1483
+ for (let i = 0; i < count; ++i) {
1484
+ while (state < lRansBase && bufOffset > bufStart) {
1485
+ state = state << 8 | buf[--bufOffset];
1486
+ }
1487
+ const rem = state & ransPrecisionMask;
1488
+ const symbol = lutTable[rem];
1489
+ out[i] = symbol;
1490
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1491
+ }
1492
+ this.state = state;
1493
+ this.bufOffset = bufOffset;
1494
+ }
1495
+ _decodeSymbolsU32(out, count, lutTable) {
1496
+ const buf = this.buf;
1497
+ const lRansBase = this.lRansBase;
1498
+ const ransPrecisionBits = this.ransPrecisionBits;
1499
+ const ransPrecisionMask = this.ransPrecisionMask;
1500
+ const probTable = this.probTable;
1501
+ const cumProbTable = this.cumProbTable;
1502
+ let state = this.state;
1503
+ let bufOffset = this.bufOffset;
1504
+ const bufStart = this.bufStart;
1505
+ for (let i = 0; i < count; ++i) {
1506
+ while (state < lRansBase && bufOffset > bufStart) {
1507
+ state = state << 8 | buf[--bufOffset];
1508
+ }
1509
+ const rem = state & ransPrecisionMask;
1510
+ const symbol = lutTable[rem];
1511
+ out[i] = symbol;
1512
+ state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
1513
+ }
1514
+ this.state = state;
1515
+ this.bufOffset = bufOffset;
1516
+ }
1517
+ // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
1518
+ ransBuildLookUpTable(tokenProbs, numSymbols) {
1519
+ const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
1520
+ const lutTable = acquirePooled(LutArray, this.ransPrecision);
1521
+ const probTable = acquirePooled(Uint32Array, numSymbols);
1522
+ const cumProbTable = acquirePooled(Uint32Array, numSymbols);
1523
+ this.lutTable = lutTable;
1524
+ this.probTable = probTable;
1525
+ this.cumProbTable = cumProbTable;
1526
+ let cumProb = 0;
1527
+ let actProb = 0;
1528
+ for (let i = 0; i < numSymbols; ++i) {
1529
+ const prob = tokenProbs[i];
1530
+ probTable[i] = prob;
1531
+ cumProbTable[i] = cumProb;
1532
+ cumProb += prob;
1533
+ if (cumProb > this.ransPrecision) {
1534
+ return false;
1535
+ }
1536
+ if (prob < 32) {
1537
+ for (let j = actProb; j < cumProb; ++j) {
1538
+ lutTable[j] = i;
1539
+ }
1540
+ } else {
1541
+ lutTable.fill(i, actProb, cumProb);
1542
+ }
1543
+ actProb = cumProb;
1544
+ }
1545
+ if (cumProb !== this.ransPrecision) {
1546
+ return false;
1547
+ }
1548
+ return true;
1549
+ }
1550
+ };
1551
+ function ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB) {
1552
+ const lutA = a.lutTable;
1553
+ const lutB = b.lutTable;
1554
+ const bufA = a.buf;
1555
+ const bufB = b.buf;
1556
+ const probA = a.probTable;
1557
+ const probB = b.probTable;
1558
+ const cumA = a.cumProbTable;
1559
+ const cumB = b.cumProbTable;
1560
+ const lBaseA = a.lRansBase;
1561
+ const lBaseB = b.lRansBase;
1562
+ const bitsA = a.ransPrecisionBits;
1563
+ const bitsB = b.ransPrecisionBits;
1564
+ const maskA = a.ransPrecisionMask;
1565
+ const maskB = b.ransPrecisionMask;
1566
+ const startA = a.bufStart;
1567
+ const startB = b.bufStart;
1568
+ let stateA = a.state;
1569
+ let stateB = b.state;
1570
+ let offA = a.bufOffset;
1571
+ let offB = b.bufOffset;
1572
+ const shared = countA < countB ? countA : countB;
1573
+ for (let i = 0; i < shared; ++i) {
1574
+ while (stateA < lBaseA && offA > startA) {
1575
+ stateA = stateA << 8 | bufA[--offA];
1576
+ }
1577
+ while (stateB < lBaseB && offB > startB) {
1578
+ stateB = stateB << 8 | bufB[--offB];
1579
+ }
1580
+ const remA = stateA & maskA;
1581
+ const remB = stateB & maskB;
1582
+ const symA = lutA[remA];
1583
+ const symB = lutB[remB];
1584
+ outA[i] = symA;
1585
+ outB[i] = symB;
1586
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1587
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1588
+ }
1589
+ a.state = stateA;
1590
+ a.bufOffset = offA;
1591
+ b.state = stateB;
1592
+ b.bufOffset = offB;
1593
+ if (shared < countA) {
1594
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1595
+ }
1596
+ if (shared < countB) {
1597
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1598
+ }
1599
+ }
1600
+ function ransDecodeSymbolsPair(a, outA, countA, b, outB, countB) {
1601
+ const lutA = a.lutTable;
1602
+ const lutB = b.lutTable;
1603
+ if (lutA instanceof Uint8Array && lutB instanceof Uint8Array) {
1604
+ ransDecodeSymbolsPairU8(a, outA, countA, b, outB, countB);
1605
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint16Array) {
1606
+ ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB);
1607
+ } else if (lutA instanceof Uint8Array && lutB instanceof Uint16Array) {
1608
+ ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB);
1609
+ } else if (lutA instanceof Uint16Array && lutB instanceof Uint8Array) {
1610
+ ransDecodeSymbolsPairU8U16(b, outB, countB, a, outA, countA);
1611
+ } else {
1612
+ a.decodeSymbols(outA, countA);
1613
+ b.decodeSymbols(outB, countB);
1614
+ }
1615
+ }
1616
+ function ransDecodeSymbolsPairU16(a, outA, countA, b, outB, countB) {
1617
+ const lutA = a.lutTable;
1618
+ const lutB = b.lutTable;
1619
+ const bufA = a.buf;
1620
+ const bufB = b.buf;
1621
+ const probA = a.probTable;
1622
+ const probB = b.probTable;
1623
+ const cumA = a.cumProbTable;
1624
+ const cumB = b.cumProbTable;
1625
+ const lBaseA = a.lRansBase;
1626
+ const lBaseB = b.lRansBase;
1627
+ const bitsA = a.ransPrecisionBits;
1628
+ const bitsB = b.ransPrecisionBits;
1629
+ const maskA = a.ransPrecisionMask;
1630
+ const maskB = b.ransPrecisionMask;
1631
+ const startA = a.bufStart;
1632
+ const startB = b.bufStart;
1633
+ let stateA = a.state;
1634
+ let stateB = b.state;
1635
+ let offA = a.bufOffset;
1636
+ let offB = b.bufOffset;
1637
+ const shared = countA < countB ? countA : countB;
1638
+ for (let i = 0; i < shared; ++i) {
1639
+ while (stateA < lBaseA && offA > startA) {
1640
+ stateA = stateA << 8 | bufA[--offA];
1641
+ }
1642
+ while (stateB < lBaseB && offB > startB) {
1643
+ stateB = stateB << 8 | bufB[--offB];
1644
+ }
1645
+ const remA = stateA & maskA;
1646
+ const remB = stateB & maskB;
1647
+ const symA = lutA[remA];
1648
+ const symB = lutB[remB];
1649
+ outA[i] = symA;
1650
+ outB[i] = symB;
1651
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1652
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1653
+ }
1654
+ a.state = stateA;
1655
+ a.bufOffset = offA;
1656
+ b.state = stateB;
1657
+ b.bufOffset = offB;
1658
+ if (shared < countA) {
1659
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1660
+ }
1661
+ if (shared < countB) {
1662
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1663
+ }
1664
+ }
1665
+ function ransDecodeSymbolsPairU8U16(a, outA, countA, b, outB, countB) {
1666
+ const lutA = a.lutTable;
1667
+ const lutB = b.lutTable;
1668
+ const bufA = a.buf;
1669
+ const bufB = b.buf;
1670
+ const probA = a.probTable;
1671
+ const probB = b.probTable;
1672
+ const cumA = a.cumProbTable;
1673
+ const cumB = b.cumProbTable;
1674
+ const lBaseA = a.lRansBase;
1675
+ const lBaseB = b.lRansBase;
1676
+ const bitsA = a.ransPrecisionBits;
1677
+ const bitsB = b.ransPrecisionBits;
1678
+ const maskA = a.ransPrecisionMask;
1679
+ const maskB = b.ransPrecisionMask;
1680
+ const startA = a.bufStart;
1681
+ const startB = b.bufStart;
1682
+ let stateA = a.state;
1683
+ let stateB = b.state;
1684
+ let offA = a.bufOffset;
1685
+ let offB = b.bufOffset;
1686
+ const shared = countA < countB ? countA : countB;
1687
+ for (let i = 0; i < shared; ++i) {
1688
+ while (stateA < lBaseA && offA > startA) {
1689
+ stateA = stateA << 8 | bufA[--offA];
1690
+ }
1691
+ while (stateB < lBaseB && offB > startB) {
1692
+ stateB = stateB << 8 | bufB[--offB];
1693
+ }
1694
+ const remA = stateA & maskA;
1695
+ const remB = stateB & maskB;
1696
+ const symA = lutA[remA];
1697
+ const symB = lutB[remB];
1698
+ outA[i] = symA;
1699
+ outB[i] = symB;
1700
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1701
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1702
+ }
1703
+ a.state = stateA;
1704
+ a.bufOffset = offA;
1705
+ b.state = stateB;
1706
+ b.bufOffset = offB;
1707
+ if (shared < countA) {
1708
+ a.decodeSymbols(outA.subarray(shared), countA - shared);
1709
+ }
1710
+ if (shared < countB) {
1711
+ b.decodeSymbols(outB.subarray(shared), countB - shared);
1712
+ }
1713
+ }
1714
+ function ransDecodeSymbolsTrioU8(a, outA, countA, b, outB, countB, c, outC, countC) {
1715
+ const lutA = a.lutTable;
1716
+ const lutB = b.lutTable;
1717
+ const lutC = c.lutTable;
1718
+ const bufA = a.buf;
1719
+ const bufB = b.buf;
1720
+ const bufC = c.buf;
1721
+ const probA = a.probTable;
1722
+ const probB = b.probTable;
1723
+ const probC = c.probTable;
1724
+ const cumA = a.cumProbTable;
1725
+ const cumB = b.cumProbTable;
1726
+ const cumC = c.cumProbTable;
1727
+ const lBaseA = a.lRansBase;
1728
+ const lBaseB = b.lRansBase;
1729
+ const lBaseC = c.lRansBase;
1730
+ const bitsA = a.ransPrecisionBits;
1731
+ const bitsB = b.ransPrecisionBits;
1732
+ const bitsC = c.ransPrecisionBits;
1733
+ const maskA = a.ransPrecisionMask;
1734
+ const maskB = b.ransPrecisionMask;
1735
+ const maskC = c.ransPrecisionMask;
1736
+ const startA = a.bufStart;
1737
+ const startB = b.bufStart;
1738
+ const startC = c.bufStart;
1739
+ let stateA = a.state;
1740
+ let stateB = b.state;
1741
+ let stateC = c.state;
1742
+ let offA = a.bufOffset;
1743
+ let offB = b.bufOffset;
1744
+ let offC = c.bufOffset;
1745
+ let shared = countA < countB ? countA : countB;
1746
+ if (countC < shared) shared = countC;
1747
+ for (let i = 0; i < shared; ++i) {
1748
+ while (stateA < lBaseA && offA > startA) {
1749
+ stateA = stateA << 8 | bufA[--offA];
1750
+ }
1751
+ while (stateB < lBaseB && offB > startB) {
1752
+ stateB = stateB << 8 | bufB[--offB];
1753
+ }
1754
+ while (stateC < lBaseC && offC > startC) {
1755
+ stateC = stateC << 8 | bufC[--offC];
1756
+ }
1757
+ const remA = stateA & maskA;
1758
+ const remB = stateB & maskB;
1759
+ const remC = stateC & maskC;
1760
+ const symA = lutA[remA];
1761
+ const symB = lutB[remB];
1762
+ const symC = lutC[remC];
1763
+ outA[i] = symA;
1764
+ outB[i] = symB;
1765
+ outC[i] = symC;
1766
+ stateA = (stateA >>> bitsA) * probA[symA] + remA - cumA[symA];
1767
+ stateB = (stateB >>> bitsB) * probB[symB] + remB - cumB[symB];
1768
+ stateC = (stateC >>> bitsC) * probC[symC] + remC - cumC[symC];
1769
+ }
1770
+ a.state = stateA;
1771
+ a.bufOffset = offA;
1772
+ b.state = stateB;
1773
+ b.bufOffset = offB;
1774
+ c.state = stateC;
1775
+ c.bufOffset = offC;
1776
+ const restA = countA - shared;
1777
+ const restB = countB - shared;
1778
+ const restC = countC - shared;
1779
+ const tails = [];
1780
+ if (restA > 0) tails.push([a, outA.subarray(shared), restA]);
1781
+ if (restB > 0) tails.push([b, outB.subarray(shared), restB]);
1782
+ if (restC > 0) tails.push([c, outC.subarray(shared), restC]);
1783
+ if (tails.length === 2) {
1784
+ ransDecodeSymbolsPairU8(tails[0][0], tails[0][1], tails[0][2], tails[1][0], tails[1][1], tails[1][2]);
1785
+ } else {
1786
+ for (const [decoder, out, count] of tails) {
1787
+ decoder.decodeSymbols(out, count);
1788
+ }
1789
+ }
1790
+ }
1791
+
1792
+ // src/decoder/compression/point_cloud/PointCloudDecoder.ts
1793
+ var PointCloudDecoder = class _PointCloudDecoder {
1794
+ _pointCloud;
1795
+ _buffer;
1796
+ _versionMajor;
1797
+ _versionMinor;
1798
+ _options;
1799
+ _attributesDecoders;
1800
+ _attributeToDecoderMap;
1801
+ constructor() {
1802
+ this._pointCloud = null;
1803
+ this._buffer = null;
1804
+ this._versionMajor = 0;
1805
+ this._versionMinor = 0;
1806
+ this._options = null;
1807
+ this._attributesDecoders = [];
1808
+ this._attributeToDecoderMap = [];
1809
+ }
1810
+ getGeometryType() {
1811
+ return EncodedGeometryType.POINT_CLOUD;
1812
+ }
1813
+ // Returns a Status; on success outHeader is populated.
1814
+ static decodeHeader(buffer, outHeader) {
1815
+ const kIoErrorMsg = "Failed to parse Draco header.";
1816
+ const bytes = buffer.decodeBytes(5);
1817
+ if (bytes === void 0) {
1818
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1819
+ }
1820
+ for (let i = 0; i < 5; i++) {
1821
+ outHeader.dracoString[i] = bytes[i];
1822
+ }
1823
+ const magic = String.fromCharCode(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]);
1824
+ if (magic !== "DRACO") {
1825
+ return new Status(StatusCode.DRACO_ERROR, "Not a Draco file.");
1826
+ }
1827
+ const versionMajor = buffer.decodeUint8();
1828
+ if (versionMajor === void 0) {
1829
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1830
+ }
1831
+ outHeader.versionMajor = versionMajor;
1832
+ const versionMinor = buffer.decodeUint8();
1833
+ if (versionMinor === void 0) {
1834
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1835
+ }
1836
+ outHeader.versionMinor = versionMinor;
1837
+ const encoderType = buffer.decodeUint8();
1838
+ if (encoderType === void 0) {
1839
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1840
+ }
1841
+ outHeader.encoderType = encoderType;
1842
+ const encoderMethod = buffer.decodeUint8();
1843
+ if (encoderMethod === void 0) {
1844
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1845
+ }
1846
+ outHeader.encoderMethod = encoderMethod;
1847
+ const flags = buffer.decodeUint16();
1848
+ if (flags === void 0) {
1849
+ return new Status(StatusCode.IO_ERROR, kIoErrorMsg);
1850
+ }
1851
+ outHeader.flags = flags;
1852
+ return okStatus();
1853
+ }
1854
+ // Main entry point for point cloud decoding.
1855
+ decode(options, inBuffer, outPointCloud) {
1856
+ this._options = options;
1857
+ this._buffer = inBuffer;
1858
+ this._pointCloud = outPointCloud;
1859
+ const header = new DracoHeader();
1860
+ const headerStatus = _PointCloudDecoder.decodeHeader(this._buffer, header);
1861
+ if (!headerStatus.ok()) {
1862
+ return headerStatus;
1863
+ }
1864
+ if (header.encoderType !== this.getGeometryType()) {
1865
+ return new Status(StatusCode.DRACO_ERROR, "Using incompatible decoder for the input geometry.");
1866
+ }
1867
+ this._versionMajor = header.versionMajor;
1868
+ this._versionMinor = header.versionMinor;
1869
+ const maxSupportedMajorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMajor : kDracoMeshBitstreamVersionMajor;
1870
+ const maxSupportedMinorVersion = header.encoderType === EncodedGeometryType.POINT_CLOUD ? kDracoPointCloudBitstreamVersionMinor : kDracoMeshBitstreamVersionMinor;
1264
1871
  if (this._versionMajor < 1 || this._versionMajor > maxSupportedMajorVersion) {
1265
1872
  return new Status(StatusCode.UNKNOWN_VERSION, "Unknown major version.");
1266
1873
  }
@@ -1376,8 +1983,27 @@ var PointCloudDecoder = class _PointCloudDecoder {
1376
1983
  return true;
1377
1984
  }
1378
1985
  decodeAllAttributes() {
1379
- for (let i = 0; i < this._attributesDecoders.length; i++) {
1380
- if (!this._attributesDecoders[i].decodeAttributes(this._buffer)) {
1986
+ const decoders = this._attributesDecoders;
1987
+ for (let i2 = 0; i2 < decoders.length; i2++) {
1988
+ if (!decoders[i2].decodeAttributesParse(this._buffer)) {
1989
+ return false;
1990
+ }
1991
+ }
1992
+ const pending = [];
1993
+ for (let i2 = 0; i2 < decoders.length; i2++) {
1994
+ decoders[i2].collectPendingSymbolStreams(pending);
1995
+ }
1996
+ let i = 0;
1997
+ for (; i + 1 < pending.length; i += 2) {
1998
+ const a = pending[i];
1999
+ const b = pending[i + 1];
2000
+ ransDecodeSymbolsPair(a.ans, a.out, a.count, b.ans, b.out, b.count);
2001
+ }
2002
+ if (i < pending.length) {
2003
+ pending[i].ans.decodeSymbols(pending[i].out, pending[i].count);
2004
+ }
2005
+ for (let k = 0; k < decoders.length; k++) {
2006
+ if (!decoders[k].decodeAttributesFinish()) {
1381
2007
  return false;
1382
2008
  }
1383
2009
  }
@@ -1445,46 +2071,62 @@ var MeshAttributeCornerTable = class {
1445
2071
  no_interior_seams_;
1446
2072
  corner_to_vertex_map_;
1447
2073
  vertex_to_left_most_corner_map_;
1448
- vertex_to_attribute_entry_id_map_;
2074
+ // Attribute-vertex count. C++ keeps a vertex -> attribute-entry map here, but
2075
+ // the decoder only ever reads its size, so track the count directly instead
2076
+ // of allocating an Int32Array per attribute corner table.
2077
+ num_attribute_vertices_;
1449
2078
  corner_table_;
1450
2079
  // Lazily built; see oppositeCornerArray.
1451
2080
  _effectiveOpposite;
1452
2081
  // Every corner passed to addSeamEdge (may contain duplicates); lets
1453
2082
  // oppositeCornerArray patch seams without scanning every corner's flag.
2083
+ // Preallocated to its exact upper bound (2 per seam edge) by the caller via
2084
+ // reserveSeamEdges -- a plain array grown by push() was measurable on
2085
+ // seam-heavy files.
1454
2086
  _seamCorners;
2087
+ _numSeamCorners;
1455
2088
  constructor() {
1456
2089
  this.is_edge_on_seam_ = [];
1457
2090
  this.is_vertex_on_seam_ = [];
1458
2091
  this.no_interior_seams_ = true;
1459
2092
  this.corner_to_vertex_map_ = [];
1460
2093
  this.vertex_to_left_most_corner_map_ = [];
1461
- this.vertex_to_attribute_entry_id_map_ = [];
2094
+ this.num_attribute_vertices_ = 0;
1462
2095
  this.corner_table_ = null;
1463
2096
  this._effectiveOpposite = null;
1464
2097
  this._seamCorners = [];
2098
+ this._numSeamCorners = 0;
1465
2099
  }
1466
2100
  initEmpty(table) {
1467
2101
  if (table === null) {
1468
2102
  return false;
1469
2103
  }
1470
- this.is_edge_on_seam_ = new Uint8Array(table.numCorners());
1471
- this.is_vertex_on_seam_ = new Uint8Array(table.numVertices());
1472
- this.corner_to_vertex_map_ = new Int32Array(table.numCorners()).fill(kInvalidVertexIndex);
1473
- this.vertex_to_attribute_entry_id_map_ = [];
2104
+ this.is_edge_on_seam_ = scratchUint8Zeroed(table.numCorners());
2105
+ this.is_vertex_on_seam_ = scratchUint8Zeroed(table.numVertices());
2106
+ this.corner_to_vertex_map_ = scratchInt32Filled(table.numCorners(), kInvalidVertexIndex);
2107
+ this.num_attribute_vertices_ = 0;
1474
2108
  this.vertex_to_left_most_corner_map_ = [];
1475
2109
  this._effectiveOpposite = null;
1476
2110
  this._seamCorners = [];
2111
+ this._numSeamCorners = 0;
1477
2112
  this.corner_table_ = table;
1478
2113
  this.no_interior_seams_ = true;
1479
2114
  return true;
1480
2115
  }
2116
+ // Sizes the seam-corner list for numSeamEdges upcoming addSeamEdge calls
2117
+ // (each adds at most two corners). Decode-scoped scratch.
2118
+ reserveSeamEdges(numSeamEdges) {
2119
+ this._seamCorners = scratchInt32(numSeamEdges * 2);
2120
+ this._numSeamCorners = 0;
2121
+ }
1481
2122
  addSeamEdge(c) {
1482
2123
  const cornerToVertex = this.corner_table_.cornerToVertexArray();
1483
2124
  const oppositeCorners = this.corner_table_.oppositeCornerArray();
1484
2125
  const isEdge = this.is_edge_on_seam_;
1485
2126
  const isVert = this.is_vertex_on_seam_;
2127
+ const seamCorners = this._seamCorners;
1486
2128
  isEdge[c] = 1;
1487
- this._seamCorners.push(c);
2129
+ seamCorners[this._numSeamCorners++] = c;
1488
2130
  let rem = c - (c / 3 | 0) * 3;
1489
2131
  isVert[cornerToVertex[rem === 2 ? c - 2 : c + 1]] = 1;
1490
2132
  isVert[cornerToVertex[rem === 0 ? c + 2 : c - 1]] = 1;
@@ -1492,7 +2134,7 @@ var MeshAttributeCornerTable = class {
1492
2134
  if (oppCorner !== kInvalidCornerIndex) {
1493
2135
  this.no_interior_seams_ = false;
1494
2136
  isEdge[oppCorner] = 1;
1495
- this._seamCorners.push(oppCorner);
2137
+ seamCorners[this._numSeamCorners++] = oppCorner;
1496
2138
  rem = oppCorner - (oppCorner / 3 | 0) * 3;
1497
2139
  isVert[cornerToVertex[rem === 2 ? oppCorner - 2 : oppCorner + 1]] = 1;
1498
2140
  isVert[cornerToVertex[rem === 0 ? oppCorner + 2 : oppCorner - 1]] = 1;
@@ -1507,12 +2149,12 @@ var MeshAttributeCornerTable = class {
1507
2149
  const ct = this.corner_table_;
1508
2150
  const numCorners = ct.numCorners();
1509
2151
  const numBaseVertices = ct.numVertices();
1510
- const leftMostMap = new Int32Array(numCorners);
2152
+ const leftMostMap = scratchInt32(numCorners);
1511
2153
  const cornerToVertex = this.corner_to_vertex_map_;
1512
2154
  const isVertexOnSeam = this.is_vertex_on_seam_;
1513
2155
  const isEdgeOnSeam = this.is_edge_on_seam_;
1514
2156
  const seamOpp = this.oppositeCornerArray();
1515
- const baseOpp = ct.oppositeCornerArray();
2157
+ const swingRight = ct.swingRightArray();
1516
2158
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
1517
2159
  let numNewVertices = 0;
1518
2160
  for (let v = 0; v < numBaseVertices; ++v) {
@@ -1522,14 +2164,10 @@ var MeshAttributeCornerTable = class {
1522
2164
  const firstVertId = numNewVertices++;
1523
2165
  leftMostMap[firstVertId] = c;
1524
2166
  cornerToVertex[c] = firstVertId;
1525
- let pv = c % 3 === 0 ? c + 2 : c - 1;
1526
- let bopp = baseOpp[pv];
1527
- let actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2167
+ let actC = swingRight[c];
1528
2168
  while (actC !== kInvalidCornerIndex && actC !== c) {
1529
2169
  cornerToVertex[actC] = firstVertId;
1530
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1531
- bopp = baseOpp[pv];
1532
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2170
+ actC = swingRight[actC];
1533
2171
  }
1534
2172
  } else {
1535
2173
  let firstVertId = numNewVertices++;
@@ -1547,9 +2185,7 @@ var MeshAttributeCornerTable = class {
1547
2185
  }
1548
2186
  cornerToVertex[firstC] = firstVertId;
1549
2187
  leftMostMap[firstVertId] = firstC;
1550
- let pv = firstC % 3 === 0 ? firstC + 2 : firstC - 1;
1551
- let bopp = baseOpp[pv];
1552
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2188
+ actC = swingRight[firstC];
1553
2189
  while (actC !== kInvalidCornerIndex && actC !== firstC) {
1554
2190
  const nAct = actC % 3 === 2 ? actC - 2 : actC + 1;
1555
2191
  if (isEdgeOnSeam[nAct]) {
@@ -1557,13 +2193,11 @@ var MeshAttributeCornerTable = class {
1557
2193
  leftMostMap[firstVertId] = actC;
1558
2194
  }
1559
2195
  cornerToVertex[actC] = firstVertId;
1560
- pv = actC % 3 === 0 ? actC + 2 : actC - 1;
1561
- bopp = baseOpp[pv];
1562
- actC = bopp < 0 ? kInvalidCornerIndex : bopp % 3 === 0 ? bopp + 2 : bopp - 1;
2196
+ actC = swingRight[actC];
1563
2197
  }
1564
2198
  }
1565
2199
  }
1566
- this.vertex_to_attribute_entry_id_map_ = new Int32Array(numNewVertices);
2200
+ this.num_attribute_vertices_ = numNewVertices;
1567
2201
  this.vertex_to_left_most_corner_map_ = leftMostMap.subarray(0, numNewVertices);
1568
2202
  return true;
1569
2203
  }
@@ -1589,7 +2223,7 @@ var MeshAttributeCornerTable = class {
1589
2223
  return this.next(this.opposite(this.next(corner)));
1590
2224
  }
1591
2225
  numVertices() {
1592
- return this.vertex_to_attribute_entry_id_map_.length;
2226
+ return this.num_attribute_vertices_;
1593
2227
  }
1594
2228
  numFaces() {
1595
2229
  return this.corner_table_.numFaces();
@@ -1617,12 +2251,13 @@ var MeshAttributeCornerTable = class {
1617
2251
  const nc = this.corner_table_.numCorners();
1618
2252
  const base = this.corner_table_.oppositeCornerArray();
1619
2253
  const seamCorners = this._seamCorners;
1620
- if (seamCorners.length === 0) {
2254
+ const numSeamCorners = this._numSeamCorners;
2255
+ if (numSeamCorners === 0) {
1621
2256
  this._effectiveOpposite = base;
1622
2257
  } else {
1623
2258
  const eff = scratchInt32(nc);
1624
2259
  eff.set(base.length === nc ? base : base.subarray(0, nc));
1625
- for (let i = 0, l = seamCorners.length; i < l; ++i) {
2260
+ for (let i = 0; i < numSeamCorners; ++i) {
1626
2261
  eff[seamCorners[i]] = kInvalidCornerIndex;
1627
2262
  }
1628
2263
  this._effectiveOpposite = eff;
@@ -1637,23 +2272,20 @@ var MeshAttributeCornerTable = class {
1637
2272
  vertexOnSeamArray() {
1638
2273
  return this.is_vertex_on_seam_;
1639
2274
  }
1640
- hasSameSeams(other) {
1641
- if (other === null || other === void 0) return false;
1642
- const seamA = this.is_edge_on_seam_;
1643
- const seamB = other.is_edge_on_seam_;
1644
- if (seamA.length !== seamB.length) return false;
1645
- for (let i = 0, l = seamA.length; i < l; ++i) {
1646
- if (seamA[i] !== seamB[i]) return false;
1647
- }
1648
- return true;
1649
- }
1650
- adoptVertexRecompute(other) {
2275
+ // Takes over another table's state wholesale. Only valid when both tables
2276
+ // were built from the same corner table and the same seam edges, in which
2277
+ // case every one of these is identical and read-only from here on.
2278
+ adoptFrom(other) {
2279
+ this.corner_table_ = other.corner_table_;
2280
+ this.is_edge_on_seam_ = other.is_edge_on_seam_;
2281
+ this.is_vertex_on_seam_ = other.is_vertex_on_seam_;
1651
2282
  this.corner_to_vertex_map_ = other.corner_to_vertex_map_;
1652
- this.vertex_to_attribute_entry_id_map_ = other.vertex_to_attribute_entry_id_map_;
2283
+ this.num_attribute_vertices_ = other.num_attribute_vertices_;
1653
2284
  this.vertex_to_left_most_corner_map_ = other.vertex_to_left_most_corner_map_;
1654
2285
  this.no_interior_seams_ = other.no_interior_seams_;
1655
2286
  this._effectiveOpposite = other._effectiveOpposite;
1656
2287
  this._seamCorners = other._seamCorners;
2288
+ this._numSeamCorners = other._numSeamCorners;
1657
2289
  }
1658
2290
  };
1659
2291
 
@@ -1667,6 +2299,21 @@ var AttributesDecoderInterface = class {
1667
2299
  decodeAttributesDecoderData(_buffer) {
1668
2300
  return false;
1669
2301
  }
2302
+ // --- Optional two-phase decode across attributes decoders ---
2303
+ // PointCloudDecoder parses every decoder first (all buffer reads are
2304
+ // size-driven, so parsing runs ahead of the deferred rANS symbol decodes),
2305
+ // then decodes the collected streams two at a time, then finishes each
2306
+ // decoder in order (so parent attributes complete before dependents).
2307
+ // Defaults keep the original single-phase behavior for decoders that do not
2308
+ // split.
2309
+ decodeAttributesParse(buffer) {
2310
+ return this.decodeAttributes(buffer);
2311
+ }
2312
+ collectPendingSymbolStreams(_out) {
2313
+ }
2314
+ decodeAttributesFinish() {
2315
+ return true;
2316
+ }
1670
2317
  decodeAttributes(_buffer) {
1671
2318
  return false;
1672
2319
  }
@@ -1813,6 +2460,22 @@ var SequentialAttributeDecoder = class {
1813
2460
  this._attributeId = attributeId;
1814
2461
  return true;
1815
2462
  }
2463
+ // --- Optional two-phase decode ---
2464
+ // The controller calls Parse for every attribute first (headers, schemes,
2465
+ // prediction data -- all size-driven cursor movement), collects the pending
2466
+ // rANS symbol streams, decodes them in pairs (see ransDecodeSymbolsPair),
2467
+ // then calls Finish per attribute in order. Decoders without a deferrable
2468
+ // stream simply do the whole decode in Parse. Defaults preserve the
2469
+ // original single-phase behavior.
2470
+ decodePortableAttributeParse(pointIds, buffer) {
2471
+ return this.decodePortableAttribute(pointIds, buffer);
2472
+ }
2473
+ pendingSymbolStream() {
2474
+ return null;
2475
+ }
2476
+ decodePortableAttributeFinish() {
2477
+ return true;
2478
+ }
1816
2479
  decodePortableAttribute(pointIds, buffer) {
1817
2480
  if (this._attribute.numComponents <= 0) {
1818
2481
  return false;
@@ -1833,7 +2496,7 @@ var SequentialAttributeDecoder = class {
1833
2496
  getPortableAttribute() {
1834
2497
  if (!this._attribute.isMappingIdentity && this._portableAttribute && this._portableAttribute.isMappingIdentity) {
1835
2498
  const size = this._attribute.indicesMapSize;
1836
- this._portableAttribute.setExplicitMappingUnfilled(size);
2499
+ this._portableAttribute.setExplicitMappingScratch(size);
1837
2500
  const src = this._attribute.indicesMap;
1838
2501
  const dst = this._portableAttribute.indicesMap;
1839
2502
  if (src.length === size) {
@@ -1846,333 +2509,44 @@ var SequentialAttributeDecoder = class {
1846
2509
  }
1847
2510
  get attribute() {
1848
2511
  return this._attribute;
1849
- }
1850
- get attributeId() {
1851
- return this._attributeId;
1852
- }
1853
- get decoder() {
1854
- return this._decoder;
1855
- }
1856
- initPredictionScheme(ps) {
1857
- for (let i = 0; i < ps.getNumParentAttributes(); i++) {
1858
- const attId = this._decoder.pointCloud().getNamedAttributeId(ps.getParentAttributeType(i));
1859
- if (attId === -1) {
1860
- return false;
1861
- }
1862
- const pa = this._decoder.getPortableAttribute(attId);
1863
- if (pa === null || !ps.setParentAttribute(pa)) {
1864
- return false;
1865
- }
1866
- }
1867
- return true;
1868
- }
1869
- // Decodes raw attribute values in their original format.
1870
- decodeValues(pointIds, buffer) {
1871
- const numValues = pointIds.length;
1872
- const entrySize = this._attribute.byteStride;
1873
- const totalSize = numValues * entrySize;
1874
- const valueData = buffer.decodeBytesView(totalSize);
1875
- if (valueData === void 0) {
1876
- return false;
1877
- }
1878
- this._attribute.buffer.write(0, valueData, totalSize);
1879
- return true;
1880
- }
1881
- setPortableAttribute(att) {
1882
- this._portableAttribute = att;
1883
- }
1884
- get portableAttribute() {
1885
- return this._portableAttribute;
1886
- }
1887
- };
1888
-
1889
- // src/decoder/compression/entropy/ANSCoding.ts
1890
- var ANS_P8_PRECISION = 256;
1891
- var ANS_L_BASE = 4096;
1892
- var ANS_IO_BASE = 256;
1893
- function memGetLe16(buf, offset) {
1894
- return buf[offset] | buf[offset + 1] << 8;
1895
- }
1896
- function memGetLe24(buf, offset) {
1897
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16;
1898
- }
1899
- function memGetLe32(buf, offset) {
1900
- return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
1901
- }
1902
- var AnsDecoder = class {
1903
- buf;
1904
- bufOffset;
1905
- // First valid byte of this decoder's slice within buf: init is passed
1906
- // absolute offsets into the source buffer to avoid a subarray allocation.
1907
- bufStart;
1908
- state;
1909
- constructor() {
1910
- this.buf = null;
1911
- this.bufOffset = 0;
1912
- this.bufStart = 0;
1913
- this.state = 0;
1914
- }
1915
- };
1916
- function ansReadInit(ans, buf, offset, base = 0) {
1917
- if (offset - base < 1) {
1918
- return 1;
1919
- }
1920
- ans.buf = buf;
1921
- ans.bufStart = base;
1922
- const x = buf[offset - 1] >> 6;
1923
- if (x === 0) {
1924
- ans.bufOffset = offset - 1;
1925
- ans.state = buf[offset - 1] & 63;
1926
- } else if (x === 1) {
1927
- if (offset - base < 2) {
1928
- return 1;
1929
- }
1930
- ans.bufOffset = offset - 2;
1931
- ans.state = memGetLe16(buf, offset - 2) & 16383;
1932
- } else if (x === 2) {
1933
- if (offset - base < 3) {
1934
- return 1;
1935
- }
1936
- ans.bufOffset = offset - 3;
1937
- ans.state = memGetLe24(buf, offset - 3) & 4194303;
1938
- } else {
1939
- return 1;
1940
- }
1941
- ans.state += ANS_L_BASE;
1942
- if (ans.state >= ANS_L_BASE * ANS_IO_BASE) {
1943
- return 1;
1944
- }
1945
- return 0;
1946
- }
1947
- function ansReadEnd(ans) {
1948
- return ans.state === ANS_L_BASE;
1949
- }
1950
- var tablePool = [];
1951
- var acquirePooled = (Ctor, size) => {
1952
- for (let i = tablePool.length - 1; i >= 0; --i) {
1953
- const buf = tablePool[i];
1954
- if (buf.constructor === Ctor && buf.length >= size) {
1955
- tablePool[i] = tablePool[tablePool.length - 1];
1956
- tablePool.pop();
1957
- return buf;
1958
- }
1959
- }
1960
- return new Ctor(size);
1961
- };
1962
- var RAnsDecoder = class {
1963
- ransPrecisionBits;
1964
- ransPrecision;
1965
- ransPrecisionMask;
1966
- lRansBase;
1967
- lutTable;
1968
- probTable;
1969
- cumProbTable;
1970
- buf;
1971
- bufOffset;
1972
- // First valid byte of this decoder's slice within buf (absolute offsets,
1973
- // see AnsDecoder.bufStart).
1974
- bufStart;
1975
- state;
1976
- constructor(ransPrecisionBits) {
1977
- this.ransPrecisionBits = ransPrecisionBits;
1978
- this.ransPrecision = 1 << ransPrecisionBits;
1979
- this.ransPrecisionMask = this.ransPrecision - 1;
1980
- this.lRansBase = this.ransPrecision * 4;
1981
- this.lutTable = null;
1982
- this.probTable = null;
1983
- this.cumProbTable = null;
1984
- this.buf = null;
1985
- this.bufOffset = 0;
1986
- this.bufStart = 0;
1987
- this.state = 0;
1988
- }
1989
- // offset is the absolute end of the encoded bytes within buf and base the
1990
- // absolute start (offset - base = encoded length). Passing the source
1991
- // buffer with absolute offsets avoids a subarray allocation per init.
1992
- // Returns 0 on success, non-zero on error.
1993
- readInit(buf, offset, base = 0) {
1994
- if (offset - base < 1) {
1995
- return 1;
1996
- }
1997
- this.buf = buf;
1998
- this.bufStart = base;
1999
- const x = buf[offset - 1] >> 6;
2000
- if (x === 0) {
2001
- this.bufOffset = offset - 1;
2002
- this.state = buf[offset - 1] & 63;
2003
- } else if (x === 1) {
2004
- if (offset - base < 2) {
2005
- return 1;
2006
- }
2007
- this.bufOffset = offset - 2;
2008
- this.state = memGetLe16(buf, offset - 2) & 16383;
2009
- } else if (x === 2) {
2010
- if (offset - base < 3) {
2011
- return 1;
2012
- }
2013
- this.bufOffset = offset - 3;
2014
- this.state = memGetLe24(buf, offset - 3) & 4194303;
2015
- } else if (x === 3) {
2016
- if (offset - base < 4) {
2017
- return 1;
2018
- }
2019
- this.bufOffset = offset - 4;
2020
- this.state = memGetLe32(buf, offset - 4) & 1073741823;
2021
- } else {
2022
- return 1;
2023
- }
2024
- this.state += this.lRansBase;
2025
- if (this.state >= this.lRansBase * ANS_IO_BASE) {
2026
- return 1;
2027
- }
2028
- return 0;
2029
- }
2030
- readEnd() {
2031
- if (this.lutTable !== null) {
2032
- tablePool.push(this.lutTable);
2033
- this.lutTable = null;
2034
- }
2035
- if (this.probTable !== null) {
2036
- tablePool.push(this.probTable);
2037
- this.probTable = null;
2038
- }
2039
- if (this.cumProbTable !== null) {
2040
- tablePool.push(this.cumProbTable);
2041
- this.cumProbTable = null;
2042
- }
2043
- return this.state === this.lRansBase;
2044
- }
2045
- ransRead() {
2046
- const buf = this.buf;
2047
- const lRansBase = this.lRansBase;
2048
- let state = this.state;
2049
- let bufOffset = this.bufOffset;
2050
- const bufStart = this.bufStart;
2051
- while (state < lRansBase && bufOffset > bufStart) {
2052
- state = state << 8 | buf[--bufOffset];
2053
- }
2054
- const quo = state >>> this.ransPrecisionBits;
2055
- const rem = state & this.ransPrecisionMask;
2056
- const symbol = this.lutTable[rem];
2057
- this.state = quo * this.probTable[symbol] + rem - this.cumProbTable[symbol];
2058
- this.bufOffset = bufOffset;
2059
- return symbol;
2060
- }
2061
- // Batch ransRead() into out[0..count): all fields hoisted to locals, state
2062
- // written back once. Removes per-symbol property reads and call indirection.
2063
- // lutTable's element type varies per decoder (Uint8/16/32 by symbol count),
2064
- // which would make the hot lutTable[rem] access site polymorphic — dispatch
2065
- // once here so each loop body stays monomorphic on its concrete type. The
2066
- // three bodies are intentionally identical copies.
2067
- decodeSymbols(out, count) {
2068
- const lutTable = this.lutTable;
2069
- if (lutTable instanceof Uint8Array) {
2070
- this._decodeSymbolsU8(out, count, lutTable);
2071
- } else if (lutTable instanceof Uint16Array) {
2072
- this._decodeSymbolsU16(out, count, lutTable);
2073
- } else {
2074
- this._decodeSymbolsU32(out, count, lutTable);
2075
- }
2076
- }
2077
- _decodeSymbolsU8(out, count, lutTable) {
2078
- const buf = this.buf;
2079
- const lRansBase = this.lRansBase;
2080
- const ransPrecisionBits = this.ransPrecisionBits;
2081
- const ransPrecisionMask = this.ransPrecisionMask;
2082
- const probTable = this.probTable;
2083
- const cumProbTable = this.cumProbTable;
2084
- let state = this.state;
2085
- let bufOffset = this.bufOffset;
2086
- const bufStart = this.bufStart;
2087
- for (let i = 0; i < count; ++i) {
2088
- while (state < lRansBase && bufOffset > bufStart) {
2089
- state = state << 8 | buf[--bufOffset];
2090
- }
2091
- const rem = state & ransPrecisionMask;
2092
- const symbol = lutTable[rem];
2093
- out[i] = symbol;
2094
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2095
- }
2096
- this.state = state;
2097
- this.bufOffset = bufOffset;
2098
- }
2099
- _decodeSymbolsU16(out, count, lutTable) {
2100
- const buf = this.buf;
2101
- const lRansBase = this.lRansBase;
2102
- const ransPrecisionBits = this.ransPrecisionBits;
2103
- const ransPrecisionMask = this.ransPrecisionMask;
2104
- const probTable = this.probTable;
2105
- const cumProbTable = this.cumProbTable;
2106
- let state = this.state;
2107
- let bufOffset = this.bufOffset;
2108
- const bufStart = this.bufStart;
2109
- for (let i = 0; i < count; ++i) {
2110
- while (state < lRansBase && bufOffset > bufStart) {
2111
- state = state << 8 | buf[--bufOffset];
2112
- }
2113
- const rem = state & ransPrecisionMask;
2114
- const symbol = lutTable[rem];
2115
- out[i] = symbol;
2116
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2117
- }
2118
- this.state = state;
2119
- this.bufOffset = bufOffset;
2120
- }
2121
- _decodeSymbolsU32(out, count, lutTable) {
2122
- const buf = this.buf;
2123
- const lRansBase = this.lRansBase;
2124
- const ransPrecisionBits = this.ransPrecisionBits;
2125
- const ransPrecisionMask = this.ransPrecisionMask;
2126
- const probTable = this.probTable;
2127
- const cumProbTable = this.cumProbTable;
2128
- let state = this.state;
2129
- let bufOffset = this.bufOffset;
2130
- const bufStart = this.bufStart;
2131
- for (let i = 0; i < count; ++i) {
2132
- while (state < lRansBase && bufOffset > bufStart) {
2133
- state = state << 8 | buf[--bufOffset];
2134
- }
2135
- const rem = state & ransPrecisionMask;
2136
- const symbol = lutTable[rem];
2137
- out[i] = symbol;
2138
- state = (state >>> ransPrecisionBits) * probTable[symbol] + rem - cumProbTable[symbol];
2139
- }
2140
- this.state = state;
2141
- this.bufOffset = bufOffset;
2142
- }
2143
- // Builds the ransPrecision-entry lookup table. Returns false on bad input data.
2144
- ransBuildLookUpTable(tokenProbs, numSymbols) {
2145
- const LutArray = numSymbols <= 256 ? Uint8Array : numSymbols <= 65536 ? Uint16Array : Uint32Array;
2146
- const lutTable = acquirePooled(LutArray, this.ransPrecision);
2147
- const probTable = acquirePooled(Uint32Array, numSymbols);
2148
- const cumProbTable = acquirePooled(Uint32Array, numSymbols);
2149
- this.lutTable = lutTable;
2150
- this.probTable = probTable;
2151
- this.cumProbTable = cumProbTable;
2152
- let cumProb = 0;
2153
- let actProb = 0;
2154
- for (let i = 0; i < numSymbols; ++i) {
2155
- const prob = tokenProbs[i];
2156
- probTable[i] = prob;
2157
- cumProbTable[i] = cumProb;
2158
- cumProb += prob;
2159
- if (cumProb > this.ransPrecision) {
2512
+ }
2513
+ get attributeId() {
2514
+ return this._attributeId;
2515
+ }
2516
+ get decoder() {
2517
+ return this._decoder;
2518
+ }
2519
+ initPredictionScheme(ps) {
2520
+ for (let i = 0; i < ps.getNumParentAttributes(); i++) {
2521
+ const attId = this._decoder.pointCloud().getNamedAttributeId(ps.getParentAttributeType(i));
2522
+ if (attId === -1) {
2160
2523
  return false;
2161
2524
  }
2162
- if (prob < 32) {
2163
- for (let j = actProb; j < cumProb; ++j) {
2164
- lutTable[j] = i;
2165
- }
2166
- } else {
2167
- lutTable.fill(i, actProb, cumProb);
2525
+ const pa = this._decoder.getPortableAttribute(attId);
2526
+ if (pa === null || !ps.setParentAttribute(pa)) {
2527
+ return false;
2168
2528
  }
2169
- actProb = cumProb;
2170
2529
  }
2171
- if (cumProb !== this.ransPrecision) {
2530
+ return true;
2531
+ }
2532
+ // Decodes raw attribute values in their original format.
2533
+ decodeValues(pointIds, buffer) {
2534
+ const numValues = pointIds.length;
2535
+ const entrySize = this._attribute.byteStride;
2536
+ const totalSize = numValues * entrySize;
2537
+ const valueData = buffer.decodeBytesView(totalSize);
2538
+ if (valueData === void 0) {
2172
2539
  return false;
2173
2540
  }
2541
+ this._attribute.buffer.write(0, valueData, totalSize);
2174
2542
  return true;
2175
2543
  }
2544
+ setPortableAttribute(att) {
2545
+ this._portableAttribute = att;
2546
+ }
2547
+ get portableAttribute() {
2548
+ return this._portableAttribute;
2549
+ }
2176
2550
  };
2177
2551
 
2178
2552
  // src/decoder/compression/entropy/RAnsSymbolDecoder.ts
@@ -2208,7 +2582,7 @@ var RAnsSymbolDecoder = class {
2208
2582
  return false;
2209
2583
  }
2210
2584
  const numSymbols = this.numSymbols_;
2211
- const probabilityTable = new Uint32Array(numSymbols);
2585
+ const probabilityTable = scratchUint32Zeroed(numSymbols);
2212
2586
  this.probabilityTable_ = probabilityTable;
2213
2587
  if (numSymbols === 0) {
2214
2588
  return true;
@@ -2289,24 +2663,73 @@ function decodeTaggedSymbols(numValues, numComponents, srcBuffer, outValues) {
2289
2663
  if (numValues > 0 && tagDecoder.numSymbols === 0) {
2290
2664
  return false;
2291
2665
  }
2666
+ const tagAns = tagDecoder.ans_;
2292
2667
  srcBuffer.startBitDecoding(false);
2293
2668
  const bd = srcBuffer._bitDecoder;
2294
- const tagAns = tagDecoder.ans_;
2669
+ const buf = bd._bitBuffer;
2670
+ const byteLength = bd._byteLength;
2671
+ let bitOffset = bd._bitOffset;
2295
2672
  let valueId = 0;
2296
2673
  for (let i = 0; i < numValues; i += numComponents) {
2297
2674
  const bitLength = tagAns.ransRead();
2298
- for (let j = 0; j < numComponents; ++j) {
2299
- const val = bd.getBits(bitLength);
2300
- if (val === void 0) {
2301
- return false;
2675
+ if (bitLength < 32) {
2676
+ const mask = (1 << bitLength) - 1;
2677
+ let j = 0;
2678
+ for (; j < numComponents; ++j) {
2679
+ const byteOffset = bitOffset >> 3;
2680
+ if (byteOffset + 4 >= byteLength) break;
2681
+ const bitShift = bitOffset & 7;
2682
+ let value = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> bitShift;
2683
+ if (bitLength > 32 - bitShift) {
2684
+ value = (value | buf[byteOffset + 4] << 32 - bitShift) >>> 0;
2685
+ }
2686
+ bitOffset += bitLength;
2687
+ outValues[valueId++] = value & mask;
2688
+ }
2689
+ if (j === numComponents) continue;
2690
+ bd._bitOffset = bitOffset;
2691
+ for (; j < numComponents; ++j) {
2692
+ const val = bd.getBits(bitLength);
2693
+ if (val === void 0) {
2694
+ return false;
2695
+ }
2696
+ outValues[valueId++] = val;
2697
+ }
2698
+ bitOffset = bd._bitOffset;
2699
+ } else {
2700
+ bd._bitOffset = bitOffset;
2701
+ for (let j = 0; j < numComponents; ++j) {
2702
+ const val = bd.getBits(bitLength);
2703
+ if (val === void 0) {
2704
+ return false;
2705
+ }
2706
+ outValues[valueId++] = val;
2302
2707
  }
2303
- outValues[valueId++] = val;
2708
+ bitOffset = bd._bitOffset;
2304
2709
  }
2305
2710
  }
2711
+ bd._bitOffset = bitOffset;
2306
2712
  tagDecoder.endDecoding();
2307
2713
  srcBuffer.endBitDecoding();
2308
2714
  return true;
2309
2715
  }
2716
+ function parseRawSymbolStream(numValues, srcBuffer) {
2717
+ const maxBitLength = srcBuffer.decodeUint8();
2718
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
2719
+ return null;
2720
+ }
2721
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
2722
+ if (!decoder.create(srcBuffer)) {
2723
+ return null;
2724
+ }
2725
+ if (numValues > 0 && decoder.numSymbols === 0) {
2726
+ return null;
2727
+ }
2728
+ if (!decoder.startDecoding(srcBuffer)) {
2729
+ return null;
2730
+ }
2731
+ return decoder;
2732
+ }
2310
2733
  function decodeRawSymbolsInternal(uniqueSymbolsBitLength, numValues, srcBuffer, outValues) {
2311
2734
  const decoder = new RAnsSymbolDecoder(uniqueSymbolsBitLength);
2312
2735
  if (!decoder.create(srcBuffer)) {
@@ -2409,6 +2832,16 @@ var PredictionSchemeDecoderInterface = class {
2409
2832
  decodePredictionData(_buffer) {
2410
2833
  return true;
2411
2834
  }
2835
+ /**
2836
+ * Like computeOriginalValues, but inCorr still holds unsigned zigzag-coded
2837
+ * corrections; the implementation unpacks each one inline, replacing the
2838
+ * standalone convertSymbolsToSignedInts pass. Returns undefined when the
2839
+ * scheme/transform combination cannot fuse (the caller then falls back to
2840
+ * the two-pass path). Base implementation: never fusable.
2841
+ */
2842
+ computeOriginalValuesZigzag(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2843
+ return void 0;
2844
+ }
2412
2845
  /** Reverts the prediction applied during encoding, writing original values to outData. */
2413
2846
  computeOriginalValues(_inCorr, _outData, _size, _numComponents, _entryToPointIdMap) {
2414
2847
  return false;
@@ -2744,7 +3177,7 @@ var OctahedronToolBox = class {
2744
3177
  // src/decoder/compression/attributes/prediction_schemes/MeshPredictionSchemeGeometricNormalPredictorArea.ts
2745
3178
  var UPPER_BOUND = 1 << 29;
2746
3179
  function buildInt32PositionCache(att, map, numEntries, tempPos) {
2747
- const cache = new Int32Array(numEntries * 3);
3180
+ const cache = scratchInt32(numEntries * 3);
2748
3181
  const bufData = att.buffer && att.buffer.data;
2749
3182
  if (att.dataType === DataType.INT32 && att.numComponents === 3 && bufData) {
2750
3183
  const src = new Int32Array(bufData.buffer);
@@ -2825,7 +3258,7 @@ var MeshPredictionSchemeGeometricNormalPredictorArea = class {
2825
3258
  const cornerToVertex = this._cornerToVertex;
2826
3259
  const vertexToDataMap = this._meshData.vertexToDataMap;
2827
3260
  const nc = cornerToVertex.length;
2828
- const c2o = new Int32Array(nc);
3261
+ const c2o = scratchInt32(nc);
2829
3262
  for (let c = 0; c < nc; ++c) {
2830
3263
  const v = cornerToVertex[c];
2831
3264
  c2o[c] = v < 0 ? -1 : vertexToDataMap[v] * 3;
@@ -3076,10 +3509,21 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3076
3509
  isInitialized() {
3077
3510
  return this._meshData.isInitialized();
3078
3511
  }
3512
+ // Zigzag-fused variant: decodes corrections that are still in their unsigned
3513
+ // zigzag form, unpacking each one inline. Only offered for the wrap
3514
+ // transform (whose corrections are the only zigzag-coded ones this scheme
3515
+ // sees); callers fall back to the standalone conversion pass otherwise.
3516
+ computeOriginalValuesZigzag(inCorr, outData, _size, numComponents, _entryToPointIdMap) {
3517
+ this._transform.init(numComponents);
3518
+ if (!this._transform.getType || this._transform.getType() !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3519
+ return void 0;
3520
+ }
3521
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, true);
3522
+ }
3079
3523
  computeOriginalValues(inCorr, outData, size, numComponents, entryToPointIdMap) {
3080
3524
  this._transform.init(numComponents);
3081
3525
  if (this._transform.getType && this._transform.getType() === PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
3082
- return this._computeOriginalValuesWrap(inCorr, outData, numComponents);
3526
+ return this._computeOriginalValuesWrap(inCorr, outData, numComponents, false);
3083
3527
  }
3084
3528
  const table = this._meshData.cornerTable;
3085
3529
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3120,12 +3564,18 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3120
3564
  }
3121
3565
  return true;
3122
3566
  }
3123
- _computeOriginalValuesWrap(inCorr, outData, numComponents) {
3567
+ // The wrap transform's corrections are zigzag-coded; with `zigzag` set the
3568
+ // decode folds the (val >>> 1) ^ -(val & 1) unpacking into each correction
3569
+ // read, replacing the standalone convertSymbolsToSignedInts pass over the
3570
+ // whole array (see computeOriginalValuesZigzag). Every correction is read
3571
+ // exactly once before its slot is overwritten, so the in-place aliasing of
3572
+ // inCorr and outData is preserved.
3573
+ _computeOriginalValuesWrap(inCorr, outData, numComponents, zigzag) {
3124
3574
  if (numComponents === 2) {
3125
- return this._computeOriginalValuesWrap2(inCorr, outData);
3575
+ return this._computeOriginalValuesWrap2(inCorr, outData, zigzag);
3126
3576
  }
3127
3577
  if (numComponents === 3) {
3128
- return this._computeOriginalValuesWrap3(inCorr, outData);
3578
+ return this._computeOriginalValuesWrap3(inCorr, outData, zigzag);
3129
3579
  }
3130
3580
  const table = this._meshData.cornerTable;
3131
3581
  const vertexToDataMap = this._meshData.vertexToDataMap;
@@ -3143,7 +3593,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3143
3593
  } else if (pred < minValue) {
3144
3594
  pred = minValue;
3145
3595
  }
3146
- let orig = pred + inCorr[c] | 0;
3596
+ const raw = inCorr[c];
3597
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3147
3598
  if (orig > maxValue) {
3148
3599
  orig -= maxDif;
3149
3600
  } else if (orig < minValue) {
@@ -3182,7 +3633,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3182
3633
  } else if (pred < minValue) {
3183
3634
  pred = minValue;
3184
3635
  }
3185
- let orig = pred + inCorr[dstOffset + c] | 0;
3636
+ const raw = inCorr[dstOffset + c];
3637
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3186
3638
  if (orig > maxValue) {
3187
3639
  orig -= maxDif;
3188
3640
  } else if (orig < minValue) {
@@ -3199,7 +3651,8 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3199
3651
  } else if (pred < minValue) {
3200
3652
  pred = minValue;
3201
3653
  }
3202
- let orig = pred + inCorr[dstOffset + c] | 0;
3654
+ const raw = inCorr[dstOffset + c];
3655
+ let orig = pred + (zigzag ? raw >>> 1 ^ -(raw & 1) : raw) | 0;
3203
3656
  if (orig > maxValue) {
3204
3657
  orig -= maxDif;
3205
3658
  } else if (orig < minValue) {
@@ -3211,7 +3664,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3211
3664
  }
3212
3665
  return true;
3213
3666
  }
3214
- _computeOriginalValuesWrap2(inCorr, outData) {
3667
+ _computeOriginalValuesWrap2(inCorr, outData, zigzag) {
3215
3668
  const table = this._meshData.cornerTable;
3216
3669
  const vertexToDataMap = this._meshData.vertexToDataMap;
3217
3670
  const oppositeCorners = table.oppositeCornerArray();
@@ -3233,8 +3686,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3233
3686
  } else if (pred1 < minValue) {
3234
3687
  pred1 = minValue;
3235
3688
  }
3236
- let orig0 = pred0 + inCorr[0] | 0;
3237
- let orig1 = pred1 + inCorr[1] | 0;
3689
+ const raw0 = inCorr[0];
3690
+ const raw1 = inCorr[1];
3691
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3692
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3238
3693
  if (orig0 > maxValue) {
3239
3694
  orig0 -= maxDif;
3240
3695
  } else if (orig0 < minValue) {
@@ -3288,8 +3743,10 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3288
3743
  } else if (pred1 < minValue) {
3289
3744
  pred1 = minValue;
3290
3745
  }
3291
- orig0 = pred0 + inCorr[dstOffset] | 0;
3292
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3746
+ const rawA = inCorr[dstOffset];
3747
+ const rawB = inCorr[dstOffset + 1];
3748
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3749
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3293
3750
  if (orig0 > maxValue) {
3294
3751
  orig0 -= maxDif;
3295
3752
  } else if (orig0 < minValue) {
@@ -3305,7 +3762,7 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3305
3762
  }
3306
3763
  return true;
3307
3764
  }
3308
- _computeOriginalValuesWrap3(inCorr, outData) {
3765
+ _computeOriginalValuesWrap3(inCorr, outData, zigzag) {
3309
3766
  const table = this._meshData.cornerTable;
3310
3767
  const vertexToDataMap = this._meshData.vertexToDataMap;
3311
3768
  const oppositeCorners = table.oppositeCornerArray();
@@ -3333,9 +3790,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3333
3790
  } else if (pred2 < minValue) {
3334
3791
  pred2 = minValue;
3335
3792
  }
3336
- let orig0 = pred0 + inCorr[0] | 0;
3337
- let orig1 = pred1 + inCorr[1] | 0;
3338
- let orig2 = pred2 + inCorr[2] | 0;
3793
+ const raw0 = inCorr[0];
3794
+ const raw1 = inCorr[1];
3795
+ const raw2 = inCorr[2];
3796
+ let orig0 = pred0 + (zigzag ? raw0 >>> 1 ^ -(raw0 & 1) : raw0) | 0;
3797
+ let orig1 = pred1 + (zigzag ? raw1 >>> 1 ^ -(raw1 & 1) : raw1) | 0;
3798
+ let orig2 = pred2 + (zigzag ? raw2 >>> 1 ^ -(raw2 & 1) : raw2) | 0;
3339
3799
  if (orig0 > maxValue) {
3340
3800
  orig0 -= maxDif;
3341
3801
  } else if (orig0 < minValue) {
@@ -3402,9 +3862,12 @@ var MeshPredictionSchemeParallelogramDecoder = class extends MeshPredictionSchem
3402
3862
  } else if (pred2 < minValue) {
3403
3863
  pred2 = minValue;
3404
3864
  }
3405
- orig0 = pred0 + inCorr[dstOffset] | 0;
3406
- orig1 = pred1 + inCorr[dstOffset + 1] | 0;
3407
- orig2 = pred2 + inCorr[dstOffset + 2] | 0;
3865
+ const rawA = inCorr[dstOffset];
3866
+ const rawB = inCorr[dstOffset + 1];
3867
+ const rawC = inCorr[dstOffset + 2];
3868
+ orig0 = pred0 + (zigzag ? rawA >>> 1 ^ -(rawA & 1) : rawA) | 0;
3869
+ orig1 = pred1 + (zigzag ? rawB >>> 1 ^ -(rawB & 1) : rawB) | 0;
3870
+ orig2 = pred2 + (zigzag ? rawC >>> 1 ^ -(rawC & 1) : rawC) | 0;
3408
3871
  if (orig0 > maxValue) {
3409
3872
  orig0 -= maxDif;
3410
3873
  } else if (orig0 < minValue) {
@@ -3911,14 +4374,49 @@ var PredictionSchemeWrapDecodingTransform = class {
3911
4374
  // src/decoder/compression/attributes/SequentialIntegerAttributeDecoder.ts
3912
4375
  var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder {
3913
4376
  _predictionScheme;
4377
+ // Two-phase decode state (see SequentialAttributeDecoder): the parse phase
4378
+ // stashes the primed raw-symbol stream here so the controller can batch and
4379
+ // pair several attributes' decodes; the finish phase consumes it.
4380
+ _pendingSymbolDecoder;
4381
+ _pendingNumValues;
4382
+ _finishPointIds;
3914
4383
  constructor() {
3915
4384
  super();
3916
4385
  this._predictionScheme = null;
4386
+ this._pendingSymbolDecoder = null;
4387
+ this._pendingNumValues = 0;
4388
+ this._finishPointIds = null;
3917
4389
  }
3918
- transformAttributeToOriginalFormat(pointIds) {
3919
- return this._storeValues(pointIds.length);
4390
+ // --- Two-phase decode (parse headers / batch symbol decode / finish) ---
4391
+ decodePortableAttributeParse(pointIds, buffer) {
4392
+ if (this.attribute.numComponents <= 0) {
4393
+ return false;
4394
+ }
4395
+ if (!this.attribute.reset(pointIds.length)) {
4396
+ return false;
4397
+ }
4398
+ this._finishPointIds = pointIds;
4399
+ return this._decodeValuesParse(pointIds, buffer);
3920
4400
  }
3921
- decodeValues(pointIds, buffer) {
4401
+ pendingSymbolStream() {
4402
+ if (this._pendingSymbolDecoder === null) {
4403
+ return null;
4404
+ }
4405
+ const portableAttributeData = this.getPortableAttributeData();
4406
+ return {
4407
+ ans: this._pendingSymbolDecoder.ans_,
4408
+ out: new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, this._pendingNumValues),
4409
+ count: this._pendingNumValues
4410
+ };
4411
+ }
4412
+ decodePortableAttributeFinish() {
4413
+ if (this._pendingSymbolDecoder !== null) {
4414
+ this._pendingSymbolDecoder.endDecoding();
4415
+ this._pendingSymbolDecoder = null;
4416
+ }
4417
+ return this._finishIntegerValues(this._finishPointIds);
4418
+ }
4419
+ _decodeValuesParse(pointIds, buffer) {
3922
4420
  const predictionSchemeMethod = buffer.decodeInt8();
3923
4421
  if (predictionSchemeMethod === void 0) return false;
3924
4422
  if (predictionSchemeMethod < PredictionSchemeMethod.PREDICTION_NONE || predictionSchemeMethod >= PredictionSchemeMethod.NUM_PREDICTION_SCHEMES) {
@@ -3937,12 +4435,6 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3937
4435
  return false;
3938
4436
  }
3939
4437
  }
3940
- if (!this.decodeIntegerValues(pointIds, buffer)) {
3941
- return false;
3942
- }
3943
- return true;
3944
- }
3945
- decodeIntegerValues(pointIds, buffer) {
3946
4438
  const numComponents = this.getNumValueComponents();
3947
4439
  if (numComponents <= 0) {
3948
4440
  return false;
@@ -3957,9 +4449,23 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3957
4449
  const compressed = buffer.decodeUint8();
3958
4450
  if (compressed === void 0) return false;
3959
4451
  if (compressed > 0) {
3960
- const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3961
- if (!decodeSymbols(numValues, numComponents, buffer, outUint32)) {
3962
- return false;
4452
+ if (numValues > 0) {
4453
+ const scheme = buffer.decodeUint8();
4454
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_RAW) {
4455
+ const decoder = parseRawSymbolStream(numValues, buffer);
4456
+ if (decoder === null) {
4457
+ return false;
4458
+ }
4459
+ this._pendingSymbolDecoder = decoder;
4460
+ this._pendingNumValues = numValues;
4461
+ } else if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
4462
+ const outUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4463
+ if (!decodeTaggedSymbols(numValues, numComponents, buffer, outUint32)) {
4464
+ return false;
4465
+ }
4466
+ } else {
4467
+ return false;
4468
+ }
3963
4469
  }
3964
4470
  } else {
3965
4471
  const numBytes = buffer.decodeUint8();
@@ -3990,15 +4496,40 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
3990
4496
  }
3991
4497
  }
3992
4498
  }
3993
- if (numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive())) {
3994
- const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
3995
- convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
3996
- }
3997
4499
  if (this._predictionScheme) {
3998
4500
  if (!this._predictionScheme.decodePredictionData(buffer)) {
3999
4501
  return false;
4000
4502
  }
4503
+ }
4504
+ return true;
4505
+ }
4506
+ // The post-symbol tail of the decode: zigzag unpacking and prediction.
4507
+ _finishIntegerValues(pointIds) {
4508
+ const numComponents = this.getNumValueComponents();
4509
+ const numValues = pointIds.length * numComponents;
4510
+ const portableAttributeData = this.getPortableAttributeData();
4511
+ if (portableAttributeData === null) {
4512
+ return false;
4513
+ }
4514
+ const needsZigzag = numValues > 0 && (this._predictionScheme === null || !this._predictionScheme.areCorrectionsPositive());
4515
+ if (this._predictionScheme) {
4001
4516
  if (numValues > 0) {
4517
+ if (needsZigzag) {
4518
+ const fused = this._predictionScheme.computeOriginalValuesZigzag(
4519
+ portableAttributeData,
4520
+ portableAttributeData,
4521
+ numValues,
4522
+ numComponents,
4523
+ pointIds
4524
+ );
4525
+ if (fused !== void 0) {
4526
+ return fused;
4527
+ }
4528
+ }
4529
+ if (needsZigzag) {
4530
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4531
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4532
+ }
4002
4533
  if (!this._predictionScheme.computeOriginalValues(
4003
4534
  portableAttributeData,
4004
4535
  portableAttributeData,
@@ -4009,9 +4540,34 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4009
4540
  return false;
4010
4541
  }
4011
4542
  }
4543
+ } else if (needsZigzag) {
4544
+ const asUint32 = new Uint32Array(portableAttributeData.buffer, portableAttributeData.byteOffset, numValues);
4545
+ convertSymbolsToSignedInts(asUint32, numValues, portableAttributeData);
4012
4546
  }
4013
4547
  return true;
4014
4548
  }
4549
+ transformAttributeToOriginalFormat(pointIds) {
4550
+ return this._storeValues(pointIds.length);
4551
+ }
4552
+ // Single-phase entry (base-class decodePortableAttribute path): parse,
4553
+ // decode any deferred symbol stream immediately, finish.
4554
+ decodeValues(pointIds, buffer) {
4555
+ this._finishPointIds = pointIds;
4556
+ if (!this._decodeValuesParse(pointIds, buffer)) {
4557
+ return false;
4558
+ }
4559
+ const pending = this._pendingSymbolDecoder;
4560
+ if (pending !== null) {
4561
+ const portableAttributeData = this.getPortableAttributeData();
4562
+ const outUint32 = new Uint32Array(
4563
+ portableAttributeData.buffer,
4564
+ portableAttributeData.byteOffset,
4565
+ this._pendingNumValues
4566
+ );
4567
+ pending.ans_.decodeSymbols(outUint32, this._pendingNumValues);
4568
+ }
4569
+ return this.decodePortableAttributeFinish();
4570
+ }
4015
4571
  // Prediction scheme for decoding integer values; subclasses override for others.
4016
4572
  createIntPredictionScheme(method, transformType) {
4017
4573
  if (transformType !== PredictionSchemeTransformType.PREDICTION_TRANSFORM_WRAP) {
@@ -4074,7 +4630,7 @@ var SequentialIntegerAttributeDecoder = class extends SequentialAttributeDecoder
4074
4630
  );
4075
4631
  const portAtt = new PointAttribute(ga);
4076
4632
  portAtt.setIdentityMapping();
4077
- portAtt.reset(numEntries);
4633
+ portAtt.resetScratch(numEntries);
4078
4634
  portAtt.uniqueId = this.attribute.uniqueId;
4079
4635
  this.setPortableAttribute(portAtt);
4080
4636
  }
@@ -4100,12 +4656,22 @@ var AttributeTransformType = {
4100
4656
  };
4101
4657
 
4102
4658
  // src/decoder/attributes/AttributeTransformData.ts
4659
+ var INITIAL_CAPACITY = 32;
4103
4660
  var AttributeTransformData = class {
4104
4661
  _transformType;
4105
- _buffer;
4662
+ // Parameter bytes, little-endian, appended in transform-defined order.
4663
+ // Capacity grows geometrically and the DataView is cached alongside it: the
4664
+ // previous DataBuffer-backed version reallocated the buffer and built a
4665
+ // fresh DataView on *every* appended value, which on primitive-heavy files
4666
+ // cost more than the dequantization it describes.
4667
+ _bytes;
4668
+ _view;
4669
+ _size;
4106
4670
  constructor() {
4107
4671
  this._transformType = AttributeTransformType.INVALID;
4108
- this._buffer = new DataBuffer();
4672
+ this._bytes = new Uint8Array(INITIAL_CAPACITY);
4673
+ this._view = new DataView(this._bytes.buffer);
4674
+ this._size = 0;
4109
4675
  }
4110
4676
  get transformType() {
4111
4677
  return this._transformType;
@@ -4113,13 +4679,29 @@ var AttributeTransformData = class {
4113
4679
  set transformType(type) {
4114
4680
  this._transformType = type;
4115
4681
  }
4682
+ // Number of parameter bytes written so far (the next append offset).
4683
+ get dataSize() {
4684
+ return this._size;
4685
+ }
4686
+ get data() {
4687
+ return this._bytes.subarray(0, this._size);
4688
+ }
4689
+ _reserve(sizeNeeded) {
4690
+ if (sizeNeeded <= this._bytes.length) return;
4691
+ let capacity = this._bytes.length * 2;
4692
+ if (capacity < sizeNeeded) capacity = sizeNeeded;
4693
+ const grown = new Uint8Array(capacity);
4694
+ grown.set(this._bytes);
4695
+ this._bytes = grown;
4696
+ this._view = new DataView(grown.buffer);
4697
+ }
4116
4698
  setParameterValue(byteOffset, value, type) {
4117
4699
  const sizeNeeded = byteOffset + this._typeSize(type);
4118
- if (sizeNeeded > this._buffer.dataSize) {
4119
- this._buffer.resize(sizeNeeded);
4700
+ this._reserve(sizeNeeded);
4701
+ if (sizeNeeded > this._size) {
4702
+ this._size = sizeNeeded;
4120
4703
  }
4121
- const data = this._buffer.data;
4122
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
4704
+ const view = this._view;
4123
4705
  switch (type) {
4124
4706
  case "int32":
4125
4707
  view.setInt32(byteOffset, value, true);
@@ -4151,7 +4733,7 @@ var AttributeTransformData = class {
4151
4733
  }
4152
4734
  }
4153
4735
  appendParameterValue(value, type) {
4154
- this.setParameterValue(this._buffer.dataSize, value, type);
4736
+ this.setParameterValue(this._size, value, type);
4155
4737
  }
4156
4738
  _typeSize(type) {
4157
4739
  switch (type) {
@@ -4721,6 +5303,12 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4721
5303
  return true;
4722
5304
  }
4723
5305
  decodeAttributes(buffer) {
5306
+ if (!this._prepareSequence()) {
5307
+ return false;
5308
+ }
5309
+ return super.decodeAttributes(buffer);
5310
+ }
5311
+ _prepareSequence() {
4724
5312
  if (!this._sequencer) {
4725
5313
  return false;
4726
5314
  }
@@ -4735,7 +5323,44 @@ var SequentialAttributeDecodersController = class extends AttributesDecoder {
4735
5323
  return false;
4736
5324
  }
4737
5325
  }
4738
- return super.decodeAttributes(buffer);
5326
+ return true;
5327
+ }
5328
+ // Two-phase decode (see AttributesDecoder): parse everything that reads the
5329
+ // buffer -- sequence, portable headers (deferring raw rANS symbol decodes),
5330
+ // and the transform parameters -- so the deferred streams of ALL attributes
5331
+ // decoders can then be paired, and finish (zigzag, prediction, inverse
5332
+ // transform) runs per decoder in the original order afterwards. None of the
5333
+ // finish work reads the buffer, and dependents only read parent portable
5334
+ // VALUES in finish, so ordering and output stay identical.
5335
+ decodeAttributesParse(buffer) {
5336
+ if (!this._prepareSequence()) {
5337
+ return false;
5338
+ }
5339
+ const numAttributes = this.getNumAttributes();
5340
+ for (let i = 0; i < numAttributes; i++) {
5341
+ if (!this._sequentialDecoders[i].decodePortableAttributeParse(this._pointIds, buffer)) {
5342
+ return false;
5343
+ }
5344
+ }
5345
+ return this.decodeDataNeededByPortableTransforms(buffer);
5346
+ }
5347
+ collectPendingSymbolStreams(out) {
5348
+ const numAttributes = this.getNumAttributes();
5349
+ for (let i = 0; i < numAttributes; i++) {
5350
+ const pending = this._sequentialDecoders[i].pendingSymbolStream();
5351
+ if (pending !== null) {
5352
+ out.push(pending);
5353
+ }
5354
+ }
5355
+ }
5356
+ decodeAttributesFinish() {
5357
+ const numAttributes = this.getNumAttributes();
5358
+ for (let i = 0; i < numAttributes; i++) {
5359
+ if (!this._sequentialDecoders[i].decodePortableAttributeFinish()) {
5360
+ return false;
5361
+ }
5362
+ }
5363
+ return this.transformAttributesToOriginalFormat();
4739
5364
  }
4740
5365
  getPortableAttribute(pointAttributeId) {
4741
5366
  const locId = this.getLocalIdForPointAttribute(pointAttributeId);
@@ -4862,14 +5487,13 @@ var DepthFirstTraverser = class {
4862
5487
  // Scratch buffers are set up here rather than in init() so a shared-traversal
4863
5488
  // -cache hit — where generateSequence returns before any traversal — skips
4864
5489
  // this entirely, including the visited-flag zero-fill (worth ~0.5% on the
4865
- // 488-primitive manablade-static bundle, which shares attribute corner tables across
5490
+ // ~500-primitive manablade bundle, which shares attribute corner tables across
4866
5491
  // primitives). Uint8Array (0/1) instead of Array(bool): these flags are read
4867
5492
  // and written on every corner of the hottest decode loop (traverseFromCorner).
4868
5493
  // Decode-scoped scratch: released in bulk at the end of the decode.
4869
5494
  onTraversalStart() {
4870
5495
  const cornerTable = this._cornerTable;
4871
5496
  this._isFaceVisited = scratchUint8Zeroed(cornerTable.numFaces());
4872
- this._isVertexVisited = scratchUint8Zeroed(cornerTable.numVertices());
4873
5497
  this._cornerTraversalStack = scratchInt32(this._numCorners);
4874
5498
  }
4875
5499
  onTraversalEnd() {
@@ -4879,7 +5503,6 @@ var DepthFirstTraverser = class {
4879
5503
  return true;
4880
5504
  }
4881
5505
  const isFaceVisited = this._isFaceVisited;
4882
- const isVertexVisited = this._isVertexVisited;
4883
5506
  const observer = this._observer;
4884
5507
  const cornerToVertex = this._cornerToVertex;
4885
5508
  const oppositeCorners = this._oppositeCorners;
@@ -4903,14 +5526,12 @@ var DepthFirstTraverser = class {
4903
5526
  if (nextVert === kInvalidVertexIndex2 || prevVert === kInvalidVertexIndex2) {
4904
5527
  return false;
4905
5528
  }
4906
- if (!isVertexVisited[nextVert]) {
4907
- isVertexVisited[nextVert] = 1;
5529
+ if (vertexToEncodedMap[nextVert] < 0) {
4908
5530
  outPointIds[numOutPoints++] = obsFaces[nextCorner];
4909
5531
  encodedToCornerMap[numValues] = nextCorner;
4910
5532
  vertexToEncodedMap[nextVert] = numValues++;
4911
5533
  }
4912
- if (!isVertexVisited[prevVert]) {
4913
- isVertexVisited[prevVert] = 1;
5534
+ if (vertexToEncodedMap[prevVert] < 0) {
4914
5535
  outPointIds[numOutPoints++] = obsFaces[prevCorner];
4915
5536
  encodedToCornerMap[numValues] = prevCorner;
4916
5537
  vertexToEncodedMap[prevVert] = numValues++;
@@ -4931,27 +5552,26 @@ var DepthFirstTraverser = class {
4931
5552
  encodingData.numValues = numValues;
4932
5553
  return false;
4933
5554
  }
4934
- if (!isVertexVisited[vertId]) {
5555
+ const faceBase = faceId * 3;
5556
+ const nextCornerId = cornerId === faceBase + 2 ? faceBase : cornerId + 1;
5557
+ if (vertexToEncodedMap[vertId] < 0) {
4935
5558
  const lc = vertexLeftmost[vertId];
4936
5559
  let onBoundary = true;
4937
- if (lc !== void 0 && lc >= 0) {
5560
+ if (lc >= 0) {
4938
5561
  const nextLc = lc % 3 === 2 ? lc - 2 : lc + 1;
4939
5562
  onBoundary = oppositeCorners[nextLc] < 0;
4940
5563
  }
4941
- isVertexVisited[vertId] = 1;
4942
5564
  outPointIds[numOutPoints++] = obsFaces[cornerId];
4943
5565
  encodedToCornerMap[numValues] = cornerId;
4944
5566
  vertexToEncodedMap[vertId] = numValues++;
4945
5567
  if (!onBoundary) {
4946
- const nextCornerId2 = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4947
- cornerId = oppositeCorners[nextCornerId2];
5568
+ cornerId = oppositeCorners[nextCornerId];
4948
5569
  faceId = cornerId / 3 | 0;
4949
5570
  continue;
4950
5571
  }
4951
5572
  }
4952
- const nextCornerId = cornerId % 3 === 2 ? cornerId - 2 : cornerId + 1;
4953
5573
  const rightCornerId = oppositeCorners[nextCornerId];
4954
- const prevCornerId = cornerId % 3 === 0 ? cornerId + 2 : cornerId - 1;
5574
+ const prevCornerId = cornerId === faceBase ? faceBase + 2 : cornerId - 1;
4955
5575
  const leftCornerId = oppositeCorners[prevCornerId];
4956
5576
  const rightFaceId = rightCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : rightCornerId / 3 | 0;
4957
5577
  const leftFaceId = leftCornerId === kInvalidCornerIndex4 ? kInvalidFaceIndex : leftCornerId / 3 | 0;
@@ -5181,6 +5801,9 @@ var MeshTraversalSequencer = class {
5181
5801
  _outPointIds;
5182
5802
  _numOutPoints;
5183
5803
  _traversalCache;
5804
+ // The cache entry the last generateSequence resolved to (hit or store);
5805
+ // carries the shared indicesMap.
5806
+ _cacheEntry;
5184
5807
  constructor(mesh, encodingData, traversalCache = null) {
5185
5808
  this._mesh = mesh;
5186
5809
  this._encodingData = encodingData;
@@ -5188,6 +5811,7 @@ var MeshTraversalSequencer = class {
5188
5811
  this._outPointIds = new Int32Array(0);
5189
5812
  this._numOutPoints = 0;
5190
5813
  this._traversalCache = traversalCache;
5814
+ this._cacheEntry = null;
5191
5815
  }
5192
5816
  setTraverser(traverser) {
5193
5817
  this._traverser = traverser;
@@ -5202,6 +5826,7 @@ var MeshTraversalSequencer = class {
5202
5826
  if (cached !== void 0) {
5203
5827
  this._outPointIds = cached.pointIds;
5204
5828
  this._encodingData.adoptTraversalResult(cached.vertexMap, cached.cornerMap, cached.numValues);
5829
+ this._cacheEntry = cached;
5205
5830
  return true;
5206
5831
  }
5207
5832
  }
@@ -5217,12 +5842,15 @@ var MeshTraversalSequencer = class {
5217
5842
  byMethod = /* @__PURE__ */ new Map();
5218
5843
  this._traversalCache.set(cacheKey, byMethod);
5219
5844
  }
5220
- byMethod.set(methodId, {
5845
+ const entry = {
5221
5846
  pointIds: this._outPointIds,
5222
5847
  vertexMap: this._encodingData.vertexToEncodedAttributeValueIndexMap,
5223
5848
  cornerMap: this._encodingData.encodedAttributeValueIndexToCornerMap,
5224
- numValues: this._encodingData.numValues
5225
- });
5849
+ numValues: this._encodingData.numValues,
5850
+ indicesMap: null
5851
+ };
5852
+ byMethod.set(methodId, entry);
5853
+ this._cacheEntry = entry;
5226
5854
  }
5227
5855
  return true;
5228
5856
  }
@@ -5233,6 +5861,11 @@ var MeshTraversalSequencer = class {
5233
5861
  this._outPointIds[this._numOutPoints++] = pointId;
5234
5862
  }
5235
5863
  updatePointToAttributeIndexMapping(attribute) {
5864
+ const entry = this._cacheEntry;
5865
+ if (entry !== null && entry.indicesMap !== null) {
5866
+ attribute.setExplicitMappingShared(entry.indicesMap);
5867
+ return true;
5868
+ }
5236
5869
  const cornerTable = this._traverser.cornerTable();
5237
5870
  const numFaces = this._mesh.numFaces();
5238
5871
  const numPoints = this._mesh.numPoints();
@@ -5254,11 +5887,14 @@ var MeshTraversalSequencer = class {
5254
5887
  }
5255
5888
  indicesMap[pointId] = attEntryId;
5256
5889
  }
5890
+ if (entry !== null) {
5891
+ entry.indicesMap = attribute.indicesMap;
5892
+ }
5257
5893
  return true;
5258
5894
  }
5259
5895
  _generateSequenceInternal() {
5260
5896
  this._numOutPoints = 0;
5261
- this._outPointIds = new Int32Array(this._mesh.numPoints());
5897
+ this._outPointIds = scratchInt32(this._mesh.numPoints());
5262
5898
  this._traverser.onTraversalStart();
5263
5899
  const numFaces = this._traverser.cornerTable().numFaces();
5264
5900
  for (let i = 0; i < numFaces && this._traverser._numVisitedFaces < numFaces; ++i) {
@@ -5449,14 +6085,14 @@ var MeshEdgebreakerDecoderImpl = class {
5449
6085
  this._attributeData = [];
5450
6086
  for (let i = 0; i < numAttributeData; ++i) {
5451
6087
  const ad = new AttributeData();
5452
- ad.attributeSeamCorners = new Int32Array(numFaces * 3);
6088
+ ad.attributeSeamCorners = scratchInt32(numFaces * 3);
5453
6089
  ad.numSeamCorners = 0;
5454
6090
  this._attributeData.push(ad);
5455
6091
  }
5456
6092
  if (!this._cornerTable.reset(numFaces, this._numEncodedVertices + numEncodedSplitSymbols)) {
5457
6093
  return false;
5458
6094
  }
5459
- this._isVertHole = new Uint8Array(this._numEncodedVertices + numEncodedSplitSymbols).fill(1);
6095
+ this._isVertHole = scratchUint8Filled(this._numEncodedVertices + numEncodedSplitSymbols, 1);
5460
6096
  if (this._decodeHoleAndTopologySplitEvents(this._decoder.buffer()) === -1) {
5461
6097
  return false;
5462
6098
  }
@@ -5481,20 +6117,37 @@ var MeshEdgebreakerDecoderImpl = class {
5481
6117
  }
5482
6118
  this._traversalDecoder.done();
5483
6119
  let previousConnectivityData = null;
6120
+ let previousSeamCorners = null;
6121
+ let previousSeamCount = 0;
5484
6122
  for (let i = 0; i < this._attributeData.length; ++i) {
5485
6123
  const connectivityData = this._attributeData[i].connectivityData;
5486
- connectivityData.initEmpty(this._cornerTable);
5487
6124
  const seamCorners = this._attributeData[i].attributeSeamCorners;
5488
6125
  const seamCount = this._attributeData[i].numSeamCorners;
5489
- for (let s = 0; s < seamCount; ++s) {
5490
- connectivityData.addSeamEdge(seamCorners[s]);
6126
+ let sameAsPrevious = previousConnectivityData !== null && seamCount === previousSeamCount;
6127
+ if (sameAsPrevious) {
6128
+ const previous = previousSeamCorners;
6129
+ for (let s = 0; s < seamCount; ++s) {
6130
+ if (seamCorners[s] !== previous[s]) {
6131
+ sameAsPrevious = false;
6132
+ break;
6133
+ }
6134
+ }
5491
6135
  }
5492
- if (connectivityData.hasSameSeams(previousConnectivityData)) {
5493
- connectivityData.adoptVertexRecompute(previousConnectivityData);
5494
- } else if (!connectivityData.recomputeVertices(null, null)) {
5495
- return false;
6136
+ if (sameAsPrevious) {
6137
+ connectivityData.adoptFrom(previousConnectivityData);
6138
+ } else {
6139
+ connectivityData.initEmpty(this._cornerTable);
6140
+ connectivityData.reserveSeamEdges(seamCount);
6141
+ for (let s = 0; s < seamCount; ++s) {
6142
+ connectivityData.addSeamEdge(seamCorners[s]);
6143
+ }
6144
+ if (!connectivityData.recomputeVertices(null, null)) {
6145
+ return false;
6146
+ }
5496
6147
  }
5497
6148
  previousConnectivityData = connectivityData;
6149
+ previousSeamCorners = seamCorners;
6150
+ previousSeamCount = seamCount;
5498
6151
  }
5499
6152
  this._posEncodingData.init(this._cornerTable.numVertices());
5500
6153
  for (let i = 0; i < this._attributeData.length; ++i) {
@@ -5533,39 +6186,27 @@ var MeshEdgebreakerDecoderImpl = class {
5533
6186
  const activeCornerStack = scratchInt32(numSymbols + this._topologySplitData.length + 16);
5534
6187
  let activeCornerStackSize = 0;
5535
6188
  const topologySplitActiveCorners = /* @__PURE__ */ new Map();
6189
+ const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5536
6190
  const invalidVertices = [];
5537
6191
  const removeInvalidVertices = this._attributeData.length === 0;
5538
6192
  let maxNumVertices = this._isVertHole.length;
5539
6193
  let numFacesDecoded = 0;
5540
6194
  const cornerToVertex = this._cornerTable._cornerToVertex;
5541
6195
  const oppositeCorners = this._cornerTable._oppositeCorners;
5542
- const numCorners = this._cornerTable.numCorners();
5543
- const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
5544
- const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
5545
- const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
5546
- const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
5547
- const leftMostCorner = (v) => v < 0 || v >= this._cornerTable._vertexCorners.length ? -1 : this._cornerTable._vertexCorners[v];
5548
- const swingLeft = (c) => {
5549
- const n = next(c);
5550
- const o = opposite(n);
5551
- return o < 0 ? -1 : next(o);
5552
- };
5553
- const swingRight = (c) => {
5554
- const p = prev(c);
5555
- const o = opposite(p);
5556
- return o < 0 ? -1 : prev(o);
5557
- };
5558
6196
  const vc = this._cornerTable;
6197
+ let vertexCorners = vc._vertexCorners;
6198
+ const isVertHole = this._isVertHole;
6199
+ const traversalDecoder = this._traversalDecoder;
5559
6200
  for (let symbolId = 0; symbolId < numSymbols; ++symbolId) {
5560
6201
  const faceIndex = numFacesDecoded++;
5561
6202
  let checkTopologySplit = false;
5562
- const symbol = this._traversalDecoder.decodeSymbol();
6203
+ const symbol = traversalDecoder.decodeSymbol();
5563
6204
  if (symbol === TOPOLOGY_C) {
5564
6205
  if (activeCornerStackSize === 0) return -1;
5565
6206
  const cornerA = activeCornerStack[activeCornerStackSize - 1];
5566
6207
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5567
6208
  const vertexX = cornerToVertex[nA];
5568
- const lmcX = vc._vertexCorners[vertexX];
6209
+ const lmcX = vertexCorners[vertexX];
5569
6210
  const cornerB = lmcX % 3 === 2 ? lmcX - 2 : lmcX + 1;
5570
6211
  if (cornerA === cornerB) return -1;
5571
6212
  if (oppositeCorners[cornerA] !== kInvalidCornerIndex6 || oppositeCorners[cornerB] !== kInvalidCornerIndex6) {
@@ -5584,8 +6225,8 @@ var MeshEdgebreakerDecoderImpl = class {
5584
6225
  cornerToVertex[corner] = vertexX;
5585
6226
  cornerToVertex[corner + 1] = vertBNext;
5586
6227
  cornerToVertex[corner + 2] = vertAPrev;
5587
- vc._vertexCorners[vertAPrev] = corner + 2;
5588
- this._isVertHole[vertexX] = 0;
6228
+ vertexCorners[vertAPrev] = corner + 2;
6229
+ isVertHole[vertexX] = 0;
5589
6230
  activeCornerStack[activeCornerStackSize - 1] = corner;
5590
6231
  } else if (symbol === TOPOLOGY_R || symbol === TOPOLOGY_L) {
5591
6232
  if (activeCornerStackSize === 0) return -1;
@@ -5606,14 +6247,20 @@ var MeshEdgebreakerDecoderImpl = class {
5606
6247
  }
5607
6248
  oppositeCorners[oppCorner] = cornerA;
5608
6249
  oppositeCorners[cornerA] = oppCorner;
5609
- const newVertIndex = this._cornerTable.addNewVertex();
5610
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6250
+ let newVertIndex;
6251
+ if (vc._numVertices < vertexCorners.length) {
6252
+ newVertIndex = vc._numVertices++;
6253
+ } else {
6254
+ newVertIndex = vc.addNewVertex();
6255
+ vertexCorners = vc._vertexCorners;
6256
+ }
6257
+ if (vc._numVertices > maxNumVertices) return -1;
5611
6258
  cornerToVertex[oppCorner] = newVertIndex;
5612
- vc._vertexCorners[newVertIndex] = oppCorner;
6259
+ vertexCorners[newVertIndex] = oppCorner;
5613
6260
  const pA = cornerA % 3 === 0 ? cornerA + 2 : cornerA - 1;
5614
6261
  const vertexR = cornerToVertex[pA];
5615
6262
  cornerToVertex[cornerR] = vertexR;
5616
- vc._vertexCorners[vertexR] = cornerR;
6263
+ vertexCorners[vertexR] = cornerR;
5617
6264
  const nA = cornerA % 3 === 2 ? cornerA - 2 : cornerA + 1;
5618
6265
  cornerToVertex[cornerL] = cornerToVertex[nA];
5619
6266
  activeCornerStack[activeCornerStackSize - 1] = corner;
@@ -5645,11 +6292,11 @@ var MeshEdgebreakerDecoderImpl = class {
5645
6292
  const pB = cornerB % 3 === 0 ? cornerB + 2 : cornerB - 1;
5646
6293
  const vertBPrev = cornerToVertex[pB];
5647
6294
  cornerToVertex[corner + 2] = vertBPrev;
5648
- vc._vertexCorners[vertBPrev] = corner + 2;
6295
+ vertexCorners[vertBPrev] = corner + 2;
5649
6296
  let cornerN = cornerB % 3 === 2 ? cornerB - 2 : cornerB + 1;
5650
6297
  const vertexN = cornerToVertex[cornerN];
5651
- this._traversalDecoder.mergeVertices(vertexP, vertexN);
5652
- vc._vertexCorners[vertexP] = vc._vertexCorners[vertexN];
6298
+ traversalDecoder.mergeVertices(vertexP, vertexN);
6299
+ vertexCorners[vertexP] = vertexCorners[vertexN];
5653
6300
  const firstCorner = cornerN;
5654
6301
  while (cornerN !== kInvalidCornerIndex6) {
5655
6302
  cornerToVertex[cornerN] = vertexP;
@@ -5660,32 +6307,38 @@ var MeshEdgebreakerDecoderImpl = class {
5660
6307
  return -1;
5661
6308
  }
5662
6309
  }
5663
- vc._vertexCorners[vertexN] = -1;
6310
+ vertexCorners[vertexN] = -1;
5664
6311
  if (removeInvalidVertices) {
5665
6312
  invalidVertices.push(vertexN);
5666
6313
  }
5667
6314
  activeCornerStack[activeCornerStackSize - 1] = corner;
5668
6315
  } else if (symbol === TOPOLOGY_E) {
5669
6316
  const corner = 3 * faceIndex;
5670
- const firstVertIndex = this._cornerTable.addNewVertex();
5671
- this._cornerTable.addNewVertex();
5672
- this._cornerTable.addNewVertex();
5673
- if (this._cornerTable.numVertices() > maxNumVertices) return -1;
6317
+ let firstVertIndex;
6318
+ if (vc._numVertices + 3 <= vertexCorners.length) {
6319
+ firstVertIndex = vc._numVertices;
6320
+ vc._numVertices += 3;
6321
+ } else {
6322
+ firstVertIndex = vc.addNewVertex();
6323
+ vc.addNewVertex();
6324
+ vc.addNewVertex();
6325
+ vertexCorners = vc._vertexCorners;
6326
+ }
6327
+ if (vc._numVertices > maxNumVertices) return -1;
5674
6328
  cornerToVertex[corner] = firstVertIndex;
5675
6329
  cornerToVertex[corner + 1] = firstVertIndex + 1;
5676
6330
  cornerToVertex[corner + 2] = firstVertIndex + 2;
5677
- vc._vertexCorners[firstVertIndex] = corner;
5678
- vc._vertexCorners[firstVertIndex + 1] = corner + 1;
5679
- vc._vertexCorners[firstVertIndex + 2] = corner + 2;
6331
+ vertexCorners[firstVertIndex] = corner;
6332
+ vertexCorners[firstVertIndex + 1] = corner + 1;
6333
+ vertexCorners[firstVertIndex + 2] = corner + 2;
5680
6334
  activeCornerStack[activeCornerStackSize++] = corner;
5681
6335
  checkTopologySplit = true;
5682
6336
  } else {
5683
6337
  return -1;
5684
6338
  }
5685
- this._traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
5686
- if (checkTopologySplit) {
6339
+ traversalDecoder.newActiveCornerReached(activeCornerStack[activeCornerStackSize - 1]);
6340
+ if (checkTopologySplit && this._topologySplitData.length > 0) {
5687
6341
  const encoderSymbolId = numSymbols - symbolId - 1;
5688
- const splitResult = { faceEdge: 0, encoderSplitSymbolId: 0 };
5689
6342
  while (this._isTopologySplit(encoderSymbolId, splitResult)) {
5690
6343
  if (splitResult.encoderSplitSymbolId < 0) return -1;
5691
6344
  const actTopCorner = activeCornerStack[activeCornerStackSize - 1];
@@ -5700,15 +6353,39 @@ var MeshEdgebreakerDecoderImpl = class {
5700
6353
  }
5701
6354
  }
5702
6355
  }
5703
- if (this._cornerTable.numVertices() > maxNumVertices) {
6356
+ if (vc._numVertices > maxNumVertices) {
6357
+ return -1;
6358
+ }
6359
+ numFacesDecoded = this._decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded);
6360
+ if (numFacesDecoded === -1) {
6361
+ return -1;
6362
+ }
6363
+ if (numFacesDecoded !== this._cornerTable.numFaces()) {
5704
6364
  return -1;
5705
6365
  }
6366
+ return this._removeInvalidVertices(invalidVertices);
6367
+ }
6368
+ // Connects the remaining active-stack corners to newly decoded start faces.
6369
+ // Returns the updated decoded-face count, or -1 on malformed input.
6370
+ _decodeStartFaces(activeCornerStack, activeCornerStackSize, numFacesDecoded) {
6371
+ const ct = this._cornerTable;
6372
+ const cornerToVertex = ct._cornerToVertex;
6373
+ const oppositeCorners = ct._oppositeCorners;
6374
+ const vertexCorners = ct._vertexCorners;
6375
+ const isVertHole = this._isVertHole;
6376
+ const traversalDecoder = this._traversalDecoder;
6377
+ const numCorners = ct.numCorners();
6378
+ const numFaces = ct.numFaces();
6379
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6380
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6381
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6382
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
5706
6383
  while (activeCornerStackSize > 0) {
5707
6384
  const corner = activeCornerStack[activeCornerStackSize - 1];
5708
6385
  activeCornerStackSize--;
5709
- const interiorFace = this._traversalDecoder.decodeStartFaceConfiguration();
6386
+ const interiorFace = traversalDecoder.decodeStartFaceConfiguration();
5710
6387
  if (interiorFace) {
5711
- if (numFacesDecoded >= this._cornerTable.numFaces()) {
6388
+ if (numFacesDecoded >= numFaces) {
5712
6389
  return -1;
5713
6390
  }
5714
6391
  const cornerA = corner;
@@ -5734,9 +6411,9 @@ var MeshEdgebreakerDecoderImpl = class {
5734
6411
  cornerToVertex[newCorner] = vertX;
5735
6412
  cornerToVertex[newCorner + 1] = vertP;
5736
6413
  cornerToVertex[newCorner + 2] = vertN;
5737
- this._isVertHole[vertX] = 0;
5738
- this._isVertHole[vertP] = 0;
5739
- this._isVertHole[vertN] = 0;
6414
+ isVertHole[vertX] = 0;
6415
+ isVertHole[vertP] = 0;
6416
+ isVertHole[vertN] = 0;
5740
6417
  this._initFaceConfigurations.push(true);
5741
6418
  this._initCorners.push(newCorner);
5742
6419
  } else {
@@ -5744,10 +6421,34 @@ var MeshEdgebreakerDecoderImpl = class {
5744
6421
  this._initCorners.push(corner);
5745
6422
  }
5746
6423
  }
5747
- if (numFacesDecoded !== this._cornerTable.numFaces()) {
5748
- return -1;
5749
- }
5750
- let numVertices = this._cornerTable.numVertices();
6424
+ return numFacesDecoded;
6425
+ }
6426
+ // Removes invalid (isolated) vertices by swapping them with the last valid
6427
+ // vertex in the table, matching C++ mesh_edgebreaker_decoder_impl.cc (the
6428
+ // forward iteration order matters). Returns the final vertex count, or -1.
6429
+ _removeInvalidVertices(invalidVertices) {
6430
+ const ct = this._cornerTable;
6431
+ const cornerToVertex = ct._cornerToVertex;
6432
+ const oppositeCorners = ct._oppositeCorners;
6433
+ const vertexCorners = ct._vertexCorners;
6434
+ const isVertHole = this._isVertHole;
6435
+ const numCorners = ct.numCorners();
6436
+ const next = (c) => c < 0 ? -1 : c % 3 === 2 ? c - 2 : c + 1;
6437
+ const prev = (c) => c < 0 ? -1 : c % 3 === 0 ? c + 2 : c - 1;
6438
+ const vertex = (c) => c < 0 || c >= numCorners ? -1 : cornerToVertex[c];
6439
+ const opposite = (c) => c < 0 || c >= numCorners ? -1 : oppositeCorners[c];
6440
+ const leftMostCorner = (v) => v < 0 || v >= vertexCorners.length ? -1 : vertexCorners[v];
6441
+ const swingLeft = (c) => {
6442
+ const n = next(c);
6443
+ const o = opposite(n);
6444
+ return o < 0 ? -1 : next(o);
6445
+ };
6446
+ const swingRight = (c) => {
6447
+ const p = prev(c);
6448
+ const o = opposite(p);
6449
+ return o < 0 ? -1 : prev(o);
6450
+ };
6451
+ let numVertices = ct.numVertices();
5751
6452
  for (let ivIdx = 0; ivIdx < invalidVertices.length; ++ivIdx) {
5752
6453
  const invalidVert = invalidVertices[ivIdx];
5753
6454
  let srcVert = numVertices - 1;
@@ -5777,10 +6478,10 @@ var MeshEdgebreakerDecoderImpl = class {
5777
6478
  cid = swingRight(cid);
5778
6479
  }
5779
6480
  }
5780
- this._cornerTable._vertexCorners[invalidVert] = leftMostCorner(srcVert);
5781
- this._cornerTable._vertexCorners[srcVert] = -1;
5782
- this._isVertHole[invalidVert] = this._isVertHole[srcVert];
5783
- this._isVertHole[srcVert] = 0;
6481
+ vertexCorners[invalidVert] = leftMostCorner(srcVert);
6482
+ vertexCorners[srcVert] = -1;
6483
+ isVertHole[invalidVert] = isVertHole[srcVert];
6484
+ isVertHole[srcVert] = 0;
5784
6485
  numVertices--;
5785
6486
  }
5786
6487
  return numVertices;
@@ -5823,14 +6524,55 @@ var MeshEdgebreakerDecoderImpl = class {
5823
6524
  // per-corner decodeNextBit work. Within each face the three corners are
5824
6525
  // visited in encoder edge order [base, next, prev] = [c, c+1, c+2] (the
5825
6526
  // caller always starts a face at its base corner, so next/prev need no wrap).
6527
+ //
6528
+ // The face comparison the C++ makes -- floor(oppCorner/3) >= floor(cc/3) for
6529
+ // the face's base corner -- is just `oppCorner >= faceBaseCorner`, since the
6530
+ // base corner is a multiple of 3. That removes the per-corner division; the
6531
+ // invalid-corner case (-1) is still handled by the branch above it.
5826
6532
  _decodeAttributeConnectivities() {
5827
6533
  const oppositeCorners = this._cornerTable.oppositeCornerArray();
5828
6534
  const attributeData = this._attributeData;
5829
6535
  const numAttrData = attributeData.length;
5830
6536
  const connectivityDecoders = this._traversalDecoder._attributeConnectivityDecoders;
5831
6537
  const numCorners = this._cornerTable.numCorners();
6538
+ if (numAttrData === 1) {
6539
+ const ad = attributeData[0];
6540
+ const seamCorners = ad.attributeSeamCorners;
6541
+ let numSeamCorners = ad.numSeamCorners;
6542
+ const decoder = connectivityDecoders[0];
6543
+ const ans = decoder.ansDecoder_;
6544
+ const p = decoder.p_;
6545
+ const buf = ans.buf;
6546
+ const bufStart = ans.bufStart;
6547
+ let state = ans.state;
6548
+ let bufOffset = ans.bufOffset;
6549
+ for (let corner = 0; corner < numCorners; corner += 3) {
6550
+ for (let k = 0; k < 3; ++k) {
6551
+ const cc = corner + k;
6552
+ const oppCorner = oppositeCorners[cc];
6553
+ if (oppCorner === kInvalidCornerIndex6) {
6554
+ seamCorners[numSeamCorners++] = cc;
6555
+ } else if (oppCorner >= corner) {
6556
+ if (state < ANS_L_BASE && bufOffset > bufStart) {
6557
+ state = state << 8 | buf[--bufOffset];
6558
+ }
6559
+ const rem = state & 255;
6560
+ const xn = (state >>> 8) * p;
6561
+ if (rem < p) {
6562
+ state = xn + rem;
6563
+ seamCorners[numSeamCorners++] = cc;
6564
+ } else {
6565
+ state = state - xn - p;
6566
+ }
6567
+ }
6568
+ }
6569
+ }
6570
+ ans.state = state;
6571
+ ans.bufOffset = bufOffset;
6572
+ ad.numSeamCorners = numSeamCorners;
6573
+ return;
6574
+ }
5832
6575
  for (let corner = 0; corner < numCorners; corner += 3) {
5833
- const srcFaceId = corner / 3 | 0;
5834
6576
  for (let k = 0; k < 3; ++k) {
5835
6577
  const cc = corner + k;
5836
6578
  const oppCorner = oppositeCorners[cc];
@@ -5839,7 +6581,7 @@ var MeshEdgebreakerDecoderImpl = class {
5839
6581
  const ad = attributeData[i];
5840
6582
  ad.attributeSeamCorners[ad.numSeamCorners++] = cc;
5841
6583
  }
5842
- } else if ((oppCorner / 3 | 0) >= srcFaceId) {
6584
+ } else if (oppCorner >= corner) {
5843
6585
  for (let i = 0; i < numAttrData; ++i) {
5844
6586
  if (connectivityDecoders[i].decodeNextBit()) {
5845
6587
  const ad = attributeData[i];
@@ -5870,10 +6612,10 @@ var MeshEdgebreakerDecoderImpl = class {
5870
6612
  const attributeData = this._attributeData;
5871
6613
  const numAttrData = attributeData.length;
5872
6614
  let numPoints = 0;
5873
- const cornerToPointMap = new Int32Array(ct.numCorners());
6615
+ const cornerToPointMap = scratchInt32(ct.numCorners());
5874
6616
  const numVertices = ct.numVertices();
5875
6617
  const vertexLeftmost = ct.vertexLeftmostCornerArray();
5876
- const baseOpp = ct.oppositeCornerArray();
6618
+ const swingRight = ct.swingRightArray();
5877
6619
  const _baseCornerToVertex = ct.cornerToVertexArray();
5878
6620
  const isVertHole = this._isVertHole;
5879
6621
  const attCornerToVertex = new Array(numAttrData);
@@ -5887,7 +6629,7 @@ var MeshEdgebreakerDecoderImpl = class {
5887
6629
  if (numAttrData === 1) {
5888
6630
  anyAttVertexOnSeam = attVertexOnSeam[0];
5889
6631
  } else {
5890
- anyAttVertexOnSeam = new Uint8Array(numVertices);
6632
+ anyAttVertexOnSeam = scratchUint8Zeroed(numVertices);
5891
6633
  for (let i = 0; i < numAttrData; ++i) {
5892
6634
  const attSeam = attVertexOnSeam[i];
5893
6635
  for (let v = 0; v < numVertices; ++v) {
@@ -5905,37 +6647,24 @@ var MeshEdgebreakerDecoderImpl = class {
5905
6647
  const initialC = c;
5906
6648
  const pointId = numPoints++;
5907
6649
  cornerToPointMap[initialC] = pointId;
5908
- let rem = initialC % 3;
5909
- let pv = rem === 0 ? initialC + 2 : initialC - 1;
5910
- let opp = baseOpp[pv];
5911
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6650
+ c = swingRight[initialC];
5912
6651
  while (c !== kInvalidCornerIndex6 && c !== initialC) {
5913
6652
  cornerToPointMap[c] = pointId;
5914
- rem = c % 3;
5915
- pv = rem === 0 ? c + 2 : c - 1;
5916
- opp = baseOpp[pv];
5917
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6653
+ c = swingRight[c];
5918
6654
  }
5919
6655
  } else {
5920
6656
  let deduplicationFirstCorner = c;
5921
- let rem, pv, opp;
5922
6657
  if (!isVertHole[v]) {
5923
6658
  if (numAttrData === 1) {
5924
6659
  const vertId = singleAttC2V[c];
5925
- rem = c % 3;
5926
- pv = rem === 0 ? c + 2 : c - 1;
5927
- opp = baseOpp[pv];
5928
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6660
+ let actC = swingRight[c];
5929
6661
  while (actC !== c) {
5930
6662
  if (actC === kInvalidCornerIndex6) return false;
5931
6663
  if (singleAttC2V[actC] !== vertId) {
5932
6664
  deduplicationFirstCorner = actC;
5933
6665
  break;
5934
6666
  }
5935
- rem = actC % 3;
5936
- pv = rem === 0 ? actC + 2 : actC - 1;
5937
- opp = baseOpp[pv];
5938
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6667
+ actC = swingRight[actC];
5939
6668
  }
5940
6669
  } else {
5941
6670
  for (let i = 0; i < numAttrData; ++i) {
@@ -5944,10 +6673,7 @@ var MeshEdgebreakerDecoderImpl = class {
5944
6673
  }
5945
6674
  const attC2V = attCornerToVertex[i];
5946
6675
  const vertId = attC2V[c];
5947
- rem = c % 3;
5948
- pv = rem === 0 ? c + 2 : c - 1;
5949
- opp = baseOpp[pv];
5950
- let actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6676
+ let actC = swingRight[c];
5951
6677
  let seamFound = false;
5952
6678
  while (actC !== c) {
5953
6679
  if (actC === kInvalidCornerIndex6) return false;
@@ -5956,10 +6682,7 @@ var MeshEdgebreakerDecoderImpl = class {
5956
6682
  seamFound = true;
5957
6683
  break;
5958
6684
  }
5959
- rem = actC % 3;
5960
- pv = rem === 0 ? actC + 2 : actC - 1;
5961
- opp = baseOpp[pv];
5962
- actC = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6685
+ actC = swingRight[actC];
5963
6686
  }
5964
6687
  if (seamFound) break;
5965
6688
  }
@@ -5968,10 +6691,7 @@ var MeshEdgebreakerDecoderImpl = class {
5968
6691
  c = deduplicationFirstCorner;
5969
6692
  cornerToPointMap[c] = numPoints++;
5970
6693
  let prevC = c;
5971
- rem = c % 3;
5972
- pv = rem === 0 ? c + 2 : c - 1;
5973
- opp = baseOpp[pv];
5974
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6694
+ c = swingRight[c];
5975
6695
  while (c !== kInvalidCornerIndex6 && c !== deduplicationFirstCorner) {
5976
6696
  let attributeSeam;
5977
6697
  if (numAttrData === 1) {
@@ -5992,10 +6712,7 @@ var MeshEdgebreakerDecoderImpl = class {
5992
6712
  cornerToPointMap[c] = cornerToPointMap[prevC];
5993
6713
  }
5994
6714
  prevC = c;
5995
- rem = c % 3;
5996
- pv = rem === 0 ? c + 2 : c - 1;
5997
- opp = baseOpp[pv];
5998
- c = opp < 0 ? kInvalidCornerIndex6 : opp % 3 === 0 ? opp + 2 : opp - 1;
6715
+ c = swingRight[c];
5999
6716
  }
6000
6717
  }
6001
6718
  }
@@ -6021,8 +6738,8 @@ var MeshAttributeIndicesEncodingData = class {
6021
6738
  this._numValues = 0;
6022
6739
  }
6023
6740
  init(numVertices) {
6024
- this._vertexToEncodedAttributeValueIndexMap = new Int32Array(numVertices);
6025
- this._encodedAttributeValueIndexToCornerMap = new Int32Array(numVertices);
6741
+ this._vertexToEncodedAttributeValueIndexMap = scratchInt32Filled(numVertices, -1);
6742
+ this._encodedAttributeValueIndexToCornerMap = scratchInt32(numVertices);
6026
6743
  this._numValues = 0;
6027
6744
  }
6028
6745
  // Adopts a traversal result from an identical corner table, avoiding a
@@ -6072,6 +6789,8 @@ var CornerTable = class {
6072
6789
  // corner -> opposite corner
6073
6790
  _vertexCorners;
6074
6791
  // vertex -> left-most corner
6792
+ _swingRight;
6793
+ // corner -> next corner around its vertex, CW
6075
6794
  constructor() {
6076
6795
  this._numFaces = 0;
6077
6796
  this._numCorners = 0;
@@ -6079,16 +6798,42 @@ var CornerTable = class {
6079
6798
  this._cornerToVertex = null;
6080
6799
  this._oppositeCorners = null;
6081
6800
  this._vertexCorners = null;
6801
+ this._swingRight = null;
6082
6802
  }
6083
6803
  reset(numFaces, numVertices) {
6084
6804
  this._numFaces = numFaces;
6085
6805
  this._numCorners = numFaces * 3;
6086
6806
  this._numVertices = 0;
6087
- this._cornerToVertex = new Int32Array(this._numCorners).fill(-1);
6088
- this._oppositeCorners = new Int32Array(this._numCorners).fill(-1);
6089
- this._vertexCorners = new Int32Array(numVertices).fill(-1);
6807
+ this._cornerToVertex = scratchInt32Filled(this._numCorners, -1);
6808
+ this._oppositeCorners = scratchInt32Filled(this._numCorners, -1);
6809
+ this._vertexCorners = scratchInt32Filled(numVertices, -1);
6810
+ this._swingRight = null;
6090
6811
  return true;
6091
6812
  }
6813
+ // swingRight(c) = previous(opposite(previous(c))) for every corner. Walking
6814
+ // the corner ring of a vertex is the inner loop of both the attribute-vertex
6815
+ // recompute and the point assignment, and each of those passes otherwise
6816
+ // pays two modulus-by-3 chains per step on top of the opposite lookup. Built
6817
+ // lazily -- callers only reach it once connectivity is final -- from
6818
+ // decode-scoped scratch, and dropped by reset().
6819
+ swingRightArray() {
6820
+ let table = this._swingRight;
6821
+ if (table === null) {
6822
+ const numCorners = this._numCorners;
6823
+ const opposite = this._oppositeCorners;
6824
+ table = scratchInt32(numCorners);
6825
+ for (let c = 0; c < numCorners; c += 3) {
6826
+ let o = opposite[c + 2];
6827
+ table[c] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6828
+ o = opposite[c];
6829
+ table[c + 1] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6830
+ o = opposite[c + 1];
6831
+ table[c + 2] = o < 0 ? kInvalidCornerIndex6 : o % 3 === 0 ? o + 2 : o - 1;
6832
+ }
6833
+ this._swingRight = table;
6834
+ }
6835
+ return table;
6836
+ }
6092
6837
  numFaces() {
6093
6838
  return this._numFaces;
6094
6839
  }
@@ -6132,8 +6877,7 @@ var CornerTable = class {
6132
6877
  this._numVertices++;
6133
6878
  if (newVertex >= this._vertexCorners.length) {
6134
6879
  const newCapacity = Math.max(newVertex + 1, this._vertexCorners.length * 2, 64);
6135
- const newArr = new Int32Array(newCapacity);
6136
- newArr.fill(-1);
6880
+ const newArr = scratchInt32Filled(newCapacity, -1);
6137
6881
  newArr.set(this._vertexCorners);
6138
6882
  this._vertexCorners = newArr;
6139
6883
  }
@@ -6164,6 +6908,10 @@ var MeshEdgebreakerTraversalDecoder = class {
6164
6908
  _attributeConnectivityDecoders;
6165
6909
  _numAttributeData;
6166
6910
  _decoderImpl;
6911
+ // _symbolBuffer's bit cursor, captured once bit decoding starts: decodeSymbol
6912
+ // runs per decoded face and would otherwise reach it through two property
6913
+ // loads and a method call per read.
6914
+ _symbolBits;
6167
6915
  constructor() {
6168
6916
  this._buffer = new DecoderBuffer();
6169
6917
  this._symbolBuffer = new DecoderBuffer();
@@ -6171,6 +6919,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6171
6919
  this._attributeConnectivityDecoders = null;
6172
6920
  this._numAttributeData = 0;
6173
6921
  this._decoderImpl = null;
6922
+ this._symbolBits = null;
6174
6923
  }
6175
6924
  init(decoder) {
6176
6925
  this._decoderImpl = decoder;
@@ -6205,6 +6954,21 @@ var MeshEdgebreakerTraversalDecoder = class {
6205
6954
  return this._startFaceDecoder.decodeNextBit() ? true : false;
6206
6955
  }
6207
6956
  decodeSymbol() {
6957
+ const bd = this._symbolBits;
6958
+ if (bd !== null) {
6959
+ const buf = bd._bitBuffer;
6960
+ const off = bd._bitOffset;
6961
+ const byteOffset = off >> 3;
6962
+ if (byteOffset + 4 < bd._byteLength) {
6963
+ const bits = (buf[byteOffset] | buf[byteOffset + 1] << 8 | buf[byteOffset + 2] << 16 | buf[byteOffset + 3] << 24) >>> (off & 7) & 7;
6964
+ if ((bits & 1) === TOPOLOGY_C) {
6965
+ bd._bitOffset = off + 1;
6966
+ return TOPOLOGY_C;
6967
+ }
6968
+ bd._bitOffset = off + 3;
6969
+ return bits;
6970
+ }
6971
+ }
6208
6972
  let symbol = this._symbolBuffer.decodeLeastSignificantBits32(1);
6209
6973
  if (symbol === TOPOLOGY_C) {
6210
6974
  return symbol;
@@ -6234,6 +6998,7 @@ var MeshEdgebreakerTraversalDecoder = class {
6234
6998
  if (traversalSize === void 0) {
6235
6999
  return false;
6236
7000
  }
7001
+ this._symbolBits = this._symbolBuffer._bitDecoder;
6237
7002
  this._buffer.init(
6238
7003
  this._symbolBuffer.dataHead,
6239
7004
  this._symbolBuffer.remainingSize,
@@ -6386,7 +7151,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6386
7151
  _maxValence;
6387
7152
  _vertexValences;
6388
7153
  _contextSymbols;
7154
+ // Int32Array, not number[]: read and written once per decoded symbol.
6389
7155
  _contextCounters;
7156
+ // corner -> vertex of _cornerTable, cached at init(); the array is created
7157
+ // once by CornerTable.reset() before the traversal decoder is initialized and
7158
+ // never replaced, so the per-symbol hot path can read it without two property
7159
+ // loads.
7160
+ _cornerToVertex;
6390
7161
  constructor() {
6391
7162
  super();
6392
7163
  this._cornerTable = null;
@@ -6397,11 +7168,13 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6397
7168
  this._maxValence = 7;
6398
7169
  this._vertexValences = new Int32Array(0);
6399
7170
  this._contextSymbols = [];
6400
- this._contextCounters = [];
7171
+ this._contextCounters = new Int32Array(0);
7172
+ this._cornerToVertex = new Int32Array(0);
6401
7173
  }
6402
7174
  init(decoder) {
6403
7175
  super.init(decoder);
6404
7176
  this._cornerTable = decoder.getCornerTable();
7177
+ this._cornerToVertex = this._cornerTable._cornerToVertex;
6405
7178
  }
6406
7179
  setNumEncodedVertices(numVertices) {
6407
7180
  this._numVertices = numVertices;
@@ -6419,10 +7192,11 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6419
7192
  if (this._numVertices < 0) {
6420
7193
  return false;
6421
7194
  }
6422
- this._vertexValences = new Int32Array(this._numVertices);
7195
+ this._vertexValences = scratchInt32Filled(this._numVertices, 0);
6423
7196
  const numUniqueValences = this._maxValence - this._minValence + 1;
6424
7197
  this._contextSymbols = new Array(numUniqueValences);
6425
- this._contextCounters = new Array(numUniqueValences);
7198
+ this._contextCounters = new Int32Array(numUniqueValences);
7199
+ const pending = [];
6426
7200
  for (let i = 0; i < numUniqueValences; ++i) {
6427
7201
  const numSymbols = decodeVarint(outBuffer);
6428
7202
  if (numSymbols === void 0) {
@@ -6432,16 +7206,72 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6432
7206
  return false;
6433
7207
  }
6434
7208
  if (numSymbols > 0) {
6435
- this._contextSymbols[i] = new Uint32Array(numSymbols);
6436
- if (!decodeSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7209
+ this._contextSymbols[i] = scratchUint32(numSymbols);
7210
+ const scheme = outBuffer.decodeUint8();
7211
+ if (scheme === SymbolCodingMethod.SYMBOL_CODING_TAGGED) {
7212
+ if (!decodeTaggedSymbols(numSymbols, 1, outBuffer, this._contextSymbols[i])) {
7213
+ return false;
7214
+ }
7215
+ this._contextCounters[i] = numSymbols;
7216
+ continue;
7217
+ }
7218
+ if (scheme !== SymbolCodingMethod.SYMBOL_CODING_RAW) {
6437
7219
  return false;
6438
7220
  }
7221
+ const maxBitLength = outBuffer.decodeUint8();
7222
+ if (maxBitLength === void 0 || maxBitLength < 1 || maxBitLength > 18) {
7223
+ return false;
7224
+ }
7225
+ const decoder = new RAnsSymbolDecoder(maxBitLength);
7226
+ if (!decoder.create(outBuffer)) {
7227
+ return false;
7228
+ }
7229
+ if (decoder.numSymbols === 0) {
7230
+ return false;
7231
+ }
7232
+ if (!decoder.startDecoding(outBuffer)) {
7233
+ return false;
7234
+ }
7235
+ pending.push({ decoder, out: this._contextSymbols[i], count: numSymbols });
6439
7236
  this._contextCounters[i] = numSymbols;
6440
7237
  } else {
6441
7238
  this._contextSymbols[i] = new Uint32Array(0);
6442
7239
  this._contextCounters[i] = 0;
6443
7240
  }
6444
7241
  }
7242
+ const allU8 = pending.every((entry) => entry.decoder.ans_.lutTable instanceof Uint8Array);
7243
+ let p = 0;
7244
+ if (allU8) {
7245
+ while (pending.length - p >= 3) {
7246
+ const a = pending[p];
7247
+ const b = pending[p + 1];
7248
+ const c = pending[p + 2];
7249
+ ransDecodeSymbolsTrioU8(
7250
+ a.decoder.ans_,
7251
+ a.out,
7252
+ a.count,
7253
+ b.decoder.ans_,
7254
+ b.out,
7255
+ b.count,
7256
+ c.decoder.ans_,
7257
+ c.out,
7258
+ c.count
7259
+ );
7260
+ p += 3;
7261
+ }
7262
+ if (pending.length - p === 2) {
7263
+ const a = pending[p];
7264
+ const b = pending[p + 1];
7265
+ ransDecodeSymbolsPairU8(a.decoder.ans_, a.out, a.count, b.decoder.ans_, b.out, b.count);
7266
+ p += 2;
7267
+ }
7268
+ }
7269
+ for (; p < pending.length; ++p) {
7270
+ pending[p].decoder.ans_.decodeSymbols(pending[p].out, pending[p].count);
7271
+ }
7272
+ for (const entry of pending) {
7273
+ entry.decoder.endDecoding();
7274
+ }
6445
7275
  return true;
6446
7276
  }
6447
7277
  decodeSymbol() {
@@ -6461,7 +7291,7 @@ var MeshEdgebreakerTraversalValenceDecoder = class extends MeshEdgebreakerTraver
6461
7291
  return this._lastSymbol;
6462
7292
  }
6463
7293
  newActiveCornerReached(corner) {
6464
- const cornerToVertex = this._cornerTable._cornerToVertex;
7294
+ const cornerToVertex = this._cornerToVertex;
6465
7295
  const valences = this._vertexValences;
6466
7296
  const next = corner % 3 === 2 ? corner - 2 : corner + 1;
6467
7297
  const prev = corner % 3 === 0 ? corner + 2 : corner - 1;