{"version":3,"sources":["../../src/bcs/deserializer.ts"],"sourcesContent":["// Copyright © Aptos Foundation\n// SPDX-License-Identifier: Apache-2.0\n\n/* eslint-disable no-bitwise */\nimport { MAX_U32_NUMBER } from \"./consts\";\nimport { Uint8, Uint16, Uint32, Uint64, Uint128, Uint256 } from \"../types\";\n\n/**\n * This interface exists to define Deserializable<T> inputs for functions that\n * deserialize a byte buffer into a type T.\n * It is not intended to be implemented or extended, because Typescript has no support\n * for static methods in interfaces.\n */\nexport interface Deserializable<T> {\n  deserialize(deserializer: Deserializer): T;\n}\n\nexport class Deserializer {\n  private buffer: ArrayBuffer;\n\n  private offset: number;\n\n  constructor(data: Uint8Array) {\n    // copies data to prevent outside mutation of buffer.\n    this.buffer = new ArrayBuffer(data.length);\n    new Uint8Array(this.buffer).set(data, 0);\n    this.offset = 0;\n  }\n\n  private read(length: number): ArrayBuffer {\n    if (this.offset + length > this.buffer.byteLength) {\n      throw new Error(\"Reached to the end of buffer\");\n    }\n\n    const bytes = this.buffer.slice(this.offset, this.offset + length);\n    this.offset += length;\n    return bytes;\n  }\n\n  /**\n   * Deserializes a string. UTF8 string is supported. Reads the string's bytes length \"l\" first,\n   * and then reads \"l\" bytes of content. Decodes the byte array into a string.\n   *\n   * BCS layout for \"string\": string_length | string_content\n   * where string_length is a u32 integer encoded as a uleb128 integer, equal to the number of bytes in string_content.\n   *\n   * @example\n   * ```ts\n   * const deserializer = new Deserializer(new Uint8Array([8, 49, 50, 51, 52, 97, 98, 99, 100]));\n   * assert(deserializer.deserializeStr() === \"1234abcd\");\n   * ```\n   */\n  deserializeStr(): string {\n    const value = this.deserializeBytes();\n    const textDecoder = new TextDecoder();\n    return textDecoder.decode(value);\n  }\n\n  /**\n   * Deserializes an array of bytes.\n   *\n   * BCS layout for \"bytes\": bytes_length | bytes\n   * where bytes_length is a u32 integer encoded as a uleb128 integer, equal to the length of the bytes array.\n   */\n  deserializeBytes(): Uint8Array {\n    const len = this.deserializeUleb128AsU32();\n    return new Uint8Array(this.read(len));\n  }\n\n  /**\n   * Deserializes an array of bytes. The number of bytes to read is already known.\n   *\n   */\n  deserializeFixedBytes(len: number): Uint8Array {\n    return new Uint8Array(this.read(len));\n  }\n\n  /**\n   * Deserializes a boolean value.\n   *\n   * BCS layout for \"boolean\": One byte. \"0x01\" for true and \"0x00\" for false.\n   */\n  deserializeBool(): boolean {\n    const bool = new Uint8Array(this.read(1))[0];\n    if (bool !== 1 && bool !== 0) {\n      throw new Error(\"Invalid boolean value\");\n    }\n    return bool === 1;\n  }\n\n  /**\n   * Deserializes a uint8 number.\n   *\n   * BCS layout for \"uint8\": One byte. Binary format in little-endian representation.\n   */\n  deserializeU8(): Uint8 {\n    return new DataView(this.read(1)).getUint8(0);\n  }\n\n  /**\n   * Deserializes a uint16 number.\n   *\n   * BCS layout for \"uint16\": Two bytes. Binary format in little-endian representation.\n   * @example\n   * ```ts\n   * const deserializer = new Deserializer(new Uint8Array([0x34, 0x12]));\n   * assert(deserializer.deserializeU16() === 4660);\n   * ```\n   */\n  deserializeU16(): Uint16 {\n    return new DataView(this.read(2)).getUint16(0, true);\n  }\n\n  /**\n   * Deserializes a uint32 number.\n   *\n   * BCS layout for \"uint32\": Four bytes. Binary format in little-endian representation.\n   * @example\n   * ```ts\n   * const deserializer = new Deserializer(new Uint8Array([0x78, 0x56, 0x34, 0x12]));\n   * assert(deserializer.deserializeU32() === 305419896);\n   * ```\n   */\n  deserializeU32(): Uint32 {\n    return new DataView(this.read(4)).getUint32(0, true);\n  }\n\n  /**\n   * Deserializes a uint64 number.\n   *\n   * BCS layout for \"uint64\": Eight bytes. Binary format in little-endian representation.\n   * @example\n   * ```ts\n   * const deserializer = new Deserializer(new Uint8Array([0x00, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12]));\n   * assert(deserializer.deserializeU64() === 1311768467750121216);\n   * ```\n   */\n  deserializeU64(): Uint64 {\n    const low = this.deserializeU32();\n    const high = this.deserializeU32();\n\n    // combine the two 32-bit values and return (little endian)\n    return BigInt((BigInt(high) << BigInt(32)) | BigInt(low));\n  }\n\n  /**\n   * Deserializes a uint128 number.\n   *\n   * BCS layout for \"uint128\": Sixteen bytes. Binary format in little-endian representation.\n   */\n  deserializeU128(): Uint128 {\n    const low = this.deserializeU64();\n    const high = this.deserializeU64();\n\n    // combine the two 64-bit values and return (little endian)\n    return BigInt((high << BigInt(64)) | low);\n  }\n\n  /**\n   * Deserializes a uint256 number.\n   *\n   * BCS layout for \"uint256\": Thirty-two bytes. Binary format in little-endian representation.\n   */\n  deserializeU256(): Uint256 {\n    const low = this.deserializeU128();\n    const high = this.deserializeU128();\n\n    // combine the two 128-bit values and return (little endian)\n    return BigInt((high << BigInt(128)) | low);\n  }\n\n  /**\n   * Deserializes a uleb128 encoded uint32 number.\n   *\n   * BCS use uleb128 encoding in two cases: (1) lengths of variable-length sequences and (2) tags of enum values\n   */\n  deserializeUleb128AsU32(): Uint32 {\n    let value: bigint = BigInt(0);\n    let shift = 0;\n\n    while (value < MAX_U32_NUMBER) {\n      const byte = this.deserializeU8();\n      value |= BigInt(byte & 0x7f) << BigInt(shift);\n\n      if ((byte & 0x80) === 0) {\n        break;\n      }\n      shift += 7;\n    }\n\n    if (value > MAX_U32_NUMBER) {\n      throw new Error(\"Overflow while parsing uleb128-encoded uint32 value\");\n    }\n\n    return Number(value);\n  }\n\n  /**\n   * Helper function that primarily exists to support alternative syntax for deserialization.\n   * That is, if we have a `const deserializer: new Deserializer(...)`, instead of having to use\n   * `MyClass.deserialize(deserializer)`, we can call `deserializer.deserialize(MyClass)`.\n   *\n   * @example const deserializer = new Deserializer(new Uint8Array([1, 2, 3]));\n   * const value = deserializer.deserialize(MyClass); // where MyClass has a `deserialize` function\n   * // value is now an instance of MyClass\n   * // equivalent to `const value = MyClass.deserialize(deserializer)`\n   * @param cls The BCS-deserializable class to deserialize the buffered bytes into.\n   *\n   * @returns the deserialized value of class type T\n   */\n  deserialize<T>(cls: Deserializable<T>): T {\n    // NOTE: `deserialize` in `cls.deserialize(this)` here is a static method defined in `cls`,\n    // It is separate from the `deserialize` instance method defined here in Deserializer.\n    return cls.deserialize(this);\n  }\n\n  /**\n   * Deserializes an array of BCS Deserializable values given an existing Deserializer\n   * instance with a loaded byte buffer.\n   *\n   * @param cls The BCS-deserializable class to deserialize the buffered bytes into.\n   * @example\n   * // serialize a vector of addresses\n   * const addresses = new Array<AccountAddress>(\n   *   AccountAddress.from(\"0x1\"),\n   *   AccountAddress.from(\"0x2\"),\n   *   AccountAddress.from(\"0xa\"),\n   *   AccountAddress.from(\"0xb\"),\n   * );\n   * const serializer = new Serializer();\n   * serializer.serializeVector(addresses);\n   * const serializedBytes = serializer.toUint8Array();\n   *\n   * // deserialize the bytes into an array of addresses\n   * const deserializer = new Deserializer(serializedBytes);\n   * const deserializedAddresses = deserializer.deserializeVector(AccountAddress);\n   * // deserializedAddresses is now an array of AccountAddress instances\n   * @returns an array of deserialized values of type T\n   */\n  deserializeVector<T>(cls: Deserializable<T>): Array<T> {\n    const length = this.deserializeUleb128AsU32();\n    const vector = new Array<T>();\n    for (let i = 0; i < length; i += 1) {\n      vector.push(this.deserialize(cls));\n    }\n    return vector;\n  }\n}\n"],"mappings":"yCAiBO,IAAMA,EAAN,KAAmB,CAKxB,YAAYC,EAAkB,CAE5B,KAAK,OAAS,IAAI,YAAYA,EAAK,MAAM,EACzC,IAAI,WAAW,KAAK,MAAM,EAAE,IAAIA,EAAM,CAAC,EACvC,KAAK,OAAS,CAChB,CAEQ,KAAKC,EAA6B,CACxC,GAAI,KAAK,OAASA,EAAS,KAAK,OAAO,WACrC,MAAM,IAAI,MAAM,8BAA8B,EAGhD,IAAMC,EAAQ,KAAK,OAAO,MAAM,KAAK,OAAQ,KAAK,OAASD,CAAM,EACjE,YAAK,QAAUA,EACRC,CACT,CAeA,gBAAyB,CACvB,IAAMC,EAAQ,KAAK,iBAAiB,EAEpC,OADoB,IAAI,YAAY,EACjB,OAAOA,CAAK,CACjC,CAQA,kBAA+B,CAC7B,IAAMC,EAAM,KAAK,wBAAwB,EACzC,OAAO,IAAI,WAAW,KAAK,KAAKA,CAAG,CAAC,CACtC,CAMA,sBAAsBA,EAAyB,CAC7C,OAAO,IAAI,WAAW,KAAK,KAAKA,CAAG,CAAC,CACtC,CAOA,iBAA2B,CACzB,IAAMC,EAAO,IAAI,WAAW,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,EAC3C,GAAIA,IAAS,GAAKA,IAAS,EACzB,MAAM,IAAI,MAAM,uBAAuB,EAEzC,OAAOA,IAAS,CAClB,CAOA,eAAuB,CACrB,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAC9C,CAYA,gBAAyB,CACvB,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,CAAC,EAAE,UAAU,EAAG,EAAI,CACrD,CAYA,gBAAyB,CACvB,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,CAAC,EAAE,UAAU,EAAG,EAAI,CACrD,CAYA,gBAAyB,CACvB,IAAMC,EAAM,KAAK,eAAe,EAC1BC,EAAO,KAAK,eAAe,EAGjC,OAAO,OAAQ,OAAOA,CAAI,GAAK,OAAO,EAAE,EAAK,OAAOD,CAAG,CAAC,CAC1D,CAOA,iBAA2B,CACzB,IAAMA,EAAM,KAAK,eAAe,EAC1BC,EAAO,KAAK,eAAe,EAGjC,OAAO,OAAQA,GAAQ,OAAO,EAAE,EAAKD,CAAG,CAC1C,CAOA,iBAA2B,CACzB,IAAMA,EAAM,KAAK,gBAAgB,EAC3BC,EAAO,KAAK,gBAAgB,EAGlC,OAAO,OAAQA,GAAQ,OAAO,GAAG,EAAKD,CAAG,CAC3C,CAOA,yBAAkC,CAChC,IAAIH,EAAgB,OAAO,CAAC,EACxBK,EAAQ,EAEZ,KAAOL,EAAQM,GAAgB,CAC7B,IAAMC,EAAO,KAAK,cAAc,EAGhC,GAFAP,GAAS,OAAOO,EAAO,GAAI,GAAK,OAAOF,CAAK,EAEvC,EAAAE,EAAO,KACV,MAEFF,GAAS,CACX,CAEA,GAAIL,EAAQM,EACV,MAAM,IAAI,MAAM,qDAAqD,EAGvE,OAAO,OAAON,CAAK,CACrB,CAeA,YAAeQ,EAA2B,CAGxC,OAAOA,EAAI,YAAY,IAAI,CAC7B,CAyBA,kBAAqBA,EAAkC,CACrD,IAAMV,EAAS,KAAK,wBAAwB,EACtCW,EAAS,IAAI,MACnB,QAASC,EAAI,EAAGA,EAAIZ,EAAQY,GAAK,EAC/BD,EAAO,KAAK,KAAK,YAAYD,CAAG,CAAC,EAEnC,OAAOC,CACT,CACF","names":["Deserializer","data","length","bytes","value","len","bool","low","high","shift","MAX_U32_NUMBER","byte","cls","vector","i"]}