objectid.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /**
  2. * Module dependencies.
  3. * @ignore
  4. */
  5. var BinaryParser = require('./binary_parser').BinaryParser;
  6. /**
  7. * Machine id.
  8. *
  9. * Create a random 3-byte value (i.e. unique for this
  10. * process). Other drivers use a md5 of the machine id here, but
  11. * that would mean an asyc call to gethostname, so we don't bother.
  12. * @ignore
  13. */
  14. var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10);
  15. // Regular expression that checks for hex value
  16. var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
  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. if(!(this instanceof ObjectID)) return new ObjectID(id);
  27. if((id instanceof ObjectID)) return id;
  28. this._bsontype = 'ObjectID';
  29. var __id = null;
  30. var valid = ObjectID.isValid(id);
  31. // Throw an error if it's not a valid setup
  32. if(!valid && id != null){
  33. throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
  34. } else if(valid && typeof id == 'string' && id.length == 24) {
  35. return ObjectID.createFromHexString(id);
  36. } else if(id == null || typeof id == 'number') {
  37. // convert to 12 byte binary string
  38. this.id = this.generate(id);
  39. } else if(id != null && id.length === 12) {
  40. // assume 12 byte string
  41. this.id = id;
  42. }
  43. if(ObjectID.cacheHexString) this.__id = this.toHexString();
  44. };
  45. // Allow usage of ObjectId as well as ObjectID
  46. var ObjectId = ObjectID;
  47. // Precomputed hex table enables speedy hex string conversion
  48. var hexTable = [];
  49. for (var i = 0; i < 256; i++) {
  50. hexTable[i] = (i <= 15 ? '0' : '') + i.toString(16);
  51. }
  52. /**
  53. * Return the ObjectID id as a 24 byte hex string representation
  54. *
  55. * @method
  56. * @return {string} return the 24 byte hex string representation.
  57. */
  58. ObjectID.prototype.toHexString = function() {
  59. if(ObjectID.cacheHexString && this.__id) return this.__id;
  60. var hexString = '';
  61. for (var i = 0; i < this.id.length; i++) {
  62. hexString += hexTable[this.id.charCodeAt(i)];
  63. }
  64. if(ObjectID.cacheHexString) this.__id = hexString;
  65. return hexString;
  66. };
  67. /**
  68. * Update the ObjectID index used in generating new ObjectID's on the driver
  69. *
  70. * @method
  71. * @return {number} returns next index value.
  72. * @ignore
  73. */
  74. ObjectID.prototype.get_inc = function() {
  75. return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF;
  76. };
  77. /**
  78. * Update the ObjectID index used in generating new ObjectID's on the driver
  79. *
  80. * @method
  81. * @return {number} returns next index value.
  82. * @ignore
  83. */
  84. ObjectID.prototype.getInc = function() {
  85. return this.get_inc();
  86. };
  87. /**
  88. * Generate a 12 byte id string used in ObjectID's
  89. *
  90. * @method
  91. * @param {number} [time] optional parameter allowing to pass in a second based timestamp.
  92. * @return {string} return the 12 byte id binary string.
  93. */
  94. ObjectID.prototype.generate = function(time) {
  95. if ('number' != typeof time) {
  96. time = parseInt(Date.now()/1000,10);
  97. }
  98. var time4Bytes = BinaryParser.encodeInt(time, 32, true, true);
  99. /* for time-based ObjectID the bytes following the time will be zeroed */
  100. var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false);
  101. var pid2Bytes = BinaryParser.fromShort(typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid % 0xFFFF);
  102. var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true);
  103. return time4Bytes + machine3Bytes + pid2Bytes + index3Bytes;
  104. };
  105. /**
  106. * Converts the id into a 24 byte hex string for printing
  107. *
  108. * @return {String} return the 24 byte hex string representation.
  109. * @ignore
  110. */
  111. ObjectID.prototype.toString = function() {
  112. return this.toHexString();
  113. };
  114. /**
  115. * Converts to a string representation of this Id.
  116. *
  117. * @return {String} return the 24 byte hex string representation.
  118. * @ignore
  119. */
  120. ObjectID.prototype.inspect = ObjectID.prototype.toString;
  121. /**
  122. * Converts to its JSON representation.
  123. *
  124. * @return {String} return the 24 byte hex string representation.
  125. * @ignore
  126. */
  127. ObjectID.prototype.toJSON = function() {
  128. return this.toHexString();
  129. };
  130. /**
  131. * Compares the equality of this ObjectID with `otherID`.
  132. *
  133. * @method
  134. * @param {object} otherID ObjectID instance to compare against.
  135. * @return {boolean} the result of comparing two ObjectID's
  136. */
  137. ObjectID.prototype.equals = function equals (otherID) {
  138. if(otherID == null) return false;
  139. var id = (otherID instanceof ObjectID || otherID.toHexString)
  140. ? otherID.id
  141. : ObjectID.createFromHexString(otherID).id;
  142. return this.id === id;
  143. }
  144. /**
  145. * Returns the generation date (accurate up to the second) that this ID was generated.
  146. *
  147. * @method
  148. * @return {date} the generation date
  149. */
  150. ObjectID.prototype.getTimestamp = function() {
  151. var timestamp = new Date();
  152. timestamp.setTime(Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)) * 1000);
  153. return timestamp;
  154. }
  155. /**
  156. * @ignore
  157. */
  158. ObjectID.index = parseInt(Math.random() * 0xFFFFFF, 10);
  159. /**
  160. * @ignore
  161. */
  162. ObjectID.createPk = function createPk () {
  163. return new ObjectID();
  164. };
  165. /**
  166. * Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID.
  167. *
  168. * @method
  169. * @param {number} time an integer number representing a number of seconds.
  170. * @return {ObjectID} return the created ObjectID
  171. */
  172. ObjectID.createFromTime = function createFromTime (time) {
  173. var id = BinaryParser.encodeInt(time, 32, true, true) +
  174. BinaryParser.encodeInt(0, 64, true, true);
  175. return new ObjectID(id);
  176. };
  177. /**
  178. * Creates an ObjectID from a hex string representation of an ObjectID.
  179. *
  180. * @method
  181. * @param {string} hexString create a ObjectID from a passed in 24 byte hexstring.
  182. * @return {ObjectID} return the created ObjectID
  183. */
  184. ObjectID.createFromHexString = function createFromHexString (hexString) {
  185. // Throw an error if it's not a valid setup
  186. if(typeof hexString === 'undefined' || hexString != null && hexString.length != 24)
  187. throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
  188. var len = hexString.length;
  189. if(len > 12*2) {
  190. throw new Error('Id cannot be longer than 12 bytes');
  191. }
  192. var result = ''
  193. , string
  194. , number;
  195. for (var index = 0; index < len; index += 2) {
  196. string = hexString.substr(index, 2);
  197. number = parseInt(string, 16);
  198. result += BinaryParser.fromByte(number);
  199. }
  200. return new ObjectID(result, hexString);
  201. };
  202. /**
  203. * Checks if a value is a valid bson ObjectId
  204. *
  205. * @method
  206. * @return {boolean} return true if the value is a valid bson ObjectId, return false otherwise.
  207. */
  208. ObjectID.isValid = function isValid(id) {
  209. if(id == null) return false;
  210. if(id != null && 'number' != typeof id && (id.length != 12 && id.length != 24)) {
  211. return false;
  212. } else {
  213. // Check specifically for hex correctness
  214. if(typeof id == 'string' && id.length == 24) return checkForHexRegExp.test(id);
  215. return true;
  216. }
  217. };
  218. /**
  219. * @ignore
  220. */
  221. Object.defineProperty(ObjectID.prototype, "generationTime", {
  222. enumerable: true
  223. , get: function () {
  224. return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true));
  225. }
  226. , set: function (value) {
  227. var value = BinaryParser.encodeInt(value, 32, true, true);
  228. this.id = value + this.id.substr(4);
  229. // delete this.__id;
  230. this.toHexString();
  231. }
  232. });
  233. /**
  234. * Expose.
  235. */
  236. module.exports = ObjectID;
  237. module.exports.ObjectID = ObjectID;
  238. module.exports.ObjectId = ObjectID;