/* This file is part of the Notesnook project (https://notesnook.com/) * * Copyright (C) 2022 Streetwriters (Private) Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // Copied from https://github.com/williamkapke/bson-objectid const { randomInt } = require("./random"); var MACHINE_ID = randomInt(); var index = (ObjectID.index = randomInt()); var pid = (typeof process === "undefined" || typeof process.pid !== "number" ? randomInt() : process.pid) % 0xffff; /** * Determine if an object is Buffer * * Author: Feross Aboukhadijeh * License: MIT * */ var isBuffer = function (obj) { return !!( obj != null && obj.constructor && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj) ); }; // Precomputed hex table enables speedy hex string conversion var hexTable = []; for (var i = 0; i < 256; i++) { hexTable[i] = (i <= 15 ? "0" : "") + i.toString(16); } // Regular expression that checks for hex value var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$"); // Lookup tables var decodeLookup = []; i = 0; while (i < 10) decodeLookup[0x30 + i] = i++; while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++; /** * Create a new immutable ObjectID instance * * @class Represents the BSON ObjectID type * @param {String|Number} id Can be a 24 byte hex string, 12 byte binary string or a Number. * @return {Object} instance of ObjectID. */ function ObjectID(id) { if (!(this instanceof ObjectID)) return new ObjectID(id); if (id && (id instanceof ObjectID || id._bsontype === "ObjectID")) return id; this._bsontype = "ObjectID"; // The most common usecase (blank id, new objectId instance) if (id == null || typeof id === "number") { // Generate a new id this.id = this.generate(id); // Return the object return; } // Check if the passed in id is valid var valid = ObjectID.isValid(id); // Throw an error if it's not a valid setup if (!valid && id != null) { throw new Error( "Argument passed in must be a single String of 12 bytes or a string of 24 hex characters" ); } else if (valid && typeof id === "string" && id.length === 24) { return ObjectID.createFromHexString(id); } else if (id != null && id.length === 12) { // assume 12 byte string this.id = id; } else if (id != null && typeof id.toHexString === "function") { // Duck-typing to support ObjectId from different npm packages return id; } else { throw new Error( "Argument passed in must be a single String of 12 bytes or a string of 24 hex characters" ); } } module.exports = ObjectID; ObjectID.default = ObjectID; /** * Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID. * * @param {Number} time an integer number representing a number of seconds. * @return {ObjectID} return the created ObjectID * @api public */ ObjectID.createFromTime = function (time) { time = parseInt(time, 10) % 0xffffffff; return new ObjectID(hex(8, time) + "0000000000000000"); }; /** * Creates an ObjectID from a hex string representation of an ObjectID. * * @param {String} hexString create a ObjectID from a passed in 24 byte hexstring. * @return {ObjectID} return the created ObjectID * @api public */ ObjectID.createFromHexString = function (hexString) { // Throw an error if it's not a valid setup if ( typeof hexString === "undefined" || (hexString != null && hexString.length !== 24) ) { throw new Error( "Argument passed in must be a single String of 12 bytes or a string of 24 hex characters" ); } // Calculate lengths var data = ""; var i = 0; while (i < 24) { data += String.fromCharCode( (decodeLookup[hexString.charCodeAt(i++)] << 4) | decodeLookup[hexString.charCodeAt(i++)] ); } return new ObjectID(data); }; /** * Checks if a value is a valid bson ObjectId * * @param {String} objectid Can be a 24 byte hex string or an instance of ObjectID. * @return {Boolean} return true if the value is a valid bson ObjectID, return false otherwise. * @api public * * THE NATIVE DOCUMENTATION ISN'T CLEAR ON THIS GUY! * http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html#objectid-isvalid */ ObjectID.isValid = function (id) { if (id == null) return false; 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 (isBuffer(id)) { return true; } // Duck-Typing detection of ObjectId like objects if ( typeof id.toHexString === "function" && (id.id instanceof Buffer || typeof id.id === "string") ) { return ( id.id.length === 12 || (id.id.length === 24 && checkForHexRegExp.test(id.id)) ); } return false; }; ObjectID.prototype = { constructor: ObjectID, /** * Return the ObjectID id as a 24 byte hex string representation * * @return {String} return the 24 byte hex string representation. * @api public */ toHexString: function () { if (!this.id || !this.id.length) { throw new Error( "invalid ObjectId, ObjectId.id must be either a string or a Buffer, but is [" + JSON.stringify(this.id) + "]" ); } if (this.id.length === 24) { return this.id; } if (isBuffer(this.id)) { return this.id.toString("hex"); } var hexString = ""; for (var i = 0; i < this.id.length; i++) { hexString += hexTable[this.id.charCodeAt(i)]; } return hexString; }, /** * Compares the equality of this ObjectID with `otherID`. * * @param {Object} otherId ObjectID instance to compare against. * @return {Boolean} the result of comparing two ObjectID's * @api public */ equals: function (otherId) { if (otherId instanceof ObjectID) { return this.toString() === otherId.toString(); } else if ( typeof otherId === "string" && ObjectID.isValid(otherId) && otherId.length === 12 && isBuffer(this.id) ) { return otherId === this.id.toString("binary"); } else if ( typeof otherId === "string" && ObjectID.isValid(otherId) && otherId.length === 24 ) { return otherId.toLowerCase() === 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; } }, /** * Returns the generation date (accurate up to the second) that this ID was generated. * * @return {Date} the generation date * @api public */ getTimestamp: function () { var timestamp = new Date(); var time; if (isBuffer(this.id)) { time = this.id[3] | (this.id[2] << 8) | (this.id[1] << 16) | (this.id[0] << 24); } else { time = this.id.charCodeAt(3) | (this.id.charCodeAt(2) << 8) | (this.id.charCodeAt(1) << 16) | (this.id.charCodeAt(0) << 24); } timestamp.setTime(Math.floor(time) * 1000); return timestamp; }, /** * Generate a 12 byte id buffer used in ObjectID's * * @method * @param {number} [time] optional parameter allowing to pass in a second based timestamp. * @return {string} return the 12 byte id buffer string. */ generate: function (time) { if ("number" !== typeof time) { time = ~~(Date.now() / 1000); } //keep it in the ring! time = parseInt(time, 10) % 0xffffffff; var inc = next(); return String.fromCharCode( (time >> 24) & 0xff, (time >> 16) & 0xff, (time >> 8) & 0xff, time & 0xff, (MACHINE_ID >> 16) & 0xff, (MACHINE_ID >> 8) & 0xff, MACHINE_ID & 0xff, (pid >> 8) & 0xff, pid & 0xff, (inc >> 16) & 0xff, (inc >> 8) & 0xff, inc & 0xff ); }, }; function next() { return (index = (index + 1) % 0xffffff); } function hex(length, n) { n = n.toString(16); return n.length === length ? n : "00000000".substring(n.length, length) + n; } // function buffer(str) { // var i = 0, // out = []; // if (str.length === 24) // for (; i < 24; out.push(parseInt(str[i] + str[i + 1], 16)), i += 2); // else if (str.length === 12) for (; i < 12; out.push(str.charCodeAt(i)), i++); // return out; // } var inspect = (Symbol && Symbol.for && Symbol.for("nodejs.util.inspect.custom")) || "inspect"; /** * Converts to a string representation of this Id. * * @return {String} return the 24 byte hex string representation. * @api private */ ObjectID.prototype[inspect] = function () { return "ObjectID(" + this + ")"; }; ObjectID.prototype.toJSON = ObjectID.prototype.toHexString; ObjectID.prototype.toString = ObjectID.prototype.toHexString;