Skip to content

Commit 2a54053

Browse files
authored
feat(UintArray): Adds support for Uint8Arrays
All API calls can now handle taking in a Uint8Array instead of a buffer. Consumers will still need to globally provide a polyfill for Buffer, as it is still used internally.
1 parent 7f8d1b6 commit 2a54053

File tree

5 files changed

+152
-35
lines changed

5 files changed

+152
-35
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ The BSON `serializeWithBufferAndIndex` method takes an object, a target buffer i
9191

9292
* `BSON.serializeWithBufferAndIndex(object, buffer, options)`
9393
* @param {Object} object the JavaScript object to serialize.
94-
* @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object.
94+
* @param {Buffer|Uint8Array} buffer the Buffer you pre-allocated to store the serialized BSON object.
9595
* @param {Boolean} [options.checkKeys=false] the serializer will check if keys are valid.
9696
* @param {Boolean} [options.serializeFunctions=false] serialize the JavaScript functions.
9797
* @param {Boolean} [options.ignoreUndefined=true] ignore undefined fields.
@@ -128,7 +128,7 @@ The BSON `deserialize` method takes a Node.js Buffer and an optional options obj
128128
The BSON `deserializeStream` method takes a Node.js Buffer, `startIndex` and allow more control over deserialization of a Buffer containing concatenated BSON documents.
129129

130130
* `BSON.deserializeStream(buffer, startIndex, numberOfDocuments, documents, docStartIndex, options)`
131-
* @param {Buffer} buffer the buffer containing the serialized set of BSON documents.
131+
* @param {Buffer|Uint8Array} buffer the buffer containing the serialized set of BSON documents.
132132
* @param {Number} startIndex the start index in the data Buffer where the deserialization is to start.
133133
* @param {Number} numberOfDocuments number of documents to deserialize.
134134
* @param {Array} documents an array where to store the deserialized documents.

lib/bson/bson.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var deserialize = require('./parser/deserializer'),
2020
serializer = require('./parser/serializer'),
2121
calculateObjectSize = require('./parser/calculate_size');
2222

23+
const ensureBuffer = require('./ensure_buffer');
24+
2325
/**
2426
* @ignore
2527
* @api private
@@ -82,7 +84,7 @@ BSON.prototype.serialize = function serialize(object, options) {
8284
* Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization.
8385
*
8486
* @param {Object} object the Javascript object to serialize.
85-
* @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object.
87+
* @param {Buffer|Uint8Array} buffer the Buffer you pre-allocated to store the serialized BSON object.
8688
* @param {Boolean} [options.checkKeys] the serializer will check if keys are valid.
8789
* @param {Boolean} [options.serializeFunctions=false] serialize the javascript functions **(default:false)**.
8890
* @param {Boolean} [options.ignoreUndefined=true] ignore undefined fields **(default:true)**.
@@ -110,6 +112,9 @@ BSON.prototype.serializeWithBufferAndIndex = function(object, finalBuffer, optio
110112
serializeFunctions,
111113
ignoreUndefined
112114
);
115+
116+
finalBuffer = ensureBuffer(finalBuffer);
117+
113118
buffer.copy(finalBuffer, startIndex, 0, serializationIndex);
114119

115120
// Return the index
@@ -119,7 +124,7 @@ BSON.prototype.serializeWithBufferAndIndex = function(object, finalBuffer, optio
119124
/**
120125
* Deserialize data as BSON.
121126
*
122-
* @param {Buffer} buffer the buffer containing the serialized set of BSON documents.
127+
* @param {Buffer|Uint8Array} buffer the buffer containing the serialized set of BSON documents.
123128
* @param {Object} [options.evalFunctions=false] evaluate functions in the BSON document scoped to the object deserialized.
124129
* @param {Object} [options.cacheFunctions=false] cache evaluated functions for reuse.
125130
* @param {Object} [options.cacheFunctionsCrc32=false] use a crc32 code for caching, otherwise use the string of the function.
@@ -133,6 +138,7 @@ BSON.prototype.serializeWithBufferAndIndex = function(object, finalBuffer, optio
133138
* @api public
134139
*/
135140
BSON.prototype.deserialize = function(buffer, options) {
141+
buffer = ensureBuffer(buffer);
136142
return deserialize(buffer, options);
137143
};
138144

