Skip to content

Commit

Permalink
feat: add support for BigInt.prototype.toJSON (#28)
Browse files Browse the repository at this point in the history
Another possible implementation was to always encode BigInt values as
strings, but that does not allow to encode it as numbers to save a few
bytes.

This solution is also more future-proof, in case some other types are
added to JavaScript.

Related:

- #28
- #25
- #24
  • Loading branch information
papandreou authored and darrachequesne committed May 20, 2022
1 parent 3888ca6 commit 9ec1fcd
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"immed" : true,
"indent" : 2,
"latedef" : true,
"newcap" : true,
"newcap" : false,
"noarg" : true,
"noempty" : true,
"nonew" : true,
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,55 @@ A browser version of notepack is also available (2.0 kB minified/gzipped)
</script>
```

## Common questions

### How to encode custom types?

This library does not currently support [extension types](https://github.com/msgpack/msgpack/blob/master/spec.md#extension-types). That being said, you can create a `toJSON()` method on your object, which will be used when encoding:

```js
class MyClass {
toJSON() {
return 42;
}
}
```

### How to handle BigInt values?

You can use the `toJSON()` method:

```js
// always as string
BigInt.prototype.toJSON = function () {
return String(this);
};

// or either as string or number, depending on the value
BigInt.prototype.toJSON = function () {
var isSafeNumber = Number.MIN_SAFE_INTEGER <= this && this <= Number.MAX_SAFE_INTEGER;
return isSafeNumber ? Number(this) : String(this);
};
```

### Handle to handle ES6 Set and Map values?

Again, `toJSON()` to the rescue:

```js
// convert the set to an array
// example: Set(3) { 1, 2, 3 } into [ 1, 2, 3 ]
Set.prototype.toJSON = function () {
return [...this];
}

// convert the map to an array of array
// example: Map(2) { 1 => '2', '3' => 4 } into [ [ 1, '2' ], [ '3', 4 ] ]
Map.prototype.toJSON = function () {
return [...this];
}
```

## Performance

Performance is currently comparable to msgpack-node (which presumably needs optimizing and suffers from JS-native overhead) and is significantly faster than other implementations. Several micro-optimizations are used to improve the performance of short string and Buffer operations.
Expand Down
4 changes: 4 additions & 0 deletions browser/encode.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ function _encode(bytes, defers, value) {
bytes.push(0xd4, 0, 0);
return 3;
}
// custom types like BigInt (typeof value === 'bigint')
if (typeof value.toJSON === 'function') {
return _encode(bytes, defers, value.toJSON());
}
throw new Error('Could not encode');
}

Expand Down
4 changes: 4 additions & 0 deletions lib/encode.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ function _encode(bytes, defers, value) {
bytes.push(0xd4, 0, 0);
return 3;
default:
// custom types like BigInt (typeof value === 'bigint')
if (typeof value.toJSON === 'function') {
return _encode(bytes, defers, value.toJSON());
}
throw new Error('Could not encode');
}
}
Expand Down
13 changes: 13 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

/* global BigInt */

const notepack = require('../');
const expect = require('chai').expect;

Expand Down Expand Up @@ -287,6 +289,17 @@ describe('notepack', function () {
expect(notepack.encode(obj)).to.deep.equal(notepack.encode('c'));
});

it('toJSON for BigInt', function () {
BigInt.prototype.toJSON = function () {
return String(this);
};
try {
checkEncode(BigInt(1234), 'a431323334');
} finally {
delete BigInt.prototype.toJSON;
}
});

it('all formats', function () {
this.timeout(20000);
const expected = {
Expand Down

0 comments on commit 9ec1fcd

Please sign in to comment.