UNPKG

notepack.io

Version:

A fast Node.js implementation of the latest MessagePack spec

295 lines (271 loc) 7.94 kB
'use strict'; const DecodeKeyCache = require('./DecodeKeyCache'); const cache = new DecodeKeyCache(); function Decoder(buffer) { this.offset = 0; this.buffer = buffer; this.useKeyCache = false; } Decoder.prototype.array = function (length) { const value = new Array(length); for (let i = 0; i < length; i++) { value[i] = this.parse(); } return value; }; Decoder.prototype.map = function (length) { let key = '', value = {}; for (let i = 0; i < length; i++) { this.useKeyCache = true; key = this.parse(); this.useKeyCache = false; value[key] = this.parse(); } return value; }; Decoder.prototype.str = function (length) { if (this.useKeyCache) { const valueFromCache = cache.get(this.buffer, this.offset, length); if (valueFromCache) { this.offset += length; return valueFromCache; } } const value = this.buffer.toString('utf8', this.offset, this.offset + length); if (this.useKeyCache) { cache.set(this.buffer, this.offset, length, value); } this.offset += length; return value; }; Decoder.prototype.bin = function (length) { const value = this.buffer.slice(this.offset, this.offset + length); this.offset += length; return value; }; Decoder.prototype.arraybuffer = function (length) { const buffer = new ArrayBuffer(length); const view = new Uint8Array(buffer); for (let j = 0; j < length; j++) { view[j] = this.buffer[this.offset + j]; } this.offset += length; return buffer; }; Decoder.prototype.parse = function () { const prefix = this.buffer[this.offset++]; let value, length = 0, type = 0, hi = 0, lo = 0; if (prefix < 0xc0) { // positive fixint if (prefix < 0x80) { return prefix; } // fixmap if (prefix < 0x90) { return this.map(prefix & 0x0f); } // fixarray if (prefix < 0xa0) { return this.array(prefix & 0x0f); } // fixstr return this.str(prefix & 0x1f); } // negative fixint if (prefix > 0xdf) { return (0xff - prefix + 1) * -1; } switch (prefix) { // nil case 0xc0: return null; // false case 0xc2: return false; // true case 0xc3: return true; // bin case 0xc4: length = this.buffer.readUInt8(this.offset); this.offset += 1; return this.bin(length); case 0xc5: length = this.buffer.readUInt16BE(this.offset); this.offset += 2; return this.bin(length); case 0xc6: length = this.buffer.readUInt32BE(this.offset); this.offset += 4; return this.bin(length); // ext case 0xc7: length = this.buffer.readUInt8(this.offset); type = this.buffer.readInt8(this.offset + 1); this.offset += 2; if (type === 0) { // custom ArrayBuffer encoding (kept for backward-compatibility) return this.arraybuffer(length); } if (type === -1) { // timestamp 96 const ns = this.buffer.readUInt32BE(this.offset); hi = this.buffer.readInt32BE(this.offset + 4); lo = this.buffer.readUInt32BE(this.offset + 8); this.offset += 12; return new Date((hi * 0x100000000 + lo) * 1e3 + ns / 1e6); } return [type, this.bin(length)]; case 0xc8: length = this.buffer.readUInt16BE(this.offset); type = this.buffer.readInt8(this.offset + 2); this.offset += 3; if (type === 0) { // custom ArrayBuffer encoding (kept for backward-compatibility) return this.arraybuffer(length); } return [type, this.bin(length)]; case 0xc9: length = this.buffer.readUInt32BE(this.offset); type = this.buffer.readInt8(this.offset + 4); this.offset += 5; if (type === 0) { // custom ArrayBuffer encoding (kept for backward-compatibility) return this.arraybuffer(length); } return [type, this.bin(length)]; // float case 0xca: value = this.buffer.readFloatBE(this.offset); this.offset += 4; return value; case 0xcb: value = this.buffer.readDoubleBE(this.offset); this.offset += 8; return value; // uint case 0xcc: value = this.buffer.readUInt8(this.offset); this.offset += 1; return value; case 0xcd: value = this.buffer.readUInt16BE(this.offset); this.offset += 2; return value; case 0xce: value = this.buffer.readUInt32BE(this.offset); this.offset += 4; return value; case 0xcf: hi = this.buffer.readUInt32BE(this.offset) * Math.pow(2, 32); lo = this.buffer.readUInt32BE(this.offset + 4); this.offset += 8; return hi + lo; // int case 0xd0: value = this.buffer.readInt8(this.offset); this.offset += 1; return value; case 0xd1: value = this.buffer.readInt16BE(this.offset); this.offset += 2; return value; case 0xd2: value = this.buffer.readInt32BE(this.offset); this.offset += 4; return value; case 0xd3: hi = this.buffer.readInt32BE(this.offset) * Math.pow(2, 32); lo = this.buffer.readUInt32BE(this.offset + 4); this.offset += 8; return hi + lo; // fixext case 0xd4: type = this.buffer.readInt8(this.offset); this.offset += 1; if (type === 0x00) { // custom encoding for 'undefined' (kept for backward-compatibility) this.offset += 1; return void 0; } return [type, this.bin(1)]; case 0xd5: type = this.buffer.readInt8(this.offset); this.offset += 1; return [type, this.bin(2)]; case 0xd6: type = this.buffer.readInt8(this.offset); this.offset += 1; if (type === -1) { // timestamp 32 value = this.buffer.readUInt32BE(this.offset); this.offset += 4; return new Date(value * 1e3); } return [type, this.bin(4)]; case 0xd7: type = this.buffer.readInt8(this.offset); this.offset += 1; if (type === 0x00) { // custom date encoding (kept for backward-compatibility) hi = this.buffer.readInt32BE(this.offset) * Math.pow(2, 32); lo = this.buffer.readUInt32BE(this.offset + 4); this.offset += 8; return new Date(hi + lo); } if (type === -1) { // timestamp 64 hi = this.buffer.readUInt32BE(this.offset); lo = this.buffer.readUInt32BE(this.offset + 4); this.offset += 8; const s = (hi & 0x3) * 0x100000000 + lo; return new Date(s * 1e3 + (hi >>> 2) / 1e6); } return [type, this.bin(8)]; case 0xd8: type = this.buffer.readInt8(this.offset); this.offset += 1; return [type, this.bin(16)]; // str case 0xd9: length = this.buffer.readUInt8(this.offset); this.offset += 1; return this.str(length); case 0xda: length = this.buffer.readUInt16BE(this.offset); this.offset += 2; return this.str(length); case 0xdb: length = this.buffer.readUInt32BE(this.offset); this.offset += 4; return this.str(length); // array case 0xdc: length = this.buffer.readUInt16BE(this.offset); this.offset += 2; return this.array(length); case 0xdd: length = this.buffer.readUInt32BE(this.offset); this.offset += 4; return this.array(length); // map case 0xde: length = this.buffer.readUInt16BE(this.offset); this.offset += 2; return this.map(length); case 0xdf: length = this.buffer.readUInt32BE(this.offset); this.offset += 4; return this.map(length); } throw new Error('Could not parse'); }; function decode(buffer) { const decoder = new Decoder(buffer); const value = decoder.parse(); if (decoder.offset !== buffer.length) { throw new Error((buffer.length - decoder.offset) + ' trailing bytes'); } return value; } module.exports = decode;