1*890232f2SAndroid Build Coastguard Workerimport { BitWidth } from './bit-width.js' 2*890232f2SAndroid Build Coastguard Workerimport { paddingSize, iwidth, uwidth, fwidth, toByteWidth, fromByteWidth } from './bit-width-util.js' 3*890232f2SAndroid Build Coastguard Workerimport { toUTF8Array } from './flexbuffers-util.js' 4*890232f2SAndroid Build Coastguard Workerimport { ValueType } from './value-type.js' 5*890232f2SAndroid Build Coastguard Workerimport { isNumber, isTypedVectorElement, toTypedVector } from './value-type-util.js' 6*890232f2SAndroid Build Coastguard Workerimport { StackValue } from './stack-value.js' 7*890232f2SAndroid Build Coastguard Worker 8*890232f2SAndroid Build Coastguard Workerinterface StackPointer { 9*890232f2SAndroid Build Coastguard Worker stackPosition: number, 10*890232f2SAndroid Build Coastguard Worker isVector: boolean 11*890232f2SAndroid Build Coastguard Worker presorted?: boolean 12*890232f2SAndroid Build Coastguard Worker} 13*890232f2SAndroid Build Coastguard Worker 14*890232f2SAndroid Build Coastguard Workerexport class Builder { 15*890232f2SAndroid Build Coastguard Worker buffer: ArrayBuffer 16*890232f2SAndroid Build Coastguard Worker view: DataView 17*890232f2SAndroid Build Coastguard Worker 18*890232f2SAndroid Build Coastguard Worker readonly stack: Array<StackValue> = []; 19*890232f2SAndroid Build Coastguard Worker readonly stackPointers: Array<StackPointer> = []; 20*890232f2SAndroid Build Coastguard Worker offset = 0; 21*890232f2SAndroid Build Coastguard Worker finished = false; 22*890232f2SAndroid Build Coastguard Worker readonly stringLookup: Record<string, StackValue> = {}; 23*890232f2SAndroid Build Coastguard Worker readonly keyLookup: Record<string, StackValue> = {}; 24*890232f2SAndroid Build Coastguard Worker readonly keyVectorLookup: Record<string, StackValue> = {}; 25*890232f2SAndroid Build Coastguard Worker readonly indirectIntLookup: Record<number, StackValue> = {}; 26*890232f2SAndroid Build Coastguard Worker readonly indirectUIntLookup: Record<number, StackValue> = {}; 27*890232f2SAndroid Build Coastguard Worker readonly indirectFloatLookup: Record<number, StackValue> = {}; 28*890232f2SAndroid Build Coastguard Worker 29*890232f2SAndroid Build Coastguard Worker constructor(size = 2048, private dedupStrings = true, private dedupKeys = true, private dedupKeyVectors = true) { 30*890232f2SAndroid Build Coastguard Worker this.buffer = new ArrayBuffer(size > 0 ? size : 2048); 31*890232f2SAndroid Build Coastguard Worker this.view = new DataView(this.buffer); 32*890232f2SAndroid Build Coastguard Worker } 33*890232f2SAndroid Build Coastguard Worker 34*890232f2SAndroid Build Coastguard Worker private align(width: BitWidth) { 35*890232f2SAndroid Build Coastguard Worker const byteWidth = toByteWidth(width); 36*890232f2SAndroid Build Coastguard Worker this.offset += paddingSize(this.offset, byteWidth); 37*890232f2SAndroid Build Coastguard Worker return byteWidth; 38*890232f2SAndroid Build Coastguard Worker } 39*890232f2SAndroid Build Coastguard Worker 40*890232f2SAndroid Build Coastguard Worker computeOffset(newValueSize: number): number { 41*890232f2SAndroid Build Coastguard Worker const targetOffset = this.offset + newValueSize; 42*890232f2SAndroid Build Coastguard Worker let size = this.buffer.byteLength; 43*890232f2SAndroid Build Coastguard Worker const prevSize = size; 44*890232f2SAndroid Build Coastguard Worker while (size < targetOffset) { 45*890232f2SAndroid Build Coastguard Worker size <<= 1; 46*890232f2SAndroid Build Coastguard Worker } 47*890232f2SAndroid Build Coastguard Worker if (prevSize < size) { 48*890232f2SAndroid Build Coastguard Worker const prevBuffer = this.buffer; 49*890232f2SAndroid Build Coastguard Worker this.buffer = new ArrayBuffer(size); 50*890232f2SAndroid Build Coastguard Worker this.view = new DataView(this.buffer); 51*890232f2SAndroid Build Coastguard Worker new Uint8Array(this.buffer).set(new Uint8Array(prevBuffer), 0); 52*890232f2SAndroid Build Coastguard Worker } 53*890232f2SAndroid Build Coastguard Worker return targetOffset; 54*890232f2SAndroid Build Coastguard Worker } 55*890232f2SAndroid Build Coastguard Worker 56*890232f2SAndroid Build Coastguard Worker pushInt(value: number, width: BitWidth): void { 57*890232f2SAndroid Build Coastguard Worker if (width === BitWidth.WIDTH8) { 58*890232f2SAndroid Build Coastguard Worker this.view.setInt8(this.offset, value); 59*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH16) { 60*890232f2SAndroid Build Coastguard Worker this.view.setInt16(this.offset, value, true); 61*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH32) { 62*890232f2SAndroid Build Coastguard Worker this.view.setInt32(this.offset, value, true); 63*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH64) { 64*890232f2SAndroid Build Coastguard Worker this.view.setBigInt64(this.offset, BigInt(value), true); 65*890232f2SAndroid Build Coastguard Worker } else { 66*890232f2SAndroid Build Coastguard Worker throw `Unexpected width: ${width} for value: ${value}`; 67*890232f2SAndroid Build Coastguard Worker } 68*890232f2SAndroid Build Coastguard Worker } 69*890232f2SAndroid Build Coastguard Worker 70*890232f2SAndroid Build Coastguard Worker pushUInt(value: number, width: BitWidth): void { 71*890232f2SAndroid Build Coastguard Worker if (width === BitWidth.WIDTH8) { 72*890232f2SAndroid Build Coastguard Worker this.view.setUint8(this.offset, value); 73*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH16) { 74*890232f2SAndroid Build Coastguard Worker this.view.setUint16(this.offset, value, true); 75*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH32) { 76*890232f2SAndroid Build Coastguard Worker this.view.setUint32(this.offset, value, true); 77*890232f2SAndroid Build Coastguard Worker } else if (width === BitWidth.WIDTH64) { 78*890232f2SAndroid Build Coastguard Worker this.view.setBigUint64(this.offset, BigInt(value), true); 79*890232f2SAndroid Build Coastguard Worker } else { 80*890232f2SAndroid Build Coastguard Worker throw `Unexpected width: ${width} for value: ${value}`; 81*890232f2SAndroid Build Coastguard Worker } 82*890232f2SAndroid Build Coastguard Worker } 83*890232f2SAndroid Build Coastguard Worker 84*890232f2SAndroid Build Coastguard Worker private writeInt(value: number, byteWidth: number) { 85*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 86*890232f2SAndroid Build Coastguard Worker this.pushInt(value, fromByteWidth(byteWidth)); 87*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 88*890232f2SAndroid Build Coastguard Worker } 89*890232f2SAndroid Build Coastguard Worker 90*890232f2SAndroid Build Coastguard Worker private writeUInt(value: number, byteWidth: number) { 91*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 92*890232f2SAndroid Build Coastguard Worker this.pushUInt(value, fromByteWidth(byteWidth)); 93*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 94*890232f2SAndroid Build Coastguard Worker } 95*890232f2SAndroid Build Coastguard Worker 96*890232f2SAndroid Build Coastguard Worker private writeBlob(arrayBuffer: ArrayBuffer) { 97*890232f2SAndroid Build Coastguard Worker const length = arrayBuffer.byteLength; 98*890232f2SAndroid Build Coastguard Worker const bitWidth = uwidth(length); 99*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(bitWidth); 100*890232f2SAndroid Build Coastguard Worker this.writeUInt(length, byteWidth); 101*890232f2SAndroid Build Coastguard Worker const blobOffset = this.offset; 102*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(length); 103*890232f2SAndroid Build Coastguard Worker new Uint8Array(this.buffer).set(new Uint8Array(arrayBuffer), blobOffset); 104*890232f2SAndroid Build Coastguard Worker this.stack.push(this.offsetStackValue(blobOffset, ValueType.BLOB, bitWidth)); 105*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 106*890232f2SAndroid Build Coastguard Worker } 107*890232f2SAndroid Build Coastguard Worker 108*890232f2SAndroid Build Coastguard Worker private writeString(str: string): void { 109*890232f2SAndroid Build Coastguard Worker if (this.dedupStrings && Object.prototype.hasOwnProperty.call(this.stringLookup, str)) { 110*890232f2SAndroid Build Coastguard Worker this.stack.push(this.stringLookup[str]); 111*890232f2SAndroid Build Coastguard Worker return; 112*890232f2SAndroid Build Coastguard Worker } 113*890232f2SAndroid Build Coastguard Worker const utf8 = toUTF8Array(str); 114*890232f2SAndroid Build Coastguard Worker const length = utf8.length; 115*890232f2SAndroid Build Coastguard Worker const bitWidth = uwidth(length); 116*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(bitWidth); 117*890232f2SAndroid Build Coastguard Worker this.writeUInt(length, byteWidth); 118*890232f2SAndroid Build Coastguard Worker const stringOffset = this.offset; 119*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(length + 1); 120*890232f2SAndroid Build Coastguard Worker new Uint8Array(this.buffer).set(utf8, stringOffset); 121*890232f2SAndroid Build Coastguard Worker const stackValue = this.offsetStackValue(stringOffset, ValueType.STRING, bitWidth); 122*890232f2SAndroid Build Coastguard Worker this.stack.push(stackValue); 123*890232f2SAndroid Build Coastguard Worker if (this.dedupStrings) { 124*890232f2SAndroid Build Coastguard Worker this.stringLookup[str] = stackValue; 125*890232f2SAndroid Build Coastguard Worker } 126*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 127*890232f2SAndroid Build Coastguard Worker } 128*890232f2SAndroid Build Coastguard Worker 129*890232f2SAndroid Build Coastguard Worker private writeKey(str: string): void { 130*890232f2SAndroid Build Coastguard Worker if (this.dedupKeys && Object.prototype.hasOwnProperty.call(this.keyLookup, str)) { 131*890232f2SAndroid Build Coastguard Worker this.stack.push(this.keyLookup[str]); 132*890232f2SAndroid Build Coastguard Worker return; 133*890232f2SAndroid Build Coastguard Worker } 134*890232f2SAndroid Build Coastguard Worker const utf8 = toUTF8Array(str); 135*890232f2SAndroid Build Coastguard Worker const length = utf8.length; 136*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(length + 1); 137*890232f2SAndroid Build Coastguard Worker new Uint8Array(this.buffer).set(utf8, this.offset); 138*890232f2SAndroid Build Coastguard Worker const stackValue = this.offsetStackValue(this.offset, ValueType.KEY, BitWidth.WIDTH8); 139*890232f2SAndroid Build Coastguard Worker this.stack.push(stackValue); 140*890232f2SAndroid Build Coastguard Worker if (this.dedupKeys) { 141*890232f2SAndroid Build Coastguard Worker this.keyLookup[str] = stackValue; 142*890232f2SAndroid Build Coastguard Worker } 143*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 144*890232f2SAndroid Build Coastguard Worker } 145*890232f2SAndroid Build Coastguard Worker 146*890232f2SAndroid Build Coastguard Worker private writeStackValue(value: StackValue, byteWidth: number): void { 147*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 148*890232f2SAndroid Build Coastguard Worker if (value.isOffset()) { 149*890232f2SAndroid Build Coastguard Worker const relativeOffset = this.offset - value.offset; 150*890232f2SAndroid Build Coastguard Worker if (byteWidth === 8 || BigInt(relativeOffset) < (BigInt(1) << BigInt(byteWidth * 8))) { 151*890232f2SAndroid Build Coastguard Worker this.writeUInt(relativeOffset, byteWidth); 152*890232f2SAndroid Build Coastguard Worker } else { 153*890232f2SAndroid Build Coastguard Worker throw `Unexpected size ${byteWidth}. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new` 154*890232f2SAndroid Build Coastguard Worker } 155*890232f2SAndroid Build Coastguard Worker } else { 156*890232f2SAndroid Build Coastguard Worker value.writeToBuffer(byteWidth); 157*890232f2SAndroid Build Coastguard Worker } 158*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 159*890232f2SAndroid Build Coastguard Worker } 160*890232f2SAndroid Build Coastguard Worker 161*890232f2SAndroid Build Coastguard Worker private integrityCheckOnValueAddition() { 162*890232f2SAndroid Build Coastguard Worker if (this.finished) { 163*890232f2SAndroid Build Coastguard Worker throw "Adding values after finish is prohibited"; 164*890232f2SAndroid Build Coastguard Worker } 165*890232f2SAndroid Build Coastguard Worker if (this.stackPointers.length !== 0 && this.stackPointers[this.stackPointers.length - 1].isVector === false) { 166*890232f2SAndroid Build Coastguard Worker if (this.stack[this.stack.length - 1].type !== ValueType.KEY) { 167*890232f2SAndroid Build Coastguard Worker throw "Adding value to a map before adding a key is prohibited"; 168*890232f2SAndroid Build Coastguard Worker } 169*890232f2SAndroid Build Coastguard Worker } 170*890232f2SAndroid Build Coastguard Worker } 171*890232f2SAndroid Build Coastguard Worker 172*890232f2SAndroid Build Coastguard Worker private integrityCheckOnKeyAddition() { 173*890232f2SAndroid Build Coastguard Worker if (this.finished) { 174*890232f2SAndroid Build Coastguard Worker throw "Adding values after finish is prohibited"; 175*890232f2SAndroid Build Coastguard Worker } 176*890232f2SAndroid Build Coastguard Worker if (this.stackPointers.length === 0 || this.stackPointers[this.stackPointers.length - 1].isVector) { 177*890232f2SAndroid Build Coastguard Worker throw "Adding key before starting a map is prohibited"; 178*890232f2SAndroid Build Coastguard Worker } 179*890232f2SAndroid Build Coastguard Worker } 180*890232f2SAndroid Build Coastguard Worker 181*890232f2SAndroid Build Coastguard Worker startVector(): void { 182*890232f2SAndroid Build Coastguard Worker this.stackPointers.push({ stackPosition: this.stack.length, isVector: true }); 183*890232f2SAndroid Build Coastguard Worker } 184*890232f2SAndroid Build Coastguard Worker 185*890232f2SAndroid Build Coastguard Worker startMap(presorted = false): void { 186*890232f2SAndroid Build Coastguard Worker this.stackPointers.push({ stackPosition: this.stack.length, isVector: false, presorted: presorted }); 187*890232f2SAndroid Build Coastguard Worker } 188*890232f2SAndroid Build Coastguard Worker 189*890232f2SAndroid Build Coastguard Worker private endVector(stackPointer: StackPointer) { 190*890232f2SAndroid Build Coastguard Worker const vecLength = this.stack.length - stackPointer.stackPosition; 191*890232f2SAndroid Build Coastguard Worker const vec = this.createVector(stackPointer.stackPosition, vecLength, 1); 192*890232f2SAndroid Build Coastguard Worker this.stack.splice(stackPointer.stackPosition, vecLength); 193*890232f2SAndroid Build Coastguard Worker this.stack.push(vec); 194*890232f2SAndroid Build Coastguard Worker } 195*890232f2SAndroid Build Coastguard Worker 196*890232f2SAndroid Build Coastguard Worker private endMap(stackPointer: StackPointer) { 197*890232f2SAndroid Build Coastguard Worker if (!stackPointer.presorted) { 198*890232f2SAndroid Build Coastguard Worker this.sort(stackPointer); 199*890232f2SAndroid Build Coastguard Worker } 200*890232f2SAndroid Build Coastguard Worker let keyVectorHash = ""; 201*890232f2SAndroid Build Coastguard Worker for (let i = stackPointer.stackPosition; i < this.stack.length; i += 2) { 202*890232f2SAndroid Build Coastguard Worker keyVectorHash += `,${this.stack[i].offset}`; 203*890232f2SAndroid Build Coastguard Worker } 204*890232f2SAndroid Build Coastguard Worker const vecLength = (this.stack.length - stackPointer.stackPosition) >> 1; 205*890232f2SAndroid Build Coastguard Worker 206*890232f2SAndroid Build Coastguard Worker if (this.dedupKeyVectors && !Object.prototype.hasOwnProperty.call(this.keyVectorLookup, keyVectorHash)) { 207*890232f2SAndroid Build Coastguard Worker this.keyVectorLookup[keyVectorHash] = this.createVector(stackPointer.stackPosition, vecLength, 2); 208*890232f2SAndroid Build Coastguard Worker } 209*890232f2SAndroid Build Coastguard Worker const keysStackValue = this.dedupKeyVectors ? this.keyVectorLookup[keyVectorHash] : this.createVector(stackPointer.stackPosition, vecLength, 2); 210*890232f2SAndroid Build Coastguard Worker const valuesStackValue = this.createVector(stackPointer.stackPosition + 1, vecLength, 2, keysStackValue); 211*890232f2SAndroid Build Coastguard Worker this.stack.splice(stackPointer.stackPosition, vecLength << 1); 212*890232f2SAndroid Build Coastguard Worker this.stack.push(valuesStackValue); 213*890232f2SAndroid Build Coastguard Worker } 214*890232f2SAndroid Build Coastguard Worker 215*890232f2SAndroid Build Coastguard Worker private sort(stackPointer: StackPointer) { 216*890232f2SAndroid Build Coastguard Worker const view = this.view 217*890232f2SAndroid Build Coastguard Worker const stack = this.stack 218*890232f2SAndroid Build Coastguard Worker 219*890232f2SAndroid Build Coastguard Worker function shouldFlip(v1: StackValue, v2: StackValue) { 220*890232f2SAndroid Build Coastguard Worker if (v1.type !== ValueType.KEY || v2.type !== ValueType.KEY) { 221*890232f2SAndroid Build Coastguard Worker throw `Stack values are not keys ${v1} | ${v2}. Check if you combined [addKey] with add... method calls properly.` 222*890232f2SAndroid Build Coastguard Worker } 223*890232f2SAndroid Build Coastguard Worker let c1, c2; 224*890232f2SAndroid Build Coastguard Worker let index = 0; 225*890232f2SAndroid Build Coastguard Worker do { 226*890232f2SAndroid Build Coastguard Worker c1 = view.getUint8(v1.offset + index); 227*890232f2SAndroid Build Coastguard Worker c2 = view.getUint8(v2.offset + index); 228*890232f2SAndroid Build Coastguard Worker if (c2 < c1) return true; 229*890232f2SAndroid Build Coastguard Worker if (c1 < c2) return false; 230*890232f2SAndroid Build Coastguard Worker index += 1; 231*890232f2SAndroid Build Coastguard Worker } while (c1 !== 0 && c2 !== 0); 232*890232f2SAndroid Build Coastguard Worker return false; 233*890232f2SAndroid Build Coastguard Worker } 234*890232f2SAndroid Build Coastguard Worker 235*890232f2SAndroid Build Coastguard Worker function swap(stack: Array<StackValue>, flipIndex: number, i: number) { 236*890232f2SAndroid Build Coastguard Worker if (flipIndex === i) return; 237*890232f2SAndroid Build Coastguard Worker const k = stack[flipIndex]; 238*890232f2SAndroid Build Coastguard Worker const v = stack[flipIndex + 1]; 239*890232f2SAndroid Build Coastguard Worker stack[flipIndex] = stack[i]; 240*890232f2SAndroid Build Coastguard Worker stack[flipIndex + 1] = stack[i + 1]; 241*890232f2SAndroid Build Coastguard Worker stack[i] = k; 242*890232f2SAndroid Build Coastguard Worker stack[i + 1] = v; 243*890232f2SAndroid Build Coastguard Worker } 244*890232f2SAndroid Build Coastguard Worker 245*890232f2SAndroid Build Coastguard Worker function selectionSort() { 246*890232f2SAndroid Build Coastguard Worker for (let i = stackPointer.stackPosition; i < stack.length; i += 2) { 247*890232f2SAndroid Build Coastguard Worker let flipIndex = i; 248*890232f2SAndroid Build Coastguard Worker for (let j = i + 2; j < stack.length; j += 2) { 249*890232f2SAndroid Build Coastguard Worker if (shouldFlip(stack[flipIndex], stack[j])) { 250*890232f2SAndroid Build Coastguard Worker flipIndex = j; 251*890232f2SAndroid Build Coastguard Worker } 252*890232f2SAndroid Build Coastguard Worker } 253*890232f2SAndroid Build Coastguard Worker if (flipIndex !== i) { 254*890232f2SAndroid Build Coastguard Worker swap(stack, flipIndex, i); 255*890232f2SAndroid Build Coastguard Worker } 256*890232f2SAndroid Build Coastguard Worker } 257*890232f2SAndroid Build Coastguard Worker } 258*890232f2SAndroid Build Coastguard Worker 259*890232f2SAndroid Build Coastguard Worker function smaller(v1: StackValue, v2: StackValue) { 260*890232f2SAndroid Build Coastguard Worker if (v1.type !== ValueType.KEY || v2.type !== ValueType.KEY) { 261*890232f2SAndroid Build Coastguard Worker throw `Stack values are not keys ${v1} | ${v2}. Check if you combined [addKey] with add... method calls properly.` 262*890232f2SAndroid Build Coastguard Worker } 263*890232f2SAndroid Build Coastguard Worker if (v1.offset === v2.offset) { 264*890232f2SAndroid Build Coastguard Worker return false; 265*890232f2SAndroid Build Coastguard Worker } 266*890232f2SAndroid Build Coastguard Worker let c1, c2; 267*890232f2SAndroid Build Coastguard Worker let index = 0; 268*890232f2SAndroid Build Coastguard Worker do { 269*890232f2SAndroid Build Coastguard Worker c1 = view.getUint8(v1.offset + index); 270*890232f2SAndroid Build Coastguard Worker c2 = view.getUint8(v2.offset + index); 271*890232f2SAndroid Build Coastguard Worker if (c1 < c2) return true; 272*890232f2SAndroid Build Coastguard Worker if (c2 < c1) return false; 273*890232f2SAndroid Build Coastguard Worker index += 1; 274*890232f2SAndroid Build Coastguard Worker } while (c1 !== 0 && c2 !== 0); 275*890232f2SAndroid Build Coastguard Worker return false; 276*890232f2SAndroid Build Coastguard Worker } 277*890232f2SAndroid Build Coastguard Worker 278*890232f2SAndroid Build Coastguard Worker function quickSort(left: number, right: number) { 279*890232f2SAndroid Build Coastguard Worker 280*890232f2SAndroid Build Coastguard Worker if (left < right) { 281*890232f2SAndroid Build Coastguard Worker const mid = left + (((right - left) >> 2)) * 2; 282*890232f2SAndroid Build Coastguard Worker const pivot = stack[mid]; 283*890232f2SAndroid Build Coastguard Worker let left_new = left; 284*890232f2SAndroid Build Coastguard Worker let right_new = right; 285*890232f2SAndroid Build Coastguard Worker 286*890232f2SAndroid Build Coastguard Worker do { 287*890232f2SAndroid Build Coastguard Worker while (smaller(stack[left_new], pivot)) { 288*890232f2SAndroid Build Coastguard Worker left_new += 2; 289*890232f2SAndroid Build Coastguard Worker } 290*890232f2SAndroid Build Coastguard Worker while (smaller(pivot, stack[right_new])) { 291*890232f2SAndroid Build Coastguard Worker right_new -= 2; 292*890232f2SAndroid Build Coastguard Worker } 293*890232f2SAndroid Build Coastguard Worker if (left_new <= right_new) { 294*890232f2SAndroid Build Coastguard Worker swap(stack, left_new, right_new); 295*890232f2SAndroid Build Coastguard Worker left_new += 2; 296*890232f2SAndroid Build Coastguard Worker right_new -= 2; 297*890232f2SAndroid Build Coastguard Worker } 298*890232f2SAndroid Build Coastguard Worker } while (left_new <= right_new); 299*890232f2SAndroid Build Coastguard Worker 300*890232f2SAndroid Build Coastguard Worker quickSort(left, right_new); 301*890232f2SAndroid Build Coastguard Worker quickSort(left_new, right); 302*890232f2SAndroid Build Coastguard Worker 303*890232f2SAndroid Build Coastguard Worker } 304*890232f2SAndroid Build Coastguard Worker } 305*890232f2SAndroid Build Coastguard Worker 306*890232f2SAndroid Build Coastguard Worker let sorted = true; 307*890232f2SAndroid Build Coastguard Worker for (let i = stackPointer.stackPosition; i < this.stack.length - 2; i += 2) { 308*890232f2SAndroid Build Coastguard Worker if (shouldFlip(this.stack[i], this.stack[i + 2])) { 309*890232f2SAndroid Build Coastguard Worker sorted = false; 310*890232f2SAndroid Build Coastguard Worker break; 311*890232f2SAndroid Build Coastguard Worker } 312*890232f2SAndroid Build Coastguard Worker } 313*890232f2SAndroid Build Coastguard Worker 314*890232f2SAndroid Build Coastguard Worker if (!sorted) { 315*890232f2SAndroid Build Coastguard Worker if (this.stack.length - stackPointer.stackPosition > 40) { 316*890232f2SAndroid Build Coastguard Worker quickSort(stackPointer.stackPosition, this.stack.length - 2); 317*890232f2SAndroid Build Coastguard Worker } else { 318*890232f2SAndroid Build Coastguard Worker selectionSort(); 319*890232f2SAndroid Build Coastguard Worker } 320*890232f2SAndroid Build Coastguard Worker } 321*890232f2SAndroid Build Coastguard Worker } 322*890232f2SAndroid Build Coastguard Worker 323*890232f2SAndroid Build Coastguard Worker end(): void { 324*890232f2SAndroid Build Coastguard Worker if (this.stackPointers.length < 1) return; 325*890232f2SAndroid Build Coastguard Worker const pointer = this.stackPointers.pop() as StackPointer; 326*890232f2SAndroid Build Coastguard Worker if (pointer.isVector) { 327*890232f2SAndroid Build Coastguard Worker this.endVector(pointer); 328*890232f2SAndroid Build Coastguard Worker } else { 329*890232f2SAndroid Build Coastguard Worker this.endMap(pointer); 330*890232f2SAndroid Build Coastguard Worker } 331*890232f2SAndroid Build Coastguard Worker } 332*890232f2SAndroid Build Coastguard Worker 333*890232f2SAndroid Build Coastguard Worker private createVector(start: number, vecLength: number, step: number, keys: StackValue | null = null) { 334*890232f2SAndroid Build Coastguard Worker let bitWidth = uwidth(vecLength); 335*890232f2SAndroid Build Coastguard Worker let prefixElements = 1; 336*890232f2SAndroid Build Coastguard Worker if (keys !== null) { 337*890232f2SAndroid Build Coastguard Worker const elementWidth = keys.elementWidth(this.offset, 0); 338*890232f2SAndroid Build Coastguard Worker if (elementWidth > bitWidth) { 339*890232f2SAndroid Build Coastguard Worker bitWidth = elementWidth; 340*890232f2SAndroid Build Coastguard Worker } 341*890232f2SAndroid Build Coastguard Worker prefixElements += 2; 342*890232f2SAndroid Build Coastguard Worker } 343*890232f2SAndroid Build Coastguard Worker let vectorType = ValueType.KEY; 344*890232f2SAndroid Build Coastguard Worker let typed = keys === null; 345*890232f2SAndroid Build Coastguard Worker for (let i = start; i < this.stack.length; i += step) { 346*890232f2SAndroid Build Coastguard Worker const elementWidth = this.stack[i].elementWidth(this.offset, i + prefixElements); 347*890232f2SAndroid Build Coastguard Worker if (elementWidth > bitWidth) { 348*890232f2SAndroid Build Coastguard Worker bitWidth = elementWidth; 349*890232f2SAndroid Build Coastguard Worker } 350*890232f2SAndroid Build Coastguard Worker if (i === start) { 351*890232f2SAndroid Build Coastguard Worker vectorType = this.stack[i].type; 352*890232f2SAndroid Build Coastguard Worker typed = typed && isTypedVectorElement(vectorType); 353*890232f2SAndroid Build Coastguard Worker } else { 354*890232f2SAndroid Build Coastguard Worker if (vectorType !== this.stack[i].type) { 355*890232f2SAndroid Build Coastguard Worker typed = false; 356*890232f2SAndroid Build Coastguard Worker } 357*890232f2SAndroid Build Coastguard Worker } 358*890232f2SAndroid Build Coastguard Worker } 359*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(bitWidth); 360*890232f2SAndroid Build Coastguard Worker const fix = typed && isNumber(vectorType) && vecLength >= 2 && vecLength <= 4; 361*890232f2SAndroid Build Coastguard Worker if (keys !== null) { 362*890232f2SAndroid Build Coastguard Worker this.writeStackValue(keys, byteWidth); 363*890232f2SAndroid Build Coastguard Worker this.writeUInt(1 << keys.width, byteWidth); 364*890232f2SAndroid Build Coastguard Worker } 365*890232f2SAndroid Build Coastguard Worker if (!fix) { 366*890232f2SAndroid Build Coastguard Worker this.writeUInt(vecLength, byteWidth); 367*890232f2SAndroid Build Coastguard Worker } 368*890232f2SAndroid Build Coastguard Worker const vecOffset = this.offset; 369*890232f2SAndroid Build Coastguard Worker for (let i = start; i < this.stack.length; i += step) { 370*890232f2SAndroid Build Coastguard Worker this.writeStackValue(this.stack[i], byteWidth); 371*890232f2SAndroid Build Coastguard Worker } 372*890232f2SAndroid Build Coastguard Worker if (!typed) { 373*890232f2SAndroid Build Coastguard Worker for (let i = start; i < this.stack.length; i += step) { 374*890232f2SAndroid Build Coastguard Worker this.writeUInt(this.stack[i].storedPackedType(), 1); 375*890232f2SAndroid Build Coastguard Worker } 376*890232f2SAndroid Build Coastguard Worker } 377*890232f2SAndroid Build Coastguard Worker if (keys !== null) { 378*890232f2SAndroid Build Coastguard Worker return this.offsetStackValue(vecOffset, ValueType.MAP, bitWidth); 379*890232f2SAndroid Build Coastguard Worker } 380*890232f2SAndroid Build Coastguard Worker if (typed) { 381*890232f2SAndroid Build Coastguard Worker const vType = toTypedVector(vectorType, fix ? vecLength : 0); 382*890232f2SAndroid Build Coastguard Worker return this.offsetStackValue(vecOffset, vType, bitWidth); 383*890232f2SAndroid Build Coastguard Worker } 384*890232f2SAndroid Build Coastguard Worker return this.offsetStackValue(vecOffset, ValueType.VECTOR, bitWidth); 385*890232f2SAndroid Build Coastguard Worker } 386*890232f2SAndroid Build Coastguard Worker 387*890232f2SAndroid Build Coastguard Worker private nullStackValue() { 388*890232f2SAndroid Build Coastguard Worker return new StackValue(this, ValueType.NULL, BitWidth.WIDTH8); 389*890232f2SAndroid Build Coastguard Worker } 390*890232f2SAndroid Build Coastguard Worker 391*890232f2SAndroid Build Coastguard Worker private boolStackValue(value: boolean) { 392*890232f2SAndroid Build Coastguard Worker return new StackValue(this, ValueType.BOOL, BitWidth.WIDTH8, value); 393*890232f2SAndroid Build Coastguard Worker } 394*890232f2SAndroid Build Coastguard Worker 395*890232f2SAndroid Build Coastguard Worker private intStackValue(value: number | bigint) { 396*890232f2SAndroid Build Coastguard Worker return new StackValue(this, ValueType.INT, iwidth(value), value as number); 397*890232f2SAndroid Build Coastguard Worker } 398*890232f2SAndroid Build Coastguard Worker 399*890232f2SAndroid Build Coastguard Worker private uintStackValue(value: number) { 400*890232f2SAndroid Build Coastguard Worker return new StackValue(this, ValueType.UINT, uwidth(value), value); 401*890232f2SAndroid Build Coastguard Worker } 402*890232f2SAndroid Build Coastguard Worker 403*890232f2SAndroid Build Coastguard Worker private floatStackValue(value: number) { 404*890232f2SAndroid Build Coastguard Worker return new StackValue(this, ValueType.FLOAT, fwidth(value), value); 405*890232f2SAndroid Build Coastguard Worker } 406*890232f2SAndroid Build Coastguard Worker 407*890232f2SAndroid Build Coastguard Worker private offsetStackValue(offset: number, valueType: ValueType, bitWidth: BitWidth): StackValue { 408*890232f2SAndroid Build Coastguard Worker return new StackValue(this, valueType, bitWidth, null, offset); 409*890232f2SAndroid Build Coastguard Worker } 410*890232f2SAndroid Build Coastguard Worker 411*890232f2SAndroid Build Coastguard Worker private finishBuffer() { 412*890232f2SAndroid Build Coastguard Worker if (this.stack.length !== 1) { 413*890232f2SAndroid Build Coastguard Worker throw `Stack has to be exactly 1, but it is ${this.stack.length}. You have to end all started vectors and maps before calling [finish]`; 414*890232f2SAndroid Build Coastguard Worker } 415*890232f2SAndroid Build Coastguard Worker const value = this.stack[0]; 416*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(value.elementWidth(this.offset, 0)); 417*890232f2SAndroid Build Coastguard Worker this.writeStackValue(value, byteWidth); 418*890232f2SAndroid Build Coastguard Worker this.writeUInt(value.storedPackedType(), 1); 419*890232f2SAndroid Build Coastguard Worker this.writeUInt(byteWidth, 1); 420*890232f2SAndroid Build Coastguard Worker this.finished = true; 421*890232f2SAndroid Build Coastguard Worker } 422*890232f2SAndroid Build Coastguard Worker 423*890232f2SAndroid Build Coastguard Worker add(value: undefined | null | boolean | bigint | number | DataView | string | Array<unknown> | Record<string, unknown> | unknown): void { 424*890232f2SAndroid Build Coastguard Worker this.integrityCheckOnValueAddition(); 425*890232f2SAndroid Build Coastguard Worker if (typeof value === 'undefined') { 426*890232f2SAndroid Build Coastguard Worker throw "You need to provide a value"; 427*890232f2SAndroid Build Coastguard Worker } 428*890232f2SAndroid Build Coastguard Worker if (value === null) { 429*890232f2SAndroid Build Coastguard Worker this.stack.push(this.nullStackValue()); 430*890232f2SAndroid Build Coastguard Worker } else if (typeof value === "boolean") { 431*890232f2SAndroid Build Coastguard Worker this.stack.push(this.boolStackValue(value)); 432*890232f2SAndroid Build Coastguard Worker } else if (typeof value === "bigint") { 433*890232f2SAndroid Build Coastguard Worker this.stack.push(this.intStackValue(value)); 434*890232f2SAndroid Build Coastguard Worker } else if (typeof value == 'number') { 435*890232f2SAndroid Build Coastguard Worker if (Number.isInteger(value)) { 436*890232f2SAndroid Build Coastguard Worker this.stack.push(this.intStackValue(value)); 437*890232f2SAndroid Build Coastguard Worker } else { 438*890232f2SAndroid Build Coastguard Worker this.stack.push(this.floatStackValue(value)); 439*890232f2SAndroid Build Coastguard Worker } 440*890232f2SAndroid Build Coastguard Worker } else if (ArrayBuffer.isView(value)) { 441*890232f2SAndroid Build Coastguard Worker this.writeBlob(value.buffer); 442*890232f2SAndroid Build Coastguard Worker } else if (typeof value === 'string' || value instanceof String) { 443*890232f2SAndroid Build Coastguard Worker this.writeString(value as string); 444*890232f2SAndroid Build Coastguard Worker } else if (Array.isArray(value)) { 445*890232f2SAndroid Build Coastguard Worker this.startVector(); 446*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < value.length; i++) { 447*890232f2SAndroid Build Coastguard Worker this.add(value[i]); 448*890232f2SAndroid Build Coastguard Worker } 449*890232f2SAndroid Build Coastguard Worker this.end(); 450*890232f2SAndroid Build Coastguard Worker } else if (typeof value === 'object') { 451*890232f2SAndroid Build Coastguard Worker const properties = Object.getOwnPropertyNames(value).sort(); 452*890232f2SAndroid Build Coastguard Worker this.startMap(true); 453*890232f2SAndroid Build Coastguard Worker for (let i = 0; i < properties.length; i++) { 454*890232f2SAndroid Build Coastguard Worker const key = properties[i]; 455*890232f2SAndroid Build Coastguard Worker this.addKey(key); 456*890232f2SAndroid Build Coastguard Worker this.add((value as Record<string, unknown>)[key]); 457*890232f2SAndroid Build Coastguard Worker } 458*890232f2SAndroid Build Coastguard Worker this.end(); 459*890232f2SAndroid Build Coastguard Worker } else { 460*890232f2SAndroid Build Coastguard Worker throw `Unexpected value input ${value}`; 461*890232f2SAndroid Build Coastguard Worker } 462*890232f2SAndroid Build Coastguard Worker } 463*890232f2SAndroid Build Coastguard Worker 464*890232f2SAndroid Build Coastguard Worker finish(): Uint8Array { 465*890232f2SAndroid Build Coastguard Worker if (!this.finished) { 466*890232f2SAndroid Build Coastguard Worker this.finishBuffer(); 467*890232f2SAndroid Build Coastguard Worker } 468*890232f2SAndroid Build Coastguard Worker const result = this.buffer.slice(0, this.offset); 469*890232f2SAndroid Build Coastguard Worker return new Uint8Array(result); 470*890232f2SAndroid Build Coastguard Worker } 471*890232f2SAndroid Build Coastguard Worker 472*890232f2SAndroid Build Coastguard Worker isFinished(): boolean { 473*890232f2SAndroid Build Coastguard Worker return this.finished; 474*890232f2SAndroid Build Coastguard Worker } 475*890232f2SAndroid Build Coastguard Worker 476*890232f2SAndroid Build Coastguard Worker addKey(key: string): void { 477*890232f2SAndroid Build Coastguard Worker this.integrityCheckOnKeyAddition(); 478*890232f2SAndroid Build Coastguard Worker this.writeKey(key); 479*890232f2SAndroid Build Coastguard Worker } 480*890232f2SAndroid Build Coastguard Worker 481*890232f2SAndroid Build Coastguard Worker addInt(value: number, indirect = false, deduplicate = false): void { 482*890232f2SAndroid Build Coastguard Worker this.integrityCheckOnValueAddition(); 483*890232f2SAndroid Build Coastguard Worker if (!indirect) { 484*890232f2SAndroid Build Coastguard Worker this.stack.push(this.intStackValue(value)); 485*890232f2SAndroid Build Coastguard Worker return; 486*890232f2SAndroid Build Coastguard Worker } 487*890232f2SAndroid Build Coastguard Worker if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectIntLookup, value)) { 488*890232f2SAndroid Build Coastguard Worker this.stack.push(this.indirectIntLookup[value]); 489*890232f2SAndroid Build Coastguard Worker return; 490*890232f2SAndroid Build Coastguard Worker } 491*890232f2SAndroid Build Coastguard Worker const stackValue = this.intStackValue(value); 492*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(stackValue.width); 493*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 494*890232f2SAndroid Build Coastguard Worker const valueOffset = this.offset; 495*890232f2SAndroid Build Coastguard Worker stackValue.writeToBuffer(byteWidth); 496*890232f2SAndroid Build Coastguard Worker const stackOffset = this.offsetStackValue(valueOffset, ValueType.INDIRECT_INT, stackValue.width); 497*890232f2SAndroid Build Coastguard Worker this.stack.push(stackOffset); 498*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 499*890232f2SAndroid Build Coastguard Worker if (deduplicate) { 500*890232f2SAndroid Build Coastguard Worker this.indirectIntLookup[value] = stackOffset; 501*890232f2SAndroid Build Coastguard Worker } 502*890232f2SAndroid Build Coastguard Worker } 503*890232f2SAndroid Build Coastguard Worker 504*890232f2SAndroid Build Coastguard Worker addUInt(value: number, indirect = false, deduplicate = false): void { 505*890232f2SAndroid Build Coastguard Worker this.integrityCheckOnValueAddition(); 506*890232f2SAndroid Build Coastguard Worker if (!indirect) { 507*890232f2SAndroid Build Coastguard Worker this.stack.push(this.uintStackValue(value)); 508*890232f2SAndroid Build Coastguard Worker return; 509*890232f2SAndroid Build Coastguard Worker } 510*890232f2SAndroid Build Coastguard Worker if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectUIntLookup, value)) { 511*890232f2SAndroid Build Coastguard Worker this.stack.push(this.indirectUIntLookup[value]); 512*890232f2SAndroid Build Coastguard Worker return; 513*890232f2SAndroid Build Coastguard Worker } 514*890232f2SAndroid Build Coastguard Worker const stackValue = this.uintStackValue(value); 515*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(stackValue.width); 516*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 517*890232f2SAndroid Build Coastguard Worker const valueOffset = this.offset; 518*890232f2SAndroid Build Coastguard Worker stackValue.writeToBuffer(byteWidth); 519*890232f2SAndroid Build Coastguard Worker const stackOffset = this.offsetStackValue(valueOffset, ValueType.INDIRECT_UINT, stackValue.width); 520*890232f2SAndroid Build Coastguard Worker this.stack.push(stackOffset); 521*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 522*890232f2SAndroid Build Coastguard Worker if (deduplicate) { 523*890232f2SAndroid Build Coastguard Worker this.indirectUIntLookup[value] = stackOffset; 524*890232f2SAndroid Build Coastguard Worker } 525*890232f2SAndroid Build Coastguard Worker } 526*890232f2SAndroid Build Coastguard Worker 527*890232f2SAndroid Build Coastguard Worker addFloat(value: number, indirect = false, deduplicate = false): void { 528*890232f2SAndroid Build Coastguard Worker this.integrityCheckOnValueAddition(); 529*890232f2SAndroid Build Coastguard Worker if (!indirect) { 530*890232f2SAndroid Build Coastguard Worker this.stack.push(this.floatStackValue(value)); 531*890232f2SAndroid Build Coastguard Worker return; 532*890232f2SAndroid Build Coastguard Worker } 533*890232f2SAndroid Build Coastguard Worker if (deduplicate && Object.prototype.hasOwnProperty.call(this.indirectFloatLookup, value)) { 534*890232f2SAndroid Build Coastguard Worker this.stack.push(this.indirectFloatLookup[value]); 535*890232f2SAndroid Build Coastguard Worker return; 536*890232f2SAndroid Build Coastguard Worker } 537*890232f2SAndroid Build Coastguard Worker const stackValue = this.floatStackValue(value); 538*890232f2SAndroid Build Coastguard Worker const byteWidth = this.align(stackValue.width); 539*890232f2SAndroid Build Coastguard Worker const newOffset = this.computeOffset(byteWidth); 540*890232f2SAndroid Build Coastguard Worker const valueOffset = this.offset; 541*890232f2SAndroid Build Coastguard Worker stackValue.writeToBuffer(byteWidth); 542*890232f2SAndroid Build Coastguard Worker const stackOffset = this.offsetStackValue(valueOffset, ValueType.INDIRECT_FLOAT, stackValue.width); 543*890232f2SAndroid Build Coastguard Worker this.stack.push(stackOffset); 544*890232f2SAndroid Build Coastguard Worker this.offset = newOffset; 545*890232f2SAndroid Build Coastguard Worker if (deduplicate) { 546*890232f2SAndroid Build Coastguard Worker this.indirectFloatLookup[value] = stackOffset; 547*890232f2SAndroid Build Coastguard Worker } 548*890232f2SAndroid Build Coastguard Worker } 549*890232f2SAndroid Build Coastguard Worker} 550