Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
280 lines (236 sloc) 9.11 KB
/**
* 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
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* 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 <string>
#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 <typename T>
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 <http://dochub.mongodb.org/core/mongodbextendedjson>
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
You can’t perform that action at this time.