cbor.js (7663B)
1 /* 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2014-2016 Patrick Gansterer <paroga@paroga.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 "use strict"; 26 const POW_2_24 = 5.960464477539063e-8; 27 const POW_2_32 = 4294967296; 28 29 function decode(data, tagger, simpleValue) { 30 const dataView = new DataView(data); 31 let offset = 0; 32 33 if (typeof tagger !== "function") { 34 tagger = function (value) { 35 return value; 36 }; 37 } 38 if (typeof simpleValue !== "function") { 39 simpleValue = function () { 40 return undefined; 41 }; 42 } 43 44 function commitRead(length, value) { 45 offset += length; 46 return value; 47 } 48 function readArrayBuffer(length) { 49 return commitRead(length, new Uint8Array(data, offset, length)); 50 } 51 function readFloat16() { 52 const tempArrayBuffer = new ArrayBuffer(4); 53 const tempDataView = new DataView(tempArrayBuffer); 54 const value = readUint16(); 55 56 const sign = value & 0x8000; 57 let exponent = value & 0x7c00; 58 const fraction = value & 0x03ff; 59 60 if (exponent === 0x7c00) { 61 exponent = 0xff << 10; 62 } else if (exponent !== 0) { 63 exponent += (127 - 15) << 10; 64 } else if (fraction !== 0) { 65 return (sign ? -1 : 1) * fraction * POW_2_24; 66 } 67 68 tempDataView.setUint32( 69 0, 70 (sign << 16) | (exponent << 13) | (fraction << 13) 71 ); 72 return tempDataView.getFloat32(0); 73 } 74 function readFloat32() { 75 return commitRead(4, dataView.getFloat32(offset)); 76 } 77 function readFloat64() { 78 return commitRead(8, dataView.getFloat64(offset)); 79 } 80 function readUint8() { 81 return commitRead(1, dataView.getUint8(offset)); 82 } 83 function readUint16() { 84 return commitRead(2, dataView.getUint16(offset)); 85 } 86 function readUint32() { 87 return commitRead(4, dataView.getUint32(offset)); 88 } 89 function readUint64() { 90 return readUint32() * POW_2_32 + readUint32(); 91 } 92 function readBreak() { 93 if (dataView.getUint8(offset) !== 0xff) { 94 return false; 95 } 96 offset += 1; 97 return true; 98 } 99 function readLength(additionalInformation) { 100 if (additionalInformation < 24) { 101 return additionalInformation; 102 } 103 if (additionalInformation === 24) { 104 return readUint8(); 105 } 106 if (additionalInformation === 25) { 107 return readUint16(); 108 } 109 if (additionalInformation === 26) { 110 return readUint32(); 111 } 112 if (additionalInformation === 27) { 113 return readUint64(); 114 } 115 if (additionalInformation === 31) { 116 return -1; 117 } 118 throw new Error("Invalid length encoding"); 119 } 120 function readIndefiniteStringLength(majorType) { 121 const initialByte = readUint8(); 122 if (initialByte === 0xff) { 123 return -1; 124 } 125 const length = readLength(initialByte & 0x1f); 126 if (length < 0 || initialByte >> 5 !== majorType) { 127 throw new Error("Invalid indefinite length element"); 128 } 129 return length; 130 } 131 132 function appendUtf16Data(utf16data, length) { 133 for (let i = 0; i < length; ++i) { 134 let value = readUint8(); 135 if (value & 0x80) { 136 if (value < 0xe0) { 137 value = ((value & 0x1f) << 6) | (readUint8() & 0x3f); 138 length -= 1; 139 } else if (value < 0xf0) { 140 value = 141 ((value & 0x0f) << 12) | 142 ((readUint8() & 0x3f) << 6) | 143 (readUint8() & 0x3f); 144 length -= 2; 145 } else { 146 value = 147 ((value & 0x0f) << 18) | 148 ((readUint8() & 0x3f) << 12) | 149 ((readUint8() & 0x3f) << 6) | 150 (readUint8() & 0x3f); 151 length -= 3; 152 } 153 } 154 155 if (value < 0x10000) { 156 utf16data.push(value); 157 } else { 158 value -= 0x10000; 159 utf16data.push(0xd800 | (value >> 10)); 160 utf16data.push(0xdc00 | (value & 0x3ff)); 161 } 162 } 163 } 164 165 // eslint-disable-next-line complexity 166 function decodeItem() { 167 const initialByte = readUint8(); 168 const majorType = initialByte >> 5; 169 const additionalInformation = initialByte & 0x1f; 170 let i; 171 let length; 172 173 if (majorType === 7) { 174 switch (additionalInformation) { 175 case 25: 176 return readFloat16(); 177 case 26: 178 return readFloat32(); 179 case 27: 180 return readFloat64(); 181 } 182 } 183 184 length = readLength(additionalInformation); 185 if (length < 0 && (majorType < 2 || majorType > 6)) { 186 throw new Error("Invalid length"); 187 } 188 189 switch (majorType) { 190 case 0: 191 return length; 192 case 1: 193 return -1 - length; 194 case 2: 195 if (length < 0) { 196 const elements = []; 197 let fullArrayLength = 0; 198 while ((length = readIndefiniteStringLength(majorType)) >= 0) { 199 fullArrayLength += length; 200 elements.push(readArrayBuffer(length)); 201 } 202 const fullArray = new Uint8Array(fullArrayLength); 203 let fullArrayOffset = 0; 204 for (i = 0; i < elements.length; ++i) { 205 fullArray.set(elements[i], fullArrayOffset); 206 fullArrayOffset += elements[i].length; 207 } 208 return fullArray; 209 } 210 return readArrayBuffer(length); 211 case 3: { 212 const utf16data = []; 213 if (length < 0) { 214 while ((length = readIndefiniteStringLength(majorType)) >= 0) { 215 appendUtf16Data(utf16data, length); 216 } 217 } else { 218 appendUtf16Data(utf16data, length); 219 } 220 return String.fromCharCode.apply(null, utf16data); 221 } 222 case 4: { 223 let retArray; 224 if (length < 0) { 225 retArray = []; 226 while (!readBreak()) { 227 retArray.push(decodeItem()); 228 } 229 } else { 230 retArray = new Array(length); 231 for (i = 0; i < length; ++i) { 232 retArray[i] = decodeItem(); 233 } 234 } 235 return retArray; 236 } 237 case 5: { 238 const retObject = {}; 239 for (i = 0; i < length || (length < 0 && !readBreak()); ++i) { 240 const key = decodeItem(); 241 retObject[key] = decodeItem(); 242 } 243 return retObject; 244 } 245 case 6: 246 return tagger(decodeItem(), length); 247 case 7: 248 switch (length) { 249 case 20: 250 return false; 251 case 21: 252 return true; 253 case 22: 254 return null; 255 case 23: 256 return undefined; 257 default: 258 return simpleValue(length); 259 } 260 } 261 262 throw new Error("Invalid major byte"); 263 } 264 265 const ret = decodeItem(); 266 if (offset !== data.byteLength) { 267 throw new Error("Remaining bytes"); 268 } 269 270 return ret; 271 } 272 273 module.exports = { decode };