tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

msgpack.js (9858B)


      1 // Copyright © 2019, Yves Goergen, https://unclassified.software/source/msgpack-js
      2 //
      3 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
      4 // associated documentation files (the “Software”), to deal in the Software without restriction,
      5 // including without limitation the rights to use, copy, modify, merge, publish, distribute,
      6 // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
      7 // furnished to do so, subject to the following conditions:
      8 //
      9 // The above copyright notice and this permission notice shall be included in all copies or
     10 // substantial portions of the Software.
     11 //
     12 // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
     13 // NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     14 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
     15 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     16 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     17 
     18 "use strict";
     19 
     20 // Deserializes a MessagePack byte array to a value.
     21 //
     22 // array: The MessagePack byte array to deserialize. This must be an Array or Uint8Array containing bytes, not a string.
     23 function deserialize(array) {
     24  const pow32 = 0x100000000; // 2^32
     25  let pos = 0;
     26  if (array instanceof ArrayBuffer) {
     27    array = new Uint8Array(array);
     28  }
     29  if (typeof array !== "object" || typeof array.length === "undefined") {
     30    throw new Error(
     31      "Invalid argument type: Expected a byte array (Array or Uint8Array) to deserialize."
     32    );
     33  }
     34  if (!array.length) {
     35    throw new Error(
     36      "Invalid argument: The byte array to deserialize is empty."
     37    );
     38  }
     39  if (!(array instanceof Uint8Array)) {
     40    array = new Uint8Array(array);
     41  }
     42  const data = read();
     43  if (pos < array.length) {
     44    // Junk data at the end
     45  }
     46  return data;
     47 
     48  // eslint-disable-next-line complexity
     49  function read() {
     50    const byte = array[pos++];
     51    if (byte >= 0x00 && byte <= 0x7f) {
     52      return byte;
     53    } // positive fixint
     54    if (byte >= 0x80 && byte <= 0x8f) {
     55      return readMap(byte - 0x80);
     56    } // fixmap
     57    if (byte >= 0x90 && byte <= 0x9f) {
     58      return readArray(byte - 0x90);
     59    } // fixarray
     60    if (byte >= 0xa0 && byte <= 0xbf) {
     61      return readStr(byte - 0xa0);
     62    } // fixstr
     63    if (byte === 0xc0) {
     64      return null;
     65    } // nil
     66    if (byte === 0xc1) {
     67      throw new Error("Invalid byte code 0xc1 found.");
     68    } // never used
     69    if (byte === 0xc2) {
     70      return false;
     71    } // false
     72    if (byte === 0xc3) {
     73      return true;
     74    } // true
     75    if (byte === 0xc4) {
     76      return readBin(-1, 1);
     77    } // bin 8
     78    if (byte === 0xc5) {
     79      return readBin(-1, 2);
     80    } // bin 16
     81    if (byte === 0xc6) {
     82      return readBin(-1, 4);
     83    } // bin 32
     84    if (byte === 0xc7) {
     85      return readExt(-1, 1);
     86    } // ext 8
     87    if (byte === 0xc8) {
     88      return readExt(-1, 2);
     89    } // ext 16
     90    if (byte === 0xc9) {
     91      return readExt(-1, 4);
     92    } // ext 32
     93    if (byte === 0xca) {
     94      return readFloat(4);
     95    } // float 32
     96    if (byte === 0xcb) {
     97      return readFloat(8);
     98    } // float 64
     99    if (byte === 0xcc) {
    100      return readUInt(1);
    101    } // uint 8
    102    if (byte === 0xcd) {
    103      return readUInt(2);
    104    } // uint 16
    105    if (byte === 0xce) {
    106      return readUInt(4);
    107    } // uint 32
    108    if (byte === 0xcf) {
    109      return readUInt(8);
    110    } // uint 64
    111    if (byte === 0xd0) {
    112      return readInt(1);
    113    } // int 8
    114    if (byte === 0xd1) {
    115      return readInt(2);
    116    } // int 16
    117    if (byte === 0xd2) {
    118      return readInt(4);
    119    } // int 32
    120    if (byte === 0xd3) {
    121      return readInt(8);
    122    } // int 64
    123    if (byte === 0xd4) {
    124      return readExt(1);
    125    } // fixext 1
    126    if (byte === 0xd5) {
    127      return readExt(2);
    128    } // fixext 2
    129    if (byte === 0xd6) {
    130      return readExt(4);
    131    } // fixext 4
    132    if (byte === 0xd7) {
    133      return readExt(8);
    134    } // fixext 8
    135    if (byte === 0xd8) {
    136      return readExt(16);
    137    } // fixext 16
    138    if (byte === 0xd9) {
    139      return readStr(-1, 1);
    140    } // str 8
    141    if (byte === 0xda) {
    142      return readStr(-1, 2);
    143    } // str 16
    144    if (byte === 0xdb) {
    145      return readStr(-1, 4);
    146    } // str 32
    147    if (byte === 0xdc) {
    148      return readArray(-1, 2);
    149    } // array 16
    150    if (byte === 0xdd) {
    151      return readArray(-1, 4);
    152    } // array 32
    153    if (byte === 0xde) {
    154      return readMap(-1, 2);
    155    } // map 16
    156    if (byte === 0xdf) {
    157      return readMap(-1, 4);
    158    } // map 32
    159    if (byte >= 0xe0 && byte <= 0xff) {
    160      return byte - 256;
    161    } // negative fixint
    162    console.debug("msgpack array:", array);
    163    throw new Error(
    164      "Invalid byte value '" +
    165        byte +
    166        "' at index " +
    167        (pos - 1) +
    168        " in the MessagePack binary data (length " +
    169        array.length +
    170        "): Expecting a range of 0 to 255. This is not a byte array."
    171    );
    172  }
    173 
    174  function readInt(size) {
    175    let value = 0;
    176    let first = true;
    177    while (size-- > 0) {
    178      if (first) {
    179        const byte = array[pos++];
    180        value += byte & 0x7f;
    181        if (byte & 0x80) {
    182          value -= 0x80; // Treat most-significant bit as -2^i instead of 2^i
    183        }
    184        first = false;
    185      } else {
    186        value *= 256;
    187        value += array[pos++];
    188      }
    189    }
    190    return value;
    191  }
    192 
    193  function readUInt(size) {
    194    let value = 0;
    195    while (size-- > 0) {
    196      value *= 256;
    197      value += array[pos++];
    198    }
    199    return value;
    200  }
    201 
    202  function readFloat(size) {
    203    const view = new DataView(array.buffer, pos, size);
    204    pos += size;
    205    if (size === 4) {
    206      return view.getFloat32(0, false);
    207    }
    208    if (size === 8) {
    209      return view.getFloat64(0, false);
    210    }
    211    throw new Error("Invalid size for readFloat.");
    212  }
    213 
    214  function readBin(size, lengthSize) {
    215    if (size < 0) {
    216      size = readUInt(lengthSize);
    217    }
    218    const readData = array.subarray(pos, pos + size);
    219    pos += size;
    220    return readData;
    221  }
    222 
    223  function readMap(size, lengthSize) {
    224    if (size < 0) {
    225      size = readUInt(lengthSize);
    226    }
    227    const readData = {};
    228    while (size-- > 0) {
    229      const key = read();
    230      readData[key] = read();
    231    }
    232    return readData;
    233  }
    234 
    235  function readArray(size, lengthSize) {
    236    if (size < 0) {
    237      size = readUInt(lengthSize);
    238    }
    239    const readData = [];
    240    while (size-- > 0) {
    241      readData.push(read());
    242    }
    243    return readData;
    244  }
    245 
    246  function readStr(size, lengthSize) {
    247    if (size < 0) {
    248      size = readUInt(lengthSize);
    249    }
    250    const start = pos;
    251    pos += size;
    252    return decodeUtf8(array, start, size);
    253  }
    254 
    255  function readExt(size, lengthSize) {
    256    if (size < 0) {
    257      size = readUInt(lengthSize);
    258    }
    259    const type = readUInt(1);
    260    const readData = readBin(size);
    261    switch (type) {
    262      case 255:
    263        return readExtDate(readData);
    264    }
    265    return { type, data: readData };
    266  }
    267 
    268  function readExtDate(givenData) {
    269    if (givenData.length === 4) {
    270      const sec =
    271        ((givenData[0] << 24) >>> 0) +
    272        ((givenData[1] << 16) >>> 0) +
    273        ((givenData[2] << 8) >>> 0) +
    274        givenData[3];
    275      return new Date(sec * 1000);
    276    }
    277    if (givenData.length === 8) {
    278      const ns =
    279        ((givenData[0] << 22) >>> 0) +
    280        ((givenData[1] << 14) >>> 0) +
    281        ((givenData[2] << 6) >>> 0) +
    282        (givenData[3] >>> 2);
    283      const sec =
    284        (givenData[3] & 0x3) * pow32 +
    285        ((givenData[4] << 24) >>> 0) +
    286        ((givenData[5] << 16) >>> 0) +
    287        ((givenData[6] << 8) >>> 0) +
    288        givenData[7];
    289      return new Date(sec * 1000 + ns / 1000000);
    290    }
    291    if (givenData.length === 12) {
    292      const ns =
    293        ((givenData[0] << 24) >>> 0) +
    294        ((givenData[1] << 16) >>> 0) +
    295        ((givenData[2] << 8) >>> 0) +
    296        givenData[3];
    297      pos -= 8;
    298      const sec = readInt(8);
    299      return new Date(sec * 1000 + ns / 1000000);
    300    }
    301    throw new Error("Invalid givenData length for a date value.");
    302  }
    303 }
    304 
    305 // Decodes a string from UTF-8 bytes.
    306 function decodeUtf8(bytes, start, length) {
    307  // Based on: https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
    308  let i = start,
    309    str = "";
    310  length += start;
    311  while (i < length) {
    312    let c = bytes[i++];
    313    if (c > 127) {
    314      if (c > 191 && c < 224) {
    315        if (i >= length) {
    316          throw new Error("UTF-8 decode: incomplete 2-byte sequence");
    317        }
    318        c = ((c & 31) << 6) | (bytes[i++] & 63);
    319      } else if (c > 223 && c < 240) {
    320        if (i + 1 >= length) {
    321          throw new Error("UTF-8 decode: incomplete 3-byte sequence");
    322        }
    323        c = ((c & 15) << 12) | ((bytes[i++] & 63) << 6) | (bytes[i++] & 63);
    324      } else if (c > 239 && c < 248) {
    325        if (i + 2 >= length) {
    326          throw new Error("UTF-8 decode: incomplete 4-byte sequence");
    327        }
    328        c =
    329          ((c & 7) << 18) |
    330          ((bytes[i++] & 63) << 12) |
    331          ((bytes[i++] & 63) << 6) |
    332          (bytes[i++] & 63);
    333      } else {
    334        throw new Error(
    335          "UTF-8 decode: unknown multibyte start 0x" +
    336            c.toString(16) +
    337            " at index " +
    338            (i - 1)
    339        );
    340      }
    341    }
    342    if (c <= 0xffff) {
    343      str += String.fromCharCode(c);
    344    } else if (c <= 0x10ffff) {
    345      c -= 0x10000;
    346      str += String.fromCharCode((c >> 10) | 0xd800);
    347      str += String.fromCharCode((c & 0x3ff) | 0xdc00);
    348    } else {
    349      throw new Error(
    350        "UTF-8 decode: code point 0x" + c.toString(16) + " exceeds UTF-16 reach"
    351      );
    352    }
    353  }
    354  return str;
    355 }
    356 
    357 // The exported functions
    358 const msgpack = {
    359  deserialize,
    360 
    361  // Compatibility with other libraries
    362  decode: deserialize,
    363 };
    364 
    365 module.exports = msgpack;