/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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_range.h" #include "mongo/base/data_view.h" #include "mongo/base/static_assert.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; } MONGO_STATIC_ASSERT_MSG(sizeof(int64_t) == kInstanceUniqueSize + kIncrementSize, "size of term must be size of instance unique + increment"); // Return OID initialized with a 8 byte term id and max Timestamp. Used for ElectionID. static OID fromTerm(int64_t term) { OID result; result.initFromTermNumber(term); return result; } // 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; } /** * This method creates and initializes an OID from a string, throwing a BadValue exception if * the string is not a valid OID. */ static OID createFromString(StringData input) { uassert(ErrorCodes::BadValue, str::stream() << "Invalid string length for parsing to OID, expected 24 but found " << input.size(), input.size() == 24); for (auto digit : input) { uassert(ErrorCodes::BadValue, str::stream() << "Invalid character found in hex string: " << digit, ('0' <= digit && digit <= '9') || ('a' <= digit && digit <= 'f') || ('A' <= digit && digit <= 'F')); } OID result; result.init(input.toString()); return result; } /** 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); /** * Sets the contents to contain the leading max Timestamp (0x7FFFFFFF) * followed by an big endian 8 byte term id */ void initFromTermNumber(int64_t term); time_t asTimeT() const; Date_t asDateT() const { return Date_t::fromMillisSinceEpoch(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); } ConstDataRange toCDR() const { return ConstDataRange(_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 $where, mr, etc... */ TenGen, }; 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