1 /* jshint browser: true */
2 /* global define, module */
3 ( // Module boilerplate to support browser globals and browserify and AMD.
4 typeof define === "function" ? function (m) { define("msgpack-js", m); } :
5 typeof exports === "object" ? function (m) { module.exports = m(); } :
6 function(m){ this.msgpack = m(); }
12 exports.inspect = inspect;
13 function inspect(buffer) {
14 if (buffer === undefined) return "undefined";
17 if (buffer instanceof ArrayBuffer) {
19 view = new DataView(buffer);
21 else if (buffer instanceof DataView) {
25 if (!view) return JSON.stringify(buffer);
27 for (var i = 0; i < buffer.byteLength; i++) {
32 var byte = view.getUint8(i).toString(16);
33 if (byte.length === 1) byte = "0" + byte;
36 return "<" + type + " " + bytes.join(" ") + ">";
39 // Encode string as utf8 into dataview at offset
40 exports.utf8Write = utf8Write;
41 function utf8Write(view, offset, string) {
42 var byteLength = view.byteLength;
43 for(var i = 0, l = string.length; i < l; i++) {
44 var codePoint = string.charCodeAt(i);
47 if (codePoint < 0x80) {
48 view.setUint8(offset++, codePoint >>> 0 & 0x7f | 0x00);
53 if (codePoint < 0x800) {
54 view.setUint8(offset++, codePoint >>> 6 & 0x1f | 0xc0);
55 view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80);
59 // Three bytes of UTF-8.
60 if (codePoint < 0x10000) {
61 view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0);
62 view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80);
63 view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80);
67 // Four bytes of UTF-8
68 if (codePoint < 0x110000) {
69 view.setUint8(offset++, codePoint >>> 18 & 0x07 | 0xf0);
70 view.setUint8(offset++, codePoint >>> 12 & 0x3f | 0x80);
71 view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80);
72 view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80);
75 throw new Error("bad codepoint " + codePoint);
79 exports.utf8Read = utf8Read;
80 function utf8Read(view, offset, length) {
82 for (var i = offset, end = offset + length; i < end; i++) {
83 var byte = view.getUint8(i);
85 if ((byte & 0x80) === 0x00) {
86 string += String.fromCharCode(byte);
90 if ((byte & 0xe0) === 0xc0) {
91 string += String.fromCharCode(
92 ((byte & 0x0f) << 6) |
93 (view.getUint8(++i) & 0x3f)
97 // Three byte character
98 if ((byte & 0xf0) === 0xe0) {
99 string += String.fromCharCode(
100 ((byte & 0x0f) << 12) |
101 ((view.getUint8(++i) & 0x3f) << 6) |
102 ((view.getUint8(++i) & 0x3f) << 0)
106 // Four byte character
107 if ((byte & 0xf8) === 0xf0) {
108 string += String.fromCharCode(
109 ((byte & 0x07) << 18) |
110 ((view.getUint8(++i) & 0x3f) << 12) |
111 ((view.getUint8(++i) & 0x3f) << 6) |
112 ((view.getUint8(++i) & 0x3f) << 0)
116 throw new Error("Invalid byte " + byte.toString(16));
121 exports.utf8ByteCount = utf8ByteCount;
122 function utf8ByteCount(string) {
124 for(var i = 0, l = string.length; i < l; i++) {
125 var codePoint = string.charCodeAt(i);
126 if (codePoint < 0x80) {
130 if (codePoint < 0x800) {
134 if (codePoint < 0x10000) {
138 if (codePoint < 0x110000) {
142 throw new Error("bad codepoint " + codePoint);
147 exports.encode = function (value) {
148 var buffer = new ArrayBuffer(encodedSize(value));
149 var view = new DataView(buffer);
150 encode(value, view, 0);
154 exports.decode = decode;
156 // https://github.com/msgpack/msgpack/blob/master/spec.md
157 // we reserve extension type 0x00 to encode javascript 'undefined'
159 function Decoder(view, offset) {
160 this.offset = offset || 0;
163 Decoder.prototype.map = function (length) {
165 for (var i = 0; i < length; i++) {
166 var key = this.parse();
167 value[key] = this.parse();
171 Decoder.prototype.bin = function (length) {
172 var value = new ArrayBuffer(length);
173 (new Uint8Array(value)).set(new Uint8Array(this.view.buffer, this.offset, length), 0);
174 this.offset += length;
177 Decoder.prototype.str = function (length) {
178 var value = utf8Read(this.view, this.offset, length);
179 this.offset += length;
182 Decoder.prototype.array = function (length) {
183 var value = new Array(length);
184 for (var i = 0; i < length; i++) {
185 value[i] = this.parse();
189 Decoder.prototype.parse = function () {
190 var type = this.view.getUint8(this.offset);
193 if ((type & 0xe0) === 0xa0) {
194 length = type & 0x1f;
196 return this.str(length);
199 if ((type & 0xf0) === 0x80) {
200 length = type & 0x0f;
202 return this.map(length);
205 if ((type & 0xf0) === 0x90) {
206 length = type & 0x0f;
208 return this.array(length);
211 if ((type & 0x80) === 0x00) {
216 if ((type & 0xe0) === 0xe0) {
217 value = this.view.getInt8(this.offset);
221 // Undefined as FixExt1
222 if (type === 0xd4 && this.view.getUint8(this.offset + 1) === 0x00) {
229 length = this.view.getUint8(this.offset + 1);
231 return this.str(length);
234 length = this.view.getUint16(this.offset + 1);
236 return this.str(length);
239 length = this.view.getUint32(this.offset + 1);
241 return this.str(length);
244 length = this.view.getUint8(this.offset + 1);
246 return this.bin(length);
249 length = this.view.getUint16(this.offset + 1);
251 return this.bin(length);
254 length = this.view.getUint32(this.offset + 1);
256 return this.bin(length);
271 value = this.view.getUint8(this.offset + 1);
276 value = this.view.getUint16(this.offset + 1);
281 value = this.view.getUint32(this.offset + 1);
286 var high = this.view.getUint32(this.offset + 1);
287 var low = this.view.getUint32(this.offset + 5);
288 value = high*0x100000000 + low;
293 value = this.view.getInt8(this.offset + 1);
298 value = this.view.getInt16(this.offset + 1);
303 value = this.view.getInt32(this.offset + 1);
308 var high = this.view.getInt32(this.offset + 1);
309 var low = this.view.getUint32(this.offset + 5);
310 value = high*0x100000000 + low;
315 length = this.view.getUint16(this.offset + 1);
317 return this.map(length);
320 length = this.view.getUint32(this.offset + 1);
322 return this.map(length);
325 length = this.view.getUint16(this.offset + 1);
327 return this.array(length);
330 length = this.view.getUint32(this.offset + 1);
332 return this.array(length);
335 value = this.view.getFloat32(this.offset + 1);
340 value = this.view.getFloat64(this.offset + 1);
344 throw new Error("Unknown type 0x" + type.toString(16));
346 function decode(buffer) {
347 var view = new DataView(buffer);
348 var decoder = new Decoder(view);
349 var value = decoder.parse();
350 if (decoder.offset !== buffer.byteLength) throw new Error((buffer.byteLength - decoder.offset) + " trailing bytes");
354 function encode(value, view, offset) {
355 var type = typeof value;
358 if (type === "string") {
359 var length = utf8ByteCount(value);
362 view.setUint8(offset, length | 0xa0);
363 utf8Write(view, offset + 1, value);
367 if (length < 0x100) {
368 view.setUint8(offset, 0xd9);
369 view.setUint8(offset + 1, length);
370 utf8Write(view, offset + 2, value);
374 if (length < 0x10000) {
375 view.setUint8(offset, 0xda);
376 view.setUint16(offset + 1, length);
377 utf8Write(view, offset + 3, value);
381 if (length < 0x100000000) {
382 view.setUint8(offset, 0xdb);
383 view.setUint32(offset + 1, length);
384 utf8Write(view, offset + 5, value);
389 if (value instanceof ArrayBuffer) {
390 var length = value.byteLength;
392 if (length < 0x100) {
393 view.setUint8(offset, 0xc4);
394 view.setUint8(offset + 1, length);
395 (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 2);
399 if (length < 0x10000) {
400 view.setUint8(offset, 0xc5);
401 view.setUint16(offset + 1, length);
402 (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 3);
406 if (length < 0x100000000) {
407 view.setUint8(offset, 0xc6);
408 view.setUint32(offset + 1, length);
409 (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 5);
414 if (type === "number") {
416 if ((value << 0) !== value) {
417 view.setUint8(offset, 0xcb);
418 view.setFloat64(offset + 1, value);
426 view.setUint8(offset, value);
431 view.setUint8(offset, 0xcc);
432 view.setUint8(offset + 1, value);
436 if (value < 0x10000) {
437 view.setUint8(offset, 0xcd);
438 view.setUint16(offset + 1, value);
442 if (value < 0x100000000) {
443 view.setUint8(offset, 0xce);
444 view.setUint32(offset + 1, value);
447 throw new Error("Number too big 0x" + value.toString(16));
450 if (value >= -0x20) {
451 view.setInt8(offset, value);
455 if (value >= -0x80) {
456 view.setUint8(offset, 0xd0);
457 view.setInt8(offset + 1, value);
461 if (value >= -0x8000) {
462 view.setUint8(offset, 0xd1);
463 view.setInt16(offset + 1, value);
467 if (value >= -0x80000000) {
468 view.setUint8(offset, 0xd2);
469 view.setInt32(offset + 1, value);
472 throw new Error("Number too small -0x" + (-value).toString(16).substr(1));
476 if (type === "undefined") {
477 view.setUint8(offset, 0xd4); // fixext 1
478 view.setUint8(offset + 1, 0); // type (undefined)
479 view.setUint8(offset + 2, 0); // data (ignored)
484 if (value === null) {
485 view.setUint8(offset, 0xc0);
490 if (type === "boolean") {
491 view.setUint8(offset, value ? 0xc3 : 0xc2);
496 if (type === "object") {
497 var length, size = 0;
498 var isArray = Array.isArray(value);
501 length = value.length;
504 var keys = Object.keys(value);
505 length = keys.length;
510 view.setUint8(offset, length | (isArray ? 0x90 : 0x80));
513 else if (length < 0x10000) {
514 view.setUint8(offset, isArray ? 0xdc : 0xde);
515 view.setUint16(offset + 1, length);
518 else if (length < 0x100000000) {
519 view.setUint8(offset, isArray ? 0xdd : 0xdf);
520 view.setUint32(offset + 1, length);
525 for (var i = 0; i < length; i++) {
526 size += encode(value[i], view, offset + size);
530 for (var i = 0; i < length; i++) {
532 size += encode(key, view, offset + size);
533 size += encode(value[key], view, offset + size);
539 throw new Error("Unknown type " + type);
542 function encodedSize(value) {
543 var type = typeof value;
546 if (type === "string") {
547 var length = utf8ByteCount(value);
551 if (length < 0x100) {
554 if (length < 0x10000) {
557 if (length < 0x100000000) {
562 if (value instanceof ArrayBuffer) {
563 var length = value.byteLength;
564 if (length < 0x100) {
567 if (length < 0x10000) {
570 if (length < 0x100000000) {
575 if (type === "number") {
578 if (value << 0 !== value) return 9;
583 if (value < 0x80) return 1;
585 if (value < 0x100) return 2;
587 if (value < 0x10000) return 3;
589 if (value < 0x100000000) return 5;
591 if (value < 0x10000000000000000) return 9;
592 throw new Error("Number too big 0x" + value.toString(16));
595 if (value >= -0x20) return 1;
597 if (value >= -0x80) return 2;
599 if (value >= -0x8000) return 3;
601 if (value >= -0x80000000) return 5;
603 if (value >= -0x8000000000000000) return 9;
604 throw new Error("Number too small -0x" + value.toString(16).substr(1));
608 if (type === "undefined") return 3;
611 if (type === "boolean" || value === null) return 1;
614 if (type === "object") {
615 var length, size = 0;
616 if (Array.isArray(value)) {
617 length = value.length;
618 for (var i = 0; i < length; i++) {
619 size += encodedSize(value[i]);
623 var keys = Object.keys(value);
624 length = keys.length;
625 for (var i = 0; i < length; i++) {
627 size += encodedSize(key) + encodedSize(value[key]);
633 if (length < 0x10000) {
636 if (length < 0x100000000) {
639 throw new Error("Array or object too long 0x" + length.toString(16));
641 throw new Error("Unknown type " + type);