1*890232f2SAndroid Build Coastguard Workerimport { ByteBuffer } from "./byte-buffer.js" 2*890232f2SAndroid Build Coastguard Workerimport { SIZEOF_SHORT, SIZE_PREFIX_LENGTH, SIZEOF_INT, FILE_IDENTIFIER_LENGTH } from "./constants.js" 3*890232f2SAndroid Build Coastguard Workerimport { Offset, IGeneratedObject } from "./types.js" 4*890232f2SAndroid Build Coastguard Worker 5*890232f2SAndroid Build Coastguard Workerexport class Builder { 6*890232f2SAndroid Build Coastguard Worker private bb: ByteBuffer 7*890232f2SAndroid Build Coastguard Worker /** Remaining space in the ByteBuffer. */ 8*890232f2SAndroid Build Coastguard Worker private space: number 9*890232f2SAndroid Build Coastguard Worker /** Minimum alignment encountered so far. */ 10*890232f2SAndroid Build Coastguard Worker private minalign = 1 11*890232f2SAndroid Build Coastguard Worker /** The vtable for the current table. */ 12*890232f2SAndroid Build Coastguard Worker private vtable: number[] | null = null 13*890232f2SAndroid Build Coastguard Worker /** The amount of fields we're actually using. */ 14*890232f2SAndroid Build Coastguard Worker private vtable_in_use = 0 15*890232f2SAndroid Build Coastguard Worker /** Whether we are currently serializing a table. */ 16*890232f2SAndroid Build Coastguard Worker private isNested = false; 17*890232f2SAndroid Build Coastguard Worker /** Starting offset of the current struct/table. */ 18*890232f2SAndroid Build Coastguard Worker private object_start = 0 19*890232f2SAndroid Build Coastguard Worker /** List of offsets of all vtables. */ 20*890232f2SAndroid Build Coastguard Worker private vtables: number[] = [] 21*890232f2SAndroid Build Coastguard Worker /** For the current vector being built. */ 22*890232f2SAndroid Build Coastguard Worker private vector_num_elems = 0 23*890232f2SAndroid Build Coastguard Worker /** False omits default values from the serialized data */ 24*890232f2SAndroid Build Coastguard Worker private force_defaults = false; 25*890232f2SAndroid Build Coastguard Worker 26*890232f2SAndroid Build Coastguard Worker private string_maps: Map<string | Uint8Array, number> | null = null; 27*890232f2SAndroid Build Coastguard Worker private text_encoder = new TextEncoder(); 28*890232f2SAndroid Build Coastguard Worker 29*890232f2SAndroid Build Coastguard Worker /** 30*890232f2SAndroid Build Coastguard Worker * Create a FlatBufferBuilder. 31*890232f2SAndroid Build Coastguard Worker */ 32*890232f2SAndroid Build Coastguard Worker constructor(opt_initial_size?: number) { 33*890232f2SAndroid Build Coastguard Worker let initial_size: number; 34*890232f2SAndroid Build Coastguard Worker 35*890232f2SAndroid Build Coastguard Worker if (!opt_initial_size) { 36*890232f2SAndroid Build Coastguard Worker initial_size = 1024; 37*890232f2SAndroid Build Coastguard Worker } else { 38*890232f2SAndroid Build Coastguard Worker initial_size = opt_initial_size; 39*890232f2SAndroid Build Coastguard Worker } 40*890232f2SAndroid Build Coastguard Worker 41*890232f2SAndroid Build Coastguard Worker /** 42*890232f2SAndroid Build Coastguard Worker * @type {ByteBuffer} 43*890232f2SAndroid Build Coastguard Worker * @private 44*890232f2SAndroid Build Coastguard Worker */ 45*890232f2SAndroid Build Coastguard Worker this.bb = ByteBuffer.allocate(initial_size); 46*890232f2SAndroid Build Coastguard Worker this.space = initial_size; 47*890232f2SAndroid Build Coastguard Worker } 48*890232f2SAndroid Build Coastguard Worker 49*890232f2SAndroid Build Coastguard Worker 50*890232f2SAndroid Build Coastguard Worker clear(): void { 51*890232f2SAndroid Build Coastguard Worker this.bb.clear(); 52*890232f2SAndroid Build Coastguard Worker this.space = this.bb.capacity(); 53*890232f2SAndroid Build Coastguard Worker this.minalign = 1; 54*890232f2SAndroid Build Coastguard Worker this.vtable = null; 55*890232f2SAndroid Build Coastguard Worker this.vtable_in_use = 0; 56*890232f2SAndroid Build Coastguard Worker this.isNested = false; 57*890232f2SAndroid Build Coastguard Worker this.object_start = 0; 58*890232f2SAndroid Build Coastguard Worker this.vtables = []; 59*890232f2SAndroid Build Coastguard Worker this.vector_num_elems = 0; 60*890232f2SAndroid Build Coastguard Worker this.force_defaults = false; 61*890232f2SAndroid Build Coastguard Worker this.string_maps = null; 62*890232f2SAndroid Build Coastguard Worker } 63*890232f2SAndroid Build Coastguard Worker 64*890232f2SAndroid Build Coastguard Worker /** 65*890232f2SAndroid Build Coastguard Worker * In order to save space, fields that are set to their default value 66*890232f2SAndroid Build Coastguard Worker * don't get serialized into the buffer. Forcing defaults provides a 67*890232f2SAndroid Build Coastguard Worker * way to manually disable this optimization. 68*890232f2SAndroid Build Coastguard Worker * 69*890232f2SAndroid Build Coastguard Worker * @param forceDefaults true always serializes default values 70*890232f2SAndroid Build Coastguard Worker */ 71*890232f2SAndroid Build Coastguard Worker forceDefaults(forceDefaults: boolean): void { 72*890232f2SAndroid Build Coastguard Worker this.force_defaults = forceDefaults; 73*890232f2SAndroid Build Coastguard Worker } 74*890232f2SAndroid Build Coastguard Worker 75*890232f2SAndroid Build Coastguard Worker /** 76*890232f2SAndroid Build Coastguard Worker * Get the ByteBuffer representing the FlatBuffer. Only call this after you've 77*890232f2SAndroid Build Coastguard Worker * called finish(). The actual data starts at the ByteBuffer's current position, 78*890232f2SAndroid Build Coastguard Worker * not necessarily at 0. 79*890232f2SAndroid Build Coastguard Worker */ 80*890232f2SAndroid Build Coastguard Worker dataBuffer(): ByteBuffer { 81*890232f2SAndroid Build Coastguard Worker return this.bb; 82*890232f2SAndroid Build Coastguard Worker } 83*890232f2SAndroid Build Coastguard Worker 84*890232f2SAndroid Build Coastguard Worker /** 85*890232f2SAndroid Build Coastguard Worker * Get the bytes representing the FlatBuffer. Only call this after you've 86*890232f2SAndroid Build Coastguard Worker * called finish(). 87*890232f2SAndroid Build Coastguard Worker */ 88*890232f2SAndroid Build Coastguard Worker asUint8Array(): Uint8Array { 89*890232f2SAndroid Build Coastguard Worker return this.bb.bytes().subarray(this.bb.position(), this.bb.position() + this.offset()); 90*890232f2SAndroid Build Coastguard Worker } 91*890232f2SAndroid Build Coastguard Worker 92*890232f2SAndroid Build Coastguard Worker /** 93*890232f2SAndroid Build Coastguard Worker * Prepare to write an element of `size` after `additional_bytes` have been 94*890232f2SAndroid Build Coastguard Worker * written, e.g. if you write a string, you need to align such the int length 95*890232f2SAndroid Build Coastguard Worker * field is aligned to 4 bytes, and the string data follows it directly. If all 96*890232f2SAndroid Build Coastguard Worker * you need to do is alignment, `additional_bytes` will be 0. 97*890232f2SAndroid Build Coastguard Worker * 98*890232f2SAndroid Build Coastguard Worker * @param size This is the of the new element to write 99*890232f2SAndroid Build Coastguard Worker * @param additional_bytes The padding size 100*890232f2SAndroid Build Coastguard Worker */ 101*890232f2SAndroid Build Coastguard Worker prep(size: number, additional_bytes: number): void { 102*890232f2SAndroid Build Coastguard Worker // Track the biggest thing we've ever aligned to. 103*890232f2SAndroid Build Coastguard Worker if (size > this.minalign) { 104*890232f2SAndroid Build Coastguard Worker this.minalign = size; 105*890232f2SAndroid Build Coastguard Worker } 106*890232f2SAndroid Build Coastguard Worker 107*890232f2SAndroid Build Coastguard Worker // Find the amount of alignment needed such that `size` is properly 108*890232f2SAndroid Build Coastguard Worker // aligned after `additional_bytes` 109*890232f2SAndroid Build Coastguard Worker const align_size = ((~(this.bb.capacity() - this.space + additional_bytes)) + 1) & (size - 1); 110*890232f2SAndroid Build Coastguard Worker 111*890232f2SAndroid Build Coastguard Worker // Reallocate the buffer if needed. 112*890232f2SAndroid Build Coastguard Worker while (this.space < align_size + size + additional_bytes) { 113*890232f2SAndroid Build Coastguard Worker const old_buf_size = this.bb.capacity(); 114*890232f2SAndroid Build Coastguard Worker this.bb = Builder.growByteBuffer(this.bb); 115*890232f2SAndroid Build Coastguard Worker this.space += this.bb.capacity() - old_buf_size; 116*890232f2SAndroid Build Coastguard Worker } 117*890232f2SAndroid Build Coastguard Worker 118*890232f2SAndroid Build Coastguard Worker this.pad(align_size); 119*890232f2SAndroid Build Coastguard Worker } 120*890232f2SAndroid Build Coastguard Worker 121*890232f2SAndroid Build Coastguard Worker pad(byte_size: number): void { 122*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < byte_size; i++) { 123*890232f2SAndroid Build Coastguard Worker this.bb.writeInt8(--this.space, 0); 124*890232f2SAndroid Build Coastguard Worker } 125*890232f2SAndroid Build Coastguard Worker } 126*890232f2SAndroid Build Coastguard Worker 127*890232f2SAndroid Build Coastguard Worker writeInt8(value: number): void { 128*890232f2SAndroid Build Coastguard Worker this.bb.writeInt8(this.space -= 1, value); 129*890232f2SAndroid Build Coastguard Worker } 130*890232f2SAndroid Build Coastguard Worker 131*890232f2SAndroid Build Coastguard Worker writeInt16(value: number): void { 132*890232f2SAndroid Build Coastguard Worker this.bb.writeInt16(this.space -= 2, value); 133*890232f2SAndroid Build Coastguard Worker } 134*890232f2SAndroid Build Coastguard Worker 135*890232f2SAndroid Build Coastguard Worker writeInt32(value: number): void { 136*890232f2SAndroid Build Coastguard Worker this.bb.writeInt32(this.space -= 4, value); 137*890232f2SAndroid Build Coastguard Worker } 138*890232f2SAndroid Build Coastguard Worker 139*890232f2SAndroid Build Coastguard Worker writeInt64(value: bigint): void { 140*890232f2SAndroid Build Coastguard Worker this.bb.writeInt64(this.space -= 8, value); 141*890232f2SAndroid Build Coastguard Worker } 142*890232f2SAndroid Build Coastguard Worker 143*890232f2SAndroid Build Coastguard Worker writeFloat32(value: number): void { 144*890232f2SAndroid Build Coastguard Worker this.bb.writeFloat32(this.space -= 4, value); 145*890232f2SAndroid Build Coastguard Worker } 146*890232f2SAndroid Build Coastguard Worker 147*890232f2SAndroid Build Coastguard Worker writeFloat64(value: number): void { 148*890232f2SAndroid Build Coastguard Worker this.bb.writeFloat64(this.space -= 8, value); 149*890232f2SAndroid Build Coastguard Worker } 150*890232f2SAndroid Build Coastguard Worker 151*890232f2SAndroid Build Coastguard Worker /** 152*890232f2SAndroid Build Coastguard Worker * Add an `int8` to the buffer, properly aligned, and grows the buffer (if necessary). 153*890232f2SAndroid Build Coastguard Worker * @param value The `int8` to add the the buffer. 154*890232f2SAndroid Build Coastguard Worker */ 155*890232f2SAndroid Build Coastguard Worker addInt8(value: number): void { 156*890232f2SAndroid Build Coastguard Worker this.prep(1, 0); 157*890232f2SAndroid Build Coastguard Worker this.writeInt8(value); 158*890232f2SAndroid Build Coastguard Worker } 159*890232f2SAndroid Build Coastguard Worker 160*890232f2SAndroid Build Coastguard Worker /** 161*890232f2SAndroid Build Coastguard Worker * Add an `int16` to the buffer, properly aligned, and grows the buffer (if necessary). 162*890232f2SAndroid Build Coastguard Worker * @param value The `int16` to add the the buffer. 163*890232f2SAndroid Build Coastguard Worker */ 164*890232f2SAndroid Build Coastguard Worker addInt16(value: number): void { 165*890232f2SAndroid Build Coastguard Worker this.prep(2, 0); 166*890232f2SAndroid Build Coastguard Worker this.writeInt16(value); 167*890232f2SAndroid Build Coastguard Worker } 168*890232f2SAndroid Build Coastguard Worker 169*890232f2SAndroid Build Coastguard Worker /** 170*890232f2SAndroid Build Coastguard Worker * Add an `int32` to the buffer, properly aligned, and grows the buffer (if necessary). 171*890232f2SAndroid Build Coastguard Worker * @param value The `int32` to add the the buffer. 172*890232f2SAndroid Build Coastguard Worker */ 173*890232f2SAndroid Build Coastguard Worker addInt32(value: number): void { 174*890232f2SAndroid Build Coastguard Worker this.prep(4, 0); 175*890232f2SAndroid Build Coastguard Worker this.writeInt32(value); 176*890232f2SAndroid Build Coastguard Worker } 177*890232f2SAndroid Build Coastguard Worker 178*890232f2SAndroid Build Coastguard Worker /** 179*890232f2SAndroid Build Coastguard Worker * Add an `int64` to the buffer, properly aligned, and grows the buffer (if necessary). 180*890232f2SAndroid Build Coastguard Worker * @param value The `int64` to add the the buffer. 181*890232f2SAndroid Build Coastguard Worker */ 182*890232f2SAndroid Build Coastguard Worker addInt64(value: bigint): void { 183*890232f2SAndroid Build Coastguard Worker this.prep(8, 0); 184*890232f2SAndroid Build Coastguard Worker this.writeInt64(value); 185*890232f2SAndroid Build Coastguard Worker } 186*890232f2SAndroid Build Coastguard Worker 187*890232f2SAndroid Build Coastguard Worker /** 188*890232f2SAndroid Build Coastguard Worker * Add a `float32` to the buffer, properly aligned, and grows the buffer (if necessary). 189*890232f2SAndroid Build Coastguard Worker * @param value The `float32` to add the the buffer. 190*890232f2SAndroid Build Coastguard Worker */ 191*890232f2SAndroid Build Coastguard Worker addFloat32(value: number): void { 192*890232f2SAndroid Build Coastguard Worker this.prep(4, 0); 193*890232f2SAndroid Build Coastguard Worker this.writeFloat32(value); 194*890232f2SAndroid Build Coastguard Worker } 195*890232f2SAndroid Build Coastguard Worker 196*890232f2SAndroid Build Coastguard Worker /** 197*890232f2SAndroid Build Coastguard Worker * Add a `float64` to the buffer, properly aligned, and grows the buffer (if necessary). 198*890232f2SAndroid Build Coastguard Worker * @param value The `float64` to add the the buffer. 199*890232f2SAndroid Build Coastguard Worker */ 200*890232f2SAndroid Build Coastguard Worker addFloat64(value: number): void { 201*890232f2SAndroid Build Coastguard Worker this.prep(8, 0); 202*890232f2SAndroid Build Coastguard Worker this.writeFloat64(value); 203*890232f2SAndroid Build Coastguard Worker } 204*890232f2SAndroid Build Coastguard Worker 205*890232f2SAndroid Build Coastguard Worker addFieldInt8(voffset: number, value: number, defaultValue: number): void { 206*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 207*890232f2SAndroid Build Coastguard Worker this.addInt8(value); 208*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 209*890232f2SAndroid Build Coastguard Worker } 210*890232f2SAndroid Build Coastguard Worker } 211*890232f2SAndroid Build Coastguard Worker 212*890232f2SAndroid Build Coastguard Worker addFieldInt16(voffset: number, value: number, defaultValue: number): void { 213*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 214*890232f2SAndroid Build Coastguard Worker this.addInt16(value); 215*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 216*890232f2SAndroid Build Coastguard Worker } 217*890232f2SAndroid Build Coastguard Worker } 218*890232f2SAndroid Build Coastguard Worker 219*890232f2SAndroid Build Coastguard Worker addFieldInt32(voffset: number, value: number, defaultValue: number): void { 220*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 221*890232f2SAndroid Build Coastguard Worker this.addInt32(value); 222*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 223*890232f2SAndroid Build Coastguard Worker } 224*890232f2SAndroid Build Coastguard Worker } 225*890232f2SAndroid Build Coastguard Worker 226*890232f2SAndroid Build Coastguard Worker addFieldInt64(voffset: number, value: bigint, defaultValue: bigint): void { 227*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value !== defaultValue) { 228*890232f2SAndroid Build Coastguard Worker this.addInt64(value); 229*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 230*890232f2SAndroid Build Coastguard Worker } 231*890232f2SAndroid Build Coastguard Worker } 232*890232f2SAndroid Build Coastguard Worker 233*890232f2SAndroid Build Coastguard Worker addFieldFloat32(voffset: number, value: number, defaultValue: number): void { 234*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 235*890232f2SAndroid Build Coastguard Worker this.addFloat32(value); 236*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 237*890232f2SAndroid Build Coastguard Worker } 238*890232f2SAndroid Build Coastguard Worker } 239*890232f2SAndroid Build Coastguard Worker 240*890232f2SAndroid Build Coastguard Worker addFieldFloat64(voffset: number, value: number, defaultValue: number): void { 241*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 242*890232f2SAndroid Build Coastguard Worker this.addFloat64(value); 243*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 244*890232f2SAndroid Build Coastguard Worker } 245*890232f2SAndroid Build Coastguard Worker } 246*890232f2SAndroid Build Coastguard Worker 247*890232f2SAndroid Build Coastguard Worker addFieldOffset(voffset: number, value: Offset, defaultValue: Offset): void { 248*890232f2SAndroid Build Coastguard Worker if (this.force_defaults || value != defaultValue) { 249*890232f2SAndroid Build Coastguard Worker this.addOffset(value); 250*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 251*890232f2SAndroid Build Coastguard Worker } 252*890232f2SAndroid Build Coastguard Worker } 253*890232f2SAndroid Build Coastguard Worker 254*890232f2SAndroid Build Coastguard Worker /** 255*890232f2SAndroid Build Coastguard Worker * Structs are stored inline, so nothing additional is being added. `d` is always 0. 256*890232f2SAndroid Build Coastguard Worker */ 257*890232f2SAndroid Build Coastguard Worker addFieldStruct(voffset: number, value: Offset, defaultValue: Offset): void { 258*890232f2SAndroid Build Coastguard Worker if (value != defaultValue) { 259*890232f2SAndroid Build Coastguard Worker this.nested(value); 260*890232f2SAndroid Build Coastguard Worker this.slot(voffset); 261*890232f2SAndroid Build Coastguard Worker } 262*890232f2SAndroid Build Coastguard Worker } 263*890232f2SAndroid Build Coastguard Worker 264*890232f2SAndroid Build Coastguard Worker /** 265*890232f2SAndroid Build Coastguard Worker * Structures are always stored inline, they need to be created right 266*890232f2SAndroid Build Coastguard Worker * where they're used. You'll get this assertion failure if you 267*890232f2SAndroid Build Coastguard Worker * created it elsewhere. 268*890232f2SAndroid Build Coastguard Worker */ 269*890232f2SAndroid Build Coastguard Worker nested(obj: Offset): void { 270*890232f2SAndroid Build Coastguard Worker if (obj != this.offset()) { 271*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: struct must be serialized inline.'); 272*890232f2SAndroid Build Coastguard Worker } 273*890232f2SAndroid Build Coastguard Worker } 274*890232f2SAndroid Build Coastguard Worker 275*890232f2SAndroid Build Coastguard Worker /** 276*890232f2SAndroid Build Coastguard Worker * Should not be creating any other object, string or vector 277*890232f2SAndroid Build Coastguard Worker * while an object is being constructed 278*890232f2SAndroid Build Coastguard Worker */ 279*890232f2SAndroid Build Coastguard Worker notNested(): void { 280*890232f2SAndroid Build Coastguard Worker if (this.isNested) { 281*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: object serialization must not be nested.'); 282*890232f2SAndroid Build Coastguard Worker } 283*890232f2SAndroid Build Coastguard Worker } 284*890232f2SAndroid Build Coastguard Worker 285*890232f2SAndroid Build Coastguard Worker /** 286*890232f2SAndroid Build Coastguard Worker * Set the current vtable at `voffset` to the current location in the buffer. 287*890232f2SAndroid Build Coastguard Worker */ 288*890232f2SAndroid Build Coastguard Worker slot(voffset: number): void { 289*890232f2SAndroid Build Coastguard Worker if (this.vtable !== null) 290*890232f2SAndroid Build Coastguard Worker this.vtable[voffset] = this.offset(); 291*890232f2SAndroid Build Coastguard Worker } 292*890232f2SAndroid Build Coastguard Worker 293*890232f2SAndroid Build Coastguard Worker /** 294*890232f2SAndroid Build Coastguard Worker * @returns Offset relative to the end of the buffer. 295*890232f2SAndroid Build Coastguard Worker */ 296*890232f2SAndroid Build Coastguard Worker offset(): Offset { 297*890232f2SAndroid Build Coastguard Worker return this.bb.capacity() - this.space; 298*890232f2SAndroid Build Coastguard Worker } 299*890232f2SAndroid Build Coastguard Worker 300*890232f2SAndroid Build Coastguard Worker /** 301*890232f2SAndroid Build Coastguard Worker * Doubles the size of the backing ByteBuffer and copies the old data towards 302*890232f2SAndroid Build Coastguard Worker * the end of the new buffer (since we build the buffer backwards). 303*890232f2SAndroid Build Coastguard Worker * 304*890232f2SAndroid Build Coastguard Worker * @param bb The current buffer with the existing data 305*890232f2SAndroid Build Coastguard Worker * @returns A new byte buffer with the old data copied 306*890232f2SAndroid Build Coastguard Worker * to it. The data is located at the end of the buffer. 307*890232f2SAndroid Build Coastguard Worker * 308*890232f2SAndroid Build Coastguard Worker * uint8Array.set() formally takes {Array<number>|ArrayBufferView}, so to pass 309*890232f2SAndroid Build Coastguard Worker * it a uint8Array we need to suppress the type check: 310*890232f2SAndroid Build Coastguard Worker * @suppress {checkTypes} 311*890232f2SAndroid Build Coastguard Worker */ 312*890232f2SAndroid Build Coastguard Worker static growByteBuffer(bb: ByteBuffer): ByteBuffer { 313*890232f2SAndroid Build Coastguard Worker const old_buf_size = bb.capacity(); 314*890232f2SAndroid Build Coastguard Worker 315*890232f2SAndroid Build Coastguard Worker // Ensure we don't grow beyond what fits in an int. 316*890232f2SAndroid Build Coastguard Worker if (old_buf_size & 0xC0000000) { 317*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: cannot grow buffer beyond 2 gigabytes.'); 318*890232f2SAndroid Build Coastguard Worker } 319*890232f2SAndroid Build Coastguard Worker 320*890232f2SAndroid Build Coastguard Worker const new_buf_size = old_buf_size << 1; 321*890232f2SAndroid Build Coastguard Worker const nbb = ByteBuffer.allocate(new_buf_size); 322*890232f2SAndroid Build Coastguard Worker nbb.setPosition(new_buf_size - old_buf_size); 323*890232f2SAndroid Build Coastguard Worker nbb.bytes().set(bb.bytes(), new_buf_size - old_buf_size); 324*890232f2SAndroid Build Coastguard Worker return nbb; 325*890232f2SAndroid Build Coastguard Worker } 326*890232f2SAndroid Build Coastguard Worker 327*890232f2SAndroid Build Coastguard Worker /** 328*890232f2SAndroid Build Coastguard Worker * Adds on offset, relative to where it will be written. 329*890232f2SAndroid Build Coastguard Worker * 330*890232f2SAndroid Build Coastguard Worker * @param offset The offset to add. 331*890232f2SAndroid Build Coastguard Worker */ 332*890232f2SAndroid Build Coastguard Worker addOffset(offset: Offset): void { 333*890232f2SAndroid Build Coastguard Worker this.prep(SIZEOF_INT, 0); // Ensure alignment is already done. 334*890232f2SAndroid Build Coastguard Worker this.writeInt32(this.offset() - offset + SIZEOF_INT); 335*890232f2SAndroid Build Coastguard Worker } 336*890232f2SAndroid Build Coastguard Worker 337*890232f2SAndroid Build Coastguard Worker /** 338*890232f2SAndroid Build Coastguard Worker * Start encoding a new object in the buffer. Users will not usually need to 339*890232f2SAndroid Build Coastguard Worker * call this directly. The FlatBuffers compiler will generate helper methods 340*890232f2SAndroid Build Coastguard Worker * that call this method internally. 341*890232f2SAndroid Build Coastguard Worker */ 342*890232f2SAndroid Build Coastguard Worker startObject(numfields: number): void { 343*890232f2SAndroid Build Coastguard Worker this.notNested(); 344*890232f2SAndroid Build Coastguard Worker if (this.vtable == null) { 345*890232f2SAndroid Build Coastguard Worker this.vtable = []; 346*890232f2SAndroid Build Coastguard Worker } 347*890232f2SAndroid Build Coastguard Worker this.vtable_in_use = numfields; 348*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < numfields; i++) { 349*890232f2SAndroid Build Coastguard Worker this.vtable[i] = 0; // This will push additional elements as needed 350*890232f2SAndroid Build Coastguard Worker } 351*890232f2SAndroid Build Coastguard Worker this.isNested = true; 352*890232f2SAndroid Build Coastguard Worker this.object_start = this.offset(); 353*890232f2SAndroid Build Coastguard Worker } 354*890232f2SAndroid Build Coastguard Worker 355*890232f2SAndroid Build Coastguard Worker /** 356*890232f2SAndroid Build Coastguard Worker * Finish off writing the object that is under construction. 357*890232f2SAndroid Build Coastguard Worker * 358*890232f2SAndroid Build Coastguard Worker * @returns The offset to the object inside `dataBuffer` 359*890232f2SAndroid Build Coastguard Worker */ 360*890232f2SAndroid Build Coastguard Worker endObject(): Offset { 361*890232f2SAndroid Build Coastguard Worker if (this.vtable == null || !this.isNested) { 362*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: endObject called without startObject'); 363*890232f2SAndroid Build Coastguard Worker } 364*890232f2SAndroid Build Coastguard Worker 365*890232f2SAndroid Build Coastguard Worker this.addInt32(0); 366*890232f2SAndroid Build Coastguard Worker const vtableloc = this.offset(); 367*890232f2SAndroid Build Coastguard Worker 368*890232f2SAndroid Build Coastguard Worker // Trim trailing zeroes. 369*890232f2SAndroid Build Coastguard Worker let i = this.vtable_in_use - 1; 370*890232f2SAndroid Build Coastguard Worker // eslint-disable-next-line no-empty 371*890232f2SAndroid Build Coastguard Worker for (; i >= 0 && this.vtable[i] == 0; i--) {} 372*890232f2SAndroid Build Coastguard Worker const trimmed_size = i + 1; 373*890232f2SAndroid Build Coastguard Worker 374*890232f2SAndroid Build Coastguard Worker // Write out the current vtable. 375*890232f2SAndroid Build Coastguard Worker for (; i >= 0; i--) { 376*890232f2SAndroid Build Coastguard Worker // Offset relative to the start of the table. 377*890232f2SAndroid Build Coastguard Worker this.addInt16(this.vtable[i] != 0 ? vtableloc - this.vtable[i] : 0); 378*890232f2SAndroid Build Coastguard Worker } 379*890232f2SAndroid Build Coastguard Worker 380*890232f2SAndroid Build Coastguard Worker const standard_fields = 2; // The fields below: 381*890232f2SAndroid Build Coastguard Worker this.addInt16(vtableloc - this.object_start); 382*890232f2SAndroid Build Coastguard Worker const len = (trimmed_size + standard_fields) * SIZEOF_SHORT; 383*890232f2SAndroid Build Coastguard Worker this.addInt16(len); 384*890232f2SAndroid Build Coastguard Worker 385*890232f2SAndroid Build Coastguard Worker // Search for an existing vtable that matches the current one. 386*890232f2SAndroid Build Coastguard Worker let existing_vtable = 0; 387*890232f2SAndroid Build Coastguard Worker const vt1 = this.space; 388*890232f2SAndroid Build Coastguard Worker outer_loop: 389*890232f2SAndroid Build Coastguard Worker for (i = 0; i < this.vtables.length; i++) { 390*890232f2SAndroid Build Coastguard Worker const vt2 = this.bb.capacity() - this.vtables[i]; 391*890232f2SAndroid Build Coastguard Worker if (len == this.bb.readInt16(vt2)) { 392*890232f2SAndroid Build Coastguard Worker for (let j = SIZEOF_SHORT; j < len; j += SIZEOF_SHORT) { 393*890232f2SAndroid Build Coastguard Worker if (this.bb.readInt16(vt1 + j) != this.bb.readInt16(vt2 + j)) { 394*890232f2SAndroid Build Coastguard Worker continue outer_loop; 395*890232f2SAndroid Build Coastguard Worker } 396*890232f2SAndroid Build Coastguard Worker } 397*890232f2SAndroid Build Coastguard Worker existing_vtable = this.vtables[i]; 398*890232f2SAndroid Build Coastguard Worker break; 399*890232f2SAndroid Build Coastguard Worker } 400*890232f2SAndroid Build Coastguard Worker } 401*890232f2SAndroid Build Coastguard Worker 402*890232f2SAndroid Build Coastguard Worker if (existing_vtable) { 403*890232f2SAndroid Build Coastguard Worker // Found a match: 404*890232f2SAndroid Build Coastguard Worker // Remove the current vtable. 405*890232f2SAndroid Build Coastguard Worker this.space = this.bb.capacity() - vtableloc; 406*890232f2SAndroid Build Coastguard Worker 407*890232f2SAndroid Build Coastguard Worker // Point table to existing vtable. 408*890232f2SAndroid Build Coastguard Worker this.bb.writeInt32(this.space, existing_vtable - vtableloc); 409*890232f2SAndroid Build Coastguard Worker } else { 410*890232f2SAndroid Build Coastguard Worker // No match: 411*890232f2SAndroid Build Coastguard Worker // Add the location of the current vtable to the list of vtables. 412*890232f2SAndroid Build Coastguard Worker this.vtables.push(this.offset()); 413*890232f2SAndroid Build Coastguard Worker 414*890232f2SAndroid Build Coastguard Worker // Point table to current vtable. 415*890232f2SAndroid Build Coastguard Worker this.bb.writeInt32(this.bb.capacity() - vtableloc, this.offset() - vtableloc); 416*890232f2SAndroid Build Coastguard Worker } 417*890232f2SAndroid Build Coastguard Worker 418*890232f2SAndroid Build Coastguard Worker this.isNested = false; 419*890232f2SAndroid Build Coastguard Worker return vtableloc as Offset; 420*890232f2SAndroid Build Coastguard Worker } 421*890232f2SAndroid Build Coastguard Worker 422*890232f2SAndroid Build Coastguard Worker /** 423*890232f2SAndroid Build Coastguard Worker * Finalize a buffer, poiting to the given `root_table`. 424*890232f2SAndroid Build Coastguard Worker */ 425*890232f2SAndroid Build Coastguard Worker finish(root_table: Offset, opt_file_identifier?: string, opt_size_prefix?: boolean): void { 426*890232f2SAndroid Build Coastguard Worker const size_prefix = opt_size_prefix ? SIZE_PREFIX_LENGTH : 0; 427*890232f2SAndroid Build Coastguard Worker if (opt_file_identifier) { 428*890232f2SAndroid Build Coastguard Worker const file_identifier = opt_file_identifier; 429*890232f2SAndroid Build Coastguard Worker this.prep(this.minalign, SIZEOF_INT + 430*890232f2SAndroid Build Coastguard Worker FILE_IDENTIFIER_LENGTH + size_prefix); 431*890232f2SAndroid Build Coastguard Worker if (file_identifier.length != FILE_IDENTIFIER_LENGTH) { 432*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: file identifier must be length ' + 433*890232f2SAndroid Build Coastguard Worker FILE_IDENTIFIER_LENGTH); 434*890232f2SAndroid Build Coastguard Worker } 435*890232f2SAndroid Build Coastguard Worker for (let i = FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) { 436*890232f2SAndroid Build Coastguard Worker this.writeInt8(file_identifier.charCodeAt(i)); 437*890232f2SAndroid Build Coastguard Worker } 438*890232f2SAndroid Build Coastguard Worker } 439*890232f2SAndroid Build Coastguard Worker this.prep(this.minalign, SIZEOF_INT + size_prefix); 440*890232f2SAndroid Build Coastguard Worker this.addOffset(root_table); 441*890232f2SAndroid Build Coastguard Worker if (size_prefix) { 442*890232f2SAndroid Build Coastguard Worker this.addInt32(this.bb.capacity() - this.space); 443*890232f2SAndroid Build Coastguard Worker } 444*890232f2SAndroid Build Coastguard Worker this.bb.setPosition(this.space); 445*890232f2SAndroid Build Coastguard Worker } 446*890232f2SAndroid Build Coastguard Worker 447*890232f2SAndroid Build Coastguard Worker /** 448*890232f2SAndroid Build Coastguard Worker * Finalize a size prefixed buffer, pointing to the given `root_table`. 449*890232f2SAndroid Build Coastguard Worker */ 450*890232f2SAndroid Build Coastguard Worker finishSizePrefixed(this: Builder, root_table: Offset, opt_file_identifier?: string): void { 451*890232f2SAndroid Build Coastguard Worker this.finish(root_table, opt_file_identifier, true); 452*890232f2SAndroid Build Coastguard Worker } 453*890232f2SAndroid Build Coastguard Worker 454*890232f2SAndroid Build Coastguard Worker /** 455*890232f2SAndroid Build Coastguard Worker * This checks a required field has been set in a given table that has 456*890232f2SAndroid Build Coastguard Worker * just been constructed. 457*890232f2SAndroid Build Coastguard Worker */ 458*890232f2SAndroid Build Coastguard Worker requiredField(table: Offset, field: number): void { 459*890232f2SAndroid Build Coastguard Worker const table_start = this.bb.capacity() - table; 460*890232f2SAndroid Build Coastguard Worker const vtable_start = table_start - this.bb.readInt32(table_start); 461*890232f2SAndroid Build Coastguard Worker const ok = this.bb.readInt16(vtable_start + field) != 0; 462*890232f2SAndroid Build Coastguard Worker 463*890232f2SAndroid Build Coastguard Worker // If this fails, the caller will show what field needs to be set. 464*890232f2SAndroid Build Coastguard Worker if (!ok) { 465*890232f2SAndroid Build Coastguard Worker throw new Error('FlatBuffers: field ' + field + ' must be set'); 466*890232f2SAndroid Build Coastguard Worker } 467*890232f2SAndroid Build Coastguard Worker } 468*890232f2SAndroid Build Coastguard Worker 469*890232f2SAndroid Build Coastguard Worker /** 470*890232f2SAndroid Build Coastguard Worker * Start a new array/vector of objects. Users usually will not call 471*890232f2SAndroid Build Coastguard Worker * this directly. The FlatBuffers compiler will create a start/end 472*890232f2SAndroid Build Coastguard Worker * method for vector types in generated code. 473*890232f2SAndroid Build Coastguard Worker * 474*890232f2SAndroid Build Coastguard Worker * @param elem_size The size of each element in the array 475*890232f2SAndroid Build Coastguard Worker * @param num_elems The number of elements in the array 476*890232f2SAndroid Build Coastguard Worker * @param alignment The alignment of the array 477*890232f2SAndroid Build Coastguard Worker */ 478*890232f2SAndroid Build Coastguard Worker startVector(elem_size: number, num_elems: number, alignment: number): void { 479*890232f2SAndroid Build Coastguard Worker this.notNested(); 480*890232f2SAndroid Build Coastguard Worker this.vector_num_elems = num_elems; 481*890232f2SAndroid Build Coastguard Worker this.prep(SIZEOF_INT, elem_size * num_elems); 482*890232f2SAndroid Build Coastguard Worker this.prep(alignment, elem_size * num_elems); // Just in case alignment > int. 483*890232f2SAndroid Build Coastguard Worker } 484*890232f2SAndroid Build Coastguard Worker 485*890232f2SAndroid Build Coastguard Worker /** 486*890232f2SAndroid Build Coastguard Worker * Finish off the creation of an array and all its elements. The array must be 487*890232f2SAndroid Build Coastguard Worker * created with `startVector`. 488*890232f2SAndroid Build Coastguard Worker * 489*890232f2SAndroid Build Coastguard Worker * @returns The offset at which the newly created array 490*890232f2SAndroid Build Coastguard Worker * starts. 491*890232f2SAndroid Build Coastguard Worker */ 492*890232f2SAndroid Build Coastguard Worker endVector(): Offset { 493*890232f2SAndroid Build Coastguard Worker this.writeInt32(this.vector_num_elems); 494*890232f2SAndroid Build Coastguard Worker return this.offset(); 495*890232f2SAndroid Build Coastguard Worker } 496*890232f2SAndroid Build Coastguard Worker 497*890232f2SAndroid Build Coastguard Worker /** 498*890232f2SAndroid Build Coastguard Worker * Encode the string `s` in the buffer using UTF-8. If the string passed has 499*890232f2SAndroid Build Coastguard Worker * already been seen, we return the offset of the already written string 500*890232f2SAndroid Build Coastguard Worker * 501*890232f2SAndroid Build Coastguard Worker * @param s The string to encode 502*890232f2SAndroid Build Coastguard Worker * @return The offset in the buffer where the encoded string starts 503*890232f2SAndroid Build Coastguard Worker */ 504*890232f2SAndroid Build Coastguard Worker createSharedString(s: string | Uint8Array): Offset { 505*890232f2SAndroid Build Coastguard Worker if (!s) { return 0 } 506*890232f2SAndroid Build Coastguard Worker 507*890232f2SAndroid Build Coastguard Worker if (!this.string_maps) { 508*890232f2SAndroid Build Coastguard Worker this.string_maps = new Map(); 509*890232f2SAndroid Build Coastguard Worker } 510*890232f2SAndroid Build Coastguard Worker 511*890232f2SAndroid Build Coastguard Worker if (this.string_maps.has(s)) { 512*890232f2SAndroid Build Coastguard Worker return this.string_maps.get(s) as Offset 513*890232f2SAndroid Build Coastguard Worker } 514*890232f2SAndroid Build Coastguard Worker const offset = this.createString(s) 515*890232f2SAndroid Build Coastguard Worker this.string_maps.set(s, offset) 516*890232f2SAndroid Build Coastguard Worker return offset 517*890232f2SAndroid Build Coastguard Worker } 518*890232f2SAndroid Build Coastguard Worker 519*890232f2SAndroid Build Coastguard Worker /** 520*890232f2SAndroid Build Coastguard Worker * Encode the string `s` in the buffer using UTF-8. If a Uint8Array is passed 521*890232f2SAndroid Build Coastguard Worker * instead of a string, it is assumed to contain valid UTF-8 encoded data. 522*890232f2SAndroid Build Coastguard Worker * 523*890232f2SAndroid Build Coastguard Worker * @param s The string to encode 524*890232f2SAndroid Build Coastguard Worker * @return The offset in the buffer where the encoded string starts 525*890232f2SAndroid Build Coastguard Worker */ 526*890232f2SAndroid Build Coastguard Worker createString(s: string | Uint8Array | null | undefined): Offset { 527*890232f2SAndroid Build Coastguard Worker if (s === null || s === undefined) { 528*890232f2SAndroid Build Coastguard Worker return 0; 529*890232f2SAndroid Build Coastguard Worker } 530*890232f2SAndroid Build Coastguard Worker 531*890232f2SAndroid Build Coastguard Worker let utf8: string | Uint8Array | number[]; 532*890232f2SAndroid Build Coastguard Worker if (s instanceof Uint8Array) { 533*890232f2SAndroid Build Coastguard Worker utf8 = s; 534*890232f2SAndroid Build Coastguard Worker } else { 535*890232f2SAndroid Build Coastguard Worker utf8 = this.text_encoder.encode(s); 536*890232f2SAndroid Build Coastguard Worker } 537*890232f2SAndroid Build Coastguard Worker 538*890232f2SAndroid Build Coastguard Worker this.addInt8(0); 539*890232f2SAndroid Build Coastguard Worker this.startVector(1, utf8.length, 1); 540*890232f2SAndroid Build Coastguard Worker this.bb.setPosition(this.space -= utf8.length); 541*890232f2SAndroid Build Coastguard Worker for (let i = 0, offset = this.space, bytes = this.bb.bytes(); i < utf8.length; i++) { 542*890232f2SAndroid Build Coastguard Worker bytes[offset++] = utf8[i]; 543*890232f2SAndroid Build Coastguard Worker } 544*890232f2SAndroid Build Coastguard Worker return this.endVector(); 545*890232f2SAndroid Build Coastguard Worker } 546*890232f2SAndroid Build Coastguard Worker 547*890232f2SAndroid Build Coastguard Worker /** 548*890232f2SAndroid Build Coastguard Worker * A helper function to pack an object 549*890232f2SAndroid Build Coastguard Worker * 550*890232f2SAndroid Build Coastguard Worker * @returns offset of obj 551*890232f2SAndroid Build Coastguard Worker */ 552*890232f2SAndroid Build Coastguard Worker createObjectOffset(obj: string | any): Offset { 553*890232f2SAndroid Build Coastguard Worker if(obj === null) { 554*890232f2SAndroid Build Coastguard Worker return 0 555*890232f2SAndroid Build Coastguard Worker } 556*890232f2SAndroid Build Coastguard Worker 557*890232f2SAndroid Build Coastguard Worker if(typeof obj === 'string') { 558*890232f2SAndroid Build Coastguard Worker return this.createString(obj); 559*890232f2SAndroid Build Coastguard Worker } else { 560*890232f2SAndroid Build Coastguard Worker return obj.pack(this); 561*890232f2SAndroid Build Coastguard Worker } 562*890232f2SAndroid Build Coastguard Worker } 563*890232f2SAndroid Build Coastguard Worker 564*890232f2SAndroid Build Coastguard Worker /** 565*890232f2SAndroid Build Coastguard Worker * A helper function to pack a list of object 566*890232f2SAndroid Build Coastguard Worker * 567*890232f2SAndroid Build Coastguard Worker * @returns list of offsets of each non null object 568*890232f2SAndroid Build Coastguard Worker */ 569*890232f2SAndroid Build Coastguard Worker createObjectOffsetList(list: string[] | any[]): Offset[] { 570*890232f2SAndroid Build Coastguard Worker const ret: number[] = []; 571*890232f2SAndroid Build Coastguard Worker 572*890232f2SAndroid Build Coastguard Worker for(let i = 0; i < list.length; ++i) { 573*890232f2SAndroid Build Coastguard Worker const val = list[i]; 574*890232f2SAndroid Build Coastguard Worker 575*890232f2SAndroid Build Coastguard Worker if(val !== null) { 576*890232f2SAndroid Build Coastguard Worker ret.push(this.createObjectOffset(val)); 577*890232f2SAndroid Build Coastguard Worker } else { 578*890232f2SAndroid Build Coastguard Worker throw new Error( 579*890232f2SAndroid Build Coastguard Worker 'FlatBuffers: Argument for createObjectOffsetList cannot contain null.'); 580*890232f2SAndroid Build Coastguard Worker } 581*890232f2SAndroid Build Coastguard Worker } 582*890232f2SAndroid Build Coastguard Worker 583*890232f2SAndroid Build Coastguard Worker return ret; 584*890232f2SAndroid Build Coastguard Worker } 585*890232f2SAndroid Build Coastguard Worker 586*890232f2SAndroid Build Coastguard Worker createStructOffsetList(list: string[] | any[], startFunc: (builder: Builder, length: number) => void): Offset { 587*890232f2SAndroid Build Coastguard Worker startFunc(this, list.length); 588*890232f2SAndroid Build Coastguard Worker this.createObjectOffsetList(list.slice().reverse()); 589*890232f2SAndroid Build Coastguard Worker return this.endVector(); 590*890232f2SAndroid Build Coastguard Worker } 591*890232f2SAndroid Build Coastguard Worker } 592