UNPKG

messagepack

Version:

A MessagePack implementation for JavaScript.

725 lines (719 loc) 18.9 kB
function typeError(tag, expected) { throw new TypeError(`unexpected tag 0x${tag.toString(16)} (${expected} expected)`); } // positive fixint: 0xxx xxxx function posFixintTag(i) { return i & 0x7f; } function isPosFixintTag(tag) { return (tag & 0x80) === 0; } function readPosFixint(tag) { return tag & 0x7f; } // negative fixint: 111x xxxx function negFixintTag(i) { return 0xe0 | (i & 0x1f); } function isNegFixintTag(tag) { return (tag & 0xe0) == 0xe0; } function readNegFixint(tag) { return tag - 0x100; } // fixstr: 101x xxxx function fixstrTag(length) { return 0xa0 | (length & 0x1f); } function isFixstrTag(tag) { return (tag & 0xe0) == 0xa0; } function readFixstr(tag) { return tag & 0x1f; } // fixarray: 1001 xxxx function fixarrayTag(length) { return 0x90 | (length & 0x0f); } function isFixarrayTag(tag) { return (tag & 0xf0) == 0x90; } function readFixarray(tag) { return tag & 0x0f; } // fixmap: 1000 xxxx function fixmapTag(length) { return 0x80 | (length & 0x0f); } function isFixmapTag(tag) { return (tag & 0xf0) == 0x80; } function readFixmap(tag) { return tag & 0x0f; } function createWriteBuffer() { let view = new DataView(new ArrayBuffer(64)); let n = 0; function need(x) { if (n + x > view.byteLength) { const arr = new Uint8Array(Math.max(n + x, view.byteLength + 64)); arr.set(new Uint8Array(view.buffer.slice(0, n))); view = new DataView(arr.buffer); } } return { put(v) { need(v.byteLength); (new Uint8Array(view.buffer)).set(new Uint8Array(v), n); n += v.byteLength; }, putI8(v) { need(1); view.setInt8(n, v); ++n; }, putI16(v) { need(2); view.setInt16(n, v); n += 2; }, putI32(v) { need(4); view.setInt32(n, v); n += 4; }, putI64(v) { need(8); const neg = v < 0; if (neg) { v = -v; } let hi = (v / 0x100000000) | 0; let lo = (v % 0x100000000) | 0; if (neg) { // 2s complement lo = (~lo + 1) | 0; hi = lo === 0 ? (~hi + 1) | 0 : ~hi; } view.setUint32(n, hi); view.setUint32(n + 4, lo); n += 8; }, putUi8(v) { need(1); view.setUint8(n, v); ++n; }, putUi16(v) { need(2); view.setUint16(n, v); n += 2; }, putUi32(v) { need(4); view.setUint32(n, v); n += 4; }, putUi64(v) { need(8); view.setUint32(n, (v / 0x100000000) | 0); view.setUint32(n + 4, v % 0x100000000); n += 8; }, putF(v) { need(8); view.setFloat64(n, v); n += 8; }, ui8array() { return new Uint8Array(view.buffer.slice(0, n)); }, }; } function createReadBuffer(buf) { let view = ArrayBuffer.isView(buf) ? new DataView(buf.buffer, buf.byteOffset, buf.byteLength) : new DataView(buf); let n = 0; return { peek() { return view.getUint8(n); }, get(len) { n += len; const off = view.byteOffset; return view.buffer.slice(off + n - len, off + n); }, getI8() { return view.getInt8(n++); }, getI16() { n += 2; return view.getInt16(n - 2); }, getI32() { n += 4; return view.getInt32(n - 4); }, getI64() { n += 8; const hi = view.getInt32(n - 8); const lo = view.getUint32(n - 4); return hi * 0x100000000 + lo; }, getUi8() { return view.getUint8(n++); }, getUi16() { n += 2; return view.getUint16(n - 2); }, getUi32() { n += 4; return view.getUint32(n - 4); }, getUi64() { n += 8; const hi = view.getUint32(n - 8); const lo = view.getUint32(n - 4); return hi * 0x100000000 + lo; }, getF32() { n += 4; return view.getFloat32(n - 4); }, getF64() { n += 8; return view.getFloat64(n - 8); }, }; } function putBlob(buf, blob, baseTag) { const n = blob.byteLength; if (n <= 255) { buf.putUi8(baseTag); buf.putUi8(n); } else if (n <= 65535) { buf.putUi8(baseTag + 1); buf.putUi16(n); } else if (n <= 4294967295) { buf.putUi8(baseTag + 2); buf.putUi32(n); } else { throw new RangeError("length limit exceeded"); } buf.put(blob); } function getBlob(buf) { const tag = buf.getUi8(); let n; switch (tag) { case 192 /* Nil */: n = 0; break; case 196 /* Bin8 */: case 217 /* Str8 */: n = buf.getUi8(); break; case 197 /* Bin16 */: case 218 /* Str16 */: n = buf.getUi16(); break; case 198 /* Bin32 */: case 219 /* Str32 */: n = buf.getUi32(); break; default: if (!isFixstrTag(tag)) { typeError(tag, "bytes or string"); } n = readFixstr(tag); } return buf.get(n); } function putArrHeader(buf, n) { if (n < 16) { buf.putUi8(fixarrayTag(n)); } else { putCollectionHeader(buf, 220 /* Array16 */, n); } } function getArrHeader(buf, expect) { const tag = buf.getUi8(); const n = isFixarrayTag(tag) ? readFixarray(tag) : getCollectionHeader(buf, tag, 220 /* Array16 */, "array"); if (expect != null && n !== expect) { throw new Error(`invalid array header size ${n}`); } return n; } function putMapHeader(buf, n) { if (n < 16) { buf.putUi8(fixmapTag(n)); } else { putCollectionHeader(buf, 222 /* Map16 */, n); } } function getMapHeader(buf, expect) { const tag = buf.getUi8(); const n = isFixmapTag(tag) ? readFixmap(tag) : getCollectionHeader(buf, tag, 222 /* Map16 */, "map"); if (expect != null && n !== expect) { throw new Error(`invalid map header size ${n}`); } return n; } function putCollectionHeader(buf, baseTag, n) { if (n <= 65535) { buf.putUi8(baseTag); buf.putUi16(n); } else if (n <= 4294967295) { buf.putUi8(baseTag + 1); buf.putUi32(n); } else { throw new RangeError("length limit exceeded"); } } function getCollectionHeader(buf, tag, baseTag, typename) { switch (tag) { case 192 /* Nil */: return 0; case baseTag: // 16 bit return buf.getUi16(); case baseTag + 1: // 32 bit return buf.getUi32(); default: typeError(tag, typename); } } const Any = { enc(buf, v) { typeOf(v).enc(buf, v); }, dec(buf) { return tagType(buf.peek()).dec(buf); }, }; const Nil = { enc(buf, v) { buf.putUi8(192 /* Nil */); }, dec(buf) { const tag = buf.getUi8(); if (tag !== 192 /* Nil */) { typeError(tag, "nil"); } return null; }, }; const Bool = { enc(buf, v) { buf.putUi8(v ? 195 /* True */ : 194 /* False */); }, dec(buf) { const tag = buf.getUi8(); switch (tag) { case 192 /* Nil */: case 194 /* False */: return false; case 195 /* True */: return true; default: typeError(tag, "bool"); } }, }; const Int = { enc(buf, v) { if (-128 <= v && v <= 127) { if (v >= 0) { buf.putUi8(posFixintTag(v)); } else if (v > -32) { buf.putUi8(negFixintTag(v)); } else { buf.putUi8(208 /* Int8 */); buf.putUi8(v); } } else if (-32768 <= v && v <= 32767) { buf.putI8(209 /* Int16 */); buf.putI16(v); } else if (-2147483648 <= v && v <= 2147483647) { buf.putI8(210 /* Int32 */); buf.putI32(v); } else { buf.putI8(211 /* Int64 */); buf.putI64(v); } }, dec(buf) { const tag = buf.getUi8(); if (isPosFixintTag(tag)) { return readPosFixint(tag); } else if (isNegFixintTag(tag)) { return readNegFixint(tag); } switch (tag) { case 192 /* Nil */: return 0; // signed int types case 208 /* Int8 */: return buf.getI8(); case 209 /* Int16 */: return buf.getI16(); case 210 /* Int32 */: return buf.getI32(); case 211 /* Int64 */: return buf.getI64(); // unsigned int types case 204 /* Uint8 */: return buf.getUi8(); case 205 /* Uint16 */: return buf.getUi16(); case 206 /* Uint32 */: return buf.getUi32(); case 207 /* Uint64 */: return buf.getUi64(); default: typeError(tag, "int"); } }, }; const Uint = { enc(buf, v) { if (v < 0) { throw new Error(`not an uint: ${v}`); } else if (v <= 127) { buf.putUi8(posFixintTag(v)); } else if (v <= 255) { buf.putUi8(204 /* Uint8 */); buf.putUi8(v); } else if (v <= 65535) { buf.putUi8(205 /* Uint16 */); buf.putUi16(v); } else if (v <= 4294967295) { buf.putUi8(206 /* Uint32 */); buf.putUi32(v); } else { buf.putUi8(207 /* Uint64 */); buf.putUi64(v); } }, dec(buf) { const v = Int.dec(buf); if (v < 0) { throw new RangeError("uint underflow"); } return v; }, }; const Float = { enc(buf, v) { buf.putUi8(203 /* Float64 */); buf.putF(v); }, dec(buf) { const tag = buf.getUi8(); switch (tag) { case 192 /* Nil */: return 0; case 202 /* Float32 */: return buf.getF32(); case 203 /* Float64 */: return buf.getF64(); default: typeError(tag, "float"); } }, }; const Bytes = { enc(buf, v) { putBlob(buf, v, 196 /* Bin8 */); }, dec: getBlob, }; const Str = { enc(buf, v) { const utf8 = toUTF8(v); if (utf8.byteLength < 32) { buf.putUi8(fixstrTag(utf8.byteLength)); buf.put(utf8); } else { putBlob(buf, utf8, 217 /* Str8 */); } }, dec(buf) { return fromUTF8(getBlob(buf)); }, }; const Time = { enc(buf, v) { const ms = v.getTime(); buf.putUi8(199 /* Ext8 */); buf.putUi8(12); buf.putI8(-1); buf.putUi32((ms % 1000) * 1000000); buf.putI64(ms / 1000); }, dec(buf) { const tag = buf.getUi8(); switch (tag) { case 214 /* FixExt4 */: // 32-bit seconds if (buf.getI8() === -1) { return new Date(buf.getUi32() * 1000); } break; case 215 /* FixExt8 */: // 34-bit seconds + 30-bit nanoseconds if (buf.getI8() === -1) { const lo = buf.getUi32(); const hi = buf.getUi32(); // seconds: hi + (lo&0x3)*0x100000000 // nanoseconds: lo>>2 == lo/4 return new Date((hi + (lo & 0x3) * 0x100000000) * 1000 + lo / 4000000); } break; case 199 /* Ext8 */: // 64-bit seconds + 32-bit nanoseconds if (buf.getUi8() === 12 && buf.getI8() === -1) { const ns = buf.getUi32(); const s = buf.getI64(); return new Date(s * 1000 + ns / 1000000); } break; } typeError(tag, "time"); }, }; const Arr = TypedArr(Any); const Map = TypedMap(Any, Any); function TypedArr(valueT) { return { encHeader: putArrHeader, decHeader: getArrHeader, enc(buf, v) { putArrHeader(buf, v.length); v.forEach(x => valueT.enc(buf, x)); }, dec(buf) { const res = []; for (let n = getArrHeader(buf); n > 0; --n) { res.push(valueT.dec(buf)); } return res; }, }; } function TypedMap(keyT, valueT) { return { encHeader: putMapHeader, decHeader: getMapHeader, enc(buf, v) { const props = Object.keys(v); putMapHeader(buf, props.length); props.forEach(p => { keyT.enc(buf, p); valueT.enc(buf, v[p]); }); }, dec(buf) { const res = {}; for (let n = getMapHeader(buf); n > 0; --n) { const k = keyT.dec(buf); res[k] = valueT.dec(buf); } return res; }, }; } function structEncoder(fields) { const ordinals = Object.keys(fields); return (buf, v) => { putMapHeader(buf, ordinals.length); ordinals.forEach(ord => { const f = fields[ord]; Int.enc(buf, Number(ord)); f[1].enc(buf, v[f[0]]); }); }; } function structDecoder(fields) { return (buf) => { const res = {}; for (let n = getMapHeader(buf); n > 0; --n) { const f = fields[Int.dec(buf)]; if (f) { res[f[0]] = f[1].dec(buf); } else { Any.dec(buf); } } return res; }; } function Struct(fields) { return { enc: structEncoder(fields), dec: structDecoder(fields), }; } function unionEncoder(branches) { return (buf, v) => { putArrHeader(buf, 2); const ord = branches.ordinalOf(v); Int.enc(buf, ord); branches[ord].enc(buf, v); }; } function unionDecoder(branches) { return (buf) => { getArrHeader(buf, 2); const t = branches[Int.dec(buf)]; if (!t) { throw new TypeError("invalid union type"); } return t.dec(buf); }; } function Union(branches) { return { enc: unionEncoder(branches), dec: unionDecoder(branches), }; } function toUTF8(v) { const n = v.length; const bin = new Uint8Array(4 * n); let pos = 0, i = 0, c; while (i < n) { c = v.charCodeAt(i++); if ((c & 0xfc00) === 0xd800) { c = (c << 10) + v.charCodeAt(i++) - 0x35fdc00; } if (c < 0x80) { bin[pos++] = c; } else if (c < 0x800) { bin[pos++] = 0xc0 + (c >> 6); bin[pos++] = 0x80 + (c & 0x3f); } else if (c < 0x10000) { bin[pos++] = 0xe0 + (c >> 12); bin[pos++] = 0x80 + ((c >> 6) & 0x3f); bin[pos++] = 0x80 + (c & 0x3f); } else { bin[pos++] = 0xf0 + (c >> 18); bin[pos++] = 0x80 + ((c >> 12) & 0x3f); bin[pos++] = 0x80 + ((c >> 6) & 0x3f); bin[pos++] = 0x80 + (c & 0x3f); } } return bin.buffer.slice(0, pos); } function fromUTF8(buf) { return (new TextDecoder("utf-8")).decode(buf); } function typeOf(v) { switch (typeof v) { case "undefined": return Nil; case "boolean": return Bool; case "number": return !isFinite(v) || Math.floor(v) !== v ? Float : v < 0 ? Int : Uint; case "string": return Str; case "object": return v === null ? Nil : Array.isArray(v) ? Arr : v instanceof Uint8Array || v instanceof ArrayBuffer ? Bytes : v instanceof Date ? Time : Map; default: throw new TypeError(`unsupported type ${typeof v}`); } } function tagType(tag) { switch (tag) { case 192 /* Nil */: return Nil; case 194 /* False */: case 195 /* True */: return Bool; case 208 /* Int8 */: case 209 /* Int16 */: case 210 /* Int32 */: case 211 /* Int64 */: return Int; case 204 /* Uint8 */: case 205 /* Uint16 */: case 206 /* Uint32 */: case 207 /* Uint64 */: return Uint; case 202 /* Float32 */: case 203 /* Float64 */: return Float; case 196 /* Bin8 */: case 197 /* Bin16 */: case 198 /* Bin32 */: return Bytes; case 217 /* Str8 */: case 218 /* Str16 */: case 219 /* Str32 */: return Str; case 220 /* Array16 */: case 221 /* Array32 */: return Arr; case 222 /* Map16 */: case 223 /* Map32 */: return Map; case 214 /* FixExt4 */: case 215 /* FixExt8 */: case 199 /* Ext8 */: return Time; default: if (isPosFixintTag(tag) || isNegFixintTag(tag)) { return Int; } if (isFixstrTag(tag)) { return Str; } if (isFixarrayTag(tag)) { return Arr; } if (isFixmapTag(tag)) { return Map; } throw new TypeError(`unsupported tag ${tag}`); } } function encode(v, typ) { const buf = createWriteBuffer(); (typ || Any).enc(buf, v); return buf.ui8array(); } function decode(buf, typ) { return (typ || Any).dec(createReadBuffer(buf)); } export { Any, Arr, Bool, Bytes, Float, Int, Map, Nil, Str, Struct, Time, TypedArr, TypedMap, Uint, Union, decode, encode, structDecoder, structEncoder, unionDecoder, unionEncoder }; //# sourceMappingURL=messagepack.es.js.map