Skip to content

Commit 8f050fd

Browse files
committed
http://d.hatena.ne.jp/uupaa/20101130
Signed-off-by: uupaa <uupaa.js@gmail.com>
1 parent 9a912d6 commit 8f050fd

File tree

2 files changed

+138
-118
lines changed

2 files changed

+138
-118
lines changed

msgpack.js

Lines changed: 130 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*!{id:msgpack.js,ver:1.02,license:"MIT",author:"uupaa.js@gmail.com"}*/
1+
/*!{id:msgpack.js,ver:1.03,license:"MIT",author:"uupaa.js@gmail.com"}*/
22

33
// === msgpack ===
44
// MessagePack -> http://msgpack.sourceforge.net/
@@ -18,15 +18,15 @@ globalScope.msgpack = {
1818
};
1919

2020
var _ie = /MSIE/.test(navigator.userAgent),
21-
_bit2num = {}, // BitStringToNumber { "00000000": 0, ... "11111111": 255 }
2221
_bin2num = {}, // BinaryStringToNumber { "\00": 0, ... "\ff": 255 }
2322
_num2bin = {}, // NumberToBinaryString { 0: "\00", ... 255: "\ff" }
2423
_num2b64 = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
2524
"abcdefghijklmnopqrstuvwxyz0123456789+/").split(""),
2625
_sign = { 8: 0x80, 16: 0x8000, 32: 0x80000000 },
27-
_pooledArray = [],
28-
_IEEE754positive = /^.(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/,
29-
_IEEE754negative = /^(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})(.{8})$/;
26+
_buf = [], // decode buffer
27+
_idx = 0, // decode buffer[index]
28+
_ary = [], // pooled array
29+
_toString = Object.prototype.toString;
3030

