diff options
Diffstat (limited to 'devtools/client/netmonitor/src/components/messages/cbor.js')
-rw-r--r-- | devtools/client/netmonitor/src/components/messages/cbor.js | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/messages/cbor.js b/devtools/client/netmonitor/src/components/messages/cbor.js new file mode 100644 index 0000000000..8b8b249391 --- /dev/null +++ b/devtools/client/netmonitor/src/components/messages/cbor.js @@ -0,0 +1,270 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2016 Patrick Gansterer <paroga@paroga.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +"use strict"; +const POW_2_24 = 5.960464477539063e-8; +const POW_2_32 = 4294967296; + +function decode(data, tagger, simpleValue) { + const dataView = new DataView(data); + let offset = 0; + + if (typeof tagger !== "function") { + tagger = function (value) { + return value; + }; + } + if (typeof simpleValue !== "function") { + simpleValue = function () { + return undefined; + }; + } + + function commitRead(length, value) { + offset += length; + return value; + } + function readArrayBuffer(length) { + return commitRead(length, new Uint8Array(data, offset, length)); + } + function readFloat16() { + const tempArrayBuffer = new ArrayBuffer(4); + const tempDataView = new DataView(tempArrayBuffer); + const value = readUint16(); + + const sign = value & 0x8000; + let exponent = value & 0x7c00; + const fraction = value & 0x03ff; + + if (exponent === 0x7c00) { + exponent = 0xff << 10; + } else if (exponent !== 0) { + exponent += (127 - 15) << 10; + } else if (fraction !== 0) { + return (sign ? -1 : 1) * fraction * POW_2_24; + } + + tempDataView.setUint32( + 0, + (sign << 16) | (exponent << 13) | (fraction << 13) + ); + return tempDataView.getFloat32(0); + } + function readFloat32() { + return commitRead(4, dataView.getFloat32(offset)); + } + function readFloat64() { + return commitRead(8, dataView.getFloat64(offset)); + } + function readUint8() { + return commitRead(1, dataView.getUint8(offset)); + } + function readUint16() { + return commitRead(2, dataView.getUint16(offset)); + } + function readUint32() { + return commitRead(4, dataView.getUint32(offset)); + } + function readUint64() { + return readUint32() * POW_2_32 + readUint32(); + } + function readBreak() { + if (dataView.getUint8(offset) !== 0xff) { + return false; + } + offset += 1; + return true; + } + function readLength(additionalInformation) { + if (additionalInformation < 24) { + return additionalInformation; + } + if (additionalInformation === 24) { + return readUint8(); + } + if (additionalInformation === 25) { + return readUint16(); + } + if (additionalInformation === 26) { + return readUint32(); + } + if (additionalInformation === 27) { + return readUint64(); + } + if (additionalInformation === 31) { + return -1; + } + throw new Error("Invalid length encoding"); + } + function readIndefiniteStringLength(majorType) { + const initialByte = readUint8(); + if (initialByte === 0xff) { + return -1; + } + const length = readLength(initialByte & 0x1f); + if (length < 0 || initialByte >> 5 !== majorType) { + throw new Error("Invalid indefinite length element"); + } + return length; + } + + function appendUtf16Data(utf16data, length) { + for (let i = 0; i < length; ++i) { + let value = readUint8(); + if (value & 0x80) { + if (value < 0xe0) { + value = ((value & 0x1f) << 6) | (readUint8() & 0x3f); + length -= 1; + } else if (value < 0xf0) { + value = + ((value & 0x0f) << 12) | + ((readUint8() & 0x3f) << 6) | + (readUint8() & 0x3f); + length -= 2; + } else { + value = + ((value & 0x0f) << 18) | + ((readUint8() & 0x3f) << 12) | + ((readUint8() & 0x3f) << 6) | + (readUint8() & 0x3f); + length -= 3; + } + } + + if (value < 0x10000) { + utf16data.push(value); + } else { + value -= 0x10000; + utf16data.push(0xd800 | (value >> 10)); + utf16data.push(0xdc00 | (value & 0x3ff)); + } + } + } + + // eslint-disable-next-line complexity + function decodeItem() { + const initialByte = readUint8(); + const majorType = initialByte >> 5; + const additionalInformation = initialByte & 0x1f; + let i; + let length; + + if (majorType === 7) { + switch (additionalInformation) { + case 25: + return readFloat16(); + case 26: + return readFloat32(); + case 27: + return readFloat64(); + } + } + + length = readLength(additionalInformation); + if (length < 0 && (majorType < 2 || majorType > 6)) { + throw new Error("Invalid length"); + } + + switch (majorType) { + case 0: + return length; + case 1: + return -1 - length; + case 2: + if (length < 0) { + const elements = []; + let fullArrayLength = 0; + while ((length = readIndefiniteStringLength(majorType)) >= 0) { + fullArrayLength += length; + elements.push(readArrayBuffer(length)); + } + const fullArray = new Uint8Array(fullArrayLength); + let fullArrayOffset = 0; + for (i = 0; i < elements.length; ++i) { + fullArray.set(elements[i], fullArrayOffset); + fullArrayOffset += elements[i].length; + } + return fullArray; + } + return readArrayBuffer(length); + case 3: + const utf16data = []; + if (length < 0) { + while ((length = readIndefiniteStringLength(majorType)) >= 0) { + appendUtf16Data(utf16data, length); + } + } else { + appendUtf16Data(utf16data, length); + } + return String.fromCharCode.apply(null, utf16data); + case 4: + let retArray; + if (length < 0) { + retArray = []; + while (!readBreak()) { + retArray.push(decodeItem()); + } + } else { + retArray = new Array(length); + for (i = 0; i < length; ++i) { + retArray[i] = decodeItem(); + } + } + return retArray; + case 5: + const retObject = {}; + for (i = 0; i < length || (length < 0 && !readBreak()); ++i) { + const key = decodeItem(); + retObject[key] = decodeItem(); + } + return retObject; + case 6: + return tagger(decodeItem(), length); + case 7: + switch (length) { + case 20: + return false; + case 21: + return true; + case 22: + return null; + case 23: + return undefined; + default: + return simpleValue(length); + } + } + + throw new Error("Invalid major byte"); + } + + const ret = decodeItem(); + if (offset !== data.byteLength) { + throw new Error("Remaining bytes"); + } + + return ret; +} + +module.exports = { decode }; |