MetadataTableProperty.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. import Check from "../Core/Check.js";
  2. import clone from "../Core/clone.js";
  3. import ComponentDatatype from "../Core/ComponentDatatype.js";
  4. import defaultValue from "../Core/defaultValue.js";
  5. import defined from "../Core/defined.js";
  6. import DeveloperError from "../Core/DeveloperError.js";
  7. import FeatureDetection from "../Core/FeatureDetection.js";
  8. import getStringFromTypedArray from "../Core/getStringFromTypedArray.js";
  9. import oneTimeWarning from "../Core/oneTimeWarning.js";
  10. import MetadataComponentType from "./MetadataComponentType.js";
  11. import MetadataClassProperty from "./MetadataClassProperty.js";
  12. import MetadataType from "./MetadataType.js";
  13. /**
  14. * A binary property in a {@MetadataTable}
  15. * <p>
  16. * For 3D Tiles Next details, see the {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_metadata|3DTILES_metadata Extension}
  17. * for 3D Tiles, as well as the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata|EXT_structural_metadata Extension}
  18. * for glTF. For the legacy glTF extension, see {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata|EXT_feature_metadata Extension}
  19. * </p>
  20. *
  21. * @param {Object} options Object with the following properties:
  22. * @param {Number} options.count The number of elements in each property array.
  23. * @param {Object} options.property The property JSON object.
  24. * @param {MetadataClassProperty} options.classProperty The class property.
  25. * @param {Object.<String, Uint8Array>} options.bufferViews An object mapping bufferView IDs to Uint8Array objects.
  26. *
  27. * @alias MetadataTableProperty
  28. * @constructor
  29. *
  30. * @private
  31. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  32. */
  33. function MetadataTableProperty(options) {
  34. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  35. const count = options.count;
  36. const property = options.property;
  37. const classProperty = options.classProperty;
  38. const bufferViews = options.bufferViews;
  39. //>>includeStart('debug', pragmas.debug);
  40. Check.typeOf.number.greaterThan("options.count", count, 0);
  41. Check.typeOf.object("options.property", property);
  42. Check.typeOf.object("options.classProperty", classProperty);
  43. Check.typeOf.object("options.bufferViews", bufferViews);
  44. //>>includeEnd('debug');
  45. const type = classProperty.type;
  46. const isArray = classProperty.isArray;
  47. const isVariableLengthArray = classProperty.isVariableLengthArray;
  48. let valueType = classProperty.valueType;
  49. const enumType = classProperty.enumType;
  50. const hasStrings = type === MetadataType.STRING;
  51. const hasBooleans = type === MetadataType.BOOLEAN;
  52. let arrayOffsets;
  53. if (isVariableLengthArray) {
  54. // EXT_structural_metadata uses arrayOffsetType.
  55. // EXT_feature_metadata uses offsetType for both arrays and strings
  56. let arrayOffsetType = defaultValue(
  57. property.arrayOffsetType,
  58. property.offsetType
  59. );
  60. arrayOffsetType = defaultValue(
  61. MetadataComponentType[arrayOffsetType],
  62. MetadataComponentType.UINT32
  63. );
  64. // EXT_structural_metadata uses arrayOffsets.
  65. // EXT_feature_metadata uses arrayOffsetBufferView
  66. const arrayOffsetBufferView = defaultValue(
  67. property.arrayOffsets,
  68. property.arrayOffsetBufferView
  69. );
  70. arrayOffsets = new BufferView(
  71. bufferViews[arrayOffsetBufferView],
  72. arrayOffsetType,
  73. count + 1
  74. );
  75. }
  76. const vectorComponentCount = MetadataType.getComponentCount(type);
  77. let arrayComponentCount;
  78. if (isVariableLengthArray) {
  79. arrayComponentCount = arrayOffsets.get(count) - arrayOffsets.get(0);
  80. } else if (isArray) {
  81. arrayComponentCount = count * classProperty.arrayLength;
  82. } else {
  83. arrayComponentCount = count;
  84. }
  85. const componentCount = vectorComponentCount * arrayComponentCount;
  86. let stringOffsets;
  87. if (hasStrings) {
  88. // EXT_structural_metadata uses stringOffsetType, EXT_feature_metadata uses offsetType for both arrays and strings
  89. let stringOffsetType = defaultValue(
  90. property.stringOffsetType,
  91. property.offsetType
  92. );
  93. stringOffsetType = defaultValue(
  94. MetadataComponentType[stringOffsetType],
  95. MetadataComponentType.UINT32
  96. );
  97. // EXT_structural_metadata uses stringOffsets.
  98. // EXT_feature_metadata uses stringOffsetBufferView
  99. const stringOffsetBufferView = defaultValue(
  100. property.stringOffsets,
  101. property.stringOffsetBufferView
  102. );
  103. stringOffsets = new BufferView(
  104. bufferViews[stringOffsetBufferView],
  105. stringOffsetType,
  106. componentCount + 1
  107. );
  108. }
  109. if (hasStrings || hasBooleans) {
  110. // STRING and BOOLEAN types need to be parsed differently than other types
  111. valueType = MetadataComponentType.UINT8;
  112. }
  113. let valueCount;
  114. if (hasStrings) {
  115. valueCount = stringOffsets.get(componentCount) - stringOffsets.get(0);
  116. } else if (hasBooleans) {
  117. valueCount = Math.ceil(componentCount / 8);
  118. } else {
  119. valueCount = componentCount;
  120. }
  121. // EXT_structural_metadata uses values
  122. // EXT_feature_metadata uses bufferView
  123. const valuesBufferView = defaultValue(property.values, property.bufferView);
  124. const values = new BufferView(
  125. bufferViews[valuesBufferView],
  126. valueType,
  127. valueCount
  128. );
  129. let offset = property.offset;
  130. let scale = property.scale;
  131. // This needs to be set before handling default values
  132. const hasValueTransform =
  133. classProperty.hasValueTransform || defined(offset) || defined(scale);
  134. // If the table does not define an offset/scale, it inherits from the
  135. // class property. The class property handles setting the default of identity:
  136. // (offset 0, scale 1) with the same array shape as the property's type
  137. // information.
  138. offset = defaultValue(offset, classProperty.offset);
  139. scale = defaultValue(scale, classProperty.scale);
  140. // Since metadata table properties are stored as packed typed
  141. // arrays, flatten the offset/scale to make it easier to apply the
  142. // transformation by iteration.
  143. offset = flatten(offset);
  144. scale = flatten(scale);
  145. let getValueFunction;
  146. let setValueFunction;
  147. const that = this;
  148. if (hasStrings) {
  149. getValueFunction = function (index) {
  150. return getString(index, that._values, that._stringOffsets);
  151. };
  152. } else if (hasBooleans) {
  153. getValueFunction = function (index) {
  154. return getBoolean(index, that._values);
  155. };
  156. setValueFunction = function (index, value) {
  157. setBoolean(index, that._values, value);
  158. };
  159. } else if (defined(enumType)) {
  160. getValueFunction = function (index) {
  161. const integer = that._values.get(index);
  162. return enumType.namesByValue[integer];
  163. };
  164. setValueFunction = function (index, value) {
  165. const integer = enumType.valuesByName[value];
  166. that._values.set(index, integer);
  167. };
  168. } else {
  169. getValueFunction = function (index) {
  170. return that._values.get(index);
  171. };
  172. setValueFunction = function (index, value) {
  173. that._values.set(index, value);
  174. };
  175. }
  176. this._arrayOffsets = arrayOffsets;
  177. this._stringOffsets = stringOffsets;
  178. this._values = values;
  179. this._classProperty = classProperty;
  180. this._count = count;
  181. this._vectorComponentCount = vectorComponentCount;
  182. this._min = property.min;
  183. this._max = property.max;
  184. this._offset = offset;
  185. this._scale = scale;
  186. this._hasValueTransform = hasValueTransform;
  187. this._getValue = getValueFunction;
  188. this._setValue = setValueFunction;
  189. this._unpackedValues = undefined;
  190. this._extras = property.extras;
  191. this._extensions = property.extensions;
  192. }
  193. Object.defineProperties(MetadataTableProperty.prototype, {
  194. /**
  195. * True if offset/scale should be applied. If both offset/scale were
  196. * undefined, they default to identity so this property is set false
  197. *
  198. * @memberof MetadataClassProperty.prototype
  199. * @type {Boolean}
  200. * @readonly
  201. * @private
  202. */
  203. hasValueTransform: {
  204. get: function () {
  205. return this._hasValueTransform;
  206. },
  207. },
  208. /**
  209. * The offset to be added to property values as part of the value transform.
  210. *
  211. * @memberof MetadataClassProperty.prototype
  212. * @type {Number|Number[]|Number[][]}
  213. * @readonly
  214. * @private
  215. */
  216. offset: {
  217. get: function () {
  218. return this._offset;
  219. },
  220. },
  221. /**
  222. * The scale to be multiplied to property values as part of the value transform.
  223. *
  224. * @memberof MetadataClassProperty.prototype
  225. * @type {Number|Number[]|Number[][]}
  226. * @readonly
  227. * @private
  228. */
  229. scale: {
  230. get: function () {
  231. return this._scale;
  232. },
  233. },
  234. /**
  235. * Extras in the JSON object.
  236. *
  237. * @memberof MetadataTableProperty.prototype
  238. * @type {*}
  239. * @readonly
  240. * @private
  241. */
  242. extras: {
  243. get: function () {
  244. return this._extras;
  245. },
  246. },
  247. /**
  248. * Extensions in the JSON object.
  249. *
  250. * @memberof MetadataTableProperty.prototype
  251. * @type {*}
  252. * @readonly
  253. * @private
  254. */
  255. extensions: {
  256. get: function () {
  257. return this._extensions;
  258. },
  259. },
  260. });
  261. /**
  262. * Returns a copy of the value at the given index.
  263. *
  264. * @param {Number} index The index.
  265. * @returns {*} The value of the property.
  266. *
  267. * @private
  268. */
  269. MetadataTableProperty.prototype.get = function (index) {
  270. //>>includeStart('debug', pragmas.debug);
  271. checkIndex(this, index);
  272. //>>includeEnd('debug');
  273. let value = get(this, index);
  274. // handle noData and default
  275. value = this._classProperty.handleNoData(value);
  276. if (!defined(value)) {
  277. value = this._classProperty.default;
  278. return this._classProperty.unpackVectorAndMatrixTypes(value);
  279. }
  280. value = this._classProperty.normalize(value);
  281. value = applyValueTransform(this, value);
  282. return this._classProperty.unpackVectorAndMatrixTypes(value);
  283. };
  284. /**
  285. * Sets the value at the given index.
  286. *
  287. * @param {Number} index The index.
  288. * @param {*} value The value of the property.
  289. *
  290. * @private
  291. */
  292. MetadataTableProperty.prototype.set = function (index, value) {
  293. const classProperty = this._classProperty;
  294. //>>includeStart('debug', pragmas.debug);
  295. Check.defined("value", value);
  296. checkIndex(this, index);
  297. const errorMessage = classProperty.validate(value);
  298. if (defined(errorMessage)) {
  299. throw new DeveloperError(errorMessage);
  300. }
  301. //>>includeEnd('debug');
  302. value = classProperty.packVectorAndMatrixTypes(value);
  303. value = unapplyValueTransform(this, value);
  304. value = classProperty.unnormalize(value);
  305. set(this, index, value);
  306. };
  307. /**
  308. * Returns a typed array containing the property values.
  309. *
  310. * @returns {*} The typed array containing the property values or <code>undefined</code> if the property values are not stored in a typed array.
  311. *
  312. * @private
  313. */
  314. MetadataTableProperty.prototype.getTypedArray = function () {
  315. // Note: depending on the class definition some properties are unpacked to
  316. // JS arrays when first accessed and values will be undefined. Generally not
  317. // a concern for fixed-length arrays of numbers.
  318. if (defined(this._values)) {
  319. return this._values.typedArray;
  320. }
  321. return undefined;
  322. };
  323. function flatten(values) {
  324. if (!Array.isArray(values)) {
  325. return values;
  326. }
  327. const result = [];
  328. for (let i = 0; i < values.length; i++) {
  329. const value = values[i];
  330. if (Array.isArray(value)) {
  331. result.push.apply(result, value);
  332. } else {
  333. result.push(value);
  334. }
  335. }
  336. return result;
  337. }
  338. function checkIndex(table, index) {
  339. const count = table._count;
  340. if (!defined(index) || index < 0 || index >= count) {
  341. const maximumIndex = count - 1;
  342. throw new DeveloperError(
  343. `index is required and between zero and count - 1. Actual value: ${maximumIndex}`
  344. );
  345. }
  346. }
  347. function get(property, index) {
  348. if (requiresUnpackForGet(property)) {
  349. unpackProperty(property);
  350. }
  351. const classProperty = property._classProperty;
  352. const isArray = classProperty.isArray;
  353. const type = classProperty.type;
  354. const componentCount = MetadataType.getComponentCount(type);
  355. if (defined(property._unpackedValues)) {
  356. const value = property._unpackedValues[index];
  357. if (isArray) {
  358. return clone(value, true);
  359. }
  360. return value;
  361. }
  362. // handle single values
  363. if (!isArray && componentCount === 1) {
  364. return property._getValue(index);
  365. }
  366. return getArrayValues(property, classProperty, index);
  367. }
  368. function getArrayValues(property, classProperty, index) {
  369. let offset;
  370. let length;
  371. if (classProperty.isVariableLengthArray) {
  372. offset = property._arrayOffsets.get(index);
  373. length = property._arrayOffsets.get(index + 1) - offset;
  374. // for vectors and matrices, the offset and length need to be multiplied
  375. // by the component count
  376. const componentCount = MetadataType.getComponentCount(classProperty.type);
  377. offset *= componentCount;
  378. length *= componentCount;
  379. } else {
  380. const arrayLength = defaultValue(classProperty.arrayLength, 1);
  381. const componentCount = arrayLength * property._vectorComponentCount;
  382. offset = index * componentCount;
  383. length = componentCount;
  384. }
  385. const values = new Array(length);
  386. for (let i = 0; i < length; i++) {
  387. values[i] = property._getValue(offset + i);
  388. }
  389. return values;
  390. }
  391. function set(property, index, value) {
  392. if (requiresUnpackForSet(property, index, value)) {
  393. unpackProperty(property);
  394. }
  395. const classProperty = property._classProperty;
  396. const isArray = classProperty.isArray;
  397. const type = classProperty.type;
  398. const componentCount = MetadataType.getComponentCount(type);
  399. if (defined(property._unpackedValues)) {
  400. if (classProperty.isArray) {
  401. value = clone(value, true);
  402. }
  403. property._unpackedValues[index] = value;
  404. return;
  405. }
  406. // Values are unpacked if the length of a variable-size array changes or the
  407. // property has strings. No need to handle these cases below.
  408. // Handle single values
  409. if (!isArray && componentCount === 1) {
  410. property._setValue(index, value);
  411. return;
  412. }
  413. let offset;
  414. let length;
  415. if (classProperty.isVariableLengthArray) {
  416. offset = property._arrayOffsets.get(index);
  417. length = property._arrayOffsets.get(index + 1) - offset;
  418. } else {
  419. const arrayLength = defaultValue(classProperty.arrayLength, 1);
  420. const componentCount = arrayLength * property._vectorComponentCount;
  421. offset = index * componentCount;
  422. length = componentCount;
  423. }
  424. for (let i = 0; i < length; ++i) {
  425. property._setValue(offset + i, value[i]);
  426. }
  427. }
  428. function getString(index, values, stringOffsets) {
  429. const stringByteOffset = stringOffsets.get(index);
  430. const stringByteLength = stringOffsets.get(index + 1) - stringByteOffset;
  431. return getStringFromTypedArray(
  432. values.typedArray,
  433. stringByteOffset,
  434. stringByteLength
  435. );
  436. }
  437. function getBoolean(index, values) {
  438. // byteIndex is floor(index / 8)
  439. const byteIndex = index >> 3;
  440. const bitIndex = index % 8;
  441. return ((values.typedArray[byteIndex] >> bitIndex) & 1) === 1;
  442. }
  443. function setBoolean(index, values, value) {
  444. // byteIndex is floor(index / 8)
  445. const byteIndex = index >> 3;
  446. const bitIndex = index % 8;
  447. if (value) {
  448. values.typedArray[byteIndex] |= 1 << bitIndex;
  449. } else {
  450. values.typedArray[byteIndex] &= ~(1 << bitIndex);
  451. }
  452. }
  453. function getInt64NumberFallback(index, values) {
  454. const dataView = values.dataView;
  455. const byteOffset = index * 8;
  456. let value = 0;
  457. const isNegative = (dataView.getUint8(byteOffset + 7) & 0x80) > 0;
  458. let carrying = true;
  459. for (let i = 0; i < 8; ++i) {
  460. let byte = dataView.getUint8(byteOffset + i);
  461. if (isNegative) {
  462. if (carrying) {
  463. if (byte !== 0x00) {
  464. byte = ~(byte - 1) & 0xff;
  465. carrying = false;
  466. }
  467. } else {
  468. byte = ~byte & 0xff;
  469. }
  470. }
  471. value += byte * Math.pow(256, i);
  472. }
  473. if (isNegative) {
  474. value = -value;
  475. }
  476. return value;
  477. }
  478. function getInt64BigIntFallback(index, values) {
  479. const dataView = values.dataView;
  480. const byteOffset = index * 8;
  481. // eslint-disable-next-line no-undef
  482. let value = BigInt(0);
  483. const isNegative = (dataView.getUint8(byteOffset + 7) & 0x80) > 0;
  484. let carrying = true;
  485. for (let i = 0; i < 8; ++i) {
  486. let byte = dataView.getUint8(byteOffset + i);
  487. if (isNegative) {
  488. if (carrying) {
  489. if (byte !== 0x00) {
  490. byte = ~(byte - 1) & 0xff;
  491. carrying = false;
  492. }
  493. } else {
  494. byte = ~byte & 0xff;
  495. }
  496. }
  497. value += BigInt(byte) * (BigInt(1) << BigInt(i * 8)); // eslint-disable-line
  498. }
  499. if (isNegative) {
  500. value = -value;
  501. }
  502. return value;
  503. }
  504. function getUint64NumberFallback(index, values) {
  505. const dataView = values.dataView;
  506. const byteOffset = index * 8;
  507. // Split 64-bit number into two 32-bit (4-byte) parts
  508. const left = dataView.getUint32(byteOffset, true);
  509. const right = dataView.getUint32(byteOffset + 4, true);
  510. // Combine the two 32-bit values
  511. const value = left + 4294967296 * right;
  512. return value;
  513. }
  514. function getUint64BigIntFallback(index, values) {
  515. const dataView = values.dataView;
  516. const byteOffset = index * 8;
  517. // Split 64-bit number into two 32-bit (4-byte) parts
  518. // eslint-disable-next-line no-undef
  519. const left = BigInt(dataView.getUint32(byteOffset, true));
  520. // eslint-disable-next-line no-undef
  521. const right = BigInt(dataView.getUint32(byteOffset + 4, true));
  522. // Combine the two 32-bit values
  523. // eslint-disable-next-line no-undef
  524. const value = left + BigInt(4294967296) * right;
  525. return value;
  526. }
  527. function getComponentDatatype(componentType) {
  528. switch (componentType) {
  529. case MetadataComponentType.INT8:
  530. return ComponentDatatype.BYTE;
  531. case MetadataComponentType.UINT8:
  532. return ComponentDatatype.UNSIGNED_BYTE;
  533. case MetadataComponentType.INT16:
  534. return ComponentDatatype.SHORT;
  535. case MetadataComponentType.UINT16:
  536. return ComponentDatatype.UNSIGNED_SHORT;
  537. case MetadataComponentType.INT32:
  538. return ComponentDatatype.INT;
  539. case MetadataComponentType.UINT32:
  540. return ComponentDatatype.UNSIGNED_INT;
  541. case MetadataComponentType.FLOAT32:
  542. return ComponentDatatype.FLOAT;
  543. case MetadataComponentType.FLOAT64:
  544. return ComponentDatatype.DOUBLE;
  545. }
  546. }
  547. function requiresUnpackForGet(property) {
  548. if (defined(property._unpackedValues)) {
  549. return false;
  550. }
  551. const classProperty = property._classProperty;
  552. const type = classProperty.type;
  553. const valueType = classProperty.valueType;
  554. if (type === MetadataType.STRING) {
  555. // Unpack since UTF-8 decoding is expensive
  556. return true;
  557. }
  558. if (
  559. valueType === MetadataComponentType.INT64 &&
  560. !FeatureDetection.supportsBigInt64Array()
  561. ) {
  562. // Unpack since the fallback INT64 getters are expensive
  563. return true;
  564. }
  565. if (
  566. valueType === MetadataComponentType.UINT64 &&
  567. !FeatureDetection.supportsBigUint64Array()
  568. ) {
  569. // Unpack since the fallback UINT64 getters are expensive
  570. return true;
  571. }
  572. return false;
  573. }
  574. function requiresUnpackForSet(property, index, value) {
  575. if (requiresUnpackForGet(property)) {
  576. return true;
  577. }
  578. const arrayOffsets = property._arrayOffsets;
  579. if (defined(arrayOffsets)) {
  580. // Unpacking is required if a variable-size array changes length since it
  581. // would be expensive to repack the binary data
  582. const oldLength = arrayOffsets.get(index + 1) - arrayOffsets.get(index);
  583. const newLength = value.length;
  584. if (oldLength !== newLength) {
  585. return true;
  586. }
  587. }
  588. return false;
  589. }
  590. function unpackProperty(property) {
  591. property._unpackedValues = unpackValues(property);
  592. // Free memory
  593. property._arrayOffsets = undefined;
  594. property._stringOffsets = undefined;
  595. property._values = undefined;
  596. }
  597. function unpackValues(property) {
  598. const count = property._count;
  599. const unpackedValues = new Array(count);
  600. const classProperty = property._classProperty;
  601. const isArray = classProperty.isArray;
  602. const type = classProperty.type;
  603. const componentCount = MetadataType.getComponentCount(type);
  604. // Handle single values
  605. if (!isArray && componentCount === 1) {
  606. for (let i = 0; i < count; ++i) {
  607. unpackedValues[i] = property._getValue(i);
  608. }
  609. return unpackedValues;
  610. }
  611. for (let i = 0; i < count; i++) {
  612. unpackedValues[i] = getArrayValues(property, classProperty, i);
  613. }
  614. return unpackedValues;
  615. }
  616. function applyValueTransform(property, value) {
  617. const classProperty = property._classProperty;
  618. const isVariableLengthArray = classProperty.isVariableLengthArray;
  619. if (!property._hasValueTransform || isVariableLengthArray) {
  620. return value;
  621. }
  622. return MetadataClassProperty.valueTransformInPlace(
  623. value,
  624. property._offset,
  625. property._scale,
  626. MetadataComponentType.applyValueTransform
  627. );
  628. }
  629. function unapplyValueTransform(property, value) {
  630. const classProperty = property._classProperty;
  631. const isVariableLengthArray = classProperty.isVariableLengthArray;
  632. if (!property._hasValueTransform || isVariableLengthArray) {
  633. return value;
  634. }
  635. return MetadataClassProperty.valueTransformInPlace(
  636. value,
  637. property._offset,
  638. property._scale,
  639. MetadataComponentType.unapplyValueTransform
  640. );
  641. }
  642. function BufferView(bufferView, componentType, length) {
  643. const that = this;
  644. let typedArray;
  645. let getFunction;
  646. let setFunction;
  647. if (componentType === MetadataComponentType.INT64) {
  648. if (!FeatureDetection.supportsBigInt()) {
  649. oneTimeWarning(
  650. "INT64 type is not fully supported on this platform. Values greater than 2^53 - 1 or less than -(2^53 - 1) may lose precision when read."
  651. );
  652. typedArray = new Uint8Array(
  653. bufferView.buffer,
  654. bufferView.byteOffset,
  655. length * 8
  656. );
  657. getFunction = function (index) {
  658. return getInt64NumberFallback(index, that);
  659. };
  660. } else if (!FeatureDetection.supportsBigInt64Array()) {
  661. typedArray = new Uint8Array(
  662. bufferView.buffer,
  663. bufferView.byteOffset,
  664. length * 8
  665. );
  666. getFunction = function (index) {
  667. return getInt64BigIntFallback(index, that);
  668. };
  669. } else {
  670. // eslint-disable-next-line
  671. typedArray = new BigInt64Array(
  672. bufferView.buffer,
  673. bufferView.byteOffset,
  674. length
  675. );
  676. setFunction = function (index, value) {
  677. // Convert the number to a BigInt before setting the value in the typed array
  678. that.typedArray[index] = BigInt(value); // eslint-disable-line
  679. };
  680. }
  681. } else if (componentType === MetadataComponentType.UINT64) {
  682. if (!FeatureDetection.supportsBigInt()) {
  683. oneTimeWarning(
  684. "UINT64 type is not fully supported on this platform. Values greater than 2^53 - 1 may lose precision when read."
  685. );
  686. typedArray = new Uint8Array(
  687. bufferView.buffer,
  688. bufferView.byteOffset,
  689. length * 8
  690. );
  691. getFunction = function (index) {
  692. return getUint64NumberFallback(index, that);
  693. };
  694. } else if (!FeatureDetection.supportsBigUint64Array()) {
  695. typedArray = new Uint8Array(
  696. bufferView.buffer,
  697. bufferView.byteOffset,
  698. length * 8
  699. );
  700. getFunction = function (index) {
  701. return getUint64BigIntFallback(index, that);
  702. };
  703. } else {
  704. // eslint-disable-next-line
  705. typedArray = new BigUint64Array(
  706. bufferView.buffer,
  707. bufferView.byteOffset,
  708. length
  709. );
  710. setFunction = function (index, value) {
  711. // Convert the number to a BigInt before setting the value in the typed array
  712. that.typedArray[index] = BigInt(value); // eslint-disable-line
  713. };
  714. }
  715. } else {
  716. const componentDatatype = getComponentDatatype(componentType);
  717. typedArray = ComponentDatatype.createArrayBufferView(
  718. componentDatatype,
  719. bufferView.buffer,
  720. bufferView.byteOffset,
  721. length
  722. );
  723. setFunction = function (index, value) {
  724. that.typedArray[index] = value;
  725. };
  726. }
  727. if (!defined(getFunction)) {
  728. getFunction = function (index) {
  729. return that.typedArray[index];
  730. };
  731. }
  732. this.typedArray = typedArray;
  733. this.dataView = new DataView(typedArray.buffer, typedArray.byteOffset);
  734. this.get = getFunction;
  735. this.set = setFunction;
  736. // for unit testing
  737. this._componentType = componentType;
  738. }
  739. export default MetadataTableProperty;