UNPKG

scratch-sb1-converter

Version:

Scratch 1 (.sb) to Scratch 2 (.sb2) conversion library for Scratch 3.0

142 lines (126 loc) 4.79 kB
import {assert} from '../util/assert'; /** * Read and write a stream of {@link BytePrimitive}s, {@link Packet}s, or byte * arrays to an ArrayBuffer. */ class ByteStream { /** * @param {ArrayBuffer} buffer - The ArrayBuffer to read from or write to. * @param {number=} [position=0] - The position to start reading or writing * from in the ArrayBuffer. */ constructor (buffer, position = 0) { /** * The ArrayBuffer to read from or write to. * @type {ArrayBuffer} */ this.buffer = buffer; /** * The position to start reading or writing from in the ArrayBuffer. * @type {number} */ this.position = position; /** * The typed array view of the buffer to read and write with. * @type {Uint8Array} */ this.uint8a = new Uint8Array(this.buffer); } /** * Read one instance of a given BytePrimitive and increment position based * on the size of that value. * @param {BytePrimitive} member - BytePrimitive to read and increment size * with. * @returns {*} Return the value produced by the BytePrimitive. */ read (member) { const value = member.read(this.uint8a, this.position); if (member.size === 0) { this.position += member.sizeOf(this.uint8a, this.position); } else { this.position += member.size; } return value; } /** * Read one instance of a given Packet subclass and increment position * based on the size of that value. * @param {PacketConstructor} StructType - Packet subclass constructor that * can read from the current stream position. * @returns {Packet} Instance of a Packet pointed at the position of the * stream before calling readStruct. */ readStruct (StructType) { const obj = new StructType(this.uint8a, this.position); this.position += StructType.size; return obj; } /** * Resize the internal buffer to allow for the needed amount of space. * @param {number} needed - How many bytes need to fit in the buffer. * @private */ resize (needed) { if (this.buffer.byteLength < needed) { const uint8a = this.uint8a; const nextPowerOf2 = Math.pow(2, Math.ceil(Math.log(needed) / Math.log(2))); this.buffer = new ArrayBuffer(nextPowerOf2); this.uint8a = new Uint8Array(this.buffer); this.uint8a.set(uint8a); } } /** * Write a value to the stream (with a BytePrimitive defining how to do so) * and increment the position. * @param {BytePrimitive} member - BytePrimitive to define how to write the * value. * @param {*} value - Value to write. * @returns {*} Value passed to the method. */ write (member, value) { if (member.size === 0) { this.resize(this.position + member.writeSizeOf(value)); } else { this.resize(this.position + member.size); } member.write(this.uint8a, this.position, value); if (member.size === 0) { this.position += member.writeSizeOf(this.uint8a, this.position); } else { this.position += member.size; } return value; } /** * Write data to the stream structured by a given Packet subclass * constructor and increment the position. * @param {PacketConstructor} StructType - The Packet subclass constructor * defining how to write the data. * @param {object} data - Data to write. * @returns {Packet} - Constructed packet after writing data. */ writeStruct (StructType, data) { this.resize(this.position + StructType.size); const st = Object.assign(new StructType(this.uint8a, this.position), data); this.position += StructType.size; return st; } /** * Write bytes from the given Uint8Array array to the stream and increment * the position. * @param {Uint8Array} bytes - Bytes to write to the stream. * @param {number=} [start=0] - Where in bytes to start writing from. * @param {number=} [end=bytes.length] - Where in bytes to stop writing, excluding position at bytes[end]. * @returns {Uint8Array} Passed bytes Uint8Array. */ writeBytes (bytes, start = 0, end = bytes.length) { assert(bytes instanceof Uint8Array, 'writeBytes must be passed an Uint8Array'); this.resize(this.position + (end - start)); for (let i = start; i < end; i++) { this.uint8a[this.position + i - start] = bytes[i]; } this.position += end - start; return bytes; } } export {ByteStream};