cbor.js (12175B)
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 (function(global, undefined) { "use strict"; 26 var POW_2_24 = 5.960464477539063e-8, 27 POW_2_32 = 4294967296, 28 POW_2_53 = 9007199254740992; 29 30 function encode(value) { 31 var data = new ArrayBuffer(256); 32 var dataView = new DataView(data); 33 var lastLength; 34 var offset = 0; 35 36 function prepareWrite(length) { 37 var newByteLength = data.byteLength; 38 var requiredLength = offset + length; 39 while (newByteLength < requiredLength) 40 newByteLength <<= 1; 41 if (newByteLength !== data.byteLength) { 42 var oldDataView = dataView; 43 data = new ArrayBuffer(newByteLength); 44 dataView = new DataView(data); 45 var uint32count = (offset + 3) >> 2; 46 for (var i = 0; i < uint32count; ++i) 47 dataView.setUint32(i << 2, oldDataView.getUint32(i << 2)); 48 } 49 50 lastLength = length; 51 return dataView; 52 } 53 function commitWrite() { 54 offset += lastLength; 55 } 56 function writeFloat64(value) { 57 commitWrite(prepareWrite(8).setFloat64(offset, value)); 58 } 59 function writeUint8(value) { 60 commitWrite(prepareWrite(1).setUint8(offset, value)); 61 } 62 function writeUint8Array(value) { 63 var dataView = prepareWrite(value.length); 64 for (var i = 0; i < value.length; ++i) 65 dataView.setUint8(offset + i, value[i]); 66 commitWrite(); 67 } 68 function writeUint16(value) { 69 commitWrite(prepareWrite(2).setUint16(offset, value)); 70 } 71 function writeUint32(value) { 72 commitWrite(prepareWrite(4).setUint32(offset, value)); 73 } 74 function writeUint64(value) { 75 var low = value % POW_2_32; 76 var high = (value - low) / POW_2_32; 77 var dataView = prepareWrite(8); 78 dataView.setUint32(offset, high); 79 dataView.setUint32(offset + 4, low); 80 commitWrite(); 81 } 82 function writeTypeAndLength(type, length) { 83 if (length < 24) { 84 writeUint8(type << 5 | length); 85 } else if (length < 0x100) { 86 writeUint8(type << 5 | 24); 87 writeUint8(length); 88 } else if (length < 0x10000) { 89 writeUint8(type << 5 | 25); 90 writeUint16(length); 91 } else if (length < 0x100000000) { 92 writeUint8(type << 5 | 26); 93 writeUint32(length); 94 } else { 95 writeUint8(type << 5 | 27); 96 writeUint64(length); 97 } 98 } 99 100 function encodeItem(value) { 101 var i; 102 103 if (value === false) 104 return writeUint8(0xf4); 105 if (value === true) 106 return writeUint8(0xf5); 107 if (value === null) 108 return writeUint8(0xf6); 109 if (value === undefined) 110 return writeUint8(0xf7); 111 112 switch (typeof value) { 113 case "number": 114 if (Math.floor(value) === value) { 115 if (0 <= value && value <= POW_2_53) 116 return writeTypeAndLength(0, value); 117 if (-POW_2_53 <= value && value < 0) 118 return writeTypeAndLength(1, -(value + 1)); 119 } 120 writeUint8(0xfb); 121 return writeFloat64(value); 122 123 case "string": 124 var utf8data = []; 125 for (i = 0; i < value.length; ++i) { 126 var charCode = value.charCodeAt(i); 127 if (charCode < 0x80) { 128 utf8data.push(charCode); 129 } else if (charCode < 0x800) { 130 utf8data.push(0xc0 | charCode >> 6); 131 utf8data.push(0x80 | charCode & 0x3f); 132 } else if (charCode < 0xd800) { 133 utf8data.push(0xe0 | charCode >> 12); 134 utf8data.push(0x80 | (charCode >> 6) & 0x3f); 135 utf8data.push(0x80 | charCode & 0x3f); 136 } else { 137 charCode = (charCode & 0x3ff) << 10; 138 charCode |= value.charCodeAt(++i) & 0x3ff; 139 charCode += 0x10000; 140 141 utf8data.push(0xf0 | charCode >> 18); 142 utf8data.push(0x80 | (charCode >> 12) & 0x3f); 143 utf8data.push(0x80 | (charCode >> 6) & 0x3f); 144 utf8data.push(0x80 | charCode & 0x3f); 145 } 146 } 147 148 writeTypeAndLength(3, utf8data.length); 149 return writeUint8Array(utf8data); 150 151 default: 152 var length; 153 if (Array.isArray(value)) { 154 length = value.length; 155 writeTypeAndLength(4, length); 156 for (i = 0; i < length; ++i) 157 encodeItem(value[i]); 158 } else if (value instanceof Uint8Array) { 159 writeTypeAndLength(2, value.length); 160 writeUint8Array(value); 161 } else { 162 var keys = Object.keys(value); 163 length = keys.length; 164 writeTypeAndLength(5, length); 165 for (i = 0; i < length; ++i) { 166 var key = keys[i]; 167 encodeItem(key); 168 encodeItem(value[key]); 169 } 170 } 171 } 172 } 173 174 encodeItem(value); 175 176 if ("slice" in data) 177 return data.slice(0, offset); 178 179 var ret = new ArrayBuffer(offset); 180 var retView = new DataView(ret); 181 for (var i = 0; i < offset; ++i) 182 retView.setUint8(i, dataView.getUint8(i)); 183 return ret; 184 } 185 186 function decode(data, tagger, simpleValue) { 187 var dataView = new DataView(data); 188 var offset = 0; 189 190 if (typeof tagger !== "function") 191 tagger = function(value) { return value; }; 192 if (typeof simpleValue !== "function") 193 simpleValue = function() { return undefined; }; 194 195 function commitRead(length, value) { 196 offset += length; 197 return value; 198 } 199 function readArrayBuffer(length) { 200 return commitRead(length, new Uint8Array(data, offset, length)); 201 } 202 function readFloat16() { 203 var tempArrayBuffer = new ArrayBuffer(4); 204 var tempDataView = new DataView(tempArrayBuffer); 205 var value = readUint16(); 206 207 var sign = value & 0x8000; 208 var exponent = value & 0x7c00; 209 var fraction = value & 0x03ff; 210 211 if (exponent === 0x7c00) 212 exponent = 0xff << 10; 213 else if (exponent !== 0) 214 exponent += (127 - 15) << 10; 215 else if (fraction !== 0) 216 return (sign ? -1 : 1) * fraction * POW_2_24; 217 218 tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13); 219 return tempDataView.getFloat32(0); 220 } 221 function readFloat32() { 222 return commitRead(4, dataView.getFloat32(offset)); 223 } 224 function readFloat64() { 225 return commitRead(8, dataView.getFloat64(offset)); 226 } 227 function readUint8() { 228 return commitRead(1, dataView.getUint8(offset)); 229 } 230 function readUint16() { 231 return commitRead(2, dataView.getUint16(offset)); 232 } 233 function readUint32() { 234 return commitRead(4, dataView.getUint32(offset)); 235 } 236 function readUint64() { 237 return readUint32() * POW_2_32 + readUint32(); 238 } 239 function readBreak() { 240 if (dataView.getUint8(offset) !== 0xff) 241 return false; 242 offset += 1; 243 return true; 244 } 245 function readLength(additionalInformation) { 246 if (additionalInformation < 24) 247 return additionalInformation; 248 if (additionalInformation === 24) 249 return readUint8(); 250 if (additionalInformation === 25) 251 return readUint16(); 252 if (additionalInformation === 26) 253 return readUint32(); 254 if (additionalInformation === 27) 255 return readUint64(); 256 if (additionalInformation === 31) 257 return -1; 258 throw "Invalid length encoding"; 259 } 260 function readIndefiniteStringLength(majorType) { 261 var initialByte = readUint8(); 262 if (initialByte === 0xff) 263 return -1; 264 var length = readLength(initialByte & 0x1f); 265 if (length < 0 || (initialByte >> 5) !== majorType) 266 throw "Invalid indefinite length element"; 267 return length; 268 } 269 270 function appendUtf16Data(utf16data, length) { 271 for (var i = 0; i < length; ++i) { 272 var value = readUint8(); 273 if (value & 0x80) { 274 if (value < 0xe0) { 275 value = (value & 0x1f) << 6 276 | (readUint8() & 0x3f); 277 length -= 1; 278 } else if (value < 0xf0) { 279 value = (value & 0x0f) << 12 280 | (readUint8() & 0x3f) << 6 281 | (readUint8() & 0x3f); 282 length -= 2; 283 } else { 284 value = (value & 0x0f) << 18 285 | (readUint8() & 0x3f) << 12 286 | (readUint8() & 0x3f) << 6 287 | (readUint8() & 0x3f); 288 length -= 3; 289 } 290 } 291 292 if (value < 0x10000) { 293 utf16data.push(value); 294 } else { 295 value -= 0x10000; 296 utf16data.push(0xd800 | (value >> 10)); 297 utf16data.push(0xdc00 | (value & 0x3ff)); 298 } 299 } 300 } 301 302 function decodeItem() { 303 var initialByte = readUint8(); 304 var majorType = initialByte >> 5; 305 var additionalInformation = initialByte & 0x1f; 306 var i; 307 var length; 308 309 if (majorType === 7) { 310 switch (additionalInformation) { 311 case 25: 312 return readFloat16(); 313 case 26: 314 return readFloat32(); 315 case 27: 316 return readFloat64(); 317 } 318 } 319 320 length = readLength(additionalInformation); 321 if (length < 0 && (majorType < 2 || 6 < majorType)) 322 throw "Invalid length"; 323 324 switch (majorType) { 325 case 0: 326 return length; 327 case 1: 328 return -1 - length; 329 case 2: 330 if (length < 0) { 331 var elements = []; 332 var fullArrayLength = 0; 333 while ((length = readIndefiniteStringLength(majorType)) >= 0) { 334 fullArrayLength += length; 335 elements.push(readArrayBuffer(length)); 336 } 337 var fullArray = new Uint8Array(fullArrayLength); 338 var fullArrayOffset = 0; 339 for (i = 0; i < elements.length; ++i) { 340 fullArray.set(elements[i], fullArrayOffset); 341 fullArrayOffset += elements[i].length; 342 } 343 return fullArray; 344 } 345 return readArrayBuffer(length); 346 case 3: 347 var utf16data = []; 348 if (length < 0) { 349 while ((length = readIndefiniteStringLength(majorType)) >= 0) 350 appendUtf16Data(utf16data, length); 351 } else 352 appendUtf16Data(utf16data, length); 353 return String.fromCharCode.apply(null, utf16data); 354 case 4: 355 var retArray; 356 if (length < 0) { 357 retArray = []; 358 while (!readBreak()) 359 retArray.push(decodeItem()); 360 } else { 361 retArray = new Array(length); 362 for (i = 0; i < length; ++i) 363 retArray[i] = decodeItem(); 364 } 365 return retArray; 366 case 5: 367 var retObject = {}; 368 for (i = 0; i < length || length < 0 && !readBreak(); ++i) { 369 var key = decodeItem(); 370 retObject[key] = decodeItem(); 371 } 372 return retObject; 373 case 6: 374 return tagger(decodeItem(), length); 375 case 7: 376 switch (length) { 377 case 20: 378 return false; 379 case 21: 380 return true; 381 case 22: 382 return null; 383 case 23: 384 return undefined; 385 default: 386 return simpleValue(length); 387 } 388 } 389 } 390 391 var ret = decodeItem(); 392 if (offset !== data.byteLength) 393 throw "Remaining bytes"; 394 return ret; 395 } 396 397 var obj = { encode: encode, decode: decode }; 398 399 if (typeof define === "function" && define.amd) 400 define("cbor/cbor", obj); 401 else if (typeof module !== "undefined" && module.exports) 402 module.exports = obj; 403 else if (!global.CBOR) 404 global.CBOR = obj; 405 406 })(this);