Permalink
Browse files

Added support for buffers to objectId

  • Loading branch information...
1 parent 6504dda commit aa0b54597a0af28cce3530d2144af708e4b66bf0 @christkv christkv committed May 17, 2016
Showing with 92 additions and 41 deletions.
  1. +74 −23 lib/bson/objectid.js
  2. +11 −1 lib/bson/parser/serializer.js
  3. +1 −1 test/node/bson_compliance_test.js
  4. +6 −2 test/node/bson_test.js
  5. +0 −14 test/node/map_tests.js
View
@@ -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,35 +221,63 @@ 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.
*
* @method
* @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);
@@ -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;
@@ -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();
@@ -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));
@@ -104,17 +104,3 @@ exports['should serialize a map'] = function(test) {
var data = bson.serialize(m, false, true);
test.equal('13000000103100010000001030000200000000', data.toString('hex'));
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-

3 comments on commit aa0b545

@vkarpov15
Contributor

Not quite sure why yet, but it looks like this commit breaks lodash and underscore's deep equality check on node 0.10:

var bson = require('bson');
var assert = require('assert');
var _ = require('lodash');

var x1 = new bson.ObjectId();
var x2 = new bson.ObjectId(x1.toHexString());

assert.ok(_.isEqual(x1, x2));

Works fine without this commit on 0.10, but after this commit bugs out.

@christkv
Member

Hmm not really sure why it breaks while assert.deepEquals works fine for node 0.10. I think there might be an issue in how they do their comparison.

@vkarpov15
Contributor
vkarpov15 commented on aa0b545 Aug 31, 2016 edited

Yeah it's the same old issue as #84, that damn undocumented enumerable offset property that buffers have in 0.10 breaks deep equality checks that don't treat buffers as special snowflakes. Since this commit switched out strings for buffers, objectids now have the same limitation. I'm not gonna worry about this too much since node 0.10 is EOL in a month, but for posterity's sake this works fine on node 0.10:

var bson = require('bson');
var assert = require('assert');
var _ = require('lodash');

var x1 = new bson.ObjectId();
var x2 = new bson.ObjectId(x1.toHexString());

console.log(x1);
console.log(x2);

assert.ok(_.isEqualWith(x1, x2, function(a, b) {
  if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) {
    return a.toString('hex') === b.toString('hex');
  }
}));
Please sign in to comment.