msgpack5 6.0.1 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/example.js CHANGED
@@ -8,8 +8,13 @@ const decode = msgpack.decode
8
8
 
9
9
  msgpack.register(0x42, MyType, mytipeEncode, mytipeDecode)
10
10
 
11
- console.log(encode({ hello: 'world' }).toString('hex'))
11
+ const hex = encode({ hello: 'world' }).toString('hex')
12
+ console.log(hex)
12
13
  // 81a568656c6c6fa5776f726c64
14
+ const obj = decode(Buffer.from(hex, 'hex'))
15
+ console.log(obj)
16
+ // { hello: 'world' }
17
+
13
18
  console.log(decode(encode({ hello: 'world' })))
14
19
  // { hello: 'world' }
15
20
  console.log(encode(a).toString('hex'))
package/index.js CHANGED
@@ -13,17 +13,25 @@ function msgpack (options) {
13
13
  const encodingTypes = []
14
14
  const decodingTypes = new Map()
15
15
 
16
- options = options || {
16
+ options = Object.assign({
17
17
  forceFloat64: false,
18
18
  compatibilityMode: false,
19
19
  // if true, skips encoding Dates using the msgpack
20
20
  // timestamp ext format (-1)
21
21
  disableTimestampEncoding: false,
22
22
  preferMap: false,
23
- // options.protoAction: 'error' (default) / 'remove' / 'ignore'
23
+ maxDepth: 100,
24
24
  protoAction: 'error'
25
+ }, options || {})
26
+
27
+ if (options.protoAction === undefined) options.protoAction = 'error'
28
+ if (options.protoAction !== 'error' && options.protoAction !== 'remove' && options.protoAction !== 'ignore') {
29
+ throw new TypeError('protoAction must be "error", "remove", or "ignore"')
25
30
  }
26
31
 
32
+ validateMaxLength(options.maxArrayLength, 'maxArrayLength')
33
+ validateMaxLength(options.maxMapLength, 'maxMapLength')
34
+
27
35
  decodingTypes.set(DateCodec.type, DateCodec.decode)
28
36
  if (!options.disableTimestampEncoding) {
29
37
  encodingTypes.push(DateCodec)
@@ -88,4 +96,10 @@ function msgpack (options) {
88
96
  }
89
97
  }
90
98
 
99
+ function validateMaxLength (value, name) {
100
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 0)) {
101
+ throw new TypeError(name + ' must be a non-negative safe integer')
102
+ }
103
+ }
104
+
91
105
  module.exports = msgpack
package/lib/decoder.js CHANGED
@@ -28,9 +28,10 @@ const SIZES = {
28
28
  0xd9: 2,
29
29
  0xda: 3,
30
30
  0xdb: 5,
31
- 0xde: 3,
32
31
  0xdc: 3,
33
- 0xdd: 5
32
+ 0xdd: 5,
33
+ 0xde: 3,
34
+ 0xdf: 5
34
35
  }
35
36
 
