// oid.h /* Copyright 2009 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once #include #include "mongo/base/data_view.h" #include "mongo/bson/util/builder.h" #include "mongo/util/time_support.h" namespace mongo { class SecureRandom; /** * Object ID type. * BSON objects typically have an _id field for the object id. This field should be the first * member of the object when present. The OID class is a special type that is a 12 byte id which * is likely to be unique to the system. You may also use other types for _id's. * When _id field is missing from a BSON object, on an insert the database may insert one * automatically in certain circumstances. * * The BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), * in the highest order 4 bytes followed by a 5 byte value unique to this machine AND process, * followed by a 3 byte counter. * * 4 byte timestamp 5 byte process unique 3 byte counter * |<----------------->|<---------------------->|<-------------> * OID layout: [----|----|----|----|----|----|----|----|----|----|----|----] * 0 4 8 12 * * The timestamp is a big endian 4 byte signed-integer. * * The process unique is an arbitrary sequence of 5 bytes. There are no endianness concerns * since it is never interpreted as a multi-byte value. * * The counter is a big endian 3 byte unsigned integer. * * Note: The timestamp and counter are big endian (in contrast to the rest of BSON) because * we use memcmp to order OIDs, and we want to ensure an increasing order. * * Warning: You MUST call OID::justForked() after a fork(). This ensures that each process will * generate unique OIDs. */ class OID { public: /** * Functor compatible with std::hash for std::unordered_{map,set} * Warning: The hash function is subject to change. Do not use in cases where hashes need * to be consistent across versions. */ struct Hasher { size_t operator() (const OID& oid) const; }; OID() : _data() {} enum { kOIDSize = 12, kTimestampSize = 4, kInstanceUniqueSize = 5, kIncrementSize = 3 }; /** init from a 24 char hex std::string */ explicit OID(const std::string &s) { init(s); } /** init from a reference to a 12-byte array */ explicit OID(const unsigned char (&arr)[kOIDSize]) { std::memcpy(_data, arr, sizeof(arr)); } /** initialize to 'null' */ void clear() { std::memset(_data, 0, kOIDSize); } int compare( const OID& other ) const { return memcmp( _data , other._data , kOIDSize ); } /** @return the object ID output as 24 hex digits */ std::string toString() const; /** @return the random/sequential part of the object ID as 6 hex digits */ std::string toIncString() const; static OID gen() { OID o((no_initialize_tag())); o.init(); return o; } // Caller must ensure that the buffer is valid for kOIDSize bytes. // this is templated because some places use unsigned char vs signed char template static OID from(T* buf) { OID o((no_initialize_tag())); std::memcpy(o._data, buf, OID::kOIDSize); return o; } static OID max() { OID o((no_initialize_tag())); std::memset(o._data, 0xFF, kOIDSize); return o; } /** sets the contents to a new oid / randomized value */ void init(); /** init from a 24 char hex std::string */ void init( const std::string& s ); /** Set to the min/max OID that could be generated at given timestamp. */ void init( Date_t date, bool max=false ); time_t asTimeT() const; Date_t asDateT() const { return asTimeT() * 1000LL; } // True iff the OID is not empty bool isSet() const { return compare(OID()) != 0; } /** * this is not consistent * do not store on disk */ void hash_combine(size_t &seed) const; /** call this after a fork to update the process id */ static void justForked(); static unsigned getMachineId(); // used by the 'features' command static void regenMachineId(); // Timestamp is 4 bytes so we just use int32_t typedef int32_t Timestamp; // Wrappers so we can return stuff by value. struct InstanceUnique { static InstanceUnique generate(SecureRandom& entropy); uint8_t bytes[kInstanceUniqueSize]; }; struct Increment { public: static Increment next(); uint8_t bytes[kIncrementSize]; }; void setTimestamp(Timestamp timestamp); void setInstanceUnique(InstanceUnique unique); void setIncrement(Increment inc); Timestamp getTimestamp() const; InstanceUnique getInstanceUnique() const; Increment getIncrement() const; ConstDataView view() const { return ConstDataView(_data); } private: // Internal mutable view DataView _view() { return DataView(_data); } // When we are going to immediately overwrite the bytes, there is no point in zero // initializing the data first. struct no_initialize_tag {}; explicit OID(no_initialize_tag) {} char _data[kOIDSize]; }; inline std::ostream& operator<<(std::ostream &s, const OID &o) { return (s << o.toString()); } inline StringBuilder& operator<<(StringBuilder& s, const OID& o) { return (s << o.toString()); } /** Formatting mode for generating JSON from BSON. See for details. */ enum JsonStringFormat { /** strict RFC format */ Strict, /** 10gen format, which is close to JS format. This form is understandable by javascript running inside the Mongo server via eval() */ TenGen, /** Javascript JSON compatible */ JS }; inline bool operator==(const OID& lhs, const OID& rhs) { return lhs.compare(rhs) == 0; } inline bool operator!=(const OID& lhs, const OID& rhs) { return lhs.compare(rhs) != 0; } inline bool operator<(const OID& lhs, const OID& rhs) { return lhs.compare(rhs) < 0; } inline bool operator<=(const OID& lhs, const OID& rhs) { return lhs.compare(rhs) <= 0; } } // namespace mongo