// @file oid.cpp
/* 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.
*/
#include "mongo/platform/basic.h"
#include "mongo/bson/oid.h"
#include
#include
#include "mongo/base/init.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/random.h"
#include "mongo/util/hex.h"
namespace mongo {
namespace {
std::unique_ptr counter;
const std::size_t kTimestampOffset = 0;
const std::size_t kInstanceUniqueOffset = kTimestampOffset + OID::kTimestampSize;
const std::size_t kIncrementOffset = kInstanceUniqueOffset + OID::kInstanceUniqueSize;
OID::InstanceUnique _instanceUnique;
} // namespace
MONGO_INITIALIZER_GENERAL(OIDGeneration, MONGO_NO_PREREQUISITES, ("default"))
(InitializerContext* context) {
std::unique_ptr entropy(SecureRandom::create());
counter.reset(new AtomicUInt32(uint32_t(entropy->nextInt64())));
_instanceUnique = OID::InstanceUnique::generate(*entropy);
return Status::OK();
}
OID::Increment OID::Increment::next() {
uint64_t nextCtr = counter->fetchAndAdd(1);
OID::Increment incr;
incr.bytes[0] = uint8_t(nextCtr >> 16);
incr.bytes[1] = uint8_t(nextCtr >> 8);
incr.bytes[2] = uint8_t(nextCtr);
return incr;
}
OID::InstanceUnique OID::InstanceUnique::generate(SecureRandom& entropy) {
int64_t rand = entropy.nextInt64();
OID::InstanceUnique u;
std::memcpy(u.bytes, &rand, kInstanceUniqueSize);
return u;
}
void OID::setTimestamp(const OID::Timestamp timestamp) {
_view().write>(timestamp, kTimestampOffset);
}
void OID::setInstanceUnique(const OID::InstanceUnique unique) {
// Byte order doesn't matter here
_view().write(unique, kInstanceUniqueOffset);
}
void OID::setIncrement(const OID::Increment inc) {
_view().write(inc, kIncrementOffset);
}
OID::Timestamp OID::getTimestamp() const {
return view().read>(kTimestampOffset);
}
OID::InstanceUnique OID::getInstanceUnique() const {
// Byte order doesn't matter here
return view().read(kInstanceUniqueOffset);
}
OID::Increment OID::getIncrement() const {
return view().read(kIncrementOffset);
}
void OID::hash_combine(size_t& seed) const {
uint32_t v;
for (int i = 0; i != kOIDSize; i += sizeof(uint32_t)) {
memcpy(&v, _data + i, sizeof(uint32_t));
boost::hash_combine(seed, v);
}
}
size_t OID::Hasher::operator()(const OID& oid) const {
size_t seed = 0;
oid.hash_combine(seed);
return seed;
}
void OID::regenMachineId() {
std::unique_ptr entropy(SecureRandom::create());
_instanceUnique = InstanceUnique::generate(*entropy);
}
unsigned OID::getMachineId() {
uint32_t ret = 0;
std::memcpy(&ret, _instanceUnique.bytes, sizeof(uint32_t));
return ret;
}
void OID::justForked() {
regenMachineId();
}
void OID::init() {
// each set* method handles endianness
setTimestamp(time(0));
setInstanceUnique(_instanceUnique);
setIncrement(Increment::next());
}
void OID::initFromTermNumber(int64_t term) {
// Each set* method handles endianness.
// Set max timestamp because the drivers compare ElectionId's to determine valid new primaries,
// and we want ElectionId's with terms to supercede ones without terms.
setTimestamp(std::numeric_limits::max());
_view().write>(term, kInstanceUniqueOffset);
}
void OID::init(const std::string& s) {
verify(s.size() == 24);
const char* p = s.c_str();
for (std::size_t i = 0; i < kOIDSize; i++) {
_data[i] = fromHex(p);
p += 2;
}
}
void OID::init(Date_t date, bool max) {
setTimestamp(uint32_t(date.toMillisSinceEpoch() / 1000));
uint64_t rest = max ? std::numeric_limits::max() : 0u;
std::memcpy(_view().view(kInstanceUniqueOffset), &rest, kInstanceUniqueSize + kIncrementSize);
}
time_t OID::asTimeT() const {
return getTimestamp();
}
std::string OID::toString() const {
return toHexLower(_data, kOIDSize);
}
std::string OID::toIncString() const {
return toHexLower(getIncrement().bytes, kIncrementSize);
}
} // namespace mongo