Comparing version 4.5.0 to 5.0.0
33
index.js
@@ -9,6 +9,8 @@ 'use strict' | ||
var buildEncode = require('./lib/encoder') | ||
var IncompleteBufferError = require('./lib/helpers.js').IncompleteBufferError | ||
var DateCodec = require('./lib/codecs/DateCodec') | ||
function msgpack (options) { | ||
var encodingTypes = [] | ||
var decodingTypes = [] | ||
var decodingTypes = new Map() | ||
@@ -18,5 +20,12 @@ options = options || { | ||
compatibilityMode: false, | ||
disableTimestampEncoding: false // if true, skips encoding Dates using the msgpack timestamp ext format (-1) | ||
// if true, skips encoding Dates using the msgpack | ||
// timestamp ext format (-1) | ||
disableTimestampEncoding: false | ||
} | ||
decodingTypes.set(DateCodec.type, DateCodec.decode) | ||
if (!options.disableTimestampEncoding) { | ||
encodingTypes.push(DateCodec) | ||
} | ||
function registerEncoder (check, encode) { | ||
@@ -26,5 +35,3 @@ assert(check, 'must have an encode function') | ||
encodingTypes.push({ | ||
check: check, encode: encode | ||
}) | ||
encodingTypes.push({ check, encode }) | ||
@@ -37,7 +44,3 @@ return this | ||
assert(decode, 'must have a decode function') | ||
decodingTypes.push({ | ||
type: type, decode: decode | ||
}) | ||
decodingTypes.set(type, decode) | ||
return this | ||
@@ -75,7 +78,7 @@ } | ||
return { | ||
encode: buildEncode(encodingTypes, options.forceFloat64, options.compatibilityMode, options.disableTimestampEncoding), | ||
encode: buildEncode(encodingTypes, options), | ||
decode: buildDecode(decodingTypes), | ||
register: register, | ||
registerEncoder: registerEncoder, | ||
registerDecoder: registerDecoder, | ||
register, | ||
registerEncoder, | ||
registerDecoder, | ||
encoder: streams.encoder, | ||
@@ -86,3 +89,3 @@ decoder: streams.decoder, | ||
type: 'msgpack5', | ||
IncompleteBufferError: buildDecode.IncompleteBufferError | ||
IncompleteBufferError | ||
} | ||
@@ -89,0 +92,0 @@ } |
'use strict' | ||
var bl = require('bl') | ||
var util = require('util') | ||
var IncompleteBufferError = require('./helpers.js').IncompleteBufferError | ||
function IncompleteBufferError (message) { | ||
Error.call(this) // super constructor | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, this.constructor) // super helper method to include stack trace in error object | ||
} | ||
this.name = this.constructor.name | ||
this.message = message || 'unable to decode' | ||
const SIZES = { | ||
0xc4: 2, | ||
0xc5: 3, | ||
0xc6: 5, | ||
0xc7: 3, | ||
0xc8: 4, | ||
0xc9: 6, | ||
0xca: 5, | ||
0xcb: 9, | ||
0xcc: 2, | ||
0xcd: 3, | ||
0xce: 5, | ||
0xcf: 9, | ||
0xd0: 2, | ||
0xd1: 3, | ||
0xd2: 5, | ||
0xd3: 9, | ||
0xd4: 3, | ||
0xd5: 4, | ||
0xd6: 6, | ||
0xd7: 10, | ||
0xd8: 18, | ||
0xd9: 2, | ||
0xda: 3, | ||
0xdb: 5, | ||
0xde: 3, | ||
0xdc: 3, | ||
0xdd: 5 | ||
} | ||
util.inherits(IncompleteBufferError, Error) | ||
function isValidDataSize (dataLength, bufLength, headerLength) { | ||
return bufLength >= headerLength + dataLength | ||
} | ||
@@ -20,293 +43,157 @@ module.exports = function buildDecode (decodingTypes) { | ||
function getSize (first) { | ||
switch (first) { | ||
case 0xc4: | ||
return 2 | ||
case 0xc5: | ||
return 3 | ||
case 0xc6: | ||
return 5 | ||
case 0xc7: | ||
return 3 | ||
case 0xc8: | ||
return 4 | ||
case 0xc9: | ||
return 6 | ||
case 0xca: | ||
return 5 | ||
case 0xcb: | ||
return 9 | ||
case 0xcc: | ||
return 2 | ||
case 0xcd: | ||
return 3 | ||
case 0xce: | ||
return 5 | ||
case 0xcf: | ||
return 9 | ||
case 0xd0: | ||
return 2 | ||
case 0xd1: | ||
return 3 | ||
case 0xd2: | ||
return 5 | ||
case 0xd3: | ||
return 9 | ||
case 0xd4: | ||
return 3 | ||
case 0xd5: | ||
return 4 | ||
case 0xd6: | ||
return 6 | ||
case 0xd7: | ||
return 10 | ||
case 0xd8: | ||
return 18 | ||
case 0xd9: | ||
return 2 | ||
case 0xda: | ||
return 3 | ||
case 0xdb: | ||
return 5 | ||
case 0xde: | ||
return 3 | ||
default: | ||
return -1 | ||
function decode (buf) { | ||
// TODO: Make it into ensureBl handler ? | ||
if (!(buf instanceof bl)) { | ||
buf = bl().append(buf) | ||
} | ||
} | ||
function hasMinBufferSize (first, length) { | ||
var size = getSize(first) | ||
var result = tryDecode(buf, 0) | ||
// Handle worst case ASAP and keep code flat | ||
if (!result) throw new IncompleteBufferError() | ||
if (size !== -1 && length < size) { | ||
return false | ||
} else { | ||
return true | ||
} | ||
buf.consume(result[1]) | ||
return result[0] | ||
} | ||
function isValidDataSize (dataLength, bufLength, headerLength) { | ||
return bufLength >= headerLength + dataLength | ||
} | ||
function tryDecode (buf, initialOffset) { | ||
if (buf.length <= initialOffset) return null | ||
function buildDecodeResult (value, bytesConsumed) { | ||
return { | ||
value: value, | ||
bytesConsumed: bytesConsumed | ||
const bufLength = buf.length - initialOffset | ||
var offset = initialOffset | ||
const first = buf.readUInt8(offset) | ||
offset += 1 | ||
const size = SIZES[first] || -1 | ||
if (bufLength < size) return null | ||
const inRange = (start, end) => first >= start && first <= end | ||
if (first < 0x80) return [first, 1] // 7-bits positive ints | ||
if ((first & 0xf0) === 0x80) { | ||
const length = first & 0x0f | ||
const headerSize = offset - initialOffset | ||
// we have an array with less than 15 elements | ||
return decodeMap(buf, offset, length, headerSize) | ||
} | ||
} | ||
if ((first & 0xf0) === 0x90) { | ||
const length = first & 0x0f | ||
const headerSize = offset - initialOffset | ||
// we have a map with less than 15 elements | ||
return decodeArray(buf, offset, length, headerSize) | ||
} | ||
function decode (buf) { | ||
if (!(buf instanceof bl)) { | ||
buf = bl().append(buf) | ||
if ((first & 0xe0) === 0xa0) { | ||
// fixstr up to 31 bytes | ||
const length = first & 0x1f | ||
if (!isValidDataSize(length, bufLength, 1)) return null | ||
const result = buf.toString('utf8', offset, offset + length) | ||
return [result, length + 1] | ||
} | ||
if (inRange(0xc0, 0xc3)) return decodeConstants(first) | ||
if (inRange(0xc4, 0xc6)) { | ||
const length = buf.readUIntBE(offset, size - 1) | ||
offset += size - 1 | ||
var result = tryDecode(buf) | ||
if (result) { | ||
buf.consume(result.bytesConsumed) | ||
return result.value | ||
} else { | ||
throw new IncompleteBufferError() | ||
if (!isValidDataSize(length, bufLength, size)) return null | ||
const result = buf.slice(offset, offset + length) | ||
return [result, size + length] | ||
} | ||
} | ||
if (inRange(0xc7, 0xc9)) { | ||
const length = buf.readUIntBE(offset, size - 2) | ||
offset += size - 2 | ||
function tryDecode (buf, offset) { | ||
offset = offset === undefined ? 0 : offset | ||
var bufLength = buf.length - offset | ||
if (bufLength <= 0) { | ||
return null | ||
const type = buf.readInt8(offset) | ||
offset += 1 | ||
if (!isValidDataSize(length, bufLength, size)) return null | ||
return decodeExt(buf, offset, type, length, size) | ||
} | ||
if (inRange(0xca, 0xcb)) return decodeFloat(buf, offset, size - 1) | ||
if (inRange(0xcc, 0xcf)) return decodeUnsignedInt(buf, offset, size - 1) | ||
if (inRange(0xd0, 0xd3)) return decodeSigned(buf, offset, size - 1) | ||
if (inRange(0xd4, 0xd8)) { | ||
var type = buf.readInt8(offset) // Signed | ||
offset += 1 | ||
return decodeExt(buf, offset, type, size - 2, 2) | ||
} | ||
var first = buf.readUInt8(offset) | ||
var length | ||
var result = 0 | ||
var type | ||
var bytePos | ||
if (inRange(0xd9, 0xdb)) { | ||
const length = buf.readUIntBE(offset, size - 1) | ||
offset += size - 1 | ||
if (!hasMinBufferSize(first, bufLength)) { | ||
return null | ||
if (!isValidDataSize(length, bufLength, size)) return null | ||
const result = buf.toString('utf8', offset, offset + length) | ||
return [result, size + length] | ||
} | ||
if (inRange(0xdc, 0xdd)) { | ||
const length = buf.readUIntBE(offset, size - 1) | ||
offset += size - 1 | ||
return decodeArray(buf, offset, length, size) | ||
} | ||
if (inRange(0xde, 0xdf)) { | ||
let length | ||
switch (first) { | ||
case 0xde: | ||
// maps up to 2^16 elements - 2 bytes | ||
length = buf.readUInt16BE(offset) | ||
offset += 2 | ||
// console.log(offset - initialOffset) | ||
return decodeMap(buf, offset, length, 3) | ||
switch (first) { | ||
case 0xc0: | ||
return buildDecodeResult(null, 1) | ||
case 0xc2: | ||
return buildDecodeResult(false, 1) | ||
case 0xc3: | ||
return buildDecodeResult(true, 1) | ||
case 0xcc: | ||
// 1-byte unsigned int | ||
result = buf.readUInt8(offset + 1) | ||
return buildDecodeResult(result, 2) | ||
case 0xcd: | ||
// 2-bytes BE unsigned int | ||
result = buf.readUInt16BE(offset + 1) | ||
return buildDecodeResult(result, 3) | ||
case 0xce: | ||
// 4-bytes BE unsigned int | ||
result = buf.readUInt32BE(offset + 1) | ||
return buildDecodeResult(result, 5) | ||
case 0xcf: | ||
// 8-bytes BE unsigned int | ||
// Read long byte by byte, big-endian | ||
for (bytePos = 7; bytePos >= 0; bytePos--) { | ||
result += (buf.readUInt8(offset + bytePos + 1) * Math.pow(2, (8 * (7 - bytePos)))) | ||
} | ||
return buildDecodeResult(result, 9) | ||
case 0xd0: | ||
// 1-byte signed int | ||
result = buf.readInt8(offset + 1) | ||
return buildDecodeResult(result, 2) | ||
case 0xd1: | ||
// 2-bytes signed int | ||
result = buf.readInt16BE(offset + 1) | ||
return buildDecodeResult(result, 3) | ||
case 0xd2: | ||
// 4-bytes signed int | ||
result = buf.readInt32BE(offset + 1) | ||
return buildDecodeResult(result, 5) | ||
case 0xd3: | ||
result = readInt64BE(buf.slice(offset + 1, offset + 9), 0) | ||
return buildDecodeResult(result, 9) | ||
case 0xca: | ||
// 4-bytes float | ||
result = buf.readFloatBE(offset + 1) | ||
return buildDecodeResult(result, 5) | ||
case 0xcb: | ||
// 8-bytes double | ||
result = buf.readDoubleBE(offset + 1) | ||
return buildDecodeResult(result, 9) | ||
case 0xd9: | ||
// strings up to 2^8 - 1 bytes | ||
length = buf.readUInt8(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 2)) { | ||
return null | ||
} | ||
result = buf.toString('utf8', offset + 2, offset + 2 + length) | ||
return buildDecodeResult(result, 2 + length) | ||
case 0xda: | ||
// strings up to 2^16 - 2 bytes | ||
length = buf.readUInt16BE(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 3)) { | ||
return null | ||
} | ||
result = buf.toString('utf8', offset + 3, offset + 3 + length) | ||
return buildDecodeResult(result, 3 + length) | ||
case 0xdb: | ||
// strings up to 2^32 - 4 bytes | ||
length = buf.readUInt32BE(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 5)) { | ||
return null | ||
} | ||
result = buf.toString('utf8', offset + 5, offset + 5 + length) | ||
return buildDecodeResult(result, 5 + length) | ||
case 0xc4: | ||
// buffers up to 2^8 - 1 bytes | ||
length = buf.readUInt8(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 2)) { | ||
return null | ||
} | ||
result = buf.slice(offset + 2, offset + 2 + length) | ||
return buildDecodeResult(result, 2 + length) | ||
case 0xc5: | ||
// buffers up to 2^16 - 1 bytes | ||
length = buf.readUInt16BE(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 3)) { | ||
return null | ||
} | ||
result = buf.slice(offset + 3, offset + 3 + length) | ||
return buildDecodeResult(result, 3 + length) | ||
case 0xc6: | ||
// buffers up to 2^32 - 1 bytes | ||
length = buf.readUInt32BE(offset + 1) | ||
if (!isValidDataSize(length, bufLength, 5)) { | ||
return null | ||
} | ||
result = buf.slice(offset + 5, offset + 5 + length) | ||
return buildDecodeResult(result, 5 + length) | ||
case 0xdc: | ||
// array up to 2^16 elements - 2 bytes | ||
if (bufLength < 3) { | ||
return null | ||
} | ||
case 0xdf: | ||
length = buf.readUInt32BE(offset) | ||
offset += 4 | ||
return decodeMap(buf, offset, length, 5) | ||
} | ||
} | ||
if (first >= 0xe0) return [first - 0x100, 1] // 5 bits negative ints | ||
length = buf.readUInt16BE(offset + 1) | ||
return decodeArray(buf, offset, length, 3) | ||
case 0xdd: | ||
// array up to 2^32 elements - 4 bytes | ||
if (bufLength < 5) { | ||
return null | ||
} | ||
throw new Error('not implemented yet') | ||
} | ||
length = buf.readUInt32BE(offset + 1) | ||
return decodeArray(buf, offset, length, 5) | ||
case 0xde: | ||
// maps up to 2^16 elements - 2 bytes | ||
length = buf.readUInt16BE(offset + 1) | ||
return decodeMap(buf, offset, length, 3) | ||
case 0xdf: | ||
length = buf.readUInt32BE(offset + 1) | ||
return decodeMap(buf, offset, length, 5) | ||
case 0xd4: | ||
return decodeFixExt(buf, offset, 1) | ||
case 0xd5: | ||
return decodeFixExt(buf, offset, 2) | ||
case 0xd6: | ||
return decodeFixExt(buf, offset, 4) | ||
case 0xd7: | ||
return decodeFixExt(buf, offset, 8) | ||
case 0xd8: | ||
return decodeFixExt(buf, offset, 16) | ||
case 0xc7: | ||
// ext up to 2^8 - 1 bytes | ||
length = buf.readUInt8(offset + 1) | ||
type = buf.readUInt8(offset + 2) | ||
if (!isValidDataSize(length, bufLength, 3)) { | ||
return null | ||
} | ||
return decodeExt(buf, offset, type, length, 3) | ||
case 0xc8: | ||
// ext up to 2^16 - 1 bytes | ||
length = buf.readUInt16BE(offset + 1) | ||
type = buf.readUInt8(offset + 3) | ||
if (!isValidDataSize(length, bufLength, 4)) { | ||
return null | ||
} | ||
return decodeExt(buf, offset, type, length, 4) | ||
case 0xc9: | ||
// ext up to 2^32 - 1 bytes | ||
length = buf.readUInt32BE(offset + 1) | ||
type = buf.readUInt8(offset + 5) | ||
if (!isValidDataSize(length, bufLength, 6)) { | ||
return null | ||
} | ||
return decodeExt(buf, offset, type, length, 6) | ||
function decodeArray (buf, initialOffset, length, headerLength) { | ||
var offset = initialOffset | ||
const result = [] | ||
var i = 0 | ||
while (i++ < length) { | ||
var decodeResult = tryDecode(buf, offset) | ||
if (!decodeResult) return null | ||
result.push(decodeResult[0]) | ||
offset += decodeResult[1] | ||
} | ||
return [result, headerLength + offset - initialOffset] | ||
} | ||
if ((first & 0xf0) === 0x90) { | ||
// we have an array with less than 15 elements | ||
length = first & 0x0f | ||
return decodeArray(buf, offset, length, 1) | ||
} else if ((first & 0xf0) === 0x80) { | ||
// we have a map with less than 15 elements | ||
length = first & 0x0f | ||
return decodeMap(buf, offset, length, 1) | ||
} else if ((first & 0xe0) === 0xa0) { | ||
// fixstr up to 31 bytes | ||
length = first & 0x1f | ||
if (isValidDataSize(length, bufLength, 1)) { | ||
result = buf.toString('utf8', offset + 1, offset + length + 1) | ||
return buildDecodeResult(result, length + 1) | ||
} else { | ||
return null | ||
function decodeMap (buf, offset, length, headerLength) { | ||
const _temp = decodeArray(buf, offset, 2 * length, headerLength) | ||
if (!_temp) return null | ||
const [ result, consumedBytes ] = _temp | ||
var isPlainObject = true | ||
for (let i = 0; i < 2 * length; i += 2) { | ||
if (typeof result[i] !== 'string') { | ||
isPlainObject = false | ||
break | ||
} | ||
} else if (first >= 0xe0) { | ||
// 5 bits negative ints | ||
result = first - 0x100 | ||
return buildDecodeResult(result, 1) | ||
} else if (first < 0x80) { | ||
// 7-bits positive ints | ||
return buildDecodeResult(first, 1) | ||
} | ||
if (isPlainObject) { | ||
const object = {} | ||
for (let i = 0; i < 2 * length; i += 2) { | ||
const key = result[i] | ||
const val = result[i + 1] | ||
object[key] = val | ||
} | ||
return [object, consumedBytes] | ||
} else { | ||
throw new Error('not implemented yet') | ||
const mapping = new Map() | ||
for (let i = 0; i < 2 * length; i += 2) { | ||
const key = result[i] | ||
const val = result[i + 1] | ||
mapping.set(key, val) | ||
} | ||
return [mapping, consumedBytes] | ||
} | ||
@@ -316,3 +203,3 @@ } | ||
function readInt64BE (buf, offset) { | ||
var negate = (buf[offset] & 0x80) == 0x80 // eslint-disable-line | ||
var negate = (buf[offset] & 0x80) == 0x80; // eslint-disable-line | ||
@@ -333,107 +220,40 @@ if (negate) { | ||
function decodeArray (buf, offset, length, headerLength) { | ||
var result = [] | ||
var i | ||
var totalBytesConsumed = 0 | ||
offset += headerLength | ||
for (i = 0; i < length; i++) { | ||
var decodeResult = tryDecode(buf, offset) | ||
if (decodeResult) { | ||
result.push(decodeResult.value) | ||
offset += decodeResult.bytesConsumed | ||
totalBytesConsumed += decodeResult.bytesConsumed | ||
} else { | ||
return null | ||
} | ||
} | ||
return buildDecodeResult(result, headerLength + totalBytesConsumed) | ||
function decodeUnsignedInt (buf, offset, size) { | ||
const maxOffset = offset + size | ||
var result = 0 | ||
while (offset < maxOffset) { result += buf.readUInt8(offset++) * Math.pow(256, maxOffset - offset) } | ||
return [result, size + 1] | ||
} | ||
function decodeMap (buf, offset, length, headerLength) { | ||
var result = {} | ||
var key | ||
var i | ||
var totalBytesConsumed = 0 | ||
offset += headerLength | ||
for (i = 0; i < length; i++) { | ||
var keyResult = tryDecode(buf, offset) | ||
if (keyResult) { | ||
offset += keyResult.bytesConsumed | ||
var valueResult = tryDecode(buf, offset) | ||
if (valueResult) { | ||
key = keyResult.value | ||
result[key] = valueResult.value | ||
offset += valueResult.bytesConsumed | ||
totalBytesConsumed += (keyResult.bytesConsumed + valueResult.bytesConsumed) | ||
} else { | ||
return null | ||
} | ||
} else { | ||
return null | ||
} | ||
} | ||
return buildDecodeResult(result, headerLength + totalBytesConsumed) | ||
function decodeConstants (first) { | ||
if (first === 0xc0) return [null, 1] | ||
if (first === 0xc2) return [false, 1] | ||
if (first === 0xc3) return [true, 1] | ||
} | ||
function decodeFixExt (buf, offset, size) { | ||
var type = buf.readInt8(offset + 1) // Signed | ||
return decodeExt(buf, offset, type, size, 2) | ||
function decodeSigned (buf, offset, size) { | ||
var result | ||
if (size === 1) result = buf.readInt8(offset) | ||
if (size === 2) result = buf.readInt16BE(offset) | ||
if (size === 4) result = buf.readInt32BE(offset) | ||
if (size === 8) result = readInt64BE(buf.slice(offset, offset + 8), 0) | ||
return [result, size + 1] | ||
} | ||
function decodeTimestamp (buf, size, headerSize) { | ||
var seconds | ||
var nanoseconds = 0 | ||
switch (size) { | ||
case 4: | ||
// timestamp 32 stores the number of seconds that have elapsed since 1970-01-01 00:00:00 UTC in an 32-bit unsigned integer | ||
seconds = buf.readUInt32BE(0) | ||
break | ||
case 8: | ||
// Timestamp 64 stores the number of seconds and nanoseconds that have elapsed | ||
// since 1970-01-01 00:00:00 UTC in 32-bit unsigned integers, split 30/34 bits | ||
var upper = buf.readUInt32BE(0) | ||
var lower = buf.readUInt32BE(4) | ||
nanoseconds = upper / 4 | ||
seconds = ((upper & 0x03) * Math.pow(2, 32)) + lower // If we use bitwise operators, we get truncated to 32bits | ||
break | ||
case 12: | ||
throw new Error('timestamp 96 is not yet implemented') | ||
} | ||
var millis = (seconds * 1000) + Math.round(nanoseconds / 1E6) | ||
return buildDecodeResult(new Date(millis), size + headerSize) | ||
function decodeFloat (buf, offset, size) { | ||
var result | ||
if (size === 4) result = buf.readFloatBE(offset) | ||
if (size === 8) result = buf.readDoubleBE(offset) | ||
return [result, size + 1] | ||
} | ||
function decodeExt (buf, offset, type, size, headerSize) { | ||
var i, | ||
toDecode | ||
const toDecode = buf.slice(offset, offset + size) | ||
offset += headerSize | ||
const decode = decodingTypes.get(type) | ||
if (!decode) throw new Error('unable to find ext type ' + type) | ||
// Pre-defined | ||
if (type < 0) { // Reserved for future extensions | ||
switch (type) { | ||
case -1: // Tiemstamp https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type | ||
toDecode = buf.slice(offset, offset + size) | ||
return decodeTimestamp(toDecode, size, headerSize) | ||
} | ||
} | ||
for (i = 0; i < decodingTypes.length; i++) { | ||
if (type === decodingTypes[i].type) { | ||
toDecode = buf.slice(offset, offset + size) | ||
var value = decodingTypes[i].decode(toDecode) | ||
return buildDecodeResult(value, headerSize + size) | ||
} | ||
} | ||
throw new Error('unable to find ext type ' + type) | ||
var value = decode(toDecode) | ||
return [value, headerSize + size] | ||
} | ||
} | ||
module.exports.IncompleteBufferError = IncompleteBufferError |
@@ -5,47 +5,19 @@ 'use strict' | ||
var bl = require('bl') | ||
var isNaN = require('./helpers.js').isNaN | ||
var isFloat = require('./helpers.js').isFloat | ||
module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilityMode, disableTimestampEncoding) { | ||
function encode (obj, avoidSlice) { | ||
var buf | ||
var len | ||
module.exports = function buildEncode (encodingTypes, options) { | ||
function encode (obj) { | ||
if (obj === undefined) throw new Error('undefined is not encodable in msgpack!') | ||
if (isNaN(obj)) throw new Error('NaN is not encodable in msgpack!') | ||
if (obj === undefined) { | ||
throw new Error('undefined is not encodable in msgpack!') | ||
} else if (isNaN(obj)) { | ||
throw new Error('NaN 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 === null) return Buffer.from([ 0xc0 ]) | ||
if (obj === true) return Buffer.from([ 0xc3 ]) | ||
if (obj === false) return Buffer.from([ 0xc2 ]) | ||
if (obj instanceof Map) return encodeMap(obj, options, encode) | ||
if (typeof obj === 'string') return encodeString(obj, options) | ||
if (obj && (obj.readUInt32LE || obj instanceof Uint8Array)) { | ||
if (obj instanceof Uint8Array) { | ||
@@ -56,227 +28,64 @@ obj = Buffer.from(obj) | ||
// 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 (!disableTimestampEncoding && typeof obj.getDate === 'function') { | ||
return encodeDate(obj) | ||
} 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) | ||
} | ||
} | ||
return bl([getBufferHeader(obj.length), obj]) | ||
} | ||
if (Array.isArray(obj)) return bl().append([ getHeader(obj.length, 0x90, 0xdc), ...obj.map(encode) ]) | ||
if (typeof obj === 'object') return encodeExt(obj, encodingTypes) || encodeObject(obj, options, encode) | ||
if (typeof obj === 'number') return encodeNumber(obj, options) | ||
if (!buf) { | ||
throw new Error('not implemented yet') | ||
} | ||
throw new Error('not implemented yet') | ||
} | ||
if (avoidSlice) { | ||
return buf | ||
} else { | ||
return buf.slice() | ||
} | ||
return function (obj) { | ||
return encode(obj).slice() | ||
} | ||
} | ||
function encodeDate (dt) { | ||
var encoded | ||
var millis = dt * 1 | ||
var seconds = Math.floor(millis / 1000) | ||
var nanos = (millis - (seconds * 1000)) * 1E6 | ||
// | ||
// | ||
// === MENTAL SEPARATOR === | ||
// | ||
// | ||
if (nanos || seconds > 0xFFFFFFFF) { | ||
// Timestamp64 | ||
encoded = Buffer.allocUnsafe(10) | ||
encoded[0] = 0xd7 | ||
encoded[1] = -1 | ||
function encodeMap (map, options, encode) { | ||
const acc = [ getHeader(map.size, 0x80, 0xde) ] | ||
const keys = [ ...map.keys() ] | ||
var upperNanos = ((nanos * 4)) | ||
var upperSeconds = seconds / Math.pow(2, 32) | ||
var upper = (upperNanos + upperSeconds) & 0xFFFFFFFF | ||
var lower = seconds & 0xFFFFFFFF | ||
encoded.writeInt32BE(upper, 2) | ||
encoded.writeInt32BE(lower, 6) | ||
} else { | ||
// Timestamp32 | ||
encoded = Buffer.allocUnsafe(6) | ||
encoded[0] = 0xd6 | ||
encoded[1] = -1 | ||
encoded.writeUInt32BE(Math.floor(millis / 1000), 2) | ||
} | ||
return bl().append(encoded) | ||
if (keys.every(item => typeof item === 'string')) { | ||
console.warn('Map with string only keys will be deserialized as an object!') | ||
} | ||
function encodeExt (obj) { | ||
var i | ||
var encoded | ||
var length = -1 | ||
var headers = [] | ||
keys.forEach(key => { | ||
acc.push(encode(key), encode(map.get(key))) | ||
}) | ||
return bl(acc) | ||
} | ||
for (i = 0; i < encodingTypes.length; i++) { | ||
if (encodingTypes[i].check(obj)) { | ||
encoded = encodingTypes[i].encode(obj) | ||
break | ||
} | ||
} | ||
function encodeObject (obj, options, encode) { | ||
var keys = [] | ||
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) && | ||
for (let 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)) | ||
} | ||
keys.push(key) | ||
} | ||
} | ||
if (length < 16) { | ||
header = Buffer.allocUnsafe(1) | ||
header[0] = 0x80 | length | ||
} else if (length < 0xFFFF) { | ||
header = Buffer.allocUnsafe(3) | ||
header[0] = 0xde | ||
header.writeUInt16BE(length, 1) | ||
} else { | ||
header = Buffer.allocUnsafe(5) | ||
header[0] = 0xdf | ||
header.writeUInt32BE(length, 1) | ||
} | ||
const acc = [ getHeader(keys.length, 0x80, 0xde) ] | ||
acc.unshift(header) | ||
if (options.sortKeys) keys.sort() | ||
var result = acc.reduce(function (list, buf) { | ||
return list.append(buf) | ||
}, bl()) | ||
keys.forEach(key => { | ||
acc.push(encode(key), encode(obj[key])) | ||
}) | ||
return result | ||
} | ||
return encode | ||
return bl(acc) | ||
} | ||
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 write64BitUint (buf, offset, num) { | ||
var lo = num % 4294967296 | ||
var hi = Math.floor(num / 4294967296) | ||
buf.writeUInt32BE(hi, offset + 0) | ||
buf.writeUInt32BE(lo, offset + 4) | ||
} | ||
@@ -286,51 +95,30 @@ | ||
var negate = num < 0 | ||
num = Math.abs(num) | ||
write64BitUint(buf, offset, num) | ||
if (negate) negate64BitInt(buf, offset) | ||
} | ||
if (negate) { | ||
num = Math.abs(num) | ||
} | ||
function negate64BitInt (buf, offset) { | ||
var i = offset + 8 | ||
var lo = num % 4294967296 | ||
var hi = num / 4294967296 | ||
buf.writeUInt32BE(Math.floor(hi), offset + 0) | ||
buf.writeUInt32BE(lo, offset + 4) | ||
// Optimization based on the fact that: | ||
// buf[i] == 0x00 => (buf[i] ^ 0xff) + 1 = 0x100 = 0x00 + 1 curry | ||
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 | ||
} | ||
while (i-- > offset) { | ||
if (buf[i] === 0x00) continue | ||
buf[i] = (buf[i] ^ 0xff) + 1 | ||
break | ||
} | ||
} | ||
function isFloat (n) { | ||
return n % 1 !== 0 | ||
while (i-- > offset) { | ||
buf[i] = buf[i] ^ 0xff | ||
} | ||
} | ||
function isNaN (n) { | ||
/* eslint-disable no-self-compare */ | ||
return n !== n && typeof n === 'number' | ||
/* eslint-enable no-self-compare */ | ||
} | ||
const fround = Math.fround | ||
function encodeFloat (obj, forceFloat64) { | ||
var useDoublePrecision = true | ||
// If `fround` is supported, we can check if a float | ||
// is double or single precision by rounding the object | ||
// to single precision and comparing the difference. | ||
// If it's not supported, it's safer to use a 64 bit | ||
// float so we don't lose precision without meaning to. | ||
if (Math.fround) { | ||
useDoublePrecision = Math.fround(obj) !== obj | ||
} | ||
if (forceFloat64) { | ||
useDoublePrecision = true | ||
} | ||
var buf | ||
if (useDoublePrecision) { | ||
if (forceFloat64 || !fround || fround(obj) !== obj) { | ||
buf = Buffer.allocUnsafe(9) | ||
@@ -347,1 +135,131 @@ buf[0] = 0xcb | ||
} | ||
function encodeExt (obj, encodingTypes) { | ||
const codec = encodingTypes.find(codec => codec.check(obj)) | ||
if (!codec) return null | ||
const encoded = codec.encode(obj) | ||
if (!encoded) return null | ||
return bl([ getExtHeader(encoded.length - 1), encoded ]) | ||
} | ||
function getExtHeader (length) { | ||
if (length === 1) return Buffer.from([ 0xd4 ]) | ||
if (length === 2) return Buffer.from([ 0xd5 ]) | ||
if (length === 4) return Buffer.from([ 0xd6 ]) | ||
if (length === 8) return Buffer.from([ 0xd7 ]) | ||
if (length === 16) return Buffer.from([ 0xd8 ]) | ||
if (length < 256) return Buffer.from([ 0xc7, length ]) | ||
if (length < 0x10000) return Buffer.from([ 0xc8, length >> 8, length & 0x00ff ]) | ||
return Buffer.from([ 0xc9, length >> 24, (length >> 16) & 0x000000ff, (length >> 8) & 0x000000ff, length & 0x000000ff ]) | ||
} | ||
function getHeader (length, tag1, tag2) { | ||
if (length < 16) return Buffer.from([ tag1 | length ]) | ||
const size = length < 0x10000 ? 2 : 4 | ||
var buf = Buffer.allocUnsafe(1 + size) | ||
buf[0] = length < 0x10000 ? tag2 : tag2 + 1 | ||
buf.writeUIntBE(length, 1, size) | ||
return buf | ||
} | ||
function encodeString (obj, options) { | ||
const len = Buffer.byteLength(obj) | ||
var buf | ||
if (len < 32) { | ||
buf = Buffer.allocUnsafe(1 + len) | ||
buf[0] = 0xa0 | len | ||
if (len > 0) { | ||
buf.write(obj, 1) | ||
} | ||
} else if (len <= 0xff && !options.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) | ||
} | ||
return buf | ||
} | ||
function getBufferHeader (length) { | ||
var header | ||
if (length <= 0xff) { | ||
header = Buffer.allocUnsafe(2) | ||
header[0] = 0xc4 | ||
header[1] = length | ||
} else if (length <= 0xffff) { | ||
header = Buffer.allocUnsafe(3) | ||
header[0] = 0xc5 | ||
header.writeUInt16BE(length, 1) | ||
} else { | ||
header = Buffer.allocUnsafe(5) | ||
header[0] = 0xc6 | ||
header.writeUInt32BE(length, 1) | ||
} | ||
return header | ||
} | ||
function encodeNumber (obj, options) { | ||
var buf | ||
if (isFloat(obj)) return encodeFloat(obj, options.forceFloat64) | ||
if (Math.abs(obj) > 9007199254740991) { | ||
return encodeFloat(obj, true) | ||
} | ||
if (obj >= 0) { | ||
if (obj < 128) { | ||
return Buffer.from([ obj ]) | ||
} else if (obj < 256) { | ||
return Buffer.from([ 0xcc, obj ]) | ||
} else if (obj < 65536) { | ||
return Buffer.from([ 0xcd, 0xff & (obj >> 8), 0xff & (obj) ]) | ||
} else if (obj <= 0xffffffff) { | ||
return Buffer.from([ 0xce, 0xff & (obj >> 24), 0xff & (obj >> 16), 0xff & (obj >> 8), 0xff & (obj) ]) | ||
} else if (obj <= 9007199254740991) { | ||
buf = Buffer.allocUnsafe(9) | ||
buf[0] = 0xcf | ||
write64BitUint(buf, 1, obj) | ||
} | ||
} 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) | ||
} | ||
} | ||
return buf | ||
} | ||
// function order(num, n = 1, step = 2) { | ||
// while (num = num >> step) n++; | ||
// return n | ||
// } |
{ | ||
"name": "msgpack5", | ||
"version": "4.5.0", | ||
"version": "5.0.0", | ||
"description": "A msgpack v5 implementation for node.js and the browser, with extension points", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -1,2 +0,2 @@ | ||
msgpack5 [![Build Status](https://travis-ci.org/mcollina/msgpack5.png)](https://travis-ci.org/mcollina/msgpack5) | ||
msgpack5 [![CI](https://github.com/mcollina/msgpack5/workflows/CI/badge.svg)](https://github.com/mcollina/msgpack5/actions?query=workflow%3ACI) | ||
======== | ||
@@ -110,2 +110,3 @@ | ||
- `forceFloat64`, a boolean to that forces all floats to be encoded as 64-bits floats. Defaults to false. | ||
- `sortKeys`, a boolean to force a determinate keys order | ||
- `compatibilityMode`, a boolean that enables "compatibility mode" which doesn't use str 8 format. Defaults to false. | ||
@@ -112,0 +113,0 @@ - `disableTimestampEncoding`, a boolean that when set disables the encoding of Dates into the [timestamp extension type](https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type). Defaults to false. |
@@ -17,3 +17,3 @@ 'use strict' | ||
var float64 = [ | ||
2 ** 150, | ||
Math.pow(2, 150), | ||
1.337, | ||
@@ -20,0 +20,0 @@ 2.2 |
@@ -74,1 +74,44 @@ 'use strict' | ||
}) | ||
test('encoding/decoding timestamp 96', function (t) { | ||
var encoder = msgpack() | ||
var timestamps = [ | ||
[new Date('0001-01-02T03:04:05.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf1, 0x88, 0x6f, 0x85, 0xa5]], | ||
[new Date('1251-01-19T03:14:08.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfa, 0xb7, 0xb2, 0xdf, 0x00]], | ||
[new Date('1526-01-19T03:14:07.999000000Z'), [0xc7, 0x0c, 0xff, 0x3b, 0x8b, 0x87, 0xc0, 0xff, 0xff, 0xff, 0xfc, 0xbc, 0xf4, 0x34, 0x7f]], | ||
[new Date('1920-02-07T06:28:16.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x23, 0xf0, 0x00]], | ||
[new Date('1969-01-02T03:04:05.678000000Z'), [0xc7, 0x0c, 0xff, 0x28, 0x69, 0x75, 0x80, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x20, 0x49, 0x25]], | ||
[new Date('2514-05-30T02:04:05.678000000Z'), [0xc7, 0x0c, 0xff, 0x28, 0x69, 0x75, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x95]] | ||
] | ||
timestamps.forEach(function (testcase) { | ||
var item = testcase[0] | ||
var expected = testcase[1] | ||
t.test('encoding ' + item.toString(), function (t) { | ||
var buf = encoder.encode(item).slice() | ||
t.equal(buf.length, expected.length, 'must have ' + expected.length + ' bytes') | ||
t.equal(buf[0], 0xc7, 'must have the correct header') | ||
t.equal(buf.readInt8(1), 12, 'must have the correct size') | ||
t.equal(buf.readInt8(2), -1, 'must have the correct type') // Signed | ||
for (var j = 3; j < buf.length; j++) { | ||
t.equal(buf[j], expected[j], 'byte ' + (j - 3) + ' match') | ||
} | ||
t.end() | ||
}) | ||
t.test('decoding ' + item, function (t) { | ||
var buf = Buffer.from(expected) | ||
var dt = encoder.decode(buf) | ||
t.equal(dt.toString(), item.toString(), 'must decode correctly\nDecoded:\t' + dt * 1 + '\nExp:\t' + item * 1) | ||
t.end() | ||
}) | ||
t.test('mirror test ' + item, function (t) { | ||
t.equal(encoder.decode(encoder.encode(item)) * 1, item * 1, 'must stay the same') | ||
t.end() | ||
}) | ||
}) | ||
t.end() | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
518567
67
249
10141