var flexbuffers = {}; flexbuffers.BitWidth = { WIDTH8: 0, WIDTH16: 1, WIDTH32: 2, WIDTH64: 3, }; flexbuffers.BitWidthUtil = {}; flexbuffers.BitWidthUtil.toByteWidth = (bitWidth) => { return 1 << bitWidth; }; flexbuffers.BitWidthUtil.iwidth = (value) => { if (value >= -128 && value <= 127) return 0/*flexbuffers.BitWidth.WIDTH8*/; if (value >= -32768 && value <= 32767) return 1/*flexbuffers.BitWidth.WIDTH16*/; if (value >= -2147483648 && value <= 2147483647) return 2/*flexbuffers.BitWidth.WIDTH32*/; return 3/*flexbuffers.BitWidth.WIDTH64*/; }; flexbuffers.BitWidthUtil.fwidth = (value) => { return value === Math.fround(value) ? 2 /*flexbuffers.BitWidth.WIDTH32*/: 3 /*flexbuffers.BitWidth.WIDTH64*/; }; flexbuffers.BitWidthUtil.uwidth = (value) => { if (value <= 255) return 0; //flexbuffers.BitWidth.WIDTH8; if (value <= 65535) return 1; //flexbuffers.BitWidth.WIDTH16; if (value <= 4294967295) return 2; //flexbuffers.BitWidth.WIDTH32; return 3; //flexbuffers.BitWidth.WIDTH64; }; flexbuffers.BitWidthUtil.fromByteWidth = (value) => { if (value === 1) return 0; //flexbuffers.BitWidth.WIDTH8; if (value === 2) return 1; //flexbuffers.BitWidth.WIDTH16; if (value === 4) return 2; //flexbuffers.BitWidth.WIDTH32; return 3; //flexbuffers.BitWidth.WIDTH64; }; flexbuffers.BitWidthUtil.paddingSize = (bufSize, scalarSize) => { return (~bufSize + 1) & (scalarSize - 1); }; flexbuffers.ValueType = { NULL: 0, INT: 1, UINT: 2, FLOAT: 3, KEY: 4, STRING: 5, INDIRECT_INT: 6, INDIRECT_UINT: 7, INDIRECT_FLOAT: 8, MAP: 9, VECTOR: 10, VECTOR_INT: 11, VECTOR_UINT: 12, VECTOR_FLOAT: 13, VECTOR_KEY: 14, VECTOR_STRING_DEPRECATED: 15, VECTOR_INT2: 16, VECTOR_UINT2: 17, VECTOR_FLOAT2: 18, VECTOR_INT3: 19, VECTOR_UINT3: 20, VECTOR_FLOAT3: 21, VECTOR_INT4: 22, VECTOR_UINT4: 23, VECTOR_FLOAT4: 24, BLOB: 25, BOOL: 26, VECTOR_BOOL: 36, }; flexbuffers.ValueTypeUtil = {}; flexbuffers.ValueTypeUtil.isInline = (value) => { return value === flexbuffers.ValueType.BOOL || value <= flexbuffers.ValueType.FLOAT; }; flexbuffers.ValueTypeUtil.isNumber = (value) => { return value >= flexbuffers.ValueType.INT && value <= flexbuffers.ValueType.FLOAT; }; flexbuffers.ValueTypeUtil.isIndirectNumber = (value) => { return value >= flexbuffers.ValueType.INDIRECT_INT && value <= flexbuffers.ValueType.INDIRECT_FLOAT; }; flexbuffers.ValueTypeUtil.isTypedVectorElement = (value) => { return value === flexbuffers.ValueType.BOOL || (value >= flexbuffers.ValueType.INT && value <= flexbuffers.ValueType.STRING); }; flexbuffers.ValueTypeUtil.isTypedVector = (value) => { return value === flexbuffers.ValueType.VECTOR_BOOL || (value >= flexbuffers.ValueType.VECTOR_INT && value <= flexbuffers.ValueType.VECTOR_STRING_DEPRECATED); }; flexbuffers.ValueTypeUtil.isFixedTypedVector = (value) => { return value >= flexbuffers.ValueType.VECTOR_INT2 && value <= flexbuffers.ValueType.VECTOR_FLOAT4; }; flexbuffers.ValueTypeUtil.isAVector = (value) => { return flexbuffers.ValueTypeUtil.isTypedVector(value) || flexbuffers.ValueTypeUtil.isFixedTypedVector(value) || value === flexbuffers.ValueType.VECTOR; }; flexbuffers.ValueTypeUtil.toTypedVector = (valueType, length) => { if (length === 0) return valueType - flexbuffers.ValueType.INT + flexbuffers.ValueType.VECTOR_INT; if (length === 2) return valueType - flexbuffers.ValueType.INT + flexbuffers.ValueType.VECTOR_INT2; if (length === 3) return valueType - flexbuffers.ValueType.INT + flexbuffers.ValueType.VECTOR_INT3; if (length === 4) return valueType - flexbuffers.ValueType.INT + flexbuffers.ValueType.VECTOR_INT4; throw "Unexpected length " + length; }; flexbuffers.ValueTypeUtil.typedVectorElementType = (valueType) => { return valueType - flexbuffers.ValueType.VECTOR_INT + flexbuffers.ValueType.INT; }; flexbuffers.ValueTypeUtil.fixedTypedVectorElementType = (valueType) => { return ((valueType - flexbuffers.ValueType.VECTOR_INT2) % 3) + flexbuffers.ValueType.INT; }; flexbuffers.ValueTypeUtil.fixedTypedVectorElementSize = (valueType) => { // The x / y >> 0 trick is to have an int division. Suppose to be faster than Math.floor() return (((valueType - flexbuffers.ValueType.VECTOR_INT2) / 3) >> 0) + 2; }; flexbuffers.ValueTypeUtil.packedType = (valueType, bitWidth) => { return bitWidth | (valueType << 2); }; flexbuffers.toReference = (buffer) => { // Add to readInt, readUInt, readFloat in order to check for offset bugs function validateOffset(dataView, offset, width) { if (dataView.byteLength <= offset + width || offset & (flexbuffers.BitWidthUtil.toByteWidth(width) - 1) !== 0) { throw "Bad offset: " + offset + ", width: " + width; } } function readInt(dataView, offset, width) { if (width < 2) { if (width < 1) { return dataView.getInt8(offset); } else { return dataView.getInt16(offset, true); } } else { if (width < 3) { return dataView.getInt32(offset, true) } else { if (dataView.setBigInt64 === undefined) { return { low: dataView.getInt32(offset, true), high: dataView.getInt32(offset + 4, true) } } return dataView.getBigInt64(offset, true) } } } function readUInt(dataView, offset, width) { if (width < 2) { if (width < 1) { return dataView.getUint8(offset); } else { return dataView.getUint16(offset, true); } } else { if (width < 3) { return dataView.getUint32(offset, true) } else { if (dataView.getBigUint64 === undefined) { return { low: dataView.getUint32(offset, true), high: dataView.getUint32(offset + 4, true) } } return dataView.getBigUint64(offset, true) } } } function readFloat(dataView, offset, width) { if (width < 2 /*flexbuffers.BitWidth.WIDTH32*/) { throw "Bad width: " + width; } if (width === 2 /*flexbuffers.BitWidth.WIDTH32*/) { return dataView.getFloat32(offset, true); } return dataView.getFloat64(offset, true); } function indirect(dataView, offset, width) { const step = readUInt(dataView, offset, width); return offset - step; } function keyIndex(key, dataView, offset, parentWidth, byteWidth, length) { const input = toUTF8Array(key); const keysVectorOffset = indirect(dataView, offset, parentWidth) - byteWidth * 3; const bitWidth = flexbuffers.BitWidthUtil.fromByteWidth(byteWidth); const indirectOffset = keysVectorOffset - readUInt(dataView, keysVectorOffset, bitWidth); const _byteWidth = readUInt(dataView, keysVectorOffset + byteWidth, bitWidth); let low = 0; let high = length - 1; while (low <= high) { const mid = (high + low) >> 1; const dif = diffKeys(input, mid, dataView, indirectOffset, _byteWidth); if (dif === 0) return mid; if (dif < 0) { high = mid - 1; } else { low = mid + 1; } } return null; } function diffKeys(input, index, dataView, offset, width) { const keyOffset = offset + index * width; const keyIndirectOffset = keyOffset - readUInt(dataView, keyOffset, flexbuffers.BitWidthUtil.fromByteWidth(width)); for (let i = 0; i < input.length; i++) { const dif = input[i] - dataView.getUint8(keyIndirectOffset + i); if (dif !== 0) { return dif; } } return dataView.getUint8(keyIndirectOffset + input.length) === 0 ? 0 : -1; } function valueForIndexWithKey(index, key, dataView, offset, parentWidth, byteWidth, length, path) { const _indirect = indirect(dataView, offset, parentWidth); const elementOffset = _indirect + index * byteWidth; const packedType = dataView.getUint8(_indirect + length * byteWidth + index); return Reference(dataView, elementOffset, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth), packedType, `${path}/${key}`) } function keyForIndex(index, dataView, offset, parentWidth, byteWidth) { const keysVectorOffset = indirect(dataView, offset, parentWidth) - byteWidth * 3; const bitWidth = flexbuffers.BitWidthUtil.fromByteWidth(byteWidth); const indirectOffset = keysVectorOffset - readUInt(dataView, keysVectorOffset, bitWidth); const _byteWidth = readUInt(dataView, keysVectorOffset + byteWidth, bitWidth); const keyOffset = indirectOffset + index * _byteWidth; const keyIndirectOffset = keyOffset - readUInt(dataView, keyOffset, flexbuffers.BitWidthUtil.fromByteWidth(_byteWidth)); let length = 0; while (dataView.getUint8(keyIndirectOffset + length) !== 0) { length++; } return fromUTF8Array(new Uint8Array(dataView.buffer, keyIndirectOffset, length)); } function Reference(dataView, offset, parentWidth, packedType, path) { const byteWidth = 1 << (packedType & 3); const valueType = packedType >> 2; let length = -1; return { isNull: function() { return valueType === flexbuffers.ValueType.NULL; }, isNumber: function() { return flexbuffers.ValueTypeUtil.isNumber(valueType) || flexbuffers.ValueTypeUtil.isIndirectNumber(valueType); }, isFloat: function() { return flexbuffers.ValueType.FLOAT === valueType || flexbuffers.ValueType.INDIRECT_FLOAT === valueType; }, isInt: function() { return this.isNumber() && !this.isFloat(); }, isString: function() { return flexbuffers.ValueType.STRING === valueType || flexbuffers.ValueType.KEY === valueType; }, isBool: function() { return flexbuffers.ValueType.BOOL === valueType; }, isBlob: function() { return flexbuffers.ValueType.BLOB === valueType; }, isVector: function() { return flexbuffers.ValueTypeUtil.isAVector(valueType); }, isMap: function() { return flexbuffers.ValueType.MAP === valueType; }, boolValue: function() { if (this.isBool()) { return readInt(dataView, offset, parentWidth) > 0; } return null; }, intValue: function() { if (valueType === flexbuffers.ValueType.INT) { return readInt(dataView, offset, parentWidth); } if (valueType === flexbuffers.ValueType.UINT) { return readUInt(dataView, offset, parentWidth); } if (valueType === flexbuffers.ValueType.INDIRECT_INT) { return readInt(dataView, indirect(dataView, offset, parentWidth), flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); } if (valueType === flexbuffers.ValueType.INDIRECT_UINT) { return readUInt(dataView, indirect(dataView, offset, parentWidth), flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); } return null; }, floatValue: function() { if (valueType === flexbuffers.ValueType.FLOAT) { return readFloat(dataView, offset, parentWidth); } if (valueType === flexbuffers.ValueType.INDIRECT_FLOAT) { return readFloat(dataView, indirect(dataView, offset, parentWidth), flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); } return null; }, numericValue: function() { return this.floatValue() || this.intValue()}, stringValue: function() { if (valueType === flexbuffers.ValueType.STRING || valueType === flexbuffers.ValueType.KEY) { const begin = indirect(dataView, offset, parentWidth); return fromUTF8Array(new Uint8Array(dataView.buffer, begin, this.length())); } return null; }, blobValue: function() { if (this.isBlob()) { const begin = indirect(dataView, offset, parentWidth); return new Uint8Array(dataView.buffer, begin, this.length()); } return null; }, get: function(key) { const length = this.length(); if (Number.isInteger(key) && flexbuffers.ValueTypeUtil.isAVector(valueType)) { if (key >= length || key < 0) { throw `Key: [${key}] is not applicable on ${path} of ${valueType} length: ${length}`; } const _indirect = indirect(dataView, offset, parentWidth); const elementOffset = _indirect + key * byteWidth; let _packedType = dataView.getUint8(_indirect + length * byteWidth + key); if (flexbuffers.ValueTypeUtil.isTypedVector(valueType)) { const _valueType = flexbuffers.ValueTypeUtil.typedVectorElementType(valueType); _packedType = flexbuffers.ValueTypeUtil.packedType(_valueType, flexbuffers.BitWidth.WIDTH8); } else if (flexbuffers.ValueTypeUtil.isFixedTypedVector(valueType)) { const _valueType = flexbuffers.ValueTypeUtil.fixedTypedVectorElementType(valueType); _packedType = flexbuffers.ValueTypeUtil.packedType(_valueType, flexbuffers.BitWidth.WIDTH8); } return Reference(dataView, elementOffset, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth), _packedType, `${path}[${key}]`); } if (typeof key === 'string') { const index = keyIndex(key, dataView, offset, parentWidth, byteWidth, length); if (index !== null) { return valueForIndexWithKey(index, key, dataView, offset, parentWidth, byteWidth, length, path) } } throw `Key [${key}] is not applicable on ${path} of ${valueType}`; }, length: function() { let size; if (length > -1) { return length; } if (flexbuffers.ValueTypeUtil.isFixedTypedVector(valueType)) { length = flexbuffers.ValueTypeUtil.fixedTypedVectorElementSize(valueType); } else if (valueType === flexbuffers.ValueType.BLOB || valueType === flexbuffers.ValueType.MAP || flexbuffers.ValueTypeUtil.isAVector(valueType)) { length = readUInt(dataView, indirect(dataView, offset, parentWidth) - byteWidth, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)) } else if (valueType === flexbuffers.ValueType.NULL) { length = 0; } else if (valueType === flexbuffers.ValueType.STRING) { const _indirect = indirect(dataView, offset, parentWidth); let sizeByteWidth = byteWidth; size = readUInt(dataView, _indirect - sizeByteWidth, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); while (dataView.getInt8(_indirect + size) !== 0) { sizeByteWidth <<= 1; size = readUInt(dataView, _indirect - sizeByteWidth, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); } length = size; } else if (valueType === flexbuffers.ValueType.KEY) { const _indirect = indirect(dataView, offset, parentWidth); size = 1; while (dataView.getInt8(_indirect + size) !== 0) { size++; } length = size; } else { length = 1; } return length; }, toObject: function() { const length = this.length(); if (this.isVector()) { let result = []; for (let i = 0; i < length; i++) { result.push(this.get(i).toObject()); } return result; } if (this.isMap()) { let result = {}; for (let i = 0; i < length; i++) { let key = keyForIndex(i, dataView, offset, parentWidth, byteWidth); result[key] = valueForIndexWithKey(i, key, dataView, offset, parentWidth, byteWidth, length, path).toObject(); } return result; } if (this.isNull()) { return null; } if (this.isBool()) { return this.boolValue(); } if (this.isNumber()) { return this.numericValue(); } return this.blobValue() || this.stringValue(); } } } const len = buffer.byteLength; if (len < 3) { throw "Buffer needs to be bigger than 3"; } const dataView = new DataView(buffer); const byteWidth = dataView.getUint8(len - 1); const packedType = dataView.getUint8(len - 2); const parentWidth = flexbuffers.BitWidthUtil.fromByteWidth(byteWidth); const offset = len - byteWidth - 2; return Reference(dataView, offset, parentWidth, packedType, "/") }; flexbuffers.toObject = (buffer) => { return flexbuffers.toReference(buffer).toObject(); }; flexbuffers.builder = (size = 2048, deduplicateString = true, deduplicateKeys = true, deduplicateKeyVectors = true) => { let buffer = new ArrayBuffer(size > 0 ? size : 2048); let view = new DataView(buffer); const stack = []; const stackPointers = []; let offset = 0; let finished = false; const stringLookup = {}; const keyLookup = {}; const keyVectorLookup = {}; const indirectIntLookup = {}; const indirectUIntLookup = {}; const indirectFloatLookup = {}; let dedupStrings = deduplicateString; let dedupKeys = deduplicateKeys; let dedupKeyVectors = deduplicateKeyVectors; function align(width) { const byteWidth = flexbuffers.BitWidthUtil.toByteWidth(width); offset += flexbuffers.BitWidthUtil.paddingSize(offset, byteWidth); return byteWidth; } function computeOffset(newValueSize) { const targetOffset = offset + newValueSize; let size = buffer.byteLength; const prevSize = size; while (size < targetOffset) { size <<= 1; } if (prevSize < size) { const prevBuffer = buffer; buffer = new ArrayBuffer(size); view = new DataView(buffer); new Uint8Array(buffer).set(new Uint8Array(prevBuffer), 0); } return targetOffset; } function pushInt(value, width) { if (width === flexbuffers.BitWidth.WIDTH8) { view.setInt8(offset, value); } else if (width === flexbuffers.BitWidth.WIDTH16) { view.setInt16(offset, value, true); } else if (width === flexbuffers.BitWidth.WIDTH32) { view.setInt32(offset, value, true); } else if (width === flexbuffers.BitWidth.WIDTH64) { view.setBigInt64(offset, BigInt(value), true); } else { throw `Unexpected width: ${width} for value: ${value}`; } } function pushUInt(value, width) { if (width === flexbuffers.BitWidth.WIDTH8) { view.setUint8(offset, value); } else if (width === flexbuffers.BitWidth.WIDTH16) { view.setUint16(offset, value, true); } else if (width === flexbuffers.BitWidth.WIDTH32) { view.setUint32(offset, value, true); } else if (width === flexbuffers.BitWidth.WIDTH64) { view.setBigUint64(offset, BigInt(value), true); } else { throw `Unexpected width: ${width} for value: ${value}`; } } function writeInt(value, byteWidth) { const newOffset = computeOffset(byteWidth); pushInt(value, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); offset = newOffset; } function writeUInt(value, byteWidth) { const newOffset = computeOffset(byteWidth); pushUInt(value, flexbuffers.BitWidthUtil.fromByteWidth(byteWidth)); offset = newOffset; } function writeBlob(arrayBuffer) { const length = arrayBuffer.byteLength; const bitWidth = flexbuffers.BitWidthUtil.uwidth(length); const byteWidth = align(bitWidth); writeUInt(length, byteWidth); const blobOffset = offset; const newOffset = computeOffset(length); new Uint8Array(buffer).set(new Uint8Array(arrayBuffer), blobOffset); stack.push(offsetStackValue(blobOffset, flexbuffers.ValueType.BLOB, bitWidth)); offset = newOffset; } function writeString(str) { if (dedupStrings && stringLookup.hasOwnProperty(str)) { stack.push(stringLookup[str]); return; } const utf8 = toUTF8Array(str); const length = utf8.length; const bitWidth = flexbuffers.BitWidthUtil.uwidth(length); const byteWidth = align(bitWidth); writeUInt(length, byteWidth); const stringOffset = offset; const newOffset = computeOffset(length + 1); new Uint8Array(buffer).set(utf8, stringOffset); const stackValue = offsetStackValue(stringOffset, flexbuffers.ValueType.STRING, bitWidth); stack.push(stackValue); if (dedupStrings) { stringLookup[str] = stackValue; } offset = newOffset; } function writeKey(str) { if (dedupKeys && keyLookup.hasOwnProperty(str)) { stack.push(keyLookup[str]); return; } const utf8 = toUTF8Array(str); const length = utf8.length; const newOffset = computeOffset(length + 1); new Uint8Array(buffer).set(utf8, offset); const stackValue = offsetStackValue(offset, flexbuffers.ValueType.KEY, flexbuffers.BitWidth.WIDTH8); stack.push(stackValue); if (dedupKeys) { keyLookup[str] = stackValue; } offset = newOffset; } function writeStackValue(value, byteWidth) { const newOffset = computeOffset(byteWidth); if (value.isOffset) { const relativeOffset = offset - value.offset; if (byteWidth === 8 || BigInt(relativeOffset) < (1n << BigInt(byteWidth * 8))) { writeUInt(relativeOffset, byteWidth); } else { throw `Unexpected size ${byteWidth}. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new` } } else { value.writeToBuffer(byteWidth); } offset = newOffset; } function integrityCheckOnValueAddition() { if (finished) { throw "Adding values after finish is prohibited"; } if (stackPointers.length !== 0 && stackPointers[stackPointers.length - 1].isVector === false) { if (stack[stack.length - 1].type !== flexbuffers.ValueType.KEY) { throw "Adding value to a map before adding a key is prohibited"; } } } function integrityCheckOnKeyAddition() { if (finished) { throw "Adding values after finish is prohibited"; } if (stackPointers.length === 0 || stackPointers[stackPointers.length - 1].isVector) { throw "Adding key before starting a map is prohibited"; } } function startVector() { stackPointers.push({stackPosition: stack.length, isVector: true}); } function startMap(presorted = false) { stackPointers.push({stackPosition: stack.length, isVector: false, presorted: presorted}); } function endVector(stackPointer) { const vecLength = stack.length - stackPointer.stackPosition; const vec = createVector(stackPointer.stackPosition, vecLength, 1); stack.splice(stackPointer.stackPosition, vecLength); stack.push(vec); } function endMap(stackPointer) { if (!stackPointer.presorted) { sort(stackPointer); } let keyVectorHash = ""; for (let i = stackPointer.stackPosition; i < stack.length; i += 2) { keyVectorHash += `,${stack[i].offset}`; } const vecLength = (stack.length - stackPointer.stackPosition) >> 1; if (dedupKeyVectors && !keyVectorLookup.hasOwnProperty(keyVectorHash)) { keyVectorLookup[keyVectorHash] = createVector(stackPointer.stackPosition, vecLength, 2); } const keysStackValue = dedupKeyVectors ? keyVectorLookup[keyVectorHash] : createVector(stackPointer.stackPosition, vecLength, 2); const valuesStackValue = createVector(stackPointer.stackPosition + 1, vecLength, 2, keysStackValue); stack.splice(stackPointer.stackPosition, vecLength << 1); stack.push(valuesStackValue); } function sort(stackPointer) { function shouldFlip(v1, v2) { if (v1.type !== flexbuffers.ValueType.KEY || v2.type !== flexbuffers.ValueType.KEY) { throw `Stack values are not keys ${v1} | ${v2}. Check if you combined [addKey] with add... method calls properly.` } let c1, c2; let index = 0; do { c1 = view.getUint8(v1.offset + index); c2 = view.getUint8(v2.offset + index); if (c2 < c1) return true; if (c1 < c2) return false; index += 1; } while (c1 !== 0 && c2 !== 0); return false; } function swap(stack, flipIndex, i) { if (flipIndex === i) return; const k = stack[flipIndex]; const v = stack[flipIndex + 1]; stack[flipIndex] = stack[i]; stack[flipIndex + 1] = stack[i + 1]; stack[i] = k; stack[i + 1] = v; } function selectionSort() { for (let i = stackPointer.stackPosition; i < stack.length; i += 2) { let flipIndex = i; for (let j = i + 2; j < stack.length; j += 2) { if (shouldFlip(stack[flipIndex], stack[j])) { flipIndex = j; } } if (flipIndex !== i) { swap(stack, flipIndex, i); } } } function smaller(v1, v2) { if (v1.type !== flexbuffers.ValueType.KEY || v2.type !== flexbuffers.ValueType.KEY) { throw `Stack values are not keys ${v1} | ${v2}. Check if you combined [addKey] with add... method calls properly.` } if(v1.offset === v2.offset) { return false; } let c1, c2; let index = 0; do { c1 = view.getUint8(v1.offset + index); c2 = view.getUint8(v2.offset + index); if(c1 < c2) return true; if(c2 < c1) return false; index += 1; } while (c1 !== 0 && c2 !== 0); return false; } function quickSort(left, right) { if (left < right) { let mid = left + (((right - left) >> 2)) * 2; let pivot = stack[mid], left_new = left, right_new = right; do { while (smaller(stack[left_new], pivot)) { left_new += 2; } while (smaller(pivot, stack[right_new])) { right_new -= 2; } if (left_new <= right_new) { swap(stack, left_new, right_new); left_new += 2; right_new -= 2; } } while (left_new <= right_new); quickSort(left, right_new); quickSort(left_new, right); } } let sorted = true; for (let i = stackPointer.stackPosition; i < stack.length - 2; i += 2) { if (shouldFlip(stack[i], stack[i + 2])) { sorted = false; break; } } if (!sorted) { if (stack.length - stackPointer.stackPosition > 40) { quickSort(stackPointer.stackPosition, stack.length - 2); } else { selectionSort(); } } } function end() { if (stackPointers.length < 1) return; const pointer = stackPointers.pop(); if (pointer.isVector) { endVector(pointer); } else { endMap(pointer); } } function createVector(start, vecLength, step, keys = null) { let bitWidth = flexbuffers.BitWidthUtil.uwidth(vecLength); let prefixElements = 1; if (keys !== null) { const elementWidth = keys.elementWidth(offset, 0); if (elementWidth > bitWidth) { bitWidth = elementWidth; } prefixElements += 2; } let vectorType = flexbuffers.ValueType.KEY; let typed = keys === null; for (let i = start; i < stack.length; i += step) { const elementWidth = stack[i].elementWidth(offset, i + prefixElements); if (elementWidth > bitWidth) { bitWidth = elementWidth; } if (i === start) { vectorType = stack[i].type; typed &= flexbuffers.ValueTypeUtil.isTypedVectorElement(vectorType); } else { if (vectorType !== stack[i].type) { typed = false; } } } const byteWidth = align(bitWidth); const fix = typed && flexbuffers.ValueTypeUtil.isNumber(vectorType) && vecLength >= 2 && vecLength <= 4; if (keys !== null) { writeStackValue(keys, byteWidth); writeUInt(1 << keys.width, byteWidth); } if (!fix) { writeUInt(vecLength, byteWidth); } const vecOffset = offset; for (let i = start; i < stack.length; i += step) { writeStackValue(stack[i], byteWidth); } if (!typed) { for (let i = start; i < stack.length; i += step) { writeUInt(stack[i].storedPackedType(), 1); } } if (keys !== null) { return offsetStackValue(vecOffset, flexbuffers.ValueType.MAP, bitWidth); } if (typed) { const vType = flexbuffers.ValueTypeUtil.toTypedVector(vectorType, fix ? vecLength : 0); return offsetStackValue(vecOffset, vType, bitWidth); } return offsetStackValue(vecOffset, flexbuffers.ValueType.VECTOR, bitWidth); } function StackValue(type, width, value, _offset) { return { type: type, width: width, value: value, offset: _offset, elementWidth: function (size, index) { if (flexbuffers.ValueTypeUtil.isInline(this.type)) return this.width; for (let i = 0; i < 4; i++) { const width = 1 << i; const offsetLoc = size + flexbuffers.BitWidthUtil.paddingSize(size, width) + index * width; const offset = offsetLoc - this.offset; const bitWidth = flexbuffers.BitWidthUtil.uwidth(offset); if (1 << bitWidth === width) { return bitWidth; } } throw `Element is unknown. Size: ${size} at index: ${index}. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new`; }, writeToBuffer: function (byteWidth) { const newOffset = computeOffset(byteWidth); if (this.type === flexbuffers.ValueType.FLOAT) { if (this.width === flexbuffers.BitWidth.WIDTH32) { view.setFloat32(offset, this.value, true); } else { view.setFloat64(offset, this.value, true); } } else if (this.type === flexbuffers.ValueType.INT) { const bitWidth = flexbuffers.BitWidthUtil.fromByteWidth(byteWidth); pushInt(value, bitWidth); } else if (this.type === flexbuffers.ValueType.UINT) { const bitWidth = flexbuffers.BitWidthUtil.fromByteWidth(byteWidth); pushUInt(value, bitWidth); } else if (this.type === flexbuffers.ValueType.NULL) { pushInt(0, this.width); } else if (this.type === flexbuffers.ValueType.BOOL) { pushInt(value ? 1 : 0, this.width); } else { throw `Unexpected type: ${type}. This might be a bug. Please create an issue https://github.com/google/flatbuffers/issues/new` } offset = newOffset; }, storedWidth: function (width = flexbuffers.BitWidth.WIDTH8) { return flexbuffers.ValueTypeUtil.isInline(this.type) ? Math.max(width, this.width) : this.width; }, storedPackedType: function (width = flexbuffers.BitWidth.WIDTH8) { return flexbuffers.ValueTypeUtil.packedType(this.type, this.storedWidth(width)); }, isOffset: !flexbuffers.ValueTypeUtil.isInline(type) } } function nullStackValue() { return StackValue(flexbuffers.ValueType.NULL, flexbuffers.BitWidth.WIDTH8); } function boolStackValue(value) { return StackValue(flexbuffers.ValueType.BOOL, flexbuffers.BitWidth.WIDTH8, value); } function intStackValue(value) { return StackValue(flexbuffers.ValueType.INT, flexbuffers.BitWidthUtil.iwidth(value), value); } function uintStackValue(value) { return StackValue(flexbuffers.ValueType.UINT, flexbuffers.BitWidthUtil.uwidth(value), value); } function floatStackValue(value) { return StackValue(flexbuffers.ValueType.FLOAT, flexbuffers.BitWidthUtil.fwidth(value), value); } function offsetStackValue(offset, valueType, bitWidth) { return StackValue(valueType, bitWidth, null, offset); } function finishBuffer() { if (stack.length !== 1) { throw `Stack has to be exactly 1, but it is ${stack.length}. You have to end all started vectors and maps before calling [finish]`; } const value = stack[0]; const byteWidth = align(value.elementWidth(offset, 0)); writeStackValue(value, byteWidth); writeUInt(value.storedPackedType(), 1); writeUInt(byteWidth, 1); finished = true; } return { add: function (value) { integrityCheckOnValueAddition(); if (typeof value === 'undefined') { throw "You need to provide a value"; } if (value === null) { stack.push(nullStackValue()); } else if (typeof value === "boolean") { stack.push(boolStackValue(value)); } else if (typeof value === "bigint") { stack.push(intStackValue(value)); } else if (typeof value == 'number') { if (Number.isInteger(value)) { stack.push(intStackValue(value)); } else { stack.push(floatStackValue(value)); } } else if (ArrayBuffer.isView(value)){ writeBlob(value.buffer); } else if (typeof value === 'string' || value instanceof String) { writeString(value); } else if (Array.isArray(value)) { startVector(); for (let i = 0; i < value.length; i++) { this.add(value[i]); } end(); } else if (typeof value === 'object'){ const properties = Object.getOwnPropertyNames(value).sort(); startMap(true); for (let i = 0; i < properties.length; i++) { const key = properties[i]; this.addKey(key); this.add(value[key]); } end(); } else { throw `Unexpected value input ${value}`; } }, finish: function() { if (!finished) { finishBuffer(); } const result = buffer.slice(0, offset); return new Uint8Array(result); }, isFinished: function() { return finished; }, addKey: function(key) { integrityCheckOnKeyAddition(); writeKey(key); }, addInt: function(value, indirect = false, deduplicate = false) { integrityCheckOnValueAddition(); if (!indirect) { stack.push(intStackValue(value)); return; } if (deduplicate && indirectIntLookup.hasOwnProperty(value)) { stack.push(indirectIntLookup[value]); return; } const stackValue = intStackValue(value); const byteWidth = align(stackValue.width); const newOffset = computeOffset(byteWidth); const valueOffset = offset; stackValue.writeToBuffer(byteWidth); const stackOffset = offsetStackValue(valueOffset, flexbuffers.ValueType.INDIRECT_INT, stackValue.width); stack.push(stackOffset); offset = newOffset; if (deduplicate) { indirectIntLookup[value] = stackOffset; } }, addUInt: function(value, indirect = false, deduplicate = false) { integrityCheckOnValueAddition(); if (!indirect) { stack.push(uintStackValue(value)); return; } if (deduplicate && indirectUIntLookup.hasOwnProperty(value)) { stack.push(indirectUIntLookup[value]); return; } const stackValue = uintStackValue(value); const byteWidth = align(stackValue.width); const newOffset = computeOffset(byteWidth); const valueOffset = offset; stackValue.writeToBuffer(byteWidth); const stackOffset = offsetStackValue(valueOffset, flexbuffers.ValueType.INDIRECT_UINT, stackValue.width); stack.push(stackOffset); offset = newOffset; if (deduplicate) { indirectUIntLookup[value] = stackOffset; } }, addFloat: function(value, indirect = false, deduplicate = false) { integrityCheckOnValueAddition(); if (!indirect) { stack.push(floatStackValue(value)); return; } if (deduplicate && indirectFloatLookup.hasOwnProperty(value)) { stack.push(indirectFloatLookup[value]); return; } const stackValue = floatStackValue(value); const byteWidth = align(stackValue.width); const newOffset = computeOffset(byteWidth); const valueOffset = offset; stackValue.writeToBuffer(byteWidth); const stackOffset = offsetStackValue(valueOffset, flexbuffers.ValueType.INDIRECT_FLOAT, stackValue.width); stack.push(stackOffset); offset = newOffset; if (deduplicate) { indirectFloatLookup[value] = stackOffset; } }, startVector: function() { startVector(); }, startMap: function() { startMap(); }, end: function() { end(); } }; }; flexbuffers.encode = (object, size = 2048, deduplicateStrings = true, deduplicateKeys = true, deduplicateKeyVectors = true) => { const builder = flexbuffers.builder(size > 0 ? size : 2048, deduplicateStrings, deduplicateKeys, deduplicateKeyVectors); builder.add(object); return builder.finish(); }; function fromUTF8Array(data) { const decoder = new TextDecoder(); return decoder.decode(data); } function toUTF8Array(str) { const encoder = new TextEncoder(); return encoder.encode(str); } // Exports for Node.js and RequireJS this.flexbuffers = flexbuffers;