You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
7.1 KiB
288 lines
7.1 KiB
'use strict' |
|
|
|
var Buffer = require('safe-buffer').Buffer |
|
var bl = require('bl') |
|
var TOLERANCE = 0.1 |
|
|
|
module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilityMode) { |
|
function encode (obj, avoidSlice) { |
|
var buf, |
|
len |
|
|
|
if (obj === undefined) { |
|
throw new Error('undefined is not encodable in msgpack!') |
|
} else if (obj === null) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = 0xc0 |
|
} else if (obj === true) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = 0xc3 |
|
} else if (obj === false) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = 0xc2 |
|
} else if (typeof obj === 'string') { |
|
len = Buffer.byteLength(obj) |
|
if (len < 32) { |
|
buf = Buffer.allocUnsafe(1 + len) |
|
buf[0] = 0xa0 | len |
|
if (len > 0) { |
|
buf.write(obj, 1) |
|
} |
|
} else if (len <= 0xff && !compatibilityMode) { |
|
// str8, but only when not in compatibility mode |
|
buf = Buffer.allocUnsafe(2 + len) |
|
buf[0] = 0xd9 |
|
buf[1] = len |
|
buf.write(obj, 2) |
|
} else if (len <= 0xffff) { |
|
buf = Buffer.allocUnsafe(3 + len) |
|
buf[0] = 0xda |
|
buf.writeUInt16BE(len, 1) |
|
buf.write(obj, 3) |
|
} else { |
|
buf = Buffer.allocUnsafe(5 + len) |
|
buf[0] = 0xdb |
|
buf.writeUInt32BE(len, 1) |
|
buf.write(obj, 5) |
|
} |
|
} else if (obj && (obj.readUInt32LE || obj instanceof Uint8Array)) { |
|
if (obj instanceof Uint8Array) { |
|
obj = Buffer.from(obj) |
|
} |
|
// weird hack to support Buffer |
|
// and Buffer-like objects |
|
if (obj.length <= 0xff) { |
|
buf = Buffer.allocUnsafe(2) |
|
buf[0] = 0xc4 |
|
buf[1] = obj.length |
|
} else if (obj.length <= 0xffff) { |
|
buf = Buffer.allocUnsafe(3) |
|
buf[0] = 0xc5 |
|
buf.writeUInt16BE(obj.length, 1) |
|
} else { |
|
buf = Buffer.allocUnsafe(5) |
|
buf[0] = 0xc6 |
|
buf.writeUInt32BE(obj.length, 1) |
|
} |
|
|
|
buf = bl([buf, obj]) |
|
} else if (Array.isArray(obj)) { |
|
if (obj.length < 16) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = 0x90 | obj.length |
|
} else if (obj.length < 65536) { |
|
buf = Buffer.allocUnsafe(3) |
|
buf[0] = 0xdc |
|
buf.writeUInt16BE(obj.length, 1) |
|
} else { |
|
buf = Buffer.allocUnsafe(5) |
|
buf[0] = 0xdd |
|
buf.writeUInt32BE(obj.length, 1) |
|
} |
|
|
|
buf = obj.reduce(function (acc, obj) { |
|
acc.append(encode(obj, true)) |
|
return acc |
|
}, bl().append(buf)) |
|
} else if (typeof obj === 'object') { |
|
buf = encodeExt(obj) || encodeObject(obj) |
|
} else if (typeof obj === 'number') { |
|
if (isFloat(obj)) { |
|
return encodeFloat(obj, forceFloat64) |
|
} else if (obj >= 0) { |
|
if (obj < 128) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = obj |
|
} else if (obj < 256) { |
|
buf = Buffer.allocUnsafe(2) |
|
buf[0] = 0xcc |
|
buf[1] = obj |
|
} else if (obj < 65536) { |
|
buf = Buffer.allocUnsafe(3) |
|
buf[0] = 0xcd |
|
buf.writeUInt16BE(obj, 1) |
|
} else if (obj <= 0xffffffff) { |
|
buf = Buffer.allocUnsafe(5) |
|
buf[0] = 0xce |
|
buf.writeUInt32BE(obj, 1) |
|
} else if (obj <= 9007199254740991) { |
|
buf = Buffer.allocUnsafe(9) |
|
buf[0] = 0xcf |
|
write64BitUint(buf, obj) |
|
} else { |
|
return encodeFloat(obj, true) |
|
} |
|
} else { |
|
if (obj >= -32) { |
|
buf = Buffer.allocUnsafe(1) |
|
buf[0] = 0x100 + obj |
|
} else if (obj >= -128) { |
|
buf = Buffer.allocUnsafe(2) |
|
buf[0] = 0xd0 |
|
buf.writeInt8(obj, 1) |
|
} else if (obj >= -32768) { |
|
buf = Buffer.allocUnsafe(3) |
|
buf[0] = 0xd1 |
|
buf.writeInt16BE(obj, 1) |
|
} else if (obj > -214748365) { |
|
buf = Buffer.allocUnsafe(5) |
|
buf[0] = 0xd2 |
|
buf.writeInt32BE(obj, 1) |
|
} else if (obj >= -9007199254740991) { |
|
buf = Buffer.allocUnsafe(9) |
|
buf[0] = 0xd3 |
|
write64BitInt(buf, 1, obj) |
|
} else { |
|
return encodeFloat(obj, true) |
|
} |
|
} |
|
} |
|
|
|
if (!buf) { |
|
throw new Error('not implemented yet') |
|
} |
|
|
|
if (avoidSlice) { |
|
return buf |
|
} else { |
|
return buf.slice() |
|
} |
|
} |
|
|
|
function encodeExt (obj) { |
|
var i |
|
var encoded |
|
var length = -1 |
|
var headers = [] |
|
|
|
for (i = 0; i < encodingTypes.length; i++) { |
|
if (encodingTypes[i].check(obj)) { |
|
encoded = encodingTypes[i].encode(obj) |
|
break |
|
} |
|
} |
|
|
|
if (!encoded) { |
|
return null |
|
} |
|
|
|
// we subtract 1 because the length does not |
|
// include the type |
|
length = encoded.length - 1 |
|
|
|
if (length === 1) { |
|
headers.push(0xd4) |
|
} else if (length === 2) { |
|
headers.push(0xd5) |
|
} else if (length === 4) { |
|
headers.push(0xd6) |
|
} else if (length === 8) { |
|
headers.push(0xd7) |
|
} else if (length === 16) { |
|
headers.push(0xd8) |
|
} else if (length < 256) { |
|
headers.push(0xc7) |
|
headers.push(length) |
|
} else if (length < 0x10000) { |
|
headers.push(0xc8) |
|
headers.push(length >> 8) |
|
headers.push(length & 0x00ff) |
|
} else { |
|
headers.push(0xc9) |
|
headers.push(length >> 24) |
|
headers.push((length >> 16) & 0x000000ff) |
|
headers.push((length >> 8) & 0x000000ff) |
|
headers.push(length & 0x000000ff) |
|
} |
|
|
|
return bl().append(Buffer.from(headers)).append(encoded) |
|
} |
|
|
|
function encodeObject (obj) { |
|
var acc = [] |
|
var length = 0 |
|
var key |
|
var header |
|
|
|
for (key in obj) { |
|
if (obj.hasOwnProperty(key) && |
|
obj[key] !== undefined && |
|
typeof obj[key] !== 'function') { |
|
++length |
|
acc.push(encode(key, true)) |
|
acc.push(encode(obj[key], true)) |
|
} |
|
} |
|
|
|
if (length < 16) { |
|
header = Buffer.allocUnsafe(1) |
|
header[0] = 0x80 | length |
|
} else { |
|
header = Buffer.allocUnsafe(3) |
|
header[0] = 0xde |
|
header.writeUInt16BE(length, 1) |
|
} |
|
|
|
acc.unshift(header) |
|
|
|
var result = acc.reduce(function (list, buf) { |
|
return list.append(buf) |
|
}, bl()) |
|
|
|
return result |
|
} |
|
|
|
return encode |
|
} |
|
|
|
function write64BitUint (buf, obj) { |
|
// Write long byte by byte, in big-endian order |
|
for (var currByte = 7; currByte >= 0; currByte--) { |
|
buf[currByte + 1] = (obj & 0xff) |
|
obj = obj / 256 |
|
} |
|
} |
|
|
|
function write64BitInt (buf, offset, num) { |
|
var negate = num < 0 |
|
|
|
if (negate) { |
|
num = Math.abs(num) |
|
} |
|
|
|
var lo = num % 4294967296 |
|
var hi = num / 4294967296 |
|
buf.writeUInt32BE(Math.floor(hi), offset + 0) |
|
buf.writeUInt32BE(lo, offset + 4) |
|
|
|
if (negate) { |
|
var carry = 1 |
|
for (var i = offset + 7; i >= offset; i--) { |
|
var v = (buf[i] ^ 0xff) + carry |
|
buf[i] = v & 0xff |
|
carry = v >> 8 |
|
} |
|
} |
|
} |
|
|
|
function isFloat (n) { |
|
return n !== Math.floor(n) |
|
} |
|
|
|
function encodeFloat (obj, forceFloat64) { |
|
var buf |
|
|
|
buf = Buffer.allocUnsafe(5) |
|
buf[0] = 0xca |
|
buf.writeFloatBE(obj, 1) |
|
|
|
// FIXME is there a way to check if a |
|
// value fits in a float? |
|
if (forceFloat64 || Math.abs(obj - buf.readFloatBE(1)) > TOLERANCE) { |
|
buf = Buffer.allocUnsafe(9) |
|
buf[0] = 0xcb |
|
buf.writeDoubleBE(obj, 1) |
|
} |
|
|
|
return buf |
|
}
|
|
|