1*890232f2SAndroid Build Coastguard Workerimport { FILE_IDENTIFIER_LENGTH, SIZEOF_INT } from "./constants.js"; 2*890232f2SAndroid Build Coastguard Workerimport { int32, isLittleEndian, float32, float64 } from "./utils.js"; 3*890232f2SAndroid Build Coastguard Workerimport { Offset, Table, IGeneratedObject } from "./types.js"; 4*890232f2SAndroid Build Coastguard Workerimport { Encoding } from "./encoding.js"; 5*890232f2SAndroid Build Coastguard Worker 6*890232f2SAndroid Build Coastguard Workerexport class ByteBuffer { 7*890232f2SAndroid Build Coastguard Worker private position_ = 0; 8*890232f2SAndroid Build Coastguard Worker private text_decoder_ = new TextDecoder(); 9*890232f2SAndroid Build Coastguard Worker 10*890232f2SAndroid Build Coastguard Worker /** 11*890232f2SAndroid Build Coastguard Worker * Create a new ByteBuffer with a given array of bytes (`Uint8Array`) 12*890232f2SAndroid Build Coastguard Worker */ 13*890232f2SAndroid Build Coastguard Worker constructor(private bytes_: Uint8Array) { } 14*890232f2SAndroid Build Coastguard Worker 15*890232f2SAndroid Build Coastguard Worker /** 16*890232f2SAndroid Build Coastguard Worker * Create and allocate a new ByteBuffer with a given size. 17*890232f2SAndroid Build Coastguard Worker */ 18*890232f2SAndroid Build Coastguard Worker static allocate(byte_size: number): ByteBuffer { 19*890232f2SAndroid Build Coastguard Worker return new ByteBuffer(new Uint8Array(byte_size)); 20*890232f2SAndroid Build Coastguard Worker } 21*890232f2SAndroid Build Coastguard Worker 22*890232f2SAndroid Build Coastguard Worker clear(): void { 23*890232f2SAndroid Build Coastguard Worker this.position_ = 0; 24*890232f2SAndroid Build Coastguard Worker } 25*890232f2SAndroid Build Coastguard Worker 26*890232f2SAndroid Build Coastguard Worker /** 27*890232f2SAndroid Build Coastguard Worker * Get the underlying `Uint8Array`. 28*890232f2SAndroid Build Coastguard Worker */ 29*890232f2SAndroid Build Coastguard Worker bytes(): Uint8Array { 30*890232f2SAndroid Build Coastguard Worker return this.bytes_; 31*890232f2SAndroid Build Coastguard Worker } 32*890232f2SAndroid Build Coastguard Worker 33*890232f2SAndroid Build Coastguard Worker /** 34*890232f2SAndroid Build Coastguard Worker * Get the buffer's position. 35*890232f2SAndroid Build Coastguard Worker */ 36*890232f2SAndroid Build Coastguard Worker position(): number { 37*890232f2SAndroid Build Coastguard Worker return this.position_; 38*890232f2SAndroid Build Coastguard Worker } 39*890232f2SAndroid Build Coastguard Worker 40*890232f2SAndroid Build Coastguard Worker /** 41*890232f2SAndroid Build Coastguard Worker * Set the buffer's position. 42*890232f2SAndroid Build Coastguard Worker */ 43*890232f2SAndroid Build Coastguard Worker setPosition(position: number): void { 44*890232f2SAndroid Build Coastguard Worker this.position_ = position; 45*890232f2SAndroid Build Coastguard Worker } 46*890232f2SAndroid Build Coastguard Worker 47*890232f2SAndroid Build Coastguard Worker /** 48*890232f2SAndroid Build Coastguard Worker * Get the buffer's capacity. 49*890232f2SAndroid Build Coastguard Worker */ 50*890232f2SAndroid Build Coastguard Worker capacity(): number { 51*890232f2SAndroid Build Coastguard Worker return this.bytes_.length; 52*890232f2SAndroid Build Coastguard Worker } 53*890232f2SAndroid Build Coastguard Worker 54*890232f2SAndroid Build Coastguard Worker readInt8(offset: number): number { 55*890232f2SAndroid Build Coastguard Worker return this.readUint8(offset) << 24 >> 24; 56*890232f2SAndroid Build Coastguard Worker } 57*890232f2SAndroid Build Coastguard Worker 58*890232f2SAndroid Build Coastguard Worker readUint8(offset: number): number { 59*890232f2SAndroid Build Coastguard Worker return this.bytes_[offset]; 60*890232f2SAndroid Build Coastguard Worker } 61*890232f2SAndroid Build Coastguard Worker 62*890232f2SAndroid Build Coastguard Worker readInt16(offset: number): number { 63*890232f2SAndroid Build Coastguard Worker return this.readUint16(offset) << 16 >> 16; 64*890232f2SAndroid Build Coastguard Worker } 65*890232f2SAndroid Build Coastguard Worker 66*890232f2SAndroid Build Coastguard Worker readUint16(offset: number): number { 67*890232f2SAndroid Build Coastguard Worker return this.bytes_[offset] | this.bytes_[offset + 1] << 8; 68*890232f2SAndroid Build Coastguard Worker } 69*890232f2SAndroid Build Coastguard Worker 70*890232f2SAndroid Build Coastguard Worker readInt32(offset: number): number { 71*890232f2SAndroid Build Coastguard Worker return this.bytes_[offset] | this.bytes_[offset + 1] << 8 | this.bytes_[offset + 2] << 16 | this.bytes_[offset + 3] << 24; 72*890232f2SAndroid Build Coastguard Worker } 73*890232f2SAndroid Build Coastguard Worker 74*890232f2SAndroid Build Coastguard Worker readUint32(offset: number): number { 75*890232f2SAndroid Build Coastguard Worker return this.readInt32(offset) >>> 0; 76*890232f2SAndroid Build Coastguard Worker } 77*890232f2SAndroid Build Coastguard Worker 78*890232f2SAndroid Build Coastguard Worker readInt64(offset: number): bigint { 79*890232f2SAndroid Build Coastguard Worker return BigInt.asIntN(64, BigInt(this.readUint32(offset)) + (BigInt(this.readUint32(offset + 4)) << BigInt(32))); 80*890232f2SAndroid Build Coastguard Worker } 81*890232f2SAndroid Build Coastguard Worker 82*890232f2SAndroid Build Coastguard Worker readUint64(offset: number): bigint { 83*890232f2SAndroid Build Coastguard Worker return BigInt.asUintN(64, BigInt(this.readUint32(offset)) + (BigInt(this.readUint32(offset + 4)) << BigInt(32))); 84*890232f2SAndroid Build Coastguard Worker } 85*890232f2SAndroid Build Coastguard Worker 86*890232f2SAndroid Build Coastguard Worker readFloat32(offset: number): number { 87*890232f2SAndroid Build Coastguard Worker int32[0] = this.readInt32(offset); 88*890232f2SAndroid Build Coastguard Worker return float32[0]; 89*890232f2SAndroid Build Coastguard Worker } 90*890232f2SAndroid Build Coastguard Worker 91*890232f2SAndroid Build Coastguard Worker readFloat64(offset: number): number { 92*890232f2SAndroid Build Coastguard Worker int32[isLittleEndian ? 0 : 1] = this.readInt32(offset); 93*890232f2SAndroid Build Coastguard Worker int32[isLittleEndian ? 1 : 0] = this.readInt32(offset + 4); 94*890232f2SAndroid Build Coastguard Worker return float64[0]; 95*890232f2SAndroid Build Coastguard Worker } 96*890232f2SAndroid Build Coastguard Worker 97*890232f2SAndroid Build Coastguard Worker writeInt8(offset: number, value: number): void { 98*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 99*890232f2SAndroid Build Coastguard Worker } 100*890232f2SAndroid Build Coastguard Worker 101*890232f2SAndroid Build Coastguard Worker writeUint8(offset: number, value: number): void { 102*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 103*890232f2SAndroid Build Coastguard Worker } 104*890232f2SAndroid Build Coastguard Worker 105*890232f2SAndroid Build Coastguard Worker writeInt16(offset: number, value: number): void { 106*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 107*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 1] = value >> 8; 108*890232f2SAndroid Build Coastguard Worker } 109*890232f2SAndroid Build Coastguard Worker 110*890232f2SAndroid Build Coastguard Worker writeUint16(offset: number, value: number): void { 111*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 112*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 1] = value >> 8; 113*890232f2SAndroid Build Coastguard Worker } 114*890232f2SAndroid Build Coastguard Worker 115*890232f2SAndroid Build Coastguard Worker writeInt32(offset: number, value: number): void { 116*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 117*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 1] = value >> 8; 118*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 2] = value >> 16; 119*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 3] = value >> 24; 120*890232f2SAndroid Build Coastguard Worker } 121*890232f2SAndroid Build Coastguard Worker 122*890232f2SAndroid Build Coastguard Worker writeUint32(offset: number, value: number): void { 123*890232f2SAndroid Build Coastguard Worker this.bytes_[offset] = value; 124*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 1] = value >> 8; 125*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 2] = value >> 16; 126*890232f2SAndroid Build Coastguard Worker this.bytes_[offset + 3] = value >> 24; 127*890232f2SAndroid Build Coastguard Worker } 128*890232f2SAndroid Build Coastguard Worker 129*890232f2SAndroid Build Coastguard Worker writeInt64(offset: number, value: bigint): void { 130*890232f2SAndroid Build Coastguard Worker this.writeInt32(offset, Number(BigInt.asIntN(32, value))); 131*890232f2SAndroid Build Coastguard Worker this.writeInt32(offset + 4, Number(BigInt.asIntN(32, value >> BigInt(32)))); 132*890232f2SAndroid Build Coastguard Worker } 133*890232f2SAndroid Build Coastguard Worker 134*890232f2SAndroid Build Coastguard Worker writeUint64(offset: number, value: bigint): void { 135*890232f2SAndroid Build Coastguard Worker this.writeUint32(offset, Number(BigInt.asUintN(32, value))); 136*890232f2SAndroid Build Coastguard Worker this.writeUint32(offset + 4, Number(BigInt.asUintN(32, value >> BigInt(32)))); 137*890232f2SAndroid Build Coastguard Worker } 138*890232f2SAndroid Build Coastguard Worker 139*890232f2SAndroid Build Coastguard Worker writeFloat32(offset: number, value: number): void { 140*890232f2SAndroid Build Coastguard Worker float32[0] = value; 141*890232f2SAndroid Build Coastguard Worker this.writeInt32(offset, int32[0]); 142*890232f2SAndroid Build Coastguard Worker } 143*890232f2SAndroid Build Coastguard Worker 144*890232f2SAndroid Build Coastguard Worker writeFloat64(offset: number, value: number): void { 145*890232f2SAndroid Build Coastguard Worker float64[0] = value; 146*890232f2SAndroid Build Coastguard Worker this.writeInt32(offset, int32[isLittleEndian ? 0 : 1]); 147*890232f2SAndroid Build Coastguard Worker this.writeInt32(offset + 4, int32[isLittleEndian ? 1 : 0]); 148*890232f2SAndroid Build Coastguard Worker } 149*890232f2SAndroid Build Coastguard Worker 150*890232f2SAndroid Build Coastguard Worker /** 151*890232f2SAndroid Build Coastguard Worker * Return the file identifier. Behavior is undefined for FlatBuffers whose 152*890232f2SAndroid Build Coastguard Worker * schema does not include a file_identifier (likely points at padding or the 153*890232f2SAndroid Build Coastguard Worker * start of a the root vtable). 154*890232f2SAndroid Build Coastguard Worker */ 155*890232f2SAndroid Build Coastguard Worker getBufferIdentifier(): string { 156*890232f2SAndroid Build Coastguard Worker if (this.bytes_.length < this.position_ + SIZEOF_INT + 157*890232f2SAndroid Build Coastguard Worker FILE_IDENTIFIER_LENGTH) { 158*890232f2SAndroid Build Coastguard Worker throw new Error( 159*890232f2SAndroid Build Coastguard Worker 'FlatBuffers: ByteBuffer is too short to contain an identifier.'); 160*890232f2SAndroid Build Coastguard Worker } 161*890232f2SAndroid Build Coastguard Worker let result = ""; 162*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < FILE_IDENTIFIER_LENGTH; i++) { 163*890232f2SAndroid Build Coastguard Worker result += String.fromCharCode( 164*890232f2SAndroid Build Coastguard Worker this.readInt8(this.position_ + SIZEOF_INT + i)); 165*890232f2SAndroid Build Coastguard Worker } 166*890232f2SAndroid Build Coastguard Worker return result; 167*890232f2SAndroid Build Coastguard Worker } 168*890232f2SAndroid Build Coastguard Worker 169*890232f2SAndroid Build Coastguard Worker /** 170*890232f2SAndroid Build Coastguard Worker * Look up a field in the vtable, return an offset into the object, or 0 if the 171*890232f2SAndroid Build Coastguard Worker * field is not present. 172*890232f2SAndroid Build Coastguard Worker */ 173*890232f2SAndroid Build Coastguard Worker __offset(bb_pos: number, vtable_offset: number): Offset { 174*890232f2SAndroid Build Coastguard Worker const vtable = bb_pos - this.readInt32(bb_pos); 175*890232f2SAndroid Build Coastguard Worker return vtable_offset < this.readInt16(vtable) ? this.readInt16(vtable + vtable_offset) : 0; 176*890232f2SAndroid Build Coastguard Worker } 177*890232f2SAndroid Build Coastguard Worker 178*890232f2SAndroid Build Coastguard Worker /** 179*890232f2SAndroid Build Coastguard Worker * Initialize any Table-derived type to point to the union at the given offset. 180*890232f2SAndroid Build Coastguard Worker */ 181*890232f2SAndroid Build Coastguard Worker __union(t: Table, offset: number): Table { 182*890232f2SAndroid Build Coastguard Worker t.bb_pos = offset + this.readInt32(offset); 183*890232f2SAndroid Build Coastguard Worker t.bb = this; 184*890232f2SAndroid Build Coastguard Worker return t; 185*890232f2SAndroid Build Coastguard Worker } 186*890232f2SAndroid Build Coastguard Worker 187*890232f2SAndroid Build Coastguard Worker /** 188*890232f2SAndroid Build Coastguard Worker * Create a JavaScript string from UTF-8 data stored inside the FlatBuffer. 189*890232f2SAndroid Build Coastguard Worker * This allocates a new string and converts to wide chars upon each access. 190*890232f2SAndroid Build Coastguard Worker * 191*890232f2SAndroid Build Coastguard Worker * To avoid the conversion to string, pass Encoding.UTF8_BYTES as the 192*890232f2SAndroid Build Coastguard Worker * "optionalEncoding" argument. This is useful for avoiding conversion when 193*890232f2SAndroid Build Coastguard Worker * the data will just be packaged back up in another FlatBuffer later on. 194*890232f2SAndroid Build Coastguard Worker * 195*890232f2SAndroid Build Coastguard Worker * @param offset 196*890232f2SAndroid Build Coastguard Worker * @param opt_encoding Defaults to UTF16_STRING 197*890232f2SAndroid Build Coastguard Worker */ 198*890232f2SAndroid Build Coastguard Worker __string(offset: number, opt_encoding?: Encoding): string | Uint8Array { 199*890232f2SAndroid Build Coastguard Worker offset += this.readInt32(offset); 200*890232f2SAndroid Build Coastguard Worker const length = this.readInt32(offset); 201*890232f2SAndroid Build Coastguard Worker offset += SIZEOF_INT; 202*890232f2SAndroid Build Coastguard Worker const utf8bytes = this.bytes_.subarray(offset, offset + length); 203*890232f2SAndroid Build Coastguard Worker if (opt_encoding === Encoding.UTF8_BYTES) 204*890232f2SAndroid Build Coastguard Worker return utf8bytes; 205*890232f2SAndroid Build Coastguard Worker else 206*890232f2SAndroid Build Coastguard Worker return this.text_decoder_.decode(utf8bytes); 207*890232f2SAndroid Build Coastguard Worker } 208*890232f2SAndroid Build Coastguard Worker 209*890232f2SAndroid Build Coastguard Worker /** 210*890232f2SAndroid Build Coastguard Worker * Handle unions that can contain string as its member, if a Table-derived type then initialize it, 211*890232f2SAndroid Build Coastguard Worker * if a string then return a new one 212*890232f2SAndroid Build Coastguard Worker * 213*890232f2SAndroid Build Coastguard Worker * WARNING: strings are immutable in JS so we can't change the string that the user gave us, this 214*890232f2SAndroid Build Coastguard Worker * makes the behaviour of __union_with_string different compared to __union 215*890232f2SAndroid Build Coastguard Worker */ 216*890232f2SAndroid Build Coastguard Worker __union_with_string(o: Table | string, offset: number) : Table | string { 217*890232f2SAndroid Build Coastguard Worker if(typeof o === 'string') { 218*890232f2SAndroid Build Coastguard Worker return this.__string(offset) as string; 219*890232f2SAndroid Build Coastguard Worker } 220*890232f2SAndroid Build Coastguard Worker return this.__union(o, offset); 221*890232f2SAndroid Build Coastguard Worker } 222*890232f2SAndroid Build Coastguard Worker 223*890232f2SAndroid Build Coastguard Worker /** 224*890232f2SAndroid Build Coastguard Worker * Retrieve the relative offset stored at "offset" 225*890232f2SAndroid Build Coastguard Worker */ 226*890232f2SAndroid Build Coastguard Worker __indirect(offset: Offset): Offset { 227*890232f2SAndroid Build Coastguard Worker return offset + this.readInt32(offset); 228*890232f2SAndroid Build Coastguard Worker } 229*890232f2SAndroid Build Coastguard Worker 230*890232f2SAndroid Build Coastguard Worker /** 231*890232f2SAndroid Build Coastguard Worker * Get the start of data of a vector whose offset is stored at "offset" in this object. 232*890232f2SAndroid Build Coastguard Worker */ 233*890232f2SAndroid Build Coastguard Worker __vector(offset: Offset): Offset { 234*890232f2SAndroid Build Coastguard Worker return offset + this.readInt32(offset) + SIZEOF_INT; // data starts after the length 235*890232f2SAndroid Build Coastguard Worker } 236*890232f2SAndroid Build Coastguard Worker 237*890232f2SAndroid Build Coastguard Worker /** 238*890232f2SAndroid Build Coastguard Worker * Get the length of a vector whose offset is stored at "offset" in this object. 239*890232f2SAndroid Build Coastguard Worker */ 240*890232f2SAndroid Build Coastguard Worker __vector_len(offset: Offset): Offset { 241*890232f2SAndroid Build Coastguard Worker return this.readInt32(offset + this.readInt32(offset)); 242*890232f2SAndroid Build Coastguard Worker } 243*890232f2SAndroid Build Coastguard Worker 244*890232f2SAndroid Build Coastguard Worker __has_identifier(ident: string): boolean { 245*890232f2SAndroid Build Coastguard Worker if (ident.length != FILE_IDENTIFIER_LENGTH) { 246*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: file identifier must be length ' + 247*890232f2SAndroid Build Coastguard Worker FILE_IDENTIFIER_LENGTH); 248*890232f2SAndroid Build Coastguard Worker } 249*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < FILE_IDENTIFIER_LENGTH; i++) { 250*890232f2SAndroid Build Coastguard Worker if (ident.charCodeAt(i) != this.readInt8(this.position() + SIZEOF_INT + i)) { 251*890232f2SAndroid Build Coastguard Worker return false; 252*890232f2SAndroid Build Coastguard Worker } 253*890232f2SAndroid Build Coastguard Worker } 254*890232f2SAndroid Build Coastguard Worker return true; 255*890232f2SAndroid Build Coastguard Worker } 256*890232f2SAndroid Build Coastguard Worker 257*890232f2SAndroid Build Coastguard Worker /** 258*890232f2SAndroid Build Coastguard Worker * A helper function for generating list for obj api 259*890232f2SAndroid Build Coastguard Worker */ 260*890232f2SAndroid Build Coastguard Worker createScalarList(listAccessor: (i: number) => unknown, listLength: number): any[] { 261*890232f2SAndroid Build Coastguard Worker const ret: any[] = []; 262*890232f2SAndroid Build Coastguard Worker for(let i = 0; i < listLength; ++i) { 263*890232f2SAndroid Build Coastguard Worker if(listAccessor(i) !== null) { 264*890232f2SAndroid Build Coastguard Worker ret.push(listAccessor(i)); 265*890232f2SAndroid Build Coastguard Worker } 266*890232f2SAndroid Build Coastguard Worker } 267*890232f2SAndroid Build Coastguard Worker 268*890232f2SAndroid Build Coastguard Worker return ret; 269*890232f2SAndroid Build Coastguard Worker } 270*890232f2SAndroid Build Coastguard Worker 271*890232f2SAndroid Build Coastguard Worker /** 272*890232f2SAndroid Build Coastguard Worker * A helper function for generating list for obj api 273*890232f2SAndroid Build Coastguard Worker * @param listAccessor function that accepts an index and return data at that index 274*890232f2SAndroid Build Coastguard Worker * @param listLength listLength 275*890232f2SAndroid Build Coastguard Worker * @param res result list 276*890232f2SAndroid Build Coastguard Worker */ 277*890232f2SAndroid Build Coastguard Worker createObjList(listAccessor: (i: number) => unknown, listLength: number): any[] { 278*890232f2SAndroid Build Coastguard Worker const ret: any[] = []; 279*890232f2SAndroid Build Coastguard Worker for(let i = 0; i < listLength; ++i) { 280*890232f2SAndroid Build Coastguard Worker const val = listAccessor(i); 281*890232f2SAndroid Build Coastguard Worker if(val !== null) { 282*890232f2SAndroid Build Coastguard Worker ret.push((val as IGeneratedObject).unpack()); 283*890232f2SAndroid Build Coastguard Worker } 284*890232f2SAndroid Build Coastguard Worker } 285*890232f2SAndroid Build Coastguard Worker 286*890232f2SAndroid Build Coastguard Worker return ret; 287*890232f2SAndroid Build Coastguard Worker } 288*890232f2SAndroid Build Coastguard Worker 289*890232f2SAndroid Build Coastguard Worker } 290