Source: node_modules/bson/lib/bson/objectid.js

  1. /**
  2. * Machine id.
  3. *
  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.
  7. * @ignore
  8. */
  9. var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10);
  10. // Regular expression that checks for hex value
  11. var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
  12. var hasBufferType = false;
  13. // Check if buffer exists
  14. try {
  15. if(Buffer && Buffer.from) hasBufferType = true;
  16. } catch(err) {};
  17. /**
  18. * Create a new ObjectID instance
  19. *
  20. * @class
  21. * @param {(string|number)} id Can be a 24 byte hex string, 12 byte binary string or a Number.
  22. * @property {number} generationTime The generation time of this ObjectId instance
  23. * @return {ObjectID} instance of ObjectID.
  24. */
  25. var ObjectID = function ObjectID(id) {
  26. // Duck-typing to support ObjectId from different npm packages
  27. if(id instanceof ObjectID) return id;
  28. if(!(this instanceof ObjectID)) return new ObjectID(id);
  29. this._bsontype = 'ObjectID';
  30. // The most common usecase (blank id, new objectId instance)
  31. if(id == null || typeof id == 'number') {
  32. // Generate a new id
  33. this.id = this.generate(id);
  34. // If we are caching the hex string
  35. if(ObjectID.cacheHexString) this.__id = this.toString('hex');
  36. // Return the object
  37. return;
  38. }
  39. // Check if the passed in id is valid
  40. var valid = ObjectID.isValid(id);
  41. // Throw an error if it's not a valid setup
  42. if(!valid && id != null){
  43. throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
  44. } else if(valid && typeof id == 'string' && id.length == 24 && hasBufferType) {
  45. return new ObjectID(new Buffer(id, 'hex'));
  46. } else if(valid && typeof id == 'string' && id.length == 24) {
  47. return ObjectID.createFromHexString(id);
  48. } else if(id != null && id.length === 12) {
  49. // assume 12 byte string
  50. this.id = id;
  51. } else if(id != null && id.toHexString) {
  52. // Duck-typing to support ObjectId from different npm packages
  53. return id;
  54. } else {
  55. throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
  56. }
  57. if(ObjectID.cacheHexString) this.__id = this.toString('hex');
  58. };
  59. // Allow usage of ObjectId as well as ObjectID
  60. var ObjectId = ObjectID;
  61. // Precomputed hex table enables speedy hex string conversion
  62. var hexTable = [];
  63. for (var i = 0; i < 256; i++) {
  64. hexTable[i] = (i <= 15 ? '0' : '') + i.toString(16);
  65. }
  66. /**
  67. * Return the ObjectID id as a 24 byte hex string representation
  68. *
  69. * @method
  70. * @return {string} return the 24 byte hex string representation.
  71. */
  72. ObjectID.prototype.toHexString = function() {
  73. if(ObjectID.cacheHexString && this.__id) return this.__id;
  74. var hexString = '';
  75. if(!this.id || !this.id.length) {
  76. throw new Error('invalid ObjectId, ObjectId.id must be either a string or a Buffer, but is [' + JSON.stringify(this.id) + ']');
  77. }
  78. if(this.id instanceof _Buffer) {
  79. hexString = convertToHex(this.id);
  80. if(ObjectID.cacheHexString) this.__id = hexString;
  81. return hexString;
  82. }
  83. for (var i = 0; i < this.id.length; i++) {
  84. hexString += hexTable[this.id.charCodeAt(i)];
  85. }
  86. if(ObjectID.cacheHexString) this.__id = hexString;
  87. return hexString;
  88. };
  89. /**
  90. * Update the ObjectID index used in generating new ObjectID's on the driver
  91. *
  92. * @method
  93. * @return {number} returns next index value.
  94. * @ignore
  95. */
  96. ObjectID.prototype.get_inc = function() {
  97. return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF;
  98. };
  99. /**
  100. * Update the ObjectID index used in generating new ObjectID's on the driver
  101. *
  102. * @method
  103. * @return {number} returns next index value.
  104. * @ignore
  105. */
  106. ObjectID.prototype.getInc = function() {
  107. return this.get_inc();
  108. };
  109. /**
  110. * Generate a 12 byte id buffer used in ObjectID's
  111. *
  112. * @method
  113. * @param {number} [time] optional parameter allowing to pass in a second based timestamp.
  114. * @return {Buffer} return the 12 byte id buffer string.
  115. */
  116. ObjectID.prototype.generate = function(time) {
  117. if ('number' != typeof time) {
  118. time = ~~(Date.now()/1000);
  119. }
  120. // Use pid
  121. var pid = (typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid) % 0xFFFF;
  122. var inc = this.get_inc();
  123. // Buffer used
  124. var buffer = new Buffer(12);
  125. // Encode time
  126. buffer[3] = time & 0xff;
  127. buffer[2] = (time >> 8) & 0xff;
  128. buffer[1] = (time >> 16) & 0xff;
  129. buffer[0] = (time >> 24) & 0xff;
  130. // Encode machine
  131. buffer[6] = MACHINE_ID & 0xff;
  132. buffer[5] = (MACHINE_ID >> 8) & 0xff;
  133. buffer[4] = (MACHINE_ID >> 16) & 0xff;
  134. // Encode pid
  135. buffer[8] = pid & 0xff;
  136. buffer[7] = (pid >> 8) & 0xff;
  137. // Encode index
  138. buffer[11] = inc & 0xff;
  139. buffer[10] = (inc >> 8) & 0xff;
  140. buffer[9] = (inc >> 16) & 0xff;
  141. // Return the buffer
  142. return buffer;
  143. };
  144. /**
  145. * Converts the id into a 24 byte hex string for printing
  146. *
  147. * @param {String} format The Buffer toString format parameter.
  148. * @return {String} return the 24 byte hex string representation.
  149. * @ignore
  150. */
  151. ObjectID.prototype.toString = function(format) {
  152. // Is the id a buffer then use the buffer toString method to return the format
  153. if(this.id && this.id.copy) {
  154. return this.id.toString(typeof format === 'string' ? format : 'hex');
  155. }
  156. // if(this.buffer )
  157. return this.toHexString();
  158. };
  159. /**
  160. * Converts to a string representation of this Id.
  161. *
  162. * @return {String} return the 24 byte hex string representation.
  163. * @ignore
  164. */
  165. ObjectID.prototype.inspect = ObjectID.prototype.toString;
  166. /**
  167. * Converts to its JSON representation.
  168. *
  169. * @return {String} return the 24 byte hex string representation.
  170. * @ignore
  171. */
  172. ObjectID.prototype.toJSON = function() {
  173. return this.toHexString();
  174. };
  175. /**
  176. * Compares the equality of this ObjectID with `otherID`.
  177. *
  178. * @method
  179. * @param {object} otherID ObjectID instance to compare against.
  180. * @return {boolean} the result of comparing two ObjectID's
  181. */
  182. ObjectID.prototype.equals = function equals (otherId) {
  183. var id;
  184. if(otherId instanceof ObjectID) {
  185. return this.toString() == otherId.toString();
  186. } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12 && this.id instanceof _Buffer) {
  187. return otherId === this.id.toString('binary');
  188. } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 24) {
  189. return otherId.toLowerCase() === this.toHexString();
  190. } else if(typeof otherId == 'string' && ObjectID.isValid(otherId) && otherId.length == 12) {
  191. return otherId === this.id;
  192. } else if(otherId != null && (otherId instanceof ObjectID || otherId.toHexString)) {
  193. return otherId.toHexString() === this.toHexString();
  194. } else {
  195. return false;
  196. }
  197. }
  198. /**
  199. * Returns the generation date (accurate up to the second) that this ID was generated.
  200. *
  201. * @method
  202. * @return {date} the generation date
  203. */
  204. ObjectID.prototype.getTimestamp = function() {
  205. var timestamp = new Date();
  206. var time = this.id[3] | this.id[2] << 8 | this.id[1] << 16 | this.id[0] << 24;
  207. timestamp.setTime(Math.floor(time) * 1000);
  208. return timestamp;
  209. }
  210. /**
  211. * @ignore
  212. */
  213. ObjectID.index = ~~(Math.random() * 0xFFFFFF);
  214. /**
  215. * @ignore
  216. */
  217. ObjectID.createPk = function createPk () {
  218. return new ObjectID();
  219. };
  220. /**
  221. * Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID.
  222. *
  223. * @method
  224. * @param {number} time an integer number representing a number of seconds.
  225. * @return {ObjectID} return the created ObjectID
  226. */
  227. ObjectID.createFromTime = function createFromTime (time) {
  228. var buffer = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
  229. // Encode time into first 4 bytes
  230. buffer[3] = time & 0xff;
  231. buffer[2] = (time >> 8) & 0xff;
  232. buffer[1] = (time >> 16) & 0xff;
  233. buffer[0] = (time >> 24) & 0xff;
  234. // Return the new objectId
  235. return new ObjectID(buffer);
  236. };
  237. // Lookup tables
  238. var encodeLookup = '0123456789abcdef'.split('')
  239. var decodeLookup = []
  240. var i = 0
  241. while (i < 10) decodeLookup[0x30 + i] = i++
  242. while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++
  243. var _Buffer = Buffer;
  244. var convertToHex = function(bytes) {
  245. return bytes.toString('hex');
  246. }
  247. /**
  248. * Creates an ObjectID from a hex string representation of an ObjectID.
  249. *
  250. * @method
  251. * @param {string} hexString create a ObjectID from a passed in 24 byte hexstring.
  252. * @return {ObjectID} return the created ObjectID
  253. */
  254. ObjectID.createFromHexString = function createFromHexString (string) {
  255. // Throw an error if it's not a valid setup
  256. if(typeof string === 'undefined' || string != null && string.length != 24) {
  257. throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
  258. }
  259. // Use Buffer.from method if available
  260. if(hasBufferType) return new ObjectID(new Buffer(string, 'hex'));
  261. // Calculate lengths
  262. var array = new _Buffer(12);
  263. var n = 0;
  264. var i = 0;
  265. while (i < 24) {
  266. array[n++] = decodeLookup[string.charCodeAt(i++)] << 4 | decodeLookup[string.charCodeAt(i++)]
  267. }
  268. return new ObjectID(array);
  269. };
  270. /**
  271. * Checks if a value is a valid bson ObjectId
  272. *
  273. * @method
  274. * @return {boolean} return true if the value is a valid bson ObjectId, return false otherwise.
  275. */
  276. ObjectID.isValid = function isValid(id) {
  277. if(id == null) return false;
  278. if(typeof id == 'number') {
  279. return true;
  280. }
  281. if(typeof id == 'string') {
  282. return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
  283. }
  284. if(id instanceof ObjectID) {
  285. return true;
  286. }
  287. if(id instanceof _Buffer) {
  288. return true;
  289. }
  290. // Duck-Typing detection of ObjectId like objects
  291. if(id.toHexString) {
  292. return id.id.length == 12 || (id.id.length == 24 && checkForHexRegExp.test(id.id));
  293. }
  294. return false;
  295. };
  296. /**
  297. * @ignore
  298. */
  299. Object.defineProperty(ObjectID.prototype, "generationTime", {
  300. enumerable: true
  301. , get: function () {
  302. return this.id[3] | this.id[2] << 8 | this.id[1] << 16 | this.id[0] << 24;
  303. }
  304. , set: function (value) {
  305. // Encode time into first 4 bytes
  306. this.id[3] = value & 0xff;
  307. this.id[2] = (value >> 8) & 0xff;
  308. this.id[1] = (value >> 16) & 0xff;
  309. this.id[0] = (value >> 24) & 0xff;
  310. }
  311. });
  312. /**
  313. * Expose.
  314. */
  315. module.exports = ObjectID;
  316. module.exports.ObjectID = ObjectID;
  317. module.exports.ObjectId = ObjectID;