UNPKG

@mysten/bcs

Version:

BCS - Canonical Binary Serialization implementation for JavaScript

1 lines 9 kB
{"version":3,"file":"writer.mjs","names":[],"sources":["../src/writer.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Encoding } from './types.js';\nimport { ulebEncode } from './uleb.js';\nimport { encodeStr } from './utils.js';\n\nexport interface BcsWriterOptions {\n\t/** The initial size (in bytes) of the buffer tht will be allocated */\n\tinitialSize?: number;\n\t/** The maximum size (in bytes) that the buffer is allowed to grow to */\n\tmaxSize?: number;\n\t/** The amount of bytes that will be allocated whenever additional memory is required */\n\tallocateSize?: number;\n}\n\n/**\n * Class used to write BCS data into a buffer. Initializer requires\n * some size of a buffer to init; default value for this buffer is 1KB.\n *\n * Most methods are chainable, so it is possible to write them in one go.\n *\n * @example\n * let serialized = new BcsWriter()\n * .write8(10)\n * .write32(1000000)\n * .write64(10000001000000)\n * .hex();\n */\n\n/**\n * Set of methods that allows data encoding/decoding as standalone\n * BCS value or a part of a composed structure/vector.\n */\nexport class BcsWriter {\n\tprivate dataView: DataView<ArrayBuffer>;\n\tprivate bytePosition: number = 0;\n\tprivate size: number;\n\tprivate maxSize: number;\n\tprivate allocateSize: number;\n\n\tconstructor({\n\t\tinitialSize = 1024,\n\t\tmaxSize = Infinity,\n\t\tallocateSize = 1024,\n\t}: BcsWriterOptions = {}) {\n\t\tthis.size = initialSize;\n\t\tthis.maxSize = maxSize;\n\t\tthis.allocateSize = allocateSize;\n\t\tthis.dataView = new DataView(new ArrayBuffer(initialSize));\n\t}\n\n\tprivate ensureSizeOrGrow(bytes: number) {\n\t\tconst requiredSize = this.bytePosition + bytes;\n\t\tif (requiredSize > this.size) {\n\t\t\tconst nextSize = Math.min(\n\t\t\t\tthis.maxSize,\n\t\t\t\tMath.max(this.size + requiredSize, this.size + this.allocateSize),\n\t\t\t);\n\t\t\tif (requiredSize > nextSize) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Attempting to serialize to BCS, but buffer does not have enough size. Allocated size: ${this.size}, Max size: ${this.maxSize}, Required size: ${requiredSize}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.size = nextSize;\n\t\t\tconst nextBuffer = new ArrayBuffer(this.size);\n\t\t\tnew Uint8Array(nextBuffer).set(new Uint8Array(this.dataView.buffer));\n\t\t\tthis.dataView = new DataView(nextBuffer);\n\t\t}\n\t}\n\n\t/**\n\t * Shift current cursor position by `bytes`.\n\t *\n\t * @param {Number} bytes Number of bytes to\n\t * @returns {this} Self for possible chaining.\n\t */\n\tshift(bytes: number): this {\n\t\tthis.bytePosition += bytes;\n\t\treturn this;\n\t}\n\t/**\n\t * Write a U8 value into a buffer and shift cursor position by 1.\n\t * @param {Number} value Value to write.\n\t * @returns {this}\n\t */\n\twrite8(value: number | bigint): this {\n\t\tthis.ensureSizeOrGrow(1);\n\t\tthis.dataView.setUint8(this.bytePosition, Number(value));\n\t\treturn this.shift(1);\n\t}\n\n\t/**\n\t * Write a U8 value into a buffer and shift cursor position by 1.\n\t * @param {Number} value Value to write.\n\t * @returns {this}\n\t */\n\twriteBytes(bytes: Uint8Array): this {\n\t\tthis.ensureSizeOrGrow(bytes.length);\n\n\t\tfor (let i = 0; i < bytes.length; i++) {\n\t\t\tthis.dataView.setUint8(this.bytePosition + i, bytes[i]);\n\t\t}\n\n\t\treturn this.shift(bytes.length);\n\t}\n\t/**\n\t * Write a U16 value into a buffer and shift cursor position by 2.\n\t * @param {Number} value Value to write.\n\t * @returns {this}\n\t */\n\twrite16(value: number | bigint): this {\n\t\tthis.ensureSizeOrGrow(2);\n\t\tthis.dataView.setUint16(this.bytePosition, Number(value), true);\n\t\treturn this.shift(2);\n\t}\n\t/**\n\t * Write a U32 value into a buffer and shift cursor position by 4.\n\t * @param {Number} value Value to write.\n\t * @returns {this}\n\t */\n\twrite32(value: number | bigint): this {\n\t\tthis.ensureSizeOrGrow(4);\n\t\tthis.dataView.setUint32(this.bytePosition, Number(value), true);\n\t\treturn this.shift(4);\n\t}\n\t/**\n\t * Write a U64 value into a buffer and shift cursor position by 8.\n\t * @param {bigint} value Value to write.\n\t * @returns {this}\n\t */\n\twrite64(value: number | bigint): this {\n\t\ttoLittleEndian(BigInt(value), 8).forEach((el) => this.write8(el));\n\n\t\treturn this;\n\t}\n\t/**\n\t * Write a U128 value into a buffer and shift cursor position by 16.\n\t *\n\t * @param {bigint} value Value to write.\n\t * @returns {this}\n\t */\n\twrite128(value: number | bigint): this {\n\t\ttoLittleEndian(BigInt(value), 16).forEach((el) => this.write8(el));\n\n\t\treturn this;\n\t}\n\t/**\n\t * Write a U256 value into a buffer and shift cursor position by 16.\n\t *\n\t * @param {bigint} value Value to write.\n\t * @returns {this}\n\t */\n\twrite256(value: number | bigint): this {\n\t\ttoLittleEndian(BigInt(value), 32).forEach((el) => this.write8(el));\n\n\t\treturn this;\n\t}\n\t/**\n\t * Write a ULEB value into a buffer and shift cursor position by number of bytes\n\t * written.\n\t * @param {Number} value Value to write.\n\t * @returns {this}\n\t */\n\twriteULEB(value: number): this {\n\t\tulebEncode(value).forEach((el) => this.write8(el));\n\t\treturn this;\n\t}\n\t/**\n\t * Write a vector into a buffer by first writing the vector length and then calling\n\t * a callback on each passed value.\n\t *\n\t * @param {Array<Any>} vector Array of elements to write.\n\t * @param {WriteVecCb} cb Callback to call on each element of the vector.\n\t * @returns {this}\n\t */\n\twriteVec(vector: any[], cb: (writer: BcsWriter, el: any, i: number, len: number) => void): this {\n\t\tthis.writeULEB(vector.length);\n\t\tArray.from(vector).forEach((el, i) => cb(this, el, i, vector.length));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds support for iterations over the object.\n\t * @returns {Uint8Array}\n\t */\n\t// oxlint-disable-next-line require-yields\n\t*[Symbol.iterator](): Iterator<number, Iterable<number>> {\n\t\tfor (let i = 0; i < this.bytePosition; i++) {\n\t\t\tyield this.dataView.getUint8(i);\n\t\t}\n\t\treturn this.toBytes();\n\t}\n\n\t/**\n\t * Get underlying buffer taking only value bytes (in case initial buffer size was bigger).\n\t * @returns {Uint8Array} Resulting bcs.\n\t */\n\ttoBytes(): Uint8Array<ArrayBuffer> {\n\t\treturn new Uint8Array(this.dataView.buffer.slice(0, this.bytePosition));\n\t}\n\n\t/**\n\t * Represent data as 'hex' or 'base64'\n\t * @param encoding Encoding to use: 'base64' or 'hex'\n\t */\n\ttoString(encoding: Encoding): string {\n\t\treturn encodeStr(this.toBytes(), encoding);\n\t}\n}\n\nfunction toLittleEndian(bigint: bigint, size: number) {\n\tconst result = new Uint8Array(size);\n\tlet i = 0;\n\twhile (bigint > 0) {\n\t\tresult[i] = Number(bigint % BigInt(256));\n\t\tbigint = bigint / BigInt(256);\n\t\ti += 1;\n\t}\n\treturn result;\n}\n"],"mappings":"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"}