@@ -159,7 +165,7 @@ BSON.prototype.calculateObjectSize = function(object, options) {
159165
/**
160166
* Deserialize stream data as BSON documents.
161167
*
162-
* @param {Buffer} data the buffer containing the serialized set of BSON documents.
168+
* @param {Buffer|Uint8Array} data the buffer containing the serialized set of BSON documents.
163169
* @param {Number} startIndex the start index in the data Buffer where the deserialization is to start.
164170
* @param {Number} numberOfDocuments number of documents to deserialize.
165171
* @param {Array} documents an array where to store the deserialized documents.
@@ -185,6 +191,7 @@ BSON.prototype.deserializeStream = function(
185191
options
186192
) {
187193
options = Object.assign({ allowObjectSmallerThanBufferSize: true }, options);
194+
data = ensureBuffer(data);
188195
var index = startIndex;
189196
// Loop over all documents
190197
for (var i = 0; i < numberOfDocuments; i++) {

lib/bson/ensure_buffer.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
/**
4+
* Makes sure that, if a Uint8Array is passed in, it is wrapped in a Buffer.
5+
*
6+
* @param {Buffer|Uint8Array} potentialBuffer The potential buffer
7+
* @returns {Buffer} the input if potentialBuffer is a buffer, or a buffer that
8+
* wraps a passed in Uint8Array
9+
* @throws {TypeError} If anything other than a Buffer or Uint8Array is passed in
10+
*/
11+
module.exports = function ensureBuffer(potentialBuffer) {
12+
if (potentialBuffer instanceof Buffer) {
13+
return potentialBuffer;
14+
}
15+
if (potentialBuffer instanceof Uint8Array) {
16+
return new Buffer(potentialBuffer.buffer);
17+
}
18+
19+
throw new TypeError('Must use either Buffer or Uint8Array');
20+
};

test/node/bson_test.js

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ var ISODate = function(string) {
8686
} else throw new Error('Invalid ISO 8601 date given.', __filename);
8787
};
8888

