Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "4"
- "6"
- "7"
- "8"
- "9"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ options:

- `forceFloat64`, a boolean to that forces all floats to be encoded as 64-bits floats. Defaults to false.
- `compatibilityMode`, a boolean that enables "compatibility mode" which doesn't use str 8 format. Defaults to false.
- `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.

-------------------------------------------------------
<a name="encode"></a>
Expand Down
5 changes: 3 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function msgpack (options) {

options = options || {
forceFloat64: false,
compatibilityMode: false
compatibilityMode: false,
disableTimestampEncoding: false // if true, skips encoding Dates using the msgpack timestamp ext format (-1)
}

function registerEncoder (check, encode) {
Expand Down Expand Up @@ -67,7 +68,7 @@ function msgpack (options) {
}

return {
encode: buildEncode(encodingTypes, options.forceFloat64, options.compatibilityMode),
encode: buildEncode(encodingTypes, options.forceFloat64, options.compatibilityMode, options.disableTimestampEncoding),
decode: buildDecode(decodingTypes),
register: register,
registerEncoder: registerEncoder,
Expand Down
38 changes: 36 additions & 2 deletions lib/decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,16 +372,50 @@ module.exports = function buildDecode (decodingTypes) {
}

function decodeFixExt (buf, offset, size) {
var type = buf.readUInt8(offset + 1)

var type = buf.readInt8(offset + 1) // Signed
return decodeExt(buf, offset, type, size, 2)
}
function decodeTimestamp (buf, size, headerSize) {
var seconds, nanoseconds
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()
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()
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 decodeExt (buf, offset, type, size, headerSize) {
var i,
toDecode

offset += headerSize

// 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)
Expand Down
33 changes: 32 additions & 1 deletion lib/encoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var Buffer = require('safe-buffer').Buffer
var bl = require('bl')
var TOLERANCE = 0.1

module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilityMode) {
module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilityMode, disableTimestampEncoding) {
function encode (obj, avoidSlice) {
var buf,
len
Expand Down Expand Up @@ -84,6 +84,8 @@ module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilit
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') {
Expand Down Expand Up @@ -149,6 +151,35 @@ module.exports = function buildEncode (encodingTypes, forceFloat64, compatibilit
}
}

function encodeDate (dt) {
var encoded
var millis = dt * 1
var seconds = Math.floor(millis / 1000)
var nanos = (millis - (seconds * 1000)) * 1E6

if (nanos || seconds > 0xFFFFFFFF) {
// Timestamp64
encoded = new Buffer(10)
encoded[0] = 0xd7
encoded[1] = -1

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 = new Buffer(6)
encoded[0] = 0xd6
encoded[1] = -1
encoded.writeUInt32BE(Math.floor(millis / 1000), 2)
}
return bl().append(encoded)
}

function encodeExt (obj) {
var i
var encoded
Expand Down
73 changes: 73 additions & 0 deletions test/timestamps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use strict'

var Buffer = require('safe-buffer').Buffer
var test = require('tape').test
var msgpack = require('../')

test('timestamp disabling', function (t) {
var encoder = msgpack({disableTimestampEncoding: true})
var timestamps = [
[new Date('2018-01-02T03:04:05.000000000Z'), [0x80]]
]

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], expected[0], 'Should return 0x80 ({}) by default')
t.end()
})
})

t.end()
})
test('encoding/decoding timestamp 64', function (t) {
var encoder = msgpack()
var timestamps = [
[new Date('2018-01-02T03:04:05.000000000Z'), [0xd6, 0xff, 0x5a, 0x4a, 0xf6, 0xa5]],
[new Date('2038-01-19T03:14:08.000000000Z'), [0xd6, 0xff, 0x80, 0x00, 0x00, 0x00]],
[new Date('2038-01-19T03:14:07.999000000Z'), [0xd7, 0xff, 0xee, 0x2E, 0x1F, 0x00, 0x7f, 0xff, 0xff, 0xff]],
[new Date('2106-02-07T06:28:16.000000000Z'), [0xd7, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]],
[new Date('2018-01-02T03:04:05.678000000Z'), [0xd7, 0xff, 0xa1, 0xa5, 0xd6, 0x00, 0x5a, 0x4a, 0xf6, 0xa5]]
]

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')
switch (expected.length) {
case 6:
t.equal(buf[0], 0xd6, 'must have the correct header')
break
case 10:
t.equal(buf[0], 0xd7, 'must have the correct header')
break
}
t.equal(buf.readInt8(1), -1, 'must have the correct type') // Signed
for (var j = 2; j < buf.length; j++) {
t.equal(buf[j], expected[j], 'byte ' + (j - 2) + ' 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()
})