diff --git a/lib/encoder.js b/lib/encoder.js index e53d42b..30727b2 100644 --- a/lib/encoder.js +++ b/lib/encoder.js @@ -26,7 +26,7 @@ module.exports = function buildEncode (encodingTypes, options) { // and Buffer-like objects return bl([getBufferHeader(obj.length), obj]) } - if (Array.isArray(obj)) return bl().append([ getHeader(obj.length, 0x90, 0xdc), ...obj.map(encode) ]) + if (Array.isArray(obj)) return encodeArray(obj, encode) if (typeof obj === 'object') return encodeExt(obj, encodingTypes) || encodeObject(obj, options, encode) if (typeof obj === 'number') return encodeNumber(obj, options) @@ -44,6 +44,22 @@ module.exports = function buildEncode (encodingTypes, options) { // // +function encodeArray (array, encode) { + const acc = [ getHeader(array.length, 0x90, 0xdc) ] + + // This has to be forEach; Array.prototype.map preserves missing values and + // Array.prototype.values yields them as undefined + array.forEach(item => { + acc.push(encode(item)) + }) + + if (acc.length !== array.length + 1) { + throw new Error('Sparse arrays are not encodable in msgpack') + } + + return bl(acc) +} + function encodeMap (map, options, encode) { const acc = [ getHeader(map.size, 0x80, 0xde) ] const keys = [ ...map.keys() ] diff --git a/test/sparse-arrays.js b/test/sparse-arrays.js new file mode 100644 index 0000000..c0c1d14 --- /dev/null +++ b/test/sparse-arrays.js @@ -0,0 +1,18 @@ +'use strict' + +const test = require('tape').test +const msgpack = require('../') + +test('throws when encoding sparse arrays', function (t) { + const encoder = msgpack() + + t.deepEqual(encoder.decode(encoder.encode(new Array(0))), []) + t.throws(() => encoder.encode(new Array(1)), /Sparse arrays/) + t.throws(() => encoder.encode(new Array(100)), /Sparse arrays/) + + const sparse = [1, 2, 3, 4] + delete sparse[3] + t.throws(() => encoder.encode(sparse), /Sparse arrays/) + + t.end() +})