89+
function runTestsOnBytesForBufferAndUint8Array(bytes, testFn) {
90+
let serialized_data = '';
91+
// Convert to chars
92+
for (let i = 0; i < bytes.length; i++) {
93+
serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]);
94+
}
95+
96+
const uint8Array = Uint8Array.from(bytes);
97+
const buffer = new Buffer(serialized_data, 'binary');
98+
99+
[uint8Array, buffer].forEach(testFn);
100+
}
101+
89102
describe('BSON', function() {
90103
/**
91104
* @ignore
@@ -224,16 +237,19 @@ describe('BSON', function() {
224237
0,
225238
0
226239
];
227-
var serialized_data = '';
228-
// Convert to chars
229-
for (var i = 0; i < bytes.length; i++) {
230-
serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]);
231-
}
232240

233-
var object = createBSON().deserialize(new Buffer(serialized_data, 'binary'));
234-
expect('a_1').to.equal(object.name);
235-
expect(false).to.equal(object.unique);
236-
expect(1).to.equal(object.key.a);
241+
runTestsOnBytesForBufferAndUint8Array(bytes, data => {
242+
let object = createBSON().deserialize(data);
243+
expect('a_1').to.equal(object.name);
244+
expect(false).to.equal(object.unique);
245+
expect(1).to.equal(object.key.a);
246+
247+
object = createBSON().deserialize(Uint8Array.from(bytes));
248+
expect('a_1').to.equal(object.name);
249+
expect(false).to.equal(object.unique);
250+
expect(1).to.equal(object.key.a);
251+
});
252+
237253
done();
238254
});
239255

@@ -525,29 +541,26 @@ describe('BSON', function() {
525541
0,
526542
0
527543
];
528-
var serialized_data = '';
529544

530-
// Convert to chars
531-
for (var i = 0; i < bytes.length; i++) {
532-
serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]);
533-
}
545+
runTestsOnBytesForBufferAndUint8Array(bytes, data => {
546+
const object = createBSON().deserialize(data);
547+
// Perform tests
548+
expect('hello').to.equal(object.string);
549+
expect([1, 2, 3]).to.deep.equal(object.array);
550+
expect(1).to.equal(object.hash.a);
551+
expect(2).to.equal(object.hash.b);
552+
expect(object.date != null).to.be.ok;
553+
expect(object.oid != null).to.be.ok;
554+
expect(object.binary != null).to.be.ok;
555+
expect(42).to.equal(object.int);
556+
expect(33.3333).to.equal(object.float);
557+
expect(object.regexp != null).to.be.ok;
558+
expect(true).to.equal(object.boolean);
559+
expect(object.where != null).to.be.ok;
560+
expect(object.dbref != null).to.be.ok;
561+
expect(object[null] == null).to.be.ok;
562+
});
534563

535-
var object = createBSON().deserialize(new Buffer(serialized_data, 'binary'));
536-
// Perform tests
537-
expect('hello').to.equal(object.string);
538-
expect([1, 2, 3]).to.deep.equal(object.array);
539-
expect(1).to.equal(object.hash.a);
540-
expect(2).to.equal(object.hash.b);
541-
expect(object.date != null).to.be.ok;
542-
expect(object.oid != null).to.be.ok;
543-
expect(object.binary != null).to.be.ok;
544-
expect(42).to.equal(object.int);
545-
expect(33.3333).to.equal(object.float);
546-
expect(object.regexp != null).to.be.ok;
547-
expect(true).to.equal(object.boolean);
548-
expect(object.where != null).to.be.ok;
549-
expect(object.dbref != null).to.be.ok;
550-
expect(object[null] == null).to.be.ok;
551564
done();
552565
});
553566

@@ -2317,4 +2330,20 @@ describe('BSON', function() {
23172330
expect(false).to.equal(id.equals(undefined));
23182331
done();
23192332
});
2333+
2334+
it('Should serialize the same values to a Buffer and a Uint8Array', function() {
2335+
const testData = { darmok: 'jalad' };
2336+
2337+
const dataLength = createBSON().serialize(testData).length;
2338+
const buffer = new Buffer(dataLength);
2339+
const uint8Array = new Uint8Array(dataLength);
2340+
2341+
createBSON().serializeWithBufferAndIndex(testData, buffer);
2342+
createBSON().serializeWithBufferAndIndex(testData, uint8Array);
2343+
2344+
const bufferRaw = Array.prototype.slice.call(buffer, 0);
2345+
const uint8ArrayRaw = Array.prototype.slice.call(uint8Array, 0);
2346+
2347+
expect(bufferRaw).to.deep.equal(uint8ArrayRaw);
2348+
});
23202349
});

test/node/ensure_buffer_test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use strict';
2+
3+
const ensureBuffer = require('../../lib/bson/ensure_buffer');
4+
const expect = require('chai').expect;
5+
6+
describe('ensureBuffer tests', function() {
7+
it('should be a function', function() {
8+
expect(ensureBuffer).to.be.a('function');
9+
});
10+
11+
it('should return the exact same buffer if a buffer is passed in', function() {
12+
const bufferIn = new Buffer(10);
13+
let bufferOut;
14+
15+
expect(function() {
16+
bufferOut = ensureBuffer(bufferIn);
17+
}).to.not.throw(Error);
18+
19+
expect(bufferOut).to.equal(bufferIn);
20+
});
21+
22+
it('should wrap a UInt8Array with a buffer', function() {
23+
const arrayIn = Uint8Array.from([1, 2, 3]);
24+
let bufferOut;
25+
26+
expect(function() {
27+
bufferOut = ensureBuffer(arrayIn);
28+
}).to.not.throw(Error);
29+
30+
expect(bufferOut).to.be.an.instanceOf(Buffer);
31+
expect(bufferOut.buffer).to.equal(arrayIn.buffer);
32+
});
33+
34+
[0, 12, -1, '', 'foo', null, undefined, ['list'], {}, /x/].forEach(function(item) {
35+
it(`should throw if input is ${typeof item}: ${item}`, function() {
36+
expect(function() {
37+
ensureBuffer(item);
38+
}).to.throw(TypeError);
39+
});
40+
});
41+
42+
[
43+
/* eslint-disable */
44+
Int8Array,
45+
Uint8ClampedArray,
46+
Int16Array,
47+
Uint16Array,
48+
Int32Array,
49+
Uint32Array,
50+
Float32Array,
51+
Float64Array
52+
/* eslint-enable */
53+
].forEach(function(TypedArray) {
54+
it(`should throw if input is typed array ${TypedArray.name}`, function() {
55+
const typedArray = new TypedArray();
56+
expect(function() {
57+
ensureBuffer(typedArray);
58+
}).to.throw(TypeError);
59+
});
60+
});
61+
});

0 commit comments

Comments
 (0)