36
37
  function isValidDataSize (dataLength, bufLength, headerLength) {
@@ -38,30 +39,135 @@ function isValidDataSize (dataLength, bufLength, headerLength) {
38
39
  }
39
40
 
40
41
  module.exports = function buildDecode (decodingTypes, options) {
41
- const context = { decodingTypes, options, decode }
42
+ const maxDepth = options.maxDepth === undefined ? 100 : options.maxDepth
43
+ if (!Number.isInteger(maxDepth) || maxDepth < 0) {
44
+ throw new TypeError('maxDepth must be a non-negative integer')
45
+ }
46
+
47
+ const context = { decodingTypes, options, maxDepth, decode }
42
48
  return decode
43
49
 
44
- function decode (buf) {
50
+ function decode (buf, decodeState) {
45
51
  if (!bl.isBufferList(buf)) {
46
52
  buf = bl(buf)
47
53
  }
48
54
 
49
- const result = tryDecode(buf, 0, context)
55
+ let result
56
+ try {
57
+ result = decodeState
58
+ ? tryDecodeIncremental(buf, decodeState, context)
59
+ : tryDecode(buf, 0, context, 0)
60
+ } catch (err) {
61
+ if (decodeState) decodeState.stack.length = 0
62
+ throw err
63
+ }
64
+
50
65
  // Handle worst case ASAP and keep code flat
51
66
  if (!result) throw new IncompleteBufferError()
52
67
 
53
- buf.consume(result[1])
68
+ if (!decodeState) buf.consume(result[1])
54
69
  return result[0]
55
70
  }
56
71
  }
57
72
 
58
- function decodeArray (buf, initialOffset, length, headerLength, context) {
73
+ function tryDecodeIncremental (buf, state, context) {
74
+ while (buf.length > 0) {
75
+ const container = decodeContainerHeader(buf)
76
+ let value
77
+
78
+ if (container === null) return null
79
+
80
+ if (container) {
81
+ const depth = state.stack.length + 1
82
+ if (depth > context.maxDepth) {
83
+ throw new Error('Maximum decode depth exceeded')
84
+ }
85
+ checkCollectionLength(
86
+ container.type,
87
+ container.length,
88
+ container.type === 'map' ? context.options.maxMapLength : context.options.maxArrayLength
89
+ )
90
+
91
+ buf.consume(container.headerLength)
92
+
93
+ const itemCount = container.type === 'map'
94
+ ? 2 * container.length
95
+ : container.length
96
+
97
+ if (itemCount > 0) {
98
+ state.stack.push({
99
+ type: container.type,
100
+ length: container.length,
101
+ itemCount,
102
+ result: []
103
+ })
104
+ continue
105
+ }
106
+
107
+ value = container.type === 'map'
108
+ ? buildMap([], 0, context)
109
+ : []
110
+ } else {
111
+ const result = tryDecode(buf, 0, context, state.stack.length)
112
+ if (!result) return null
113
+
114
+ buf.consume(result[1])
115
+ value = result[0]
116
+ }
117
+
118
+ const completed = completeIncrementalValue(state, value, context)
119
+ if (completed) return completed
120
+ }
121
+
122
+ return null
123
+ }
124
+
125
+ function completeIncrementalValue (state, value, context) {
126
+ while (state.stack.length > 0) {
127
+ const frame = state.stack[state.stack.length - 1]
128
+ frame.result.push(value)
129
+
130
+ if (frame.result.length < frame.itemCount) return null
131
+
132
+ state.stack.pop()
133
+ value = frame.type === 'map'
134
+ ? buildMap(frame.result, frame.length, context)
135
+ : frame.result
136
+ }
137
+
138
+ return [value]
139
+ }
140
+
141
+ function decodeContainerHeader (buf) {
142
+ const first = buf.readUInt8(0)
143
+
144
+ if ((first & 0xf0) === 0x80) {
145
+ return { type: 'map', length: first & 0x0f, headerLength: 1 }
146
+ }
147
+ if ((first & 0xf0) === 0x90) {
148
+ return { type: 'array', length: first & 0x0f, headerLength: 1 }
149
+ }
150
+
151
+ let headerLength
152
+ if (first === 0xdc || first === 0xde) headerLength = 3
153
+ if (first === 0xdd || first === 0xdf) headerLength = 5
154
+ if (!headerLength) return false
155
+ if (buf.length < headerLength) return null
156
+
157
+ return {
158
+ type: first === 0xdc || first === 0xdd ? 'array' : 'map',
159
+ length: buf.readUIntBE(1, headerLength - 1),
160
+ headerLength
161
+ }
162
+ }
163
+
164
+ function decodeItems (buf, initialOffset, length, headerLength, context, depth) {
59
165
  let offset = initialOffset
60
166
  const result = []
61
167
  let i = 0
62
168
 
63
169
  while (i++ < length) {
64
- const decodeResult = tryDecode(buf, offset, context)
170
+ const decodeResult = tryDecode(buf, offset, context, depth)
65
171
  if (!decodeResult) return null
66
172
 
67
173
  result.push(decodeResult[0])
@@ -70,11 +176,32 @@ function decodeArray (buf, initialOffset, length, headerLength, context) {
70
176
  return [result, headerLength + offset - initialOffset]
71
177
  }
72
178
 
73
- function decodeMap (buf, offset, length, headerLength, context) {
74
- const _temp = decodeArray(buf, offset, 2 * length, headerLength, context)
179
+ function checkCollectionLength (type, length, maxLength) {
180
+ if (maxLength !== undefined && length > maxLength) {
181
+ throw new RangeError(type + ' length ' + length + ' exceeds configured limit of ' + maxLength)
182
+ }
183
+ }
184
+
185
+ function decodeArray (buf, initialOffset, length, headerLength, context, depth) {
186
+ if (depth > context.maxDepth) {
187
+ throw new Error('Maximum decode depth exceeded')
188
+ }
189
+ checkCollectionLength('array', length, context.options.maxArrayLength)
190
+ return decodeItems(buf, initialOffset, length, headerLength, context, depth)
191
+ }
192
+
193
+ function decodeMap (buf, offset, length, headerLength, context, depth) {
194
+ if (depth > context.maxDepth) {
195
+ throw new Error('Maximum decode depth exceeded')
196
+ }
197
+ checkCollectionLength('map', length, context.options.maxMapLength)
198
+ const _temp = decodeItems(buf, offset, 2 * length, headerLength, context, depth)
75
199
  if (!_temp) return null
76
200
  const [result, consumedBytes] = _temp
201
+ return [buildMap(result, length, context), consumedBytes]
202
+ }
77
203
 
204
+ function buildMap (result, length, context) {
78
205
  let isPlainObject = !context.options.preferMap
79
206
 
80
207
  if (isPlainObject) {
@@ -104,7 +231,7 @@ function decodeMap (buf, offset, length, headerLength, context) {
104
231
 
105
232
  object[key] = val
106
233
  }
107
- return [object, consumedBytes]
234
+ return object
108
235
  } else {
109
236
  const mapping = new Map()
110
237
  for (let i = 0; i < 2 * length; i += 2) {
@@ -112,11 +239,11 @@ function decodeMap (buf, offset, length, headerLength, context) {
112
239
  const val = result[i + 1]
113
240
  mapping.set(key, val)
114
241
  }
115
- return [mapping, consumedBytes]
242
+ return mapping
116
243
  }
117
244
  }
118
245
 
119
- function tryDecode (buf, initialOffset, context) {
246
+ function tryDecode (buf, initialOffset, context, depth) {
120
247
  if (buf.length <= initialOffset) return null
121
248
 
122
249
  const bufLength = buf.length - initialOffset
@@ -133,13 +260,13 @@ function tryDecode (buf, initialOffset, context) {
133
260
  const length = first & 0x0f
134
261
  const headerSize = offset - initialOffset
135
262
  // we have a map with less than 15 elements
136
- return decodeMap(buf, offset, length, headerSize, context)
263
+ return decodeMap(buf, offset, length, headerSize, context, depth + 1)
137
264
  }
138
265
  if ((first & 0xf0) === 0x90) {
139
266
  const length = first & 0x0f
140
267
  const headerSize = offset - initialOffset
141
268
  // we have an array with less than 15 elements
142
- return decodeArray(buf, offset, length, headerSize, context)
269
+ return decodeArray(buf, offset, length, headerSize, context, depth + 1)
143
270
  }
144
271
 
145
272
  if ((first & 0xe0) === 0xa0) {
@@ -149,6 +276,7 @@ function tryDecode (buf, initialOffset, context) {
149
276
  const result = buf.toString('utf8', offset, offset + length)
150
277
  return [result, length + 1]
151
278
  }
279
+ if (first === 0xc1) throw new Error('0xc1 is a reserved MessagePack byte')
152
280
  if (first >= 0xc0 && first <= 0xc3) return decodeConstants(first)
153
281
  if (first >= 0xc4 && first <= 0xc6) {
154
282
  const length = buf.readUIntBE(offset, size - 1)
@@ -188,7 +316,7 @@ function tryDecode (buf, initialOffset, context) {
188
316
  if (first >= 0xdc && first <= 0xdd) {
189
317
  const length = buf.readUIntBE(offset, size - 1)
190
318
  offset += size - 1
191
- return decodeArray(buf, offset, length, size, context)
319
+ return decodeArray(buf, offset, length, size, context, depth + 1)
192
320
  }
193
321
  if (first >= 0xde && first <= 0xdf) {
194
322
  let length
@@ -198,12 +326,12 @@ function tryDecode (buf, initialOffset, context) {
198
326
  length = buf.readUInt16BE(offset)
199
327
  offset += 2
200
328
  // console.log(offset - initialOffset)
201
- return decodeMap(buf, offset, length, 3, context)
329
+ return decodeMap(buf, offset, length, 3, context, depth + 1)
202
330
 
203
331
  case 0xdf:
204
332
  length = buf.readUInt32BE(offset)
205
333
  offset += 4
206
- return decodeMap(buf, offset, length, 5, context)
334
+ return decodeMap(buf, offset, length, 5, context, depth + 1)
207
335
  }
208
336
  }
209
337
  if (first >= 0xe0) return [first - 0x100, 1] // 5 bits negative ints
@@ -216,7 +344,7 @@ function decodeSigned (buf, offset, size) {
216
344
  if (size === 1) result = buf.readInt8(offset)
217
345
  if (size === 2) result = buf.readInt16BE(offset)
218
346
  if (size === 4) result = buf.readInt32BE(offset)
219
- if (size === 8) result = readInt64BE(buf.slice(offset, offset + 8), 0)
347
+ if (size === 8) result = readInt64BE(buf, offset)
220
348
  return [result, size + 1]
221
349
  }
222
350
 
@@ -251,18 +379,14 @@ function decodeFloat (buf, offset, size) {
251
379
  }
252
380
 
253
381
  function readInt64BE (buf, offset) {
254
- var negate = (buf[offset] & 0x80) == 0x80; // eslint-disable-line
382
+ const negate = (buf.readUInt8(offset) & 0x80) === 0x80
383
+ let hi = buf.readUInt32BE(offset + 0)
384
+ let lo = buf.readUInt32BE(offset + 4)
255
385
 
256
386
  if (negate) {
257
- let carry = 1
258
- for (let i = offset + 7; i >= offset; i--) {
259
- const v = (buf[i] ^ 0xff) + carry
260
- buf[i] = v & 0xff
261
- carry = v >> 8
262
- }
387
+ lo = (~lo + 1) >>> 0
388
+ hi = (~hi + (lo === 0 ? 1 : 0)) >>> 0
263
389
  }
264
390
 
265
- const hi = buf.readUInt32BE(offset + 0)
266
- const lo = buf.readUInt32BE(offset + 4)
267
391
  return (hi * 4294967296 + lo) * (negate ? -1 : +1)
268
392
  }
package/lib/encoder.js CHANGED
@@ -22,7 +22,8 @@ module.exports = function buildEncode (encodingTypes, options) {
22
22
  }
23
23
  // weird hack to support Buffer
24
24
  // and Buffer-like objects
25
- return bl([getBufferHeader(obj.length), obj])
25
+ const _getBufferHeader = options.compatibilityMode ? getCompatibleBufferHeader : getBufferHeader
26
+ return bl([_getBufferHeader(obj.length), obj])
26
27
  }
27
28
  if (Array.isArray(obj)) return encodeArray(obj, encode)
28
29
  if (typeof obj === 'object') return encodeExt(obj, encodingTypes) || encodeObject(obj, options, encode)
@@ -225,6 +226,26 @@ function getBufferHeader (length) {
225
226
  return header
226
227
  }
227
228
 
229
+ function getCompatibleBufferHeader (length) {
230
+ let header
231
+ if (length <= 0x1f) {
232
+ // fix raw header: 101XXXXX
233
+ header = Buffer.allocUnsafe(1)
234
+ header[0] = 0xa0 | length
235
+ } else if (length <= 0xffff) {
236
+ // raw 16 header: 0xda, XXXXXXXX, XXXXXXXX
237
+ header = Buffer.allocUnsafe(3)
238
+ header[0] = 0xda
239
+ header.writeUInt16BE(length, 1)
240
+ } else {
241
+ // raw 32 header: 0xdb, XXXXXXXX, XXXXXXXX, XXXXXXXX, XXXXXXXX
242
+ header = Buffer.allocUnsafe(5)
243
+ header[0] = 0xdb
244
+ header.writeUInt32BE(length, 1)
245
+ }
246
+ return header
247
+ }
248
+
228
249
  function encodeNumber (obj, options) {
229
250
  let buf
230
251
  if (isFloat(obj)) return encodeFloat(obj, options.forceFloat64)
package/lib/streams.js CHANGED
@@ -54,6 +54,7 @@ function Decoder (opts) {
54
54
  Base.call(this, opts)
55
55
 
56
56
  this._chunks = bl()
57
+ this._decodeState = { stack: [] }
57
58
  this._wrap = ('wrap' in opts) && opts.wrap
58
59
  }
59
60
 
@@ -64,26 +65,26 @@ Decoder.prototype._transform = function (buf, enc, done) {
64
65
  this._chunks.append(buf)
65
66
  }
66
67
 
67
- try {
68
- let result = this._msgpack.decode(this._chunks)
69
- if (this._wrap) {
70
- result = { value: result }
71
- }
72
- this.push(result)
73
- } catch (err) {
74
- if (err instanceof this._msgpack.IncompleteBufferError) {
75
- done()
76
- } else {
77
- this.emit('error', err)
68
+ while (this._chunks.length > 0) {
69
+ try {
70
+ let result = this._msgpack.decode(this._chunks, this._decodeState)
71
+ if (this._wrap) {
72
+ result = { value: result }
73
+ }
74
+ this.push(result)
75
+ } catch (err) {
76
+ if (err instanceof this._msgpack.IncompleteBufferError) {
77
+ done()
78
+ } else {
79
+ this._chunks = bl()
80
+ this.destroy(err)
81
+ done()
82
+ }
83
+ return
78
84
  }
79
- return
80
85
  }
81
86
 
82
- if (this._chunks.length > 0) {
83
- this._transform(null, enc, done)
84
- } else {
85
- done()
86
- }
87
+ done()
87
88
  }
88
89
 
89
90
  module.exports.decoder = Decoder
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msgpack5",
3
- "version": "6.0.1",
3
+ "version": "6.1.0",
4
4
  "description": "A msgpack v5 implementation for node.js and the browser, with extension points",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const Buffer = require('safe-buffer').Buffer
4
+ const test = require('tape').test
5
+ const msgpack = require('../')
6
+ const bl = require('bl')
7
+
8
+ test('decoding incomplete map32 headers', function (t) {
9
+ const pack = msgpack()
10
+
11
+ for (let length = 1; length < 5; length++) {
12
+ const buf = Buffer.alloc(length)
13
+ buf[0] = 0xdf
14
+ const input = bl().append(buf)
15
+
16
+ t.throws(function () {
17
+ pack.decode(input)
18
+ }, pack.IncompleteBufferError, 'must reject a ' + length + '-byte header as incomplete')
19
+ t.equal(input.length, length, 'must not consume an incomplete header')
20
+ }
21
+
22
+ t.end()
23
+ })
24
+
25
+ test('decoding an empty map32', function (t) {
26
+ const pack = msgpack()
27
+ const buf = Buffer.from([0xdf, 0x00, 0x00, 0x00, 0x00])
28
+
29
+ t.deepEqual(pack.decode(buf), {}, 'must decode a complete map32 header')
30
+ t.end()
31
+ })
@@ -34,6 +34,27 @@ test('encoding/decoding 64-bits big-endian signed integers', function (t) {
34
34
  t.end()
35
35
  })
36
36
 
37
+ test('decoding a negative 64-bits integer does not mutate the input', function (t) {
38
+ const encoder = msgpack()
39
+ const encoded = Buffer.from([0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe])
40
+ const expected = encoded.toString('hex')
41
+
42
+ t.equal(encoder.decode(encoded), -2, 'decodes a direct buffer')
43
+ t.equal(encoded.toString('hex'), expected, 'does not mutate a direct buffer')
44
+ t.equal(encoder.decode(encoded), -2, 'decodes the same buffer consistently')
45
+ t.equal(encoded.toString('hex'), expected, 'leaves the buffer unchanged after repeated decoding')
46
+
47
+ const singleChunk = Buffer.from(encoded)
48
+ t.equal(encoder.decode(bl().append(singleChunk)), -2, 'decodes a single-chunk BufferList')
49
+ t.equal(singleChunk.toString('hex'), expected, 'does not mutate the underlying chunk')
50
+
51
+ const firstChunk = Buffer.from(encoded.slice(0, 4))
52
+ const secondChunk = Buffer.from(encoded.slice(4))
53
+ t.equal(encoder.decode(bl().append(firstChunk).append(secondChunk)), -2, 'decodes a split-chunk BufferList')
54
+ t.equal(Buffer.concat([firstChunk, secondChunk]).toString('hex'), expected, 'does not mutate split chunks')
55
+ t.end()
56
+ })
57
+
37
58
  test('decoding an incomplete 64-bits big-endian signed integer', function (t) {
38
59
  const encoder = msgpack()
39
60
  let buf = Buffer.allocUnsafe(8)
@@ -0,0 +1,64 @@
1
+ 'use strict'
2
+
3
+ const Buffer = require('safe-buffer').Buffer
4
+ const test = require('tape').test
5
+ const msgpack = require('../')
6
+
7
+ test('maxArrayLength limits decoded arrays', function (t) {
8
+ const decoder = msgpack({ maxArrayLength: 2 })
9
+
10
+ t.deepEqual(decoder.decode(Buffer.from([0x92, 0x01, 0x02])), [1, 2], 'allows the configured limit')
11
+ t.throws(function () {
12
+ decoder.decode(Buffer.from([0x93, 0x01, 0x02, 0x03]))
13
+ }, /array length 3 exceeds configured limit of 2/, 'rejects fixarray over the limit')
14
+ t.throws(function () {
15
+ decoder.decode(Buffer.from([0xdc, 0x00, 0x03]))
16
+ }, /array length 3 exceeds configured limit of 2/, 'rejects array16 before decoding its elements')
17
+ t.throws(function () {
18
+ decoder.decode(Buffer.from([0xdd, 0x00, 0x00, 0x00, 0x03]))
19
+ }, /array length 3 exceeds configured limit of 2/, 'rejects array32 before decoding its elements')
20
+ t.end()
21
+ })
22
+
23
+ test('maxMapLength limits decoded maps', function (t) {
24
+ const decoder = msgpack({ maxMapLength: 1 })
25
+
26
+ t.deepEqual(decoder.decode(Buffer.from([0x81, 0xa1, 0x61, 0x01])), { a: 1 }, 'allows the configured limit')
27
+ t.throws(function () {
28
+ decoder.decode(Buffer.from([0x82, 0xa1, 0x61, 0x01, 0xa1, 0x62, 0x02]))
29
+ }, /map length 2 exceeds configured limit of 1/, 'rejects fixmap over the limit')
30
+ t.throws(function () {
31
+ decoder.decode(Buffer.from([0xde, 0x00, 0x02]))
32
+ }, /map length 2 exceeds configured limit of 1/, 'rejects map16 before decoding its entries')
33
+ t.throws(function () {
34
+ decoder.decode(Buffer.from([0xdf, 0x00, 0x00, 0x00, 0x02]))
35
+ }, /map length 2 exceeds configured limit of 1/, 'rejects map32 before decoding its entries')
36
+ t.end()
37
+ })
38
+
39
+ test('collection limits apply independently and to nested values', function (t) {
40
+ const decoder = msgpack({ maxArrayLength: 1, maxMapLength: 1 })
41
+
42
+ t.deepEqual(decoder.decode(Buffer.from([0x81, 0xa1, 0x61, 0x91, 0x01])), { a: [1] }, 'allows nested collections at their limits')
43
+ t.throws(function () {
44
+ decoder.decode(Buffer.from([0x81, 0xa1, 0x61, 0x92, 0x01, 0x02]))
45
+ }, /array length 2 exceeds configured limit of 1/, 'rejects a nested array over its limit')
46
+ t.end()
47
+ })
48
+
49
+ test('collection limits must be non-negative safe integers', function (t) {
50
+ const invalid = [-1, 1.5, Infinity, null, '1']
51
+
52
+ invalid.forEach(function (value) {
53
+ t.throws(function () {
54
+ msgpack({ maxArrayLength: value })
55
+ }, /maxArrayLength must be a non-negative safe integer/, 'rejects maxArrayLength ' + value)
56
+ t.throws(function () {
57
+ msgpack({ maxMapLength: value })
58
+ }, /maxMapLength must be a non-negative safe integer/, 'rejects maxMapLength ' + value)
59
+ })
60
+ t.doesNotThrow(function () {
61
+ msgpack({ maxArrayLength: 0, maxMapLength: Number.MAX_SAFE_INTEGER })
62
+ }, 'accepts boundary values')
63
+ t.end()
64
+ })
@@ -3,6 +3,13 @@
3
3
  const test = require('tape').test
4
4
  const msgpack = require('../')
5
5
 
6
+ function buildBuffer (size) {
7
+ const buf = Buffer.allocUnsafe(size)
8
+ buf.fill('a')
9
+
10
+ return buf
11
+ }
12
+
6
13
  test('encode/compatibility mode', function (t) {
7
14
  const compatEncoder = msgpack({
8
15
  compatibilityMode: true
@@ -37,4 +44,30 @@ test('encode/compatibility mode', function (t) {
37
44
  t.deepEqual(buf1, buf2, 'must be equal for two byte strings')
38
45
  t.end()
39
46
  })
47
+
48
+ const fixRawBuffer = buildBuffer(1)
49
+ const raw16Buffer = buildBuffer(Math.pow(2, 16) - 1)
50
+ const raw32Buffer = buildBuffer(Math.pow(2, 16) + 1)
51
+
52
+ t.test('compat. encoding a Buffer of length ' + fixRawBuffer.length, function (t) {
53
+ // fix raw header: 0xa0 | 1 = 0xa1
54
+ const buf = compatEncoder.encode(fixRawBuffer)
55
+ t.equal(buf[0], 0xa1, 'must have the proper header (fix raw)')
56
+ t.equal(buf.toString('utf8', 1, Buffer.byteLength(fixRawBuffer) + 1), fixRawBuffer.toString('utf8'), 'must decode correctly')
57
+ t.end()
58
+ })
59
+
60
+ t.test('compat. encoding a Buffer of length ' + raw16Buffer.length, function (t) {
61
+ const buf = compatEncoder.encode(raw16Buffer)
62
+ t.equal(buf[0], 0xda, 'must have the proper header (raw 16)')
63
+ t.equal(buf.toString('utf8', 3, Buffer.byteLength(raw16Buffer) + 3), raw16Buffer.toString('utf8'), 'must decode correctly')
64
+ t.end()
65
+ })
66
+
67
+ t.test('compat. encoding a Buffer of length ' + raw32Buffer.length, function (t) {
68
+ const buf = compatEncoder.encode(raw32Buffer)
69
+ t.equal(buf[0], 0xdb, 'must have the proper header (raw 32)')
70
+ t.equal(buf.toString('utf8', 5, Buffer.byteLength(raw32Buffer) + 5), raw32Buffer.toString('utf8'), 'must decode correctly')
71
+ t.end()
72
+ })
40
73
  })