@itwin/core-bentley
Version:
Bentley JavaScript core components
146 lines • 7.83 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Utils
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ByteStream = void 0;
const Assert_1 = require("./Assert");
const Id_1 = require("./Id");
/** Allows the contents of an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
* to be consumed sequentially using methods to extract
* data of a particular type from the bytes beginning at the current read position.
* Methods and properties beginning with 'read' and taking no arguments consume data at the current read position and advance it
* by the size of the data read. The read position can also be directly adjusted by the caller.
* @public
*/
class ByteStream {
_view;
_byteOffset;
_curPos = 0;
/** Construct a new ByteStream with the read position set to the beginning.
* @param buffer The underlying buffer from which data is to be extracted.
* @param subView If defined, specifies the subset of the underlying buffer's data to use.
* This constructor is subject to two common mistakes:
*
* 1. Given `bytes: Uint8Array`, `new ByteStream(bytes)` will compile but at run-time will produce an error asserting that
* the DataView constructor requires an ArrayBuffer. The correct usage is `new ByteStream(bytes.buffer)`.
* 2. Given `bytes: Uint8Array`, `new ByteStream(bytes.buffer)` creates a stream for the entire range of bytes represented by the underlying
* ArrayBuffer. If `bytes` represents only a **sub-range** of the underlying buffer's data, the results will be unexpected unless the optional `subView`
* argument is supplied, with correct offset and length.
*
* For both of the above reasons, this constructor is private, and [[fromUint8Array]] or [[fromArrayBuffer]] should be used to create a ByteStream.
*/
constructor(buffer, subView) {
if (undefined !== subView) {
this._view = new DataView(buffer, subView.byteOffset, subView.byteLength);
this._byteOffset = subView.byteOffset;
}
else {
this._view = new DataView(buffer);
this._byteOffset = 0;
}
}
/** Construct a new ByteStream for the range of bytes represented by `bytes`, which may be a subset of the range of bytes
* represented by the underlying ArrayBuffer. The read position will be set to the beginning of the range of bytes.
* @param bytes The Uint8Array from which data is to be extracted.
*/
static fromUint8Array(bytes) {
const { byteOffset, byteLength } = bytes;
return new ByteStream(bytes.buffer, { byteOffset, byteLength });
}
/** Construct a new ByteStream with the read position set to the beginning.
* @param buffer The underlying buffer from which data is to be extracted.
* @param subView If defined, specifies the subset of the underlying buffer's data to use.
*/
static fromArrayBuffer(buffer, subView) {
return new ByteStream(buffer, subView);
}
/** The number of bytes in this stream */
get length() {
return this._view.byteLength;
}
/** The number of bytes remaining to be read, from [[curPos]] to the end of the stream. */
get remainingLength() {
return this.length - this.curPos;
}
/** Returns true if the current read position has been advanced past the end of the stream. This generally indicates that an attempt was made to read more data than is available.
* @see [[isAtTheEnd]]
*/
get isPastTheEnd() {
return this.curPos > this.length;
}
/** Returns true if the current read position has advanced precisely to the end of the stream, indicating all of the data has been consumed.
* @see [[isPastTheEnd]].
*/
get isAtTheEnd() {
return this.curPos === this.length;
}
/** The current read position as an index into the stream of bytes. */
get curPos() { return this._curPos; }
set curPos(pos) {
this._curPos = pos;
(0, Assert_1.assert)(!this.isPastTheEnd);
}
/** Adds the specified number of bytes to the current read position */
advance(numBytes) {
this.curPos = (this.curPos + numBytes);
return !this.isPastTheEnd;
}
/** Subtracts the specified number of bytes from the current read position */
rewind(numBytes) {
if (this.curPos - numBytes < 0)
return false;
this.curPos = this.curPos - numBytes;
return true;
}
/** Resets the current read position to the beginning of the stream */
reset() { this.curPos = 0; }
/** Read a unsigned 8-bit integer from the current read position and advance by 1 byte. */
readUint8() { return this.read(1, (view) => view.getUint8(this.curPos)); }
/** Read an unsigned 16-bit integer from the current read position and advance by 2 bytes. */
readUint16() { return this.read(2, (view) => view.getUint16(this.curPos, true)); }
/** Read an unsigned 32-bit integer from the current read position and advance by 4 bytes. */
readUint32() { return this.read(4, (view) => view.getUint32(this.curPos, true)); }
/** Read a signed 32-bit integer from the current read position and advance by 4 bytes. */
readInt32() { return this.read(4, (view) => view.getInt32(this.curPos, true)); }
/** Read a 32-bit floating point number from the current read position and advance by 4 bytes. */
readFloat32() { return this.read(4, (view) => view.getFloat32(this.curPos, true)); }
/** Read a 64-bit floating point number from the current read position and advance by 8 bytes. */
readFloat64() { return this.read(8, (view) => view.getFloat64(this.curPos, true)); }
/** Read an unsigned 64-bit integer from the current read position, advance by 8 bytes, and return the 64-bit value as an Id64String. */
readId64() { return Id_1.Id64.fromUint32Pair(this.readUint32(), this.readUint32()); }
/** Read an unsigned 24-bit integer from the current read position and advance by 3 bytes. */
readUint24() { return this.readUint8() | (this.readUint8() << 8) | (this.readUint8() << 16); }
/** Read the specified number of bytes beginning at the current read position into a Uint8Array and advance by the specified number of byte.
* @param numBytes The number of bytes to read.
*/
nextBytes(numBytes) {
const bytes = new Uint8Array(this.arrayBuffer, this.curPos + this._byteOffset, numBytes);
this.advance(numBytes);
return bytes;
}
/** Read the specified number of bytes at the specified offset without changing the read position. */
readBytes(readPos, numBytes) {
return new Uint8Array(this.arrayBuffer, readPos + this._byteOffset, numBytes);
}
/** Read the specified number of unsigned 32-bit integers from the current read position and advance the read position. */
nextUint32s(numUint32s) {
const numBytes = numUint32s * 4;
const uint32s = new Uint32Array(this.arrayBuffer, this.curPos + this._byteOffset, numUint32s);
this.advance(numBytes);
return uint32s;
}
/** Returns the underlying array buffer */
get arrayBuffer() { return this._view.buffer; }
read(numBytes, read) {
const result = read(this._view);
this.advance(numBytes);
return result;
}
}
exports.ByteStream = ByteStream;
//# sourceMappingURL=ByteStream.js.map