xref: /aosp_15_r20/external/flatbuffers/ts/byte-buffer.ts (revision 890232f25432b36107d06881e0a25aaa6b473652)
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