4 * Create a random 3-byte value (i.e. unique for this
5 * process). Other drivers use a md5 of the machine id here, but
6 * that would mean an asyc call to gethostname, so we don't bother.
9 var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10);
11 // Regular expression that checks for hex value
12 var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
13 var hasBufferType = false;
15 // Check if buffer exists
17 if(Buffer && Buffer.from) hasBufferType = true;
21 * Create a new ObjectID instance
24 * @param {(string|number)} id Can be a 24 byte hex string, 12 byte binary string or a Number.
25 * @property {number} generationTime The generation time of this ObjectId instance
26 * @return {ObjectID} instance of ObjectID.
28 var ObjectID = function ObjectID(id) {
29 // Duck-typing to support ObjectId from different npm packages
30 if(id instanceof ObjectID) return id;
31 if(!(this instanceof ObjectID)) return new ObjectID(id);
33 this._bsontype = 'ObjectID';
35 // The most common usecase (blank id, new objectId instance)
36 if(id == null || typeof id == 'number') {
38 this.id = this.generate(id);
39 // If we are caching the hex string
40 if(ObjectID.cacheHexString) this.__id = this.toString('hex');
45 // Check if the passed in id is valid
46 var valid = ObjectID.isValid(id);
48 // Throw an error if it's not a valid setup
49 if(!valid && id != null){
50 throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
51 } else if(valid && typeof id == 'string' && id.length == 24 && hasBufferType) {
52 return new ObjectID(Buffer.from(id, 'hex'));
53 } else if(valid && typeof id == 'string' && id.length == 24) {
54 return ObjectID.createFromHexString(id);
55 } else if(id != null && id.length === 12) {
56 // assume 12 byte string
58 } else if(id != null && id.toHexString) {
59 // Duck-typing to support ObjectId from different npm packages
62 throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
65 if(ObjectID.cacheHexString) this.__id = this.toString('hex');
68 // Allow usage of ObjectId as well as ObjectID
69 var ObjectId = ObjectID;
71 // Precomputed hex table enables speedy hex string conversion
73 for (var i = 0; i < 256; i++) {
74 hexTable[i] = (i <= 15 ? '0' : '') + i.toString(16);
78 * Return the ObjectID id as a 24 byte hex string representation
81 * @return {string} return the 24 byte hex string representation.
83 ObjectID.prototype.toHexString = function() {
84 if(ObjectID.cacheHexString && this.__id) return this.__id;
87 if(!this.id || !this.id.length) {
88 throw new Error('invalid ObjectId, ObjectId.id must be either a string or a Buffer, but is [' + JSON.stringify(this.id) + ']');
91 if(this.id instanceof _Buffer) {
92 hexString = convertToHex(this.id);
93 if(ObjectID.cacheHexString) this.__id = hexString;
97 for (var i = 0; i < this.id.length; i++) {
98 hexString += hexTable[this.id.charCodeAt(i)];
101 if(ObjectID.cacheHexString) this.__id = hexString;
106 * Update the ObjectID index used in generating new ObjectID's on the driver
109 * @return {number} returns next index value.
112 ObjectID.prototype.get_inc = function() {
113 return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF;
117 * Update the ObjectID index used in generating new ObjectID's on the driver
120 * @return {number} returns next index value.
123 ObjectID.prototype.getInc = function() {
124 return this.get_inc();
128 * Generate a 12 byte id buffer used in ObjectID's
131 * @param {number} [time] optional parameter allowing to pass in a second based timestamp.
132 * @return {Buffer} return the 12 byte id buffer string.
134 ObjectID.prototype.generate = function(time) {
135 if ('number' != typeof time) {
136 time = ~~(Date.now()/1000);
140 var pid = (typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid) % 0xFFFF;
141 var inc = this.get_inc();
143 var buffer = new Buffer(12);
145 buffer[3] = time & 0xff;
146 buffer[2] = (time >> 8) & 0xff;
147 buffer[1] = (time >> 16) & 0xff;
148 buffer[0] = (time >> 24) & 0xff;
150 buffer[6] = MACHINE_ID & 0xff;
151 buffer[5] = (MACHINE_ID >> 8) & 0xff;
152 buffer[4] = (MACHINE_ID >> 16) & 0xff;
154 buffer[8] = pid & 0xff;
155 buffer[7] = (pid >> 8) & 0xff;
157 buffer[11] = inc & 0xff;
158 buffer[10] = (inc >> 8) & 0xff;
159 buffer[9] = (inc >> 16) & 0xff;
165 * Converts the id into a 24 byte hex string for printing
167 * @param {String} format The Buffer toString format parameter.
168 * @return {String} return the 24 byte hex string representation.
171 ObjectID.prototype.toString = function(format) {
172 // Is the id a buffer then use the buffer toString method to return the format
173 if(this.id && this.id.copy) {
174 return this.id.toString(format || 'hex');
178 return this.toHexString();
182 * Converts to a string representation of this Id.
184 * @return {String} return the 24 byte hex string representation.
187 ObjectID.prototype.inspect = ObjectID.prototype.toString;
190 * Converts to its JSON representation.
192 * @return {String} return the 24 byte hex string representation.
195 ObjectID.prototype.toJSON = function() {
196 return this.toHexString();
200 * Compares the equality of this ObjectID with `otherID`.
203 * @param {object} otherID ObjectID instance to compare against.
204 * @return {boolean} the result of comparing two ObjectID's
206 ObjectID.prototype.equals = function equals (otherId) {
209 if(otherId instanceof ObjectID) {
210 return this.toString() == otherId.toString();
211 } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12 && this.id instanceof _Buffer) {
212 return otherId === this.id.toString('binary');
213 } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 24) {
214 return otherId.toLowerCase() === this.toHexString();
215 } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12) {
216 return otherId === this.id;
217 } else if(otherId != null && (otherId instanceof ObjectID || otherId.toHexString)) {
218 return otherId.toHexString() === this.toHexString();
225 * Returns the generation date (accurate up to the second) that this ID was generated.
228 * @return {date} the generation date
230 ObjectID.prototype.getTimestamp = function() {
231 var timestamp = new Date();
232 var time = this.id[3] | this.id[2] << 8 | this.id[1] << 16 | this.id[0] << 24;
233 timestamp.setTime(Math.floor(time) * 1000);
240 ObjectID.index = ~~(Math.random() * 0xFFFFFF);
245 ObjectID.createPk = function createPk () {
246 return new ObjectID();
250 * Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID.
253 * @param {number} time an integer number representing a number of seconds.
254 * @return {ObjectID} return the created ObjectID
256 ObjectID.createFromTime = function createFromTime (time) {
257 var buffer = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
258 // Encode time into first 4 bytes
259 buffer[3] = time & 0xff;
260 buffer[2] = (time >> 8) & 0xff;
261 buffer[1] = (time >> 16) & 0xff;
262 buffer[0] = (time >> 24) & 0xff;
263 // Return the new objectId
264 return new ObjectID(buffer);
268 var encodeLookup = '0123456789abcdef'.split('')
269 var decodeLookup = []
271 while (i < 10) decodeLookup[0x30 + i] = i++
272 while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++
274 var _Buffer = Buffer;
275 var convertToHex = function(bytes) {
276 return bytes.toString('hex');
280 * Creates an ObjectID from a hex string representation of an ObjectID.
283 * @param {string} hexString create a ObjectID from a passed in 24 byte hexstring.
284 * @return {ObjectID} return the created ObjectID
286 ObjectID.createFromHexString = function createFromHexString (string) {
287 // Throw an error if it's not a valid setup
288 if(typeof string === 'undefined' || string != null && string.length != 24) {
289 throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
292 // Use Buffer.from method if available
293 if(hasBufferType) return new ObjectID(Buffer.from(string, 'hex'));
296 var array = new _Buffer(12);
301 array[n++] = decodeLookup[string.charCodeAt(i++)] << 4 | decodeLookup[string.charCodeAt(i++)]
304 return new ObjectID(array);
308 * Checks if a value is a valid bson ObjectId
311 * @return {boolean} return true if the value is a valid bson ObjectId, return false otherwise.
313 ObjectID.isValid = function isValid(id) {
314 if(id == null) return false;
316 if(typeof id == 'number') {
320 if(typeof id == 'string') {
321 return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
324 if(id instanceof ObjectID) {
328 if(id instanceof _Buffer) {
332 // Duck-Typing detection of ObjectId like objects
334 return id.id.length == 12 || (id.id.length == 24 && checkForHexRegExp.test(id.id));
343 Object.defineProperty(ObjectID.prototype, "generationTime", {
346 return this.id[3] | this.id[2] << 8 | this.id[1] << 16 | this.id[0] << 24;
348 , set: function (value) {
349 // Encode time into first 4 bytes
350 this.id[3] = value & 0xff;
351 this.id[2] = (value >> 8) & 0xff;
352 this.id[1] = (value >> 16) & 0xff;
353 this.id[0] = (value >> 24) & 0xff;
360 module.exports = ObjectID;
361 module.exports.ObjectID = ObjectID;
362 module.exports.ObjectId = ObjectID;