Repository URL to install this package:
|
Version:
0.3.0 ▾
|
msgpack-js
/
msgpack.js
|
|---|
"use strict";
var bops = require('bops');
exports.encode = function (value) {
var toJSONed = []
var size = sizeof(value)
if(size == 0)
return undefined
var buffer = bops.create(size);
encode(value, buffer, 0);
return buffer;
};
exports.decode = decode;
// http://wiki.msgpack.org/display/MSGPACK/Format+specification
// I've extended the protocol to have two new types that were previously reserved.
// buffer 16 11011000 0xd8
// buffer 32 11011001 0xd9
// These work just like raw16 and raw32 except they are node buffers instead of strings.
//
// Also I've added a type for `undefined`
// undefined 11000100 0xc4
function Decoder(buffer, offset) {
this.offset = offset || 0;
this.buffer = buffer;
}
Decoder.prototype.map = function (length) {
var value = {};
for (var i = 0; i < length; i++) {
var key = this.parse();
value[key] = this.parse();
}
return value;
};
Decoder.prototype.buf = function (length) {
var value = bops.subarray(this.buffer, this.offset, this.offset + length);
this.offset += length;
return value;
};
Decoder.prototype.raw = function (length) {
var value = bops.to(bops.subarray(this.buffer, this.offset, this.offset + length));
this.offset += length;
return value;
};
Decoder.prototype.array = function (length) {
var value = new Array(length);
for (var i = 0; i < length; i++) {
value[i] = this.parse();
}
return value;
};
Decoder.prototype.parse = function () {
var type = this.buffer[this.offset];
var value, length;
// FixRaw
if ((type & 0xe0) === 0xa0) {
length = type & 0x1f;
this.offset++;
return this.raw(length);
}
// FixMap
if ((type & 0xf0) === 0x80) {
length = type & 0x0f;
this.offset++;
return this.map(length);
}
// FixArray
if ((type & 0xf0) === 0x90) {
length = type & 0x0f;
this.offset++;
return this.array(length);
}
// Positive FixNum
if ((type & 0x80) === 0x00) {
this.offset++;
return type;
}
// Negative Fixnum
if ((type & 0xe0) === 0xe0) {
value = bops.readInt8(this.buffer, this.offset);
this.offset++;
return value;
}
switch (type) {
// raw 16
case 0xda:
length = bops.readUInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return this.raw(length);
// raw 32
case 0xdb:
length = bops.readUInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return this.raw(length);
// nil
case 0xc0:
this.offset++;
return null;
// false
case 0xc2:
this.offset++;
return false;
// true
case 0xc3:
this.offset++;
return true;
// undefined
case 0xc4:
this.offset++;
return undefined;
// uint8
case 0xcc:
value = this.buffer[this.offset + 1];
this.offset += 2;
return value;
// uint 16
case 0xcd:
value = bops.readUInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return value;
// uint 32
case 0xce:
value = bops.readUInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return value;
// uint64
case 0xcf:
value = bops.readUInt64BE(this.buffer, this.offset + 1);
this.offset += 9;
return value;
// int 8
case 0xd0:
value = bops.readInt8(this.buffer, this.offset + 1);
this.offset += 2;
return value;
// int 16
case 0xd1:
value = bops.readInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return value;
// int 32
case 0xd2:
value = bops.readInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return value;
// int 64
case 0xd3:
value = bops.readInt64BE(this.buffer, this.offset + 1);
this.offset += 9;
return value;
// map 16
case 0xde:
length = bops.readUInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return this.map(length);
// map 32
case 0xdf:
length = bops.readUInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return this.map(length);
// array 16
case 0xdc:
length = bops.readUInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return this.array(length);
// array 32
case 0xdd:
length = bops.readUInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return this.array(length);
// buffer 16
case 0xd8:
length = bops.readUInt16BE(this.buffer, this.offset + 1);
this.offset += 3;
return this.buf(length);
// buffer 32
case 0xd9:
length = bops.readUInt32BE(this.buffer, this.offset + 1);
this.offset += 5;
return this.buf(length);
// float
case 0xca:
value = bops.readFloatBE(this.buffer, this.offset + 1);
this.offset += 5;
return value;
// double
case 0xcb:
value = bops.readDoubleBE(this.buffer, this.offset + 1);
this.offset += 9;
return value;
}
throw new Error("Unknown type 0x" + type.toString(16));
};
function decode(buffer) {
var decoder = new Decoder(buffer);
var value = decoder.parse();
if (decoder.offset !== buffer.length) throw new Error((buffer.length - decoder.offset) + " trailing bytes");
return value;
}
function encodeableKeys (value) {
return Object.keys(value).filter(function (e) {
return 'function' !== typeof value[e] || !!value[e].toJSON
})
}
function encode(value, buffer, offset) {
var type = typeof value;
var length, size;
// Strings Bytes
if (type === "string") {
value = bops.from(value);
length = value.length;
// fix raw
if (length < 0x20) {
buffer[offset] = length | 0xa0;
bops.copy(value, buffer, offset + 1);
return 1 + length;
}
// raw 16
if (length < 0x10000) {
buffer[offset] = 0xda;
bops.writeUInt16BE(buffer, length, offset + 1);
bops.copy(value, buffer, offset + 3);
return 3 + length;
}
// raw 32
if (length < 0x100000000) {
buffer[offset] = 0xdb;
bops.writeUInt32BE(buffer, length, offset + 1);
bops.copy(value, buffer, offset + 5);
return 5 + length;
}
}
if (bops.is(value)) {
length = value.length;
// buffer 16
if (length < 0x10000) {
buffer[offset] = 0xd8;
bops.writeUInt16BE(buffer, length, offset + 1);
bops.copy(value, buffer, offset + 3);
return 3 + length;
}
// buffer 32
if (length < 0x100000000) {
buffer[offset] = 0xd9;
bops.writeUInt32BE(buffer, length, offset + 1);
bops.copy(value, buffer, offset + 5);
return 5 + length;
}
}
if (type === "number") {
// Floating Point
if ((value << 0) !== value) {
buffer[offset] = 0xcb;
bops.writeDoubleBE(buffer, value, offset + 1);
return 9;
}
// Integers
if (value >=0) {
// positive fixnum
if (value < 0x80) {
buffer[offset] = value;
return 1;
}
// uint 8
if (value < 0x100) {
buffer[offset] = 0xcc;
buffer[offset + 1] = value;
return 2;
}
// uint 16
if (value < 0x10000) {
buffer[offset] = 0xcd;
bops.writeUInt16BE(buffer, value, offset + 1);
return 3;
}
// uint 32
if (value < 0x100000000) {
buffer[offset] = 0xce;
bops.writeUInt32BE(buffer, value, offset + 1);
return 5;
}
// uint 64
if (value < 0x10000000000000000) {
buffer[offset] = 0xcf;
bops.writeUInt64BE(buffer, value, offset + 1);
return 9;
}
throw new Error("Number too big 0x" + value.toString(16));
}
// negative fixnum
if (value >= -0x20) {
bops.writeInt8(buffer, value, offset);
return 1;
}
// int 8
if (value >= -0x80) {
buffer[offset] = 0xd0;
bops.writeInt8(buffer, value, offset + 1);
return 2;
}
// int 16
if (value >= -0x8000) {
buffer[offset] = 0xd1;
bops.writeInt16BE(buffer, value, offset + 1);
return 3;
}
// int 32
if (value >= -0x80000000) {
buffer[offset] = 0xd2;
bops.writeInt32BE(buffer, value, offset + 1);
return 5;
}
// int 64
if (value >= -0x8000000000000000) {
buffer[offset] = 0xd3;
bops.writeInt64BE(buffer, value, offset + 1);
return 9;
}
throw new Error("Number too small -0x" + value.toString(16).substr(1));
}
// undefined
if (type === "undefined") {
buffer[offset] = 0xc4;
return 1;
}
// null
if (value === null) {
buffer[offset] = 0xc0;
return 1;
}
// Boolean
if (type === "boolean") {
buffer[offset] = value ? 0xc3 : 0xc2;
return 1;
}
if('function' === typeof value.toJSON)
return encode(value.toJSON(), buffer, offset)
// Container Types
if (type === "object") {
size = 0;
var isArray = Array.isArray(value);
if (isArray) {
length = value.length;
}
else {
var keys = encodeableKeys(value)
length = keys.length;
}
if (length < 0x10) {
buffer[offset] = length | (isArray ? 0x90 : 0x80);
size = 1;
}
else if (length < 0x10000) {
buffer[offset] = isArray ? 0xdc : 0xde;
bops.writeUInt16BE(buffer, length, offset + 1);
size = 3;
}
else if (length < 0x100000000) {
buffer[offset] = isArray ? 0xdd : 0xdf;
bops.writeUInt32BE(buffer, length, offset + 1);
size = 5;
}
if (isArray) {
for (var i = 0; i < length; i++) {
size += encode(value[i], buffer, offset + size);
}
}
else {
for (var i = 0; i < length; i++) {
var key = keys[i];
size += encode(key, buffer, offset + size);
size += encode(value[key], buffer, offset + size);
}
}
return size;
}
if(type === "function")
return undefined
throw new Error("Unknown type " + type);
}
function sizeof(value) {
var type = typeof value;
var length, size;
// Raw Bytes
if (type === "string") {
// TODO: this creates a throw-away buffer which is probably expensive on browsers.
length = bops.from(value).length;
if (length < 0x20) {
return 1 + length;
}
if (length < 0x10000) {
return 3 + length;
}
if (length < 0x100000000) {
return 5 + length;
}
}
if (bops.is(value)) {
length = value.length;
if (length < 0x10000) {
return 3 + length;
}
if (length < 0x100000000) {
return 5 + length;
}
}
if (type === "number") {
// Floating Point
// double
if (value << 0 !== value) return 9;
// Integers
if (value >=0) {
// positive fixnum
if (value < 0x80) return 1;
// uint 8
if (value < 0x100) return 2;
// uint 16
if (value < 0x10000) return 3;
// uint 32
if (value < 0x100000000) return 5;
// uint 64
if (value < 0x10000000000000000) return 9;
throw new Error("Number too big 0x" + value.toString(16));
}
// negative fixnum
if (value >= -0x20) return 1;
// int 8
if (value >= -0x80) return 2;
// int 16
if (value >= -0x8000) return 3;
// int 32
if (value >= -0x80000000) return 5;
// int 64
if (value >= -0x8000000000000000) return 9;
throw new Error("Number too small -0x" + value.toString(16).substr(1));
}
// Boolean, null, undefined
if (type === "boolean" || type === "undefined" || value === null) return 1;
if('function' === typeof value.toJSON)
return sizeof(value.toJSON())
// Container Types
if (type === "object") {
if('function' === typeof value.toJSON)
value = value.toJSON()
size = 0;
if (Array.isArray(value)) {
length = value.length;
for (var i = 0; i < length; i++) {
size += sizeof(value[i]);
}
}
else {
var keys = encodeableKeys(value)
length = keys.length;
for (var i = 0; i < length; i++) {
var key = keys[i];
size += sizeof(key) + sizeof(value[key]);
}
}
if (length < 0x10) {
return 1 + size;
}
if (length < 0x10000) {
return 3 + size;
}
if (length < 0x100000000) {
return 5 + size;
}
throw new Error("Array or object too long 0x" + length.toString(16));
}
if(type === "function")
return 0
throw new Error("Unknown type " + type);
}