Skip to content

Commit

Permalink
Merge pull request #151 from BishrutSubedi/biss/Uuid_builder
Browse files Browse the repository at this point in the history
This PR adds UUID builder for uprotocol uuidv8
  • Loading branch information
gregmedd authored Jun 18, 2024
2 parents 0e98904 + 4a34abb commit dbd3159
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 13 deletions.
2 changes: 1 addition & 1 deletion include/up-cpp/datamodel/builder/Uuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct UuidBuilder {
v1::UUID build();

private:
UuidBuilder();
UuidBuilder(bool testing);

const bool testing_{false};
std::function<std::chrono::system_clock::time_point()> time_source_;
Expand Down
41 changes: 41 additions & 0 deletions include/up-cpp/datamodel/constants/UuidConstants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache License Version 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: Apache-2.0

#ifndef UP_CPP_DATAMODEL_UUID_CONSTANTS_H
#define UP_CPP_DATAMODEL_UUID_CONSTANTS_H

namespace uprotocol::datamodel {

// Masks and shifts for various UUID fields
constexpr uint64_t UUID_TIMESTAMP_MASK = 0xFFFFFFFFFFFF;
constexpr uint64_t UUID_TIMESTAMP_SHIFT = 16;
constexpr uint64_t UUID_VERSION_MASK = 0xF;
constexpr uint64_t UUID_VERSION_SHIFT = 12;
constexpr uint64_t UUID_VARIANT_MASK = 0x3;
constexpr uint64_t UUID_VARIANT_SHIFT = 62;
constexpr uint64_t UUID_COUNTER_MASK = 0xFFF;
constexpr uint64_t UUID_RANDOM_MASK = 0x3FFFFFFFFFFFFFFF;

// Constants for UUID version and variant
constexpr uint8_t UUID_VERSION_8 = 8;
constexpr uint8_t UUID_VARIANT_RFC4122 = 2;

// Other constants
constexpr uint32_t UUID_BYTE_SIZE = 16;
constexpr uint32_t UUID_PART_SIZE = 4;
constexpr uint32_t HEX_BASE = 16;
constexpr uint64_t MASK_32_BITS = 0xFFFFFFFF;
constexpr uint64_t MASK_16_BITS = 0xFFFF;
constexpr uint64_t MASK_14_BITS = 0x3FFF;

} // namespace uprotocol::datamodel

#endif // UP_CPP_DATAMODEL_UUID_CONSTANTS_H
101 changes: 101 additions & 0 deletions src/datamodel/builder/Uuid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,104 @@
// SPDX-License-Identifier: Apache-2.0

#include "up-cpp/datamodel/builder/Uuid.h"

#include <random>
#include <stdexcept>

#include "up-cpp/datamodel/constants/UuidConstants.h"

namespace uprotocol::datamodel::builder {

struct UuidBuilder::UuidSharedState {
std::mt19937_64 random_engine{std::random_device{}()};
uint16_t counter{0};
std::chrono::time_point<std::chrono::system_clock,
std::chrono::milliseconds>
last_unix_ts_ms{};
uint64_t rand_b{0};
bool rand_b_initialized{false};
};

UuidBuilder UuidBuilder::getBuilder() { return UuidBuilder(false); }

UuidBuilder UuidBuilder::getTestBuilder() { return UuidBuilder(true); }

UuidBuilder& UuidBuilder::withTimeSource(
std::function<std::chrono::system_clock::time_point()>&& time_source) {
if (!testing_) {
throw std::domain_error(
"Cannot set time source on non-test UuidBuilder");
}
time_source_ = std::move(time_source);
return *this;
}

UuidBuilder& UuidBuilder::withRandomSource(
std::function<uint64_t()>&& random_source) {
if (!testing_) {
throw std::domain_error(
"Cannot set random source on non-test UuidBuilder");
}
random_source_ = std::move(random_source);
return *this;
}

UuidBuilder& UuidBuilder::withIndependentState() {
if (!testing_) {
throw std::domain_error(
"Cannot set independent state on non-test UuidBuilder");
}
shared_state_ = std::make_shared<UuidSharedState>();
return *this;
}

v1::UUID UuidBuilder::build() {
v1::UUID uuid;
auto now = time_source_ ? time_source_() : std::chrono::system_clock::now();
auto unix_ts_ms =
std::chrono::time_point_cast<std::chrono::milliseconds>(now);

if (unix_ts_ms != shared_state_->last_unix_ts_ms) {
// Reset the counter if the timestamp tick has changed
shared_state_->counter = 0;
shared_state_->last_unix_ts_ms = unix_ts_ms;
}

uint64_t msb = static_cast<uint64_t>(unix_ts_ms.time_since_epoch().count())
<< UUID_TIMESTAMP_SHIFT;
msb |= static_cast<uint64_t>(8)
<< UUID_VERSION_SHIFT; // Set the version to 8

if (shared_state_->counter == 4095) {
// Counter has reached maximum value, freeze it
msb |= shared_state_->counter;
} else {
msb |= shared_state_->counter++;
}

uint64_t lsb;
if (!shared_state_->rand_b_initialized) {
shared_state_->rand_b = random_source_
? random_source_()
: std::uniform_int_distribution<uint64_t>{}(
shared_state_->random_engine) &
UUID_RANDOM_MASK;
shared_state_->rand_b_initialized = true;
}
lsb = shared_state_->rand_b;

// set the Variant to 10b
lsb |= static_cast<uint64_t>(UUID_VARIANT_RFC4122) << UUID_VARIANT_SHIFT;

uuid.set_msb(msb);
uuid.set_lsb(lsb);
return uuid;
}

UuidBuilder::UuidBuilder(bool testing)
: testing_(testing),
time_source_(nullptr),
random_source_(nullptr),
shared_state_(std::make_shared<UuidSharedState>()) {}

} // namespace uprotocol::datamodel::builder
Loading

0 comments on commit dbd3159

Please sign in to comment.