3131
// for WebWorkers Code Block
3232
self.importScripts && (onmessage = function(event) {
@@ -52,19 +52,19 @@ function msgpackpack(data, // @param Mix:
5252

5353
// msgpack.unpack
5454
function msgpackunpack(data) { // @param BinaryString/ByteArray:
55-
// @return Mix:
55+
// @return Mix/undefined: undefined is error return
5656
// [1][String to mix] msgpack.unpack("...") -> {}
5757
// [2][ByteArray to mix] msgpack.unpack([...]) -> {}
5858

59-
return { data: typeof data === "string" ? toByteArray(data)
60-
: data,
61-
index: -1, decode: decode }.decode();
59+
_buf = typeof data === "string" ? toByteArray(data) : data;
60+
_idx = -1;
61+
return decode();
6262
}
6363

6464
// inner - encoder
6565
function encode(rv, // @param ByteArray: result
6666
mix) { // @param Mix: source data
67-
var size = 0, i = 0, iz, c, ary, hash, pos,
67+
var size = 0, i = 0, iz, c, pos,
6868
high, low, i64 = 0, sign, exp, frac;
6969

7070
if (mix == null) { // null or undefined
@@ -111,38 +111,49 @@ function encode(rv, // @param ByteArray: result
111111
}
112112
if (i64) {
113113
high = Math.floor(mix / 0x100000000);
114-
low = mix & (0x100000000 - 1);
114+
low = mix & 0xffffffff;
115115
rv.push(mix < 0 ? 0xd3 : 0xcf,
116-
(high >> 24) & 0xff, (high >> 16) & 0xff,
117-
(high >> 8) & 0xff, high & 0xff,
118-
(low >> 24) & 0xff, (low >> 16) & 0xff,
119-
(low >> 8) & 0xff, low & 0xff);
116+
(high >> 24) & 0xff, (high >> 16) & 0xff,
117+
(high >> 8) & 0xff, high & 0xff,
118+
(low >> 24) & 0xff, (low >> 16) & 0xff,
119+
(low >> 8) & 0xff, low & 0xff);
120120
}
121121
} else { // double
122-
// THX! edvakf
123-
// http://javascript.g.hatena.ne.jp/edvakf/20100614/1276503044
124-
hash = _bit2num;
122+
// THX!! @edvakf
123+
// http://javascript.g.hatena.ne.jp/edvakf/20101128/1291000731
125124
sign = mix < 0;
126125
sign && (mix *= -1);
127126

127+
// exp => 11 bits
128128
// add offset 1023 to ensure positive
129129
// 0.6931471805599453 = Math.LN2;
130130
exp = ((Math.log(mix) / 0.6931471805599453) + 1023) | 0;
131131

132+
// frac => 53 bits
132133
// shift 52 - (exp - 1023) bits to make integer part exactly 53 bits,
133134
// then throw away trash less than decimal point
134-
frac = (Math.floor(mix * Math.pow(2, 52 + 1023 - exp))).
135-
toString(2).slice(1);
136-
137-
// exp is between 1 and 2047. make it 11 bits
138-
// http://d.hatena.ne.jp/uupaa/20101128
139-
_pooledArray = !sign ? _IEEE754positive.exec((exp + 4096).toString(2) + frac)
140-
: _IEEE754negative.exec((exp + 2048).toString(2) + frac);
141-
ary = _pooledArray; // alias
142-
rv.push(0xcb, hash[ary[1]], hash[ary[2]],
143-
hash[ary[3]], hash[ary[4]],
144-
hash[ary[5]], hash[ary[6]],
145-
hash[ary[7]], hash[ary[8]]);
135+
frac = Math.floor(mix * Math.pow(2, 52 + 1023 - exp));
136+
137+
// S+-Exp(11)--++-----------------Fraction(52bits)-----------------------+
138+
// || || |
139+
// v+----------++--------------------------------------------------------+
140+
// 00000000|00000000|00000000|00000000|00000000|00000000|00000000|00000000
141+
// 6 5 55 4 4 3 2 1 8 0
142+
// 3 6 21 8 0 2 4 6
143+
//
144+
// +----------high(32bits)-----------+ +----------low(32bits)------------+
145+
// | | | |
146+
// +---------------------------------+ +---------------------------------+
147+
// 3 2 21 1 8 0
148+
// 1 4 09 6
149+
low = frac & 0xffffffff;
150+
high = ((frac / 0x100000000) & 0xfffff) | (exp << 20);
151+
sign && (high += 0x80000000);
152+
153+
rv.push(0xcb, (high >> 24) & 0xff, (high >> 16) & 0xff,
154+
(high >> 8) & 0xff, high & 0xff,
155+
(low >> 24) & 0xff, (low >> 16) & 0xff,
156+
(low >> 8) & 0xff, low & 0xff);
146157
}
147158
break;
148159
case "string":
@@ -181,25 +192,35 @@ function encode(rv, // @param ByteArray: result
181192
}
182193
break;
183194
default: // array or hash
184-
if (Object.prototype.toString.call(mix) === "[object Array]") { // array
195+
if (_toString.call(mix) === "[object Array]") { // array
185196
size = mix.length;
186197
setType(rv, 16, size, [0x90, 0xdc, 0xdd]);
187198
for (; i < size; ++i) {
188199
encode(rv, mix[i]);
189200
}
190201
} else { // hash
191-
if (Object.keys) {
192-
size = Object.keys(mix).length;
193-
} else {
194-
for (i in mix) {
195-
mix.hasOwnProperty(i) && ++size;
196-
}
197-
}
198-
setType(rv, 16, size, [0x80, 0xde, 0xdf]);
202+
// http://d.hatena.ne.jp/uupaa/20101129
203+
pos = rv.length; // keep rewrite position
204+
205+
// set default type [0x80 + 0]
206+
rv.push(0x80);
207+
size = 0;
208+
199209
for (i in mix) {
210+
++size;
200211
encode(rv, i);
201212
encode(rv, mix[i]);
202213
}
214+
215+
// rewrite hash type.
216+
if (size && size < 16) {
217+
rv[pos] = 0x80 + size;
218+
} else if (size < 0x10000) { // 16
219+
rv.splice(pos, 1, 0xde, size >> 8, size & 0xff);
220+
} else if (size < 0x100000000) { // 32
221+
rv.splice(pos, 1, 0xdf, size >>> 24, (size >> 16) & 0xff,
222+
(size >> 8) & 0xff, size & 0xff);
223+
}
203224
}
204225
}
205226
}
@@ -208,10 +229,8 @@ function encode(rv, // @param ByteArray: result
208229

209230
// inner - decoder
210231
function decode() { // @return Mix:
211-
var rv, undef, size, i = 0, iz, msb = 0, c, sign, exp, frac, key, ary,
212-
that = this,
213-
data = that.data,
214-
type = data[++that.index];
232+
var ary, hash, num = 0, i = 0, iz, msb = 0, c, sign, exp, frac, key,
233+
buf = _buf, type = buf[++_idx];
215234

216235
if (type >= 0xe0) { // Negative FixNum (111x xxxx) (-32 ~ -1)
217236
return type - 0x100;
@@ -220,113 +239,108 @@ function decode() { // @return Mix:
220239
return type;
221240
}
222241
if (type < 0x90) { // FixMap (1000 xxxx)
223-
size = type - 0x80;
224-
type = 0x80;
242+
num = type - (type = 0x80);
225243
} else if (type < 0xa0) { // FixArray (1001 xxxx)
226-
size = type - 0x90;
227-
type = 0x90;
244+
num = type - (type = 0x90);
228245
} else if (type < 0xc0) { // FixRaw (101x xxxx)
229-
size = type - 0xa0;
230-
type = 0xa0;
246+
num = type - (type = 0xa0);
231247
}
232248
switch (type) {
233249
case 0xc0: return null;
234250
case 0xc2: return false;
235251
case 0xc3: return true;
236-
case 0xca: rv = readByte(that, 4); // float
237-
sign = rv & _sign[32]; // 1bit
238-
exp = (rv >> 23) & 0xff; // 8bits
239-
frac = rv & 0x7fffff; // 23bits
240-
if (!rv || rv === 0x80000000) { // 0.0 or -0.0
252+
case 0xca: // float
253+
num = buf[++_idx] * 0x1000000 + (buf[++_idx] << 16) +
254+
(buf[++_idx] << 8) + buf[++_idx];
255+
sign = num & _sign[32]; // 1bit
256+
exp = (num >> 23) & 0xff; // 8bits
257+
frac = num & 0x7fffff; // 23bits
258+
if (!num || num === 0x80000000) { // 0.0 or -0.0
241259
return 0;
242260
}
243261
if (exp === 0xff) { // NaN or Infinity
244262
return frac ? NaN : Infinity;
245263
}
246264
return (sign ? -1 : 1) *
247265
(frac | 0x800000) * Math.pow(2, exp - 127 - 23); // 127: bias
248-
case 0xcb: rv = readByte(that, 4); // double
249-
sign = rv & _sign[32]; // 1bit
250-
exp = (rv >> 20) & 0x7ff; // 11bits
251-
frac = rv & 0xfffff; // 52bits - 32bits (high word)
252-
if (!rv || rv === 0x80000000) { // 0.0 or -0.0
266+
case 0xcb: // double
267+
num = buf[++_idx] * 0x1000000 + (buf[++_idx] << 16) +
268+
(buf[++_idx] << 8) + buf[++_idx];
269+
sign = num & _sign[32]; // 1bit
270+
exp = (num >> 20) & 0x7ff; // 11bits
271+
frac = num & 0xfffff; // 52bits - 32bits (high word)
272+
if (!num || num === 0x80000000) { // 0.0 or -0.0
273+
_idx += 4;
253274
return 0;
254275
}
255276
if (exp === 0x7ff) { // NaN or Infinity
277+
_idx += 4;
256278
return frac ? NaN : Infinity;
257279
}
280+
num = buf[++_idx] * 0x1000000 + (buf[++_idx] << 16) +
281+
(buf[++_idx] << 8) + buf[++_idx];
258282
return (sign ? -1 : 1) *
259-
((frac | 0x100000) * Math.pow(2, exp - 1023 - 20) // 1023: bias
260-
+ readByte(that, 4) * Math.pow(2, exp - 1023 - 52));
261-
case 0xcf: return readByte(that, 4) * Math.pow(2, 32) +
262-
readByte(that, 4); // uint 64
263-
case 0xce: return readByte(that, 4); // uint 32
264-
case 0xcd: return readByte(that, 2); // uint 16
265-
case 0xcc: return readByte(that, 1); // uint 8
266-
case 0xd3: return decodeInt64(that); // int 64
267-
case 0xd2: rv = readByte(that, 4); // int 32
268-
case 0xd1: rv === undef && (rv = readByte(that, 2)); // int 16
269-
case 0xd0: rv === undef && (rv = readByte(that, 1)); // int 8
283+
((frac | 0x100000) * Math.pow(2, exp - 1023 - 20) // 1023: bias
284+
+ num * Math.pow(2, exp - 1023 - 52));
285+
// 0xcf: uint64, 0xce: uint32, 0xcd: uint16
286+
case 0xcf: num = buf[++_idx] * 0x1000000 + (buf[++_idx] << 16) +
287+
(buf[++_idx] << 8) + buf[++_idx];
288+
return num * 0x100000000 +
289+
buf[++_idx] * 0x1000000 + (buf[++_idx] << 16) +
290+
(buf[++_idx] << 8) + buf[++_idx];
291+
case 0xce: num += buf[++_idx] * 0x1000000 + (buf[++_idx] << 16);
292+
case 0xcd: num += buf[++_idx] << 8;
293+
case 0xcc: return num + buf[++_idx];
294+
case 0xd3: return decodeInt64();
295+
// 0xd2: int32, 0xd1: int16, 0xd0: int8
296+
case 0xd2: num += buf[++_idx] * 0x1000000 + (buf[++_idx] << 16);
297+
case 0xd1: num += buf[++_idx] << 8;
298+
case 0xd0: num += buf[++_idx];
270299
msb = 4 << ((type & 0x3) + 1); // 8, 16, 32
271-
return rv < _sign[msb] ? rv : rv - _sign[msb] * 2;
272-
case 0xdb: size = readByte(that, 4); // raw 32
273-
case 0xda: size === undef && (size = readByte(that, 2)); // raw 16
274-
case 0xa0: i = that.index + 1; // raw
275-
that.index += size;
300+
return num < _sign[msb] ? num : num - _sign[msb] * 2;
301+
// 0xdb: raw32, 0xda: raw16, 0xa0: raw
302+
case 0xdb: num += buf[++_idx] * 0x1000000 + (buf[++_idx] << 16);
303+
case 0xda: num += (buf[++_idx] << 8) + buf[++_idx];
304+
case 0xa0: i = _idx + 1;
276305
// utf8.decode
277-
_pooledArray = [];
278-
ary = _pooledArray; // alias
279-
for (iz = i + size; i < iz; ++i) {
280-
c = data[i]; // first byte
281-
if (c < 0x80) { // ASCII(0x00 ~ 0x7f)
282-
ary.push(c);
283-
} else if (c < 0xe0) {
284-
ary.push((c & 0x1f) << 6 | (data[++i] & 0x3f));
285-
} else if (c < 0xf0) {
286-
ary.push((c & 0x0f) << 12 | (data[++i] & 0x3f) << 6
287-
| (data[++i] & 0x3f));
288-
}
306+
_ary = [];
307+
ary = _ary; // alias
308+
for (iz = i + num; i < iz; ++i) {
309+
c = buf[i]; // first byte
310+
ary.push(c < 0x80 ? c : // ASCII(0x00 ~ 0x7f)
311+
c < 0xe0 ? ((c & 0x1f) << 6 | (buf[++i] & 0x3f)) :
312+
c < 0xf0 ? ((c & 0x0f) << 12 | (buf[++i] & 0x3f) << 6
313+
| (buf[++i] & 0x3f)) : 0);
289314
}
315+
_idx += num;
290316
return ary.length < 1024000 ? String.fromCharCode.apply(null, ary)
291317
: byteArrayToByteString(ary);
292-
case 0xdf: size = readByte(that, 4); // map 32
293-
case 0xde: size === undef && (size = readByte(that, 2)); // map 16
294-
case 0x80: for (rv = {}; i < size; ++i) { // map
295-
key = that.decode();
296-
rv[key] = that.decode(); // key/value pair
318+
// 0xdf: map32, 0xde: map16, 0x80: map
319+
case 0xdf: num += buf[++_idx] * 0x1000000 + (buf[++_idx] << 16);
320+
case 0xde: num += (buf[++_idx] << 8) + buf[++_idx];
321+
case 0x80: for (hash = {}; i < num; ++i) {
322+
key = decode();
323+
hash[key] = decode(); // key/value pair
297324
}
298-
return rv;
299-
case 0xdd: size = readByte(that, 4); // array 32
300-
case 0xdc: size === undef && (size = readByte(that, 2)); // array 16
301-
case 0x90: for (rv = []; i < size; ++i) { // array
302-
rv.push(that.decode());
325+
return hash;
326+
// 0xdd: array32, 0xdc: array16, 0x90: array
327+
case 0xdd: num += buf[++_idx] * 0x1000000 + (buf[++_idx] << 16);
328+
case 0xdc: num += (buf[++_idx] << 8) + buf[++_idx];
329+
case 0x90: for (ary = []; i < num; ++i) {
330+
ary.push(decode());
303331
}
332+
return ary;
304333
}
305-
return rv;
306-
}
307-
308-
// inner - read byte
309-
function readByte(that, // @param Object:
310-
size) { // @param Number:
311-
// @return Number:
312-
var rv = 0, data = that.data, i = that.index;
313-
314-
switch (size) {
315-
case 4: rv += data[++i] * 0x1000000 + (data[++i] << 16);
316-
case 2: rv += data[++i] << 8;
317-
case 1: rv += data[++i];
318-
}
319-
that.index = i;
320-
return rv;
334+
return;
321335
}
322336

323337
// inner - decode int64
324-
function decodeInt64(that) { // @param Object:
338+
function decodeInt64() {
325339
// @return Number:
326340
var rv, overflow = 0,
327-
bytes = that.data.slice(that.index + 1, that.index + 9);
341+
bytes = _buf.slice(_idx + 1, _idx + 9);
328342

329-
that.index += 8;
343+
_idx += 8;
330344

331345
// avoid overflow
332346
if (bytes[0] & 0x80) {
@@ -637,7 +651,6 @@ function base64encode(data) { // @param ByteArray:
637651

638652
for (; i < 0x100; ++i) {
639653
v = String.fromCharCode(i);
640-
_bit2num[("0000000" + i.toString(2)).slice(-8)] = i;
641654
_bin2num[v] = i; // "\00" -> 0x00
642655
_num2bin[i] = v; // 0 -> "\00"
643656
}

test/bench.htm

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@
112112
uu.id("json").value = "(*snip*)";
113113
uu.log("data size = " + dataSize);
114114
}
115+
function load10() {
116+
mix = {"0":"0\u0000\u0000\u0000\u0000","1":{"1":false},"2":null,"3":null,"4":0.032400000294840005,"5":[{"5":{"5":5}},false,false],"6":{"6":-6},"7":-0.05670000051597,"8":8,"9":9};
117+
uu.id("json").value = '{"0":"0\u0000\u0000\u0000\u0000","1":{"1":false},"2":null,"3":null,"4":0.032400000294840005,"5":[{"5":{"5":5}},false,false],"6":{"6":-6},"7":-0.05670000051597,"8":8,"9":9}';
118+
uu.log("data size = " + 10);
119+
}
115120
</script>
116121
</head><body>
117122
<h1>Benchmark</h1>
@@ -133,6 +138,7 @@ <h1>Benchmark</h1>
133138
<input type="button" value="Load 100000" onclick="solid(solid100000, 100000)" />
134139
-->
135140
<input type="button" value="Load 10000" onclick="solid(solid10000, 10000)" />
141+
<input type="button" value="Load 10" onclick="load10()" />
136142
<br />
137143
</td>
138144
<td> -&gt; </td>
@@ -149,7 +155,8 @@ <h1>Benchmark</h1>
149155
</td>
150156
</tr>
151157
</table>
152-
<textarea id="json" cols="120" rows="5"></textarea>
158+
<textarea id="json" cols="40" rows="2"></textarea>
159+
<br />
153160
<input type="button" id="Biggest" value="Create 100000" onclick="create(100000)" />
154161
<input type="button" value="Create 10000" onclick="create(10000)" />
155162
<input type="button" value="Create 1000" onclick="create(1000)" />

0 commit comments

Comments
 (0)