blob: cf98da0186c80932109d4cb98ec4fe4e1d0ba87f [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This needs to be included first to let uuid.h sort out the system header collisions
#ifndef WIN32
#include "uuid++.hh"
#endif
#include "utils/Id.h"
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <memory>
#include <string>
#include <limits>
#include "core/logging/LoggerConfiguration.h"
#ifdef WIN32
#include "Rpc.h"
#include "Winsock2.h"
#pragma comment(lib, "Rpcrt4.lib")
#pragma comment(lib, "Ws2_32.lib")
#endif
#include "utils/StringUtils.h"
namespace org {
namespace apache {
namespace nifi {
namespace minifi {
namespace utils {
#ifdef WIN32
namespace {
void windowsUuidToUuidField(UUID* uuid, Identifier::Data& out) {
uint32_t Data1BE = htonl(uuid->Data1);
memcpy(out.data(), &Data1BE, 4);
uint16_t Data2BE = htons(uuid->Data2);
memcpy(out.data() + 4, &Data2BE, 2);
uint16_t Data3BE = htons(uuid->Data3);
memcpy(out.data() + 6, &Data3BE, 2);
memcpy(out.data() + 8, uuid->Data4, 8);
}
void windowsUuidGenerateTime(Identifier::Data& out) {
UUID uuid;
UuidCreateSequential(&uuid);
windowsUuidToUuidField(&uuid, out);
}
void windowsUuidGenerateRandom(Identifier::Data& out) {
UUID uuid;
UuidCreate(&uuid);
windowsUuidToUuidField(&uuid, out);
}
} // namespace
#endif
Identifier::Identifier(const Data& data) : data_(data) {}
Identifier& Identifier::operator=(const Data& data) {
data_ = data;
return *this;
}
Identifier& Identifier::operator=(const std::string& idStr) {
utils::optional<Identifier> id = Identifier::parse(idStr);
if (!id) {
throw std::runtime_error("Couldn't parse UUID");
}
*this = id.value();
return *this;
}
bool Identifier::isNil() const {
return *this == Identifier{};
}
bool Identifier::operator!=(const Identifier& other) const {
return !(*this == other);
}
bool Identifier::operator==(const Identifier& other) const {
return data_ == other.data_;
}
std::string Identifier::to_string() const {
char uuidStr[37]{}; // 36+1 for the \0
snprintf(uuidStr, sizeof(uuidStr), UUID_FORMAT_STRING,
data_[0], data_[1], data_[2], data_[3],
data_[4], data_[5],
data_[6], data_[7],
data_[8], data_[9],
data_[10], data_[11], data_[12], data_[13], data_[14], data_[15]);
return {uuidStr};
}
utils::optional<Identifier> Identifier::parse(const std::string &str) {
Identifier id;
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx is 36 long: 16 bytes * 2 hex digits / byte + 4 hyphens
if (str.length() != 36) return {};
int charIdx = 0;
int byteIdx = 0;
auto input = reinterpret_cast<const uint8_t*>(str.c_str());
// [xxxxxxxx]-xxxx-xxxx-xxxx-xxxxxxxxxxxx
while (byteIdx < 4) {
if (!parseByte(id.data_, input, charIdx, byteIdx)) return {};
}
// xxxxxxxx[-]xxxx-xxxx-xxxx-xxxxxxxxxxxx
if (input[charIdx++] != '-') return {};
// xxxxxxxx-[xxxx-xxxx-xxxx-]xxxxxxxxxxxx - 3x 2 bytes and a hyphen
for (size_t idx = 0; idx < 3; ++idx) {
if (!parseByte(id.data_, input, charIdx, byteIdx)) return {};
if (!parseByte(id.data_, input, charIdx, byteIdx)) return {};
if (input[charIdx++] != '-') return {};
}
// xxxxxxxx-xxxx-xxxx-xxxx-[xxxxxxxxxxxx] - the rest, i.e. until byte 16
while (byteIdx < 16) {
if (!parseByte(id.data_, input, charIdx, byteIdx)) return {};
}
return id;
}
bool Identifier::parseByte(Data &data, const uint8_t *input, int &charIdx, int &byteIdx) {
uint8_t upper, lower;
if (!StringUtils::from_hex(input[charIdx++], upper)
|| !StringUtils::from_hex(input[charIdx++], lower)) {
return false;
}
data[byteIdx++] = (upper << 4) | lower;
return true;
}
IdGenerator::IdGenerator()
: implementation_(UUID_TIME_IMPL),
logger_(logging::LoggerFactory<IdGenerator>::getLogger()),
incrementor_(0) {
#ifndef WIN32
uuid_impl_ = std::unique_ptr<uuid>(new uuid());
#endif
}
IdGenerator::~IdGenerator() = default;
uint64_t IdGenerator::getDeviceSegmentFromString(const std::string& str, int numBits) const {
uint64_t deviceSegment = 0;
for (size_t i = 0; i < str.length(); i++) {
unsigned char c = toupper(str[i]);
if (c >= '0' && c <= '9') {
deviceSegment = deviceSegment + (c - '0');
} else if (c >= 'A' && c <= 'F') {
deviceSegment = deviceSegment + (c - 'A' + 10);
} else {
logging::LOG_ERROR(logger_) << "Expected hex char (0-9, A-F). Got " << c;
}
deviceSegment = deviceSegment << 4;
}
deviceSegment <<= 64 - (4 * (str.length() + 1));
deviceSegment >>= 64 - numBits;
logging::LOG_DEBUG(logger_) << "Using user defined device segment: " << std::hex << deviceSegment;
deviceSegment <<= 64 - numBits;
return deviceSegment;
}
uint64_t IdGenerator::getRandomDeviceSegment(int numBits) const {
uint64_t deviceSegment = 0;
Identifier::Data random_uuid{};
for (int word = 0; word < 2; word++) {
#ifdef WIN32
windowsUuidGenerateRandom(random_uuid);
#else
uuid temp_uuid;
temp_uuid.make(UUID_MAKE_V4);
void* uuid_bin = temp_uuid.binary();
memcpy(random_uuid.data(), uuid_bin, 16);
free(uuid_bin);
#endif
for (int i = 0; i < 4; i++) {
deviceSegment += random_uuid[i];
deviceSegment <<= 8;
}
}
deviceSegment >>= 64 - numBits;
logging::LOG_DEBUG(logger_) << "Using random defined device segment:" << deviceSegment;
deviceSegment <<= 64 - numBits;
return deviceSegment;
}
void IdGenerator::initialize(const std::shared_ptr<Properties>& properties) {
std::string implementation_str;
implementation_ = UUID_TIME_IMPL;
if (properties->get("uid.implementation", implementation_str)) {
std::transform(implementation_str.begin(), implementation_str.end(), implementation_str.begin(), ::tolower);
if (UUID_RANDOM_STR == implementation_str || UUID_WINDOWS_RANDOM_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_random for uids.";
implementation_ = UUID_RANDOM_IMPL;
} else if (UUID_DEFAULT_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate for uids.";
implementation_ = UUID_DEFAULT_IMPL;
} else if (MINIFI_UID_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using minifi uid implementation for uids";
implementation_ = MINIFI_UID_IMPL;
uint64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
int device_bits = properties->getInt("uid.minifi.device.segment.bits", 16);
std::string device_segment;
uint64_t prefix = timestamp;
if (device_bits > 0) {
if (properties->get("uid.minifi.device.segment", device_segment)) {
prefix = getDeviceSegmentFromString(device_segment, device_bits);
} else {
logging::LOG_WARN(logger_) << "uid.minifi.device.segment not specified, generating random device segment";
prefix = getRandomDeviceSegment(device_bits);
}
timestamp <<= device_bits;
timestamp >>= device_bits;
prefix = prefix + timestamp;
logging::LOG_DEBUG(logger_) << "Using minifi uid prefix: " << std::hex << prefix;
}
for (int i = 0; i < 8; i++) {
unsigned char prefix_element = (prefix >> ((7 - i) * 8)) & std::numeric_limits<unsigned char>::max();
deterministic_prefix_[i] = prefix_element;
}
incrementor_ = 0;
} else if (UUID_TIME_STR == implementation_str || UUID_WINDOWS_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_time implementation for uids.";
} else {
logging::LOG_DEBUG(logger_) << "Invalid value for uid.implementation (" << implementation_str << "). Using uuid_generate_time implementation for uids.";
}
} else {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_time implementation for uids.";
}
}
#ifndef WIN32
bool IdGenerator::generateWithUuidImpl(unsigned int mode, Identifier::Data& output) {
void* uuid = nullptr;
try {
std::lock_guard<std::mutex> lock(uuid_mutex_);
uuid_impl_->make(mode);
uuid = uuid_impl_->binary();
} catch (uuid_error_t& uuid_error) {
logger_->log_error("Failed to generate UUID, error: %s", uuid_error.string());
return false;
}
memcpy(output.data(), uuid, 16);
free(uuid);
return true;
}
#endif
Identifier IdGenerator::generate() {
Identifier::Data output{};
switch (implementation_) {
case UUID_RANDOM_IMPL:
case UUID_DEFAULT_IMPL:
#ifdef WIN32
windowsUuidGenerateRandom(output);
#else
generateWithUuidImpl(UUID_MAKE_V4, output);
#endif
break;
case MINIFI_UID_IMPL: {
std::memcpy(output.data(), deterministic_prefix_, sizeof(deterministic_prefix_));
uint64_t incrementor_value = incrementor_++;
for (int i = 8; i < 16; i++) {
output[i] = (incrementor_value >> ((15 - i) * 8)) & std::numeric_limits<unsigned char>::max();
}
}
break;
case UUID_TIME_IMPL:
default:
#ifdef WIN32
windowsUuidGenerateTime(output);
#else
generateWithUuidImpl(UUID_MAKE_V1, output);
#endif
break;
}
return Identifier{output};
}
} /* namespace utils */
} /* namespace minifi */
} /* namespace nifi */
} /* namespace apache */
} /* namespace org */