From aa0b54597a0af28cce3530d2144af708e4b66bf0 Mon Sep 17 00:00:00 2001 From: Christian Kvalheim Date: Tue, 17 May 2016 14:43:55 +0200 Subject: [PATCH] Added support for buffers to objectId --- lib/bson/objectid.js | 97 +++++++++++++++++++++++-------- lib/bson/parser/serializer.js | 12 +++- test/node/bson_compliance_test.js | 2 +- test/node/bson_test.js | 8 ++- test/node/map_tests.js | 14 ----- 5 files changed, 92 insertions(+), 41 deletions(-) diff --git a/lib/bson/objectid.js b/lib/bson/objectid.js index cb9ce0a3..7d4e4f29 100644 --- a/lib/bson/objectid.js +++ b/lib/bson/objectid.js @@ -74,6 +74,12 @@ ObjectID.prototype.toHexString = function() { var hexString = ''; + if(this.id instanceof _Buffer) { + hexString = convertToHex(this.id); + if(ObjectID.cacheHexString) this.__id = hexString; + return hexString; + } + for (var i = 0; i < this.id.length; i++) { hexString += hexTable[this.id.charCodeAt(i)]; } @@ -113,7 +119,7 @@ ObjectID.prototype.getInc = function() { */ ObjectID.prototype.generate = function(time) { if ('number' != typeof time) { - time = parseInt(Date.now()/1000,10); + time = ~~(Date.now()/1000); } var time4Bytes = BinaryParser.encodeInt(time, 32, true, true); @@ -160,18 +166,22 @@ ObjectID.prototype.toJSON = function() { * @param {object} otherID ObjectID instance to compare against. * @return {boolean} the result of comparing two ObjectID's */ -ObjectID.prototype.equals = function equals (otherID) { +ObjectID.prototype.equals = function equals (otherId) { var id; - if(otherID != null && (otherID instanceof ObjectID || otherID.toHexString)) { - id = otherID.id; - } else if(typeof otherID == 'string' && ObjectID.isValid(otherID)) { - id = ObjectID.createFromHexString(otherID).id; + if(otherId instanceof ObjectID) { + return this.toString() == otherId.toString(); + } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12 && this.id instanceof _Buffer) { + return otherId === this.id.toString('binary'); + } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 24) { + return otherId === this.toHexString(); + } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12) { + return otherId === this.id; + } else if(otherId != null && (otherId instanceof ObjectID || otherId.toHexString)) { + return otherId.toHexString() === this.toHexString(); } else { return false; } - - return this.id === id; } /** @@ -189,7 +199,7 @@ ObjectID.prototype.getTimestamp = function() { /** * @ignore */ -ObjectID.index = parseInt(Math.random() * 0xFFFFFF, 10); +ObjectID.index = ~~(Math.random() * 0xFFFFFF); /** * @ignore @@ -211,6 +221,18 @@ ObjectID.createFromTime = function createFromTime (time) { return new ObjectID(id); }; +// Lookup tables +var encodeLookup = '0123456789abcdef'.split('') +var decodeLookup = [] +var i = 0 +while (i < 10) decodeLookup[0x30 + i] = i++ +while (i < 16) decodeLookup[0x61 - 10 + i] = i++ + +var _Buffer = Buffer; +var convertToHex = function(bytes) { + return bytes.toString('hex'); +} + /** * Creates an ObjectID from a hex string representation of an ObjectID. * @@ -218,28 +240,44 @@ ObjectID.createFromTime = function createFromTime (time) { * @param {string} hexString create a ObjectID from a passed in 24 byte hexstring. * @return {ObjectID} return the created ObjectID */ -ObjectID.createFromHexString = function createFromHexString (hexString) { +ObjectID.createFromHexString = function createFromHexString (string) { // Throw an error if it's not a valid setup - if(typeof hexString === 'undefined' || hexString != null && hexString.length != 24) + if(typeof string === 'undefined' || string != null && string.length != 24) throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"); - var len = hexString.length; + var length = string.length; - if(len > 12*2) { + if(length > 12*2) { throw new Error('Id cannot be longer than 12 bytes'); } - var result = '' - , string - , number; + // Calculate lengths + var sizeof = length >> 1; + var array = new _Buffer(sizeof); + var n = 0; + var i = 0; - for (var index = 0; index < len; index += 2) { - string = hexString.substr(index, 2); - number = parseInt(string, 16); - result += BinaryParser.fromByte(number); + while (i < length) { + array[n++] = decodeLookup[string.charCodeAt(i++)] << 4 | decodeLookup[string.charCodeAt(i++)] } - return new ObjectID(result, hexString); + // var result = array.toString('binary'); + // console.log("!!!!!!!!!!!!!!!!!!!") + // console.log(array.toString('binary')) + + + // var result = '' + // , string + // , number; + // + // for (var index = 0; index < len; index += 2) { + // string = hexString.substr(index, 2); + // // number = 0 + parseInt(string, 16); + // number = 0 + string; + // result += BinaryParser.fromByte(number); + // }ObjectID + + return new ObjectID(array); }; /** @@ -251,18 +289,27 @@ ObjectID.createFromHexString = function createFromHexString (hexString) { ObjectID.isValid = function isValid(id) { if(id == null) return false; - if(typeof id == 'number') + if(typeof id == 'number') { return true; + } + if(typeof id == 'string') { return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id)); } + if(id instanceof ObjectID) { return true; } + + if(id instanceof _Buffer) { + return true; + } + // Duck-Typing detection of ObjectId like objects if(id.toHexString) { return id.id.length == 12 || (id.id.length == 24 && checkForHexRegExp.test(id.id)); } + return false; }; @@ -272,7 +319,11 @@ ObjectID.isValid = function isValid(id) { Object.defineProperty(ObjectID.prototype, "generationTime", { enumerable: true , get: function () { - return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)); + if(this.id instanceof _Buffer) { + return this.id[0] | this.id[1] << 8 | this.id[2] << 16 | this.id[3] << 24; + } else { + return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)); + } } , set: function (value) { var value = BinaryParser.encodeInt(value, 32, true, true); diff --git a/lib/bson/parser/serializer.js b/lib/bson/parser/serializer.js index b991aec0..a536908c 100644 --- a/lib/bson/parser/serializer.js +++ b/lib/bson/parser/serializer.js @@ -15,6 +15,12 @@ var writeIEEE754 = require('../float_parser').writeIEEE754, DBRef = require('../db_ref').DBRef, Binary = require('../binary').Binary; +try { + var _Buffer = Uint8Array; +} catch(e) { + var _Buffer = Buffer; +} + var regexp = /\x00/ // To ensure that 0.4 of node works correctly @@ -239,7 +245,11 @@ var serializeObjectId = function(buffer, key, value, index) { buffer[index++] = 0; // Write the objectId into the shared buffer - buffer.write(value.id, index, 'binary') + if(typeof value.id == 'string') { + buffer.write(value.id, index, 'binary') + } else { + value.id.copy(buffer, index, 0, 12); + } // Ajust index return index + 12; diff --git a/test/node/bson_compliance_test.js b/test/node/bson_compliance_test.js index 1f2df961..8d80a5f0 100644 --- a/test/node/bson_compliance_test.js +++ b/test/node/bson_compliance_test.js @@ -125,7 +125,7 @@ exports['Pass all valid BSON serialization scenarios ./compliance/valid.json'] = // Attempt to deserialize var object = bson.deserialize(buffer, {promoteLongs:false}); // // Validate the object - test.deepEqual(expectedDocument, object); + test.deepEqual(JSON.stringify(expectedDocument), JSON.stringify(object)); }); test.done(); diff --git a/test/node/bson_test.js b/test/node/bson_test.js index 3fb51d7f..500356ab 100644 --- a/test/node/bson_test.js +++ b/test/node/bson_test.js @@ -1276,7 +1276,7 @@ exports['Should deserialize correctly'] = function(test) { assertBuffersEqual(test, serialized_data, serialized_data2, 0); var doc2 = new BSON.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); - test.deepEqual(doc, doc2) + test.deepEqual(JSON.stringify(doc), JSON.stringify(doc2)) test.done(); } @@ -1296,7 +1296,10 @@ exports['Should correctly serialize and deserialize MinKey and MaxKey values'] = assertBuffersEqual(test, serialized_data, serialized_data2, 0); var doc2 = new BSON.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); - test.deepEqual(doc, doc2) + // Peform equality checks + test.equal(JSON.stringify(doc), JSON.stringify(doc2)); + test.ok(doc._id.equals(doc2._id)) + // process.exit(0) test.ok(doc2.minKey instanceof MinKey); test.ok(doc2.maxKey instanceof MaxKey); test.done(); @@ -1744,6 +1747,7 @@ exports['Should fail to create ObjectID due to illegal hex code'] = function(tes return tmp.toHexString(); } }; + test.equal(true, tmp.equals(objectIdLike)); test.equal(true, tmp.equals(new ObjectId(objectIdLike))); test.equal(true, ObjectID.isValid(objectIdLike)); diff --git a/test/node/map_tests.js b/test/node/map_tests.js index 3f8c8fbd..d85b645b 100644 --- a/test/node/map_tests.js +++ b/test/node/map_tests.js @@ -104,17 +104,3 @@ exports['should serialize a map'] = function(test) { var data = bson.serialize(m, false, true); test.equal('13000000103100010000001030000200000000', data.toString('hex')); } - - - - - - - - - - - - - -