|
@@ -1,3 +1,5 @@
|
|
|
+/* jshint browser: true */
|
|
|
+/* global define, module */
|
|
|
( // Module boilerplate to support browser globals and browserify and AMD.
|
|
|
typeof define === "function" ? function (m) { define("msgpack-js", m); } :
|
|
|
typeof exports === "object" ? function (m) { module.exports = m(); } :
|
|
@@ -54,7 +56,7 @@ function utf8Write(view, offset, string) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // Three bytes of UTF-8.
|
|
|
+ // Three bytes of UTF-8.
|
|
|
if (codePoint < 0x10000) {
|
|
|
view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0);
|
|
|
view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80);
|
|
@@ -87,7 +89,7 @@ function utf8Read(view, offset, length) {
|
|
|
// Two byte character
|
|
|
if ((byte & 0xe0) === 0xc0) {
|
|
|
string += String.fromCharCode(
|
|
|
- ((byte & 0x1f) << 6) |
|
|
|
+ ((byte & 0x1f) << 6) |
|
|
|
(view.getUint8(++i) & 0x3f)
|
|
|
);
|
|
|
continue;
|
|
@@ -143,7 +145,7 @@ function utf8ByteCount(string) {
|
|
|
}
|
|
|
|
|
|
exports.encode = function (value) {
|
|
|
- var buffer = new ArrayBuffer(sizeof(value));
|
|
|
+ var buffer = new ArrayBuffer(encodedSize(value));
|
|
|
var view = new DataView(buffer);
|
|
|
encode(value, view, 0);
|
|
|
return buffer;
|
|
@@ -151,14 +153,8 @@ exports.encode = function (value) {
|
|
|
|
|
|
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
|
|
|
+// https://github.com/msgpack/msgpack/blob/master/spec.md
|
|
|
+// we reserve extension type 0x00 to encode javascript 'undefined'
|
|
|
|
|
|
function Decoder(view, offset) {
|
|
|
this.offset = offset || 0;
|
|
@@ -172,13 +168,13 @@ Decoder.prototype.map = function (length) {
|
|
|
}
|
|
|
return value;
|
|
|
};
|
|
|
-Decoder.prototype.buf = function (length) {
|
|
|
+Decoder.prototype.bin = function (length) {
|
|
|
var value = new ArrayBuffer(length);
|
|
|
(new Uint8Array(value)).set(new Uint8Array(this.view.buffer, this.offset, length), 0);
|
|
|
this.offset += length;
|
|
|
return value;
|
|
|
};
|
|
|
-Decoder.prototype.raw = function (length) {
|
|
|
+Decoder.prototype.str = function (length) {
|
|
|
var value = utf8Read(this.view, this.offset, length);
|
|
|
this.offset += length;
|
|
|
return value;
|
|
@@ -193,11 +189,11 @@ Decoder.prototype.array = function (length) {
|
|
|
Decoder.prototype.parse = function () {
|
|
|
var type = this.view.getUint8(this.offset);
|
|
|
var value, length;
|
|
|
- // FixRaw
|
|
|
+ // FixStr
|
|
|
if ((type & 0xe0) === 0xa0) {
|
|
|
length = type & 0x1f;
|
|
|
this.offset++;
|
|
|
- return this.raw(length);
|
|
|
+ return this.str(length);
|
|
|
}
|
|
|
// FixMap
|
|
|
if ((type & 0xf0) === 0x80) {
|
|
@@ -222,17 +218,42 @@ Decoder.prototype.parse = function () {
|
|
|
this.offset++;
|
|
|
return value;
|
|
|
}
|
|
|
+ // Undefined as FixExt1
|
|
|
+ if (type === 0xd4 && this.view.getUint8(this.offset + 1) === 0x00) {
|
|
|
+ this.offset += 3;
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
switch (type) {
|
|
|
- // raw 16
|
|
|
+ // str 8
|
|
|
+ case 0xd9:
|
|
|
+ length = this.view.getUint8(this.offset + 1);
|
|
|
+ this.offset += 2;
|
|
|
+ return this.str(length);
|
|
|
+ // str 16
|
|
|
case 0xda:
|
|
|
length = this.view.getUint16(this.offset + 1);
|
|
|
this.offset += 3;
|
|
|
- return this.raw(length);
|
|
|
- // raw 32
|
|
|
+ return this.str(length);
|
|
|
+ // str 32
|
|
|
case 0xdb:
|
|
|
length = this.view.getUint32(this.offset + 1);
|
|
|
this.offset += 5;
|
|
|
- return this.raw(length);
|
|
|
+ return this.str(length);
|
|
|
+ // bin 8
|
|
|
+ case 0xc4:
|
|
|
+ length = this.view.getUint8(this.offset + 1);
|
|
|
+ this.offset += 2;
|
|
|
+ return this.bin(length);
|
|
|
+ // bin 16
|
|
|
+ case 0xc5:
|
|
|
+ length = this.view.getUint16(this.offset + 1);
|
|
|
+ this.offset += 3;
|
|
|
+ return this.bin(length);
|
|
|
+ // bin 32
|
|
|
+ case 0xc6:
|
|
|
+ length = this.view.getUint32(this.offset + 1);
|
|
|
+ this.offset += 5;
|
|
|
+ return this.bin(length);
|
|
|
// nil
|
|
|
case 0xc0:
|
|
|
this.offset++;
|
|
@@ -245,10 +266,6 @@ Decoder.prototype.parse = function () {
|
|
|
case 0xc3:
|
|
|
this.offset++;
|
|
|
return true;
|
|
|
- // undefined
|
|
|
- case 0xc4:
|
|
|
- this.offset++;
|
|
|
- return undefined;
|
|
|
// uint8
|
|
|
case 0xcc:
|
|
|
value = this.view.getUint8(this.offset + 1);
|
|
@@ -264,6 +281,13 @@ Decoder.prototype.parse = function () {
|
|
|
value = this.view.getUint32(this.offset + 1);
|
|
|
this.offset += 5;
|
|
|
return value;
|
|
|
+ // uint 64
|
|
|
+ case 0xcf:
|
|
|
+ var high = this.view.getUint32(this.offset + 1);
|
|
|
+ var low = this.view.getUint32(this.offset + 5);
|
|
|
+ value = high*0x100000000 + low;
|
|
|
+ this.offset += 9;
|
|
|
+ return value;
|
|
|
// int 8
|
|
|
case 0xd0:
|
|
|
value = this.view.getInt8(this.offset + 1);
|
|
@@ -279,6 +303,13 @@ Decoder.prototype.parse = function () {
|
|
|
value = this.view.getInt32(this.offset + 1);
|
|
|
this.offset += 5;
|
|
|
return value;
|
|
|
+ // int 64
|
|
|
+ case 0xd3:
|
|
|
+ var high = this.view.getInt32(this.offset + 1);
|
|
|
+ var low = this.view.getUint32(this.offset + 5);
|
|
|
+ value = high*0x100000000 + low;
|
|
|
+ this.offset += 9;
|
|
|
+ return value;
|
|
|
// map 16
|
|
|
case 0xde:
|
|
|
length = this.view.getUint16(this.offset + 1);
|
|
@@ -299,16 +330,6 @@ Decoder.prototype.parse = function () {
|
|
|
length = this.view.getUint32(this.offset + 1);
|
|
|
this.offset += 5;
|
|
|
return this.array(length);
|
|
|
- // buffer 16
|
|
|
- case 0xd8:
|
|
|
- length = this.view.getUint16(this.offset + 1);
|
|
|
- this.offset += 3;
|
|
|
- return this.buf(length);
|
|
|
- // buffer 32
|
|
|
- case 0xd9:
|
|
|
- length = this.view.getUint32(this.offset + 1);
|
|
|
- this.offset += 5;
|
|
|
- return this.buf(length);
|
|
|
// float
|
|
|
case 0xca:
|
|
|
value = this.view.getFloat32(this.offset + 1);
|
|
@@ -336,20 +357,27 @@ function encode(value, view, offset) {
|
|
|
// Strings Bytes
|
|
|
if (type === "string") {
|
|
|
var length = utf8ByteCount(value);
|
|
|
- // fix raw
|
|
|
+ // fix str
|
|
|
if (length < 0x20) {
|
|
|
view.setUint8(offset, length | 0xa0);
|
|
|
utf8Write(view, offset + 1, value);
|
|
|
return 1 + length;
|
|
|
}
|
|
|
- // raw 16
|
|
|
+ // str 8
|
|
|
+ if (length < 0x100) {
|
|
|
+ view.setUint8(offset, 0xd9);
|
|
|
+ view.setUint8(offset + 1, length);
|
|
|
+ utf8Write(view, offset + 2, value);
|
|
|
+ return 2 + length;
|
|
|
+ }
|
|
|
+ // str 16
|
|
|
if (length < 0x10000) {
|
|
|
view.setUint8(offset, 0xda);
|
|
|
view.setUint16(offset + 1, length);
|
|
|
utf8Write(view, offset + 3, value);
|
|
|
return 3 + length;
|
|
|
}
|
|
|
- // raw 32
|
|
|
+ // str 32
|
|
|
if (length < 0x100000000) {
|
|
|
view.setUint8(offset, 0xdb);
|
|
|
view.setUint32(offset + 1, length);
|
|
@@ -360,22 +388,29 @@ function encode(value, view, offset) {
|
|
|
|
|
|
if (value instanceof ArrayBuffer) {
|
|
|
var length = value.byteLength;
|
|
|
- // buffer 16
|
|
|
+ // bin 8
|
|
|
+ if (length < 0x100) {
|
|
|
+ view.setUint8(offset, 0xc4);
|
|
|
+ view.setUint8(offset + 1, length);
|
|
|
+ (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 2);
|
|
|
+ return 2 + length;
|
|
|
+ }
|
|
|
+ // bin 16
|
|
|
if (length < 0x10000) {
|
|
|
- view.setUint8(offset, 0xd8);
|
|
|
+ view.setUint8(offset, 0xc5);
|
|
|
view.setUint16(offset + 1, length);
|
|
|
(new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 3);
|
|
|
return 3 + length;
|
|
|
}
|
|
|
- // buffer 32
|
|
|
+ // bin 32
|
|
|
if (length < 0x100000000) {
|
|
|
- view.setUint8(offset, 0xd9);
|
|
|
+ view.setUint8(offset, 0xc6);
|
|
|
view.setUint32(offset + 1, length);
|
|
|
(new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 5);
|
|
|
return 5 + length;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (type === "number") {
|
|
|
// Floating Point
|
|
|
if ((value << 0) !== value) {
|
|
@@ -436,13 +471,15 @@ function encode(value, view, offset) {
|
|
|
}
|
|
|
throw new Error("Number too small -0x" + (-value).toString(16).substr(1));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// undefined
|
|
|
if (type === "undefined") {
|
|
|
- view.setUint8(offset, 0xc4);
|
|
|
- return 1;
|
|
|
+ view.setUint8(offset, 0xd4); // fixext 1
|
|
|
+ view.setUint8(offset + 1, 0); // type (undefined)
|
|
|
+ view.setUint8(offset + 2, 0); // data (ignored)
|
|
|
+ return 3;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// null
|
|
|
if (value === null) {
|
|
|
view.setUint8(offset, 0xc0);
|
|
@@ -454,7 +491,7 @@ function encode(value, view, offset) {
|
|
|
view.setUint8(offset, value ? 0xc3 : 0xc2);
|
|
|
return 1;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Container Types
|
|
|
if (type === "object") {
|
|
|
var length, size = 0;
|
|
@@ -496,13 +533,13 @@ function encode(value, view, offset) {
|
|
|
size += encode(value[key], view, offset + size);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return size;
|
|
|
}
|
|
|
throw new Error("Unknown type " + type);
|
|
|
}
|
|
|
|
|
|
-function sizeof(value) {
|
|
|
+function encodedSize(value) {
|
|
|
var type = typeof value;
|
|
|
|
|
|
// Raw Bytes
|
|
@@ -511,6 +548,9 @@ function sizeof(value) {
|
|
|
if (length < 0x20) {
|
|
|
return 1 + length;
|
|
|
}
|
|
|
+ if (length < 0x100) {
|
|
|
+ return 2 + length;
|
|
|
+ }
|
|
|
if (length < 0x10000) {
|
|
|
return 3 + length;
|
|
|
}
|
|
@@ -518,9 +558,12 @@ function sizeof(value) {
|
|
|
return 5 + length;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (value instanceof ArrayBuffer) {
|
|
|
var length = value.byteLength;
|
|
|
+ if (length < 0x100) {
|
|
|
+ return 2 + length;
|
|
|
+ }
|
|
|
if (length < 0x10000) {
|
|
|
return 3 + length;
|
|
|
}
|
|
@@ -528,7 +571,7 @@ function sizeof(value) {
|
|
|
return 5 + length;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (type === "number") {
|
|
|
// Floating Point
|
|
|
// double
|
|
@@ -560,17 +603,20 @@ function sizeof(value) {
|
|
|
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;
|
|
|
-
|
|
|
+
|
|
|
+ // undefined
|
|
|
+ if (type === "undefined") return 3;
|
|
|
+
|
|
|
+ // Boolean, null
|
|
|
+ if (type === "boolean" || value === null) return 1;
|
|
|
+
|
|
|
// Container Types
|
|
|
if (type === "object") {
|
|
|
var length, size = 0;
|
|
|
if (Array.isArray(value)) {
|
|
|
length = value.length;
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
- size += sizeof(value[i]);
|
|
|
+ size += encodedSize(value[i]);
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
@@ -578,7 +624,7 @@ function sizeof(value) {
|
|
|
length = keys.length;
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
var key = keys[i];
|
|
|
- size += sizeof(key) + sizeof(value[key]);
|
|
|
+ size += encodedSize(key) + encodedSize(value[key]);
|
|
|
}
|
|
|
}
|
|
|
if (length < 0x10) {
|