|
|
@ -0,0 +1,406 @@ |
|
|
|
/* |
|
|
|
* 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. |
|
|
|
*/ |
|
|
|
|
|
|
|
(function(global, undefined) { "use strict"; |
|
|
|
var POW_2_24 = 5.960464477539063e-8, |
|
|
|
POW_2_32 = 4294967296, |
|
|
|
POW_2_53 = 9007199254740992; |
|
|
|
|
|
|
|
function encode(value) { |
|
|
|
var data = new ArrayBuffer(256); |
|
|
|
var dataView = new DataView(data); |
|
|
|
var lastLength; |
|
|
|
var offset = 0; |
|
|
|
|
|
|
|
function prepareWrite(length) { |
|
|
|
var newByteLength = data.byteLength; |
|
|
|
var requiredLength = offset + length; |
|
|
|
while (newByteLength < requiredLength) |
|
|
|
newByteLength <<= 1; |
|
|
|
if (newByteLength !== data.byteLength) { |
|
|
|
var oldDataView = dataView; |
|
|
|
data = new ArrayBuffer(newByteLength); |
|
|
|
dataView = new DataView(data); |
|
|
|
var uint32count = (offset + 3) >> 2; |
|
|
|
for (var i = 0; i < uint32count; ++i) |
|
|
|
dataView.setUint32(i << 2, oldDataView.getUint32(i << 2)); |
|
|
|
} |
|
|
|
|
|
|
|
lastLength = length; |
|
|
|
return dataView; |
|
|
|
} |
|
|
|
function commitWrite() { |
|
|
|
offset += lastLength; |
|
|
|
} |
|
|
|
function writeFloat64(value) { |
|
|
|
commitWrite(prepareWrite(8).setFloat64(offset, value)); |
|
|
|
} |
|
|
|
function writeUint8(value) { |
|
|
|
commitWrite(prepareWrite(1).setUint8(offset, value)); |
|
|
|
} |
|
|
|
function writeUint8Array(value) { |
|
|
|
var dataView = prepareWrite(value.length); |
|
|
|
for (var i = 0; i < value.length; ++i) |
|
|
|
dataView.setUint8(offset + i, value[i]); |
|
|
|
commitWrite(); |
|
|
|
} |
|
|
|
function writeUint16(value) { |
|
|
|
commitWrite(prepareWrite(2).setUint16(offset, value)); |
|
|
|
} |
|
|
|
function writeUint32(value) { |
|
|
|
commitWrite(prepareWrite(4).setUint32(offset, value)); |
|
|
|
} |
|
|
|
function writeUint64(value) { |
|
|
|
var low = value % POW_2_32; |
|
|
|
var high = (value - low) / POW_2_32; |
|
|
|
var dataView = prepareWrite(8); |
|
|
|
dataView.setUint32(offset, high); |
|
|
|
dataView.setUint32(offset + 4, low); |
|
|
|
commitWrite(); |
|
|
|
} |
|
|
|
function writeTypeAndLength(type, length) { |
|
|
|
if (length < 24) { |
|
|
|
writeUint8(type << 5 | length); |
|
|
|
} else if (length < 0x100) { |
|
|
|
writeUint8(type << 5 | 24); |
|
|
|
writeUint8(length); |
|
|
|
} else if (length < 0x10000) { |
|
|
|
writeUint8(type << 5 | 25); |
|
|
|
writeUint16(length); |
|
|
|
} else if (length < 0x100000000) { |
|
|
|
writeUint8(type << 5 | 26); |
|
|
|
writeUint32(length); |
|
|
|
} else { |
|
|
|
writeUint8(type << 5 | 27); |
|
|
|
writeUint64(length); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function encodeItem(value) { |
|
|
|
var i; |
|
|
|
|
|
|
|
if (value === false) |
|
|
|
return writeUint8(0xf4); |
|
|
|
if (value === true) |
|
|
|
return writeUint8(0xf5); |
|
|
|
if (value === null) |
|
|
|
return writeUint8(0xf6); |
|
|
|
if (value === undefined) |
|
|
|
return writeUint8(0xf7); |
|
|
|
|
|
|
|
switch (typeof value) { |
|
|
|
case "number": |
|
|
|
if (Math.floor(value) === value) { |
|
|
|
if (0 <= value && value <= POW_2_53) |
|
|
|
return writeTypeAndLength(0, value); |
|
|
|
if (-POW_2_53 <= value && value < 0) |
|
|
|
return writeTypeAndLength(1, -(value + 1)); |
|
|
|
} |
|
|
|
writeUint8(0xfb); |
|
|
|
return writeFloat64(value); |
|
|
|
|
|
|
|
case "string": |
|
|
|
var utf8data = []; |
|
|
|
for (i = 0; i < value.length; ++i) { |
|
|
|
var charCode = value.charCodeAt(i); |
|
|
|
if (charCode < 0x80) { |
|
|
|
utf8data.push(charCode); |
|
|
|
} else if (charCode < 0x800) { |
|
|
|
utf8data.push(0xc0 | charCode >> 6); |
|
|
|
utf8data.push(0x80 | charCode & 0x3f); |
|
|
|
} else if (charCode < 0xd800) { |
|
|
|
utf8data.push(0xe0 | charCode >> 12); |
|
|
|
utf8data.push(0x80 | (charCode >> 6) & 0x3f); |
|
|
|
utf8data.push(0x80 | charCode & 0x3f); |
|
|
|
} else { |
|
|
|
charCode = (charCode & 0x3ff) << 10; |
|
|
|
charCode |= value.charCodeAt(++i) & 0x3ff; |
|
|
|
charCode += 0x10000; |
|
|
|
|
|
|
|
utf8data.push(0xf0 | charCode >> 18); |
|
|
|
utf8data.push(0x80 | (charCode >> 12) & 0x3f); |
|
|
|
utf8data.push(0x80 | (charCode >> 6) & 0x3f); |
|
|
|
utf8data.push(0x80 | charCode & 0x3f); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
writeTypeAndLength(3, utf8data.length); |
|
|
|
return writeUint8Array(utf8data); |
|
|
|
|
|
|
|
default: |
|
|
|
var length; |
|
|
|
if (Array.isArray(value)) { |
|
|
|
length = value.length; |
|
|
|
writeTypeAndLength(4, length); |
|
|
|
for (i = 0; i < length; ++i) |
|
|
|
encodeItem(value[i]); |
|
|
|
} else if (value instanceof Uint8Array) { |
|
|
|
writeTypeAndLength(2, value.length); |
|
|
|
writeUint8Array(value); |
|
|
|
} else { |
|
|
|
var keys = Object.keys(value); |
|
|
|
length = keys.length; |
|
|
|
writeTypeAndLength(5, length); |
|
|
|
for (i = 0; i < length; ++i) { |
|
|
|
var key = keys[i]; |
|
|
|
encodeItem(key); |
|
|
|
encodeItem(value[key]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
encodeItem(value); |
|
|
|
|
|
|
|
if ("slice" in data) |
|
|
|
return data.slice(0, offset); |
|
|
|
|
|
|
|
var ret = new ArrayBuffer(offset); |
|
|
|
var retView = new DataView(ret); |
|
|
|
for (var i = 0; i < offset; ++i) |
|
|
|
retView.setUint8(i, dataView.getUint8(i)); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
function decode(data, tagger, simpleValue) { |
|
|
|
var dataView = new DataView(data); |
|
|
|
var 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() { |
|
|
|
var tempArrayBuffer = new ArrayBuffer(4); |
|
|
|
var tempDataView = new DataView(tempArrayBuffer); |
|
|
|
var value = readUint16(); |
|
|
|
|
|
|
|
var sign = value & 0x8000; |
|
|
|
var exponent = value & 0x7c00; |
|
|
|
var 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 "Invalid length encoding"; |
|
|
|
} |
|
|
|
function readIndefiniteStringLength(majorType) { |
|
|
|
var initialByte = readUint8(); |
|
|
|
if (initialByte === 0xff) |
|
|
|
return -1; |
|
|
|
var length = readLength(initialByte & 0x1f); |
|
|
|
if (length < 0 || (initialByte >> 5) !== majorType) |
|
|
|
throw "Invalid indefinite length element"; |
|
|
|
return length; |
|
|
|
} |
|
|
|
|
|
|
|
function appendUtf16Data(utf16data, length) { |
|
|
|
for (var i = 0; i < length; ++i) { |
|
|
|
var 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)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function decodeItem() { |
|
|
|
var initialByte = readUint8(); |
|
|
|
var majorType = initialByte >> 5; |
|
|
|
var additionalInformation = initialByte & 0x1f; |
|
|
|
var i; |
|
|
|
var 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 || 6 < majorType)) |
|
|
|
throw "Invalid length"; |
|
|
|
|
|
|
|
switch (majorType) { |
|
|
|
case 0: |
|
|
|
return length; |
|
|
|
case 1: |
|
|
|
return -1 - length; |
|
|
|
case 2: |
|
|
|
if (length < 0) { |
|
|
|
var elements = []; |
|
|
|
var fullArrayLength = 0; |
|
|
|
while ((length = readIndefiniteStringLength(majorType)) >= 0) { |
|
|
|
fullArrayLength += length; |
|
|
|
elements.push(readArrayBuffer(length)); |
|
|
|
} |
|
|
|
var fullArray = new Uint8Array(fullArrayLength); |
|
|
|
var 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: |
|
|
|
var utf16data = []; |
|
|
|
if (length < 0) { |
|
|
|
while ((length = readIndefiniteStringLength(majorType)) >= 0) |
|
|
|
appendUtf16Data(utf16data, length); |
|
|
|
} else |
|
|
|
appendUtf16Data(utf16data, length); |
|
|
|
return String.fromCharCode.apply(null, utf16data); |
|
|
|
case 4: |
|
|
|
var 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: |
|
|
|
var retObject = {}; |
|
|
|
for (i = 0; i < length || length < 0 && !readBreak(); ++i) { |
|
|
|
var 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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var ret = decodeItem(); |
|
|
|
if (offset !== data.byteLength) |
|
|
|
throw "Remaining bytes"; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
var obj = { encode: encode, decode: decode }; |
|
|
|
|
|
|
|
if (typeof define === "function" && define.amd) |
|
|
|
define("cbor/cbor", obj); |
|
|
|
else if (typeof module !== "undefined" && module.exports) |
|
|
|
module.exports = obj; |
|
|
|
else if (!global.CBOR) |
|
|
|
global.CBOR = obj; |
|
|
|
|
|
|
|
})(this); |