Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This PR adds UUID builder for uprotocol uuidv8 #151

Merged
merged 2 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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{}()};
gregmedd marked this conversation as resolved.
Show resolved Hide resolved
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>()) {}
gregmedd marked this conversation as resolved.
Show resolved Hide resolved

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