function MetadataTableProperty(options) {
  options = options ?? Frozen.EMPTY_OBJECT;
  const count = options.count;
  const property = options.property;
  const classProperty = options.classProperty;
  const bufferViews = options.bufferViews;

  //>>includeStart('debug', pragmas.debug);
  Check.typeOf.number.greaterThan("options.count", count, 0);
  Check.typeOf.object("options.property", property);
  Check.typeOf.object("options.classProperty", classProperty);
  Check.typeOf.object("options.bufferViews", bufferViews);
  //>>includeEnd('debug');

  const type = classProperty.type;
  const isArray = classProperty.isArray;
  const isVariableLengthArray = classProperty.isVariableLengthArray;

  let valueType = classProperty.valueType;
  const enumType = classProperty.enumType;

  const hasStrings = type === MetadataType.STRING;
  const hasBooleans = type === MetadataType.BOOLEAN;

  let byteLength = 0;

  let arrayOffsets;
  if (isVariableLengthArray) {
    // EXT_structural_metadata uses arrayOffsetType.
    // EXT_feature_metadata uses offsetType for both arrays and strings
    let arrayOffsetType = property.arrayOffsetType ?? property.offsetType;
    arrayOffsetType =
      MetadataComponentType[arrayOffsetType] ?? MetadataComponentType.UINT32;

    // EXT_structural_metadata uses arrayOffsets.
    // EXT_feature_metadata uses arrayOffsetBufferView
    const arrayOffsetBufferView =
      property.arrayOffsets ?? property.arrayOffsetBufferView;
    arrayOffsets = new BufferView(
      bufferViews[arrayOffsetBufferView],
      arrayOffsetType,
      count + 1,
    );

    byteLength += arrayOffsets.typedArray.byteLength;
  }

  const vectorComponentCount = MetadataType.getComponentCount(type);

  let arrayComponentCount;
  if (isVariableLengthArray) {
    arrayComponentCount = arrayOffsets.get(count) - arrayOffsets.get(0);
  } else if (isArray) {
    arrayComponentCount = count * classProperty.arrayLength;
  } else {
    arrayComponentCount = count;
  }

  const componentCount = vectorComponentCount * arrayComponentCount;

  let stringOffsets;
  if (hasStrings) {
    // EXT_structural_metadata uses stringOffsetType, EXT_feature_metadata uses offsetType for both arrays and strings
    let stringOffsetType = property.stringOffsetType ?? property.offsetType;
    stringOffsetType =
      MetadataComponentType[stringOffsetType] ?? MetadataComponentType.UINT32;

    // EXT_structural_metadata uses stringOffsets.
    // EXT_feature_metadata uses stringOffsetBufferView
    const stringOffsetBufferView =
      property.stringOffsets ?? property.stringOffsetBufferView;
    stringOffsets = new BufferView(
      bufferViews[stringOffsetBufferView],
      stringOffsetType,
      componentCount + 1,
    );

    byteLength += stringOffsets.typedArray.byteLength;
  }

  if (hasStrings || hasBooleans) {
    // STRING and BOOLEAN types need to be parsed differently than other types
    valueType = MetadataComponentType.UINT8;
  }

  let valueCount;
  if (hasStrings) {
    valueCount = stringOffsets.get(componentCount) - stringOffsets.get(0);
  } else if (hasBooleans) {
    valueCount = Math.ceil(componentCount / 8);
  } else {
    valueCount = componentCount;
  }

  // EXT_structural_metadata uses values
  // EXT_feature_metadata uses bufferView
  const valuesBufferView = property.values ?? property.bufferView;
  const values = new BufferView(
    bufferViews[valuesBufferView],
    valueType,
    valueCount,
  );
  byteLength += values.typedArray.byteLength;

  let offset = property.offset;
  let scale = property.scale;

  // This needs to be set before handling default values
  const hasValueTransform =
    classProperty.hasValueTransform || defined(offset) || defined(scale);

  // If the table does not define an offset/scale, it inherits from the
  // class property. The class property handles setting the default of identity:
  // (offset 0, scale 1) with the same array shape as the property's type
  // information.
  offset = offset ?? classProperty.offset;
  scale = scale ?? classProperty.scale;

  // Since metadata table properties are stored as packed typed
  // arrays, flatten the offset/scale to make it easier to apply the
  // transformation by iteration.
  offset = flatten(offset);
  scale = flatten(scale);

  let getValueFunction;
  let setValueFunction;
  const that = this;
  if (hasStrings) {
    getValueFunction = function (index) {
      return getString(index, that._values, that._stringOffsets);
    };
  } else if (hasBooleans) {
    getValueFunction = function (index) {
      return getBoolean(index, that._values);
    };
    setValueFunction = function (index, value) {
      setBoolean(index, that._values, value);
    };
  } else if (defined(enumType)) {
    getValueFunction = function (index) {
      const integer = that._values.get(index);
      return enumType.namesByValue[integer];
    };
    setValueFunction = function (index, value) {
      const integer = enumType.valuesByName[value];
      that._values.set(index, integer);
    };
  } else {
    getValueFunction = function (index) {
      return that._values.get(index);
    };
    setValueFunction = function (index, value) {
      that._values.set(index, value);
    };
  }

  this._arrayOffsets = arrayOffsets;
  this._stringOffsets = stringOffsets;
  this._values = values;
  this._classProperty = classProperty;
  this._count = count;
  this._vectorComponentCount = vectorComponentCount;
  this._min = property.min;
  this._max = property.max;
  this._offset = offset;
  this._scale = scale;
  this._hasValueTransform = hasValueTransform;
  this._getValue = getValueFunction;
  this._setValue = setValueFunction;
  this._unpackedValues = undefined;
  this._extras = property.extras;
  this._extensions = property.extensions;
  this._byteLength = byteLength;
}
A binary property in a {@MetadataTable}
<p>
For 3D Tiles Next details, see the {@link https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_metadata|3DTILES_metadata Extension}
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}
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}
</p>

@param {object} options Object with the following properties:
@param {number} options.count The number of elements in each property array.
@param {object} options.property The property JSON object.
@param {MetadataClassProperty} options.classProperty The class property.
@param {Object<string, Uint8Array>} options.bufferViews An object mapping bufferView IDs to Uint8Array objects.

@alias MetadataTableProperty
@constructor

@private
@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.