2021-05-27 10:59:22 -04:00
|
|
|
/*
|
|
|
|
uuid.h
|
|
|
|
|
|
|
|
Copyright (c) 2021 Kevin Matz (kevin.matz@gmail.com)
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "uuid.h"
|
|
|
|
#include <algorithm>
|
2021-08-02 16:29:18 -04:00
|
|
|
#include <chrono>
|
2021-05-27 10:59:22 -04:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <ctime>
|
|
|
|
|
|
|
|
namespace UUID {
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::uuid
|
|
|
|
*/
|
|
|
|
uuid::uuid()
|
|
|
|
: raw_(new uint8_t[16])
|
|
|
|
{
|
2021-05-27 10:59:22 -04:00
|
|
|
type_ = NIL;
|
|
|
|
version_ = VOID;
|
2021-06-19 15:30:27 -04:00
|
|
|
timestamp_ = 0;
|
2021-05-27 10:59:22 -04:00
|
|
|
clock_seq_ = 0;
|
|
|
|
node_ = 0;
|
2021-08-02 16:29:18 -04:00
|
|
|
setBytes_();
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::uuid
|
|
|
|
* @param other
|
|
|
|
*/
|
|
|
|
uuid::uuid(const uuid& other)
|
|
|
|
: raw_(new uint8_t[16])
|
|
|
|
{
|
|
|
|
type_ = other.type_;
|
|
|
|
version_ = other.version_;
|
|
|
|
timestamp_ = other.timestamp_;
|
|
|
|
clock_seq_ = other.clock_seq_;
|
|
|
|
node_ = other.node_;
|
2021-08-02 16:29:18 -04:00
|
|
|
setBytes_();
|
2021-08-02 10:09:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief uuid::~uuid
|
|
|
|
*/
|
|
|
|
uuid::~uuid()
|
|
|
|
{
|
|
|
|
delete[] raw_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief uuid::fromArray
|
|
|
|
* @param bytes
|
|
|
|
*
|
|
|
|
* create this UUID from a byte array
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
void uuid::fromArray_(const uint8_t * bytes)
|
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memcpy(raw_, bytes, 16);
|
2021-08-02 16:29:18 -04:00
|
|
|
setFields_();
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::fromCstring
|
|
|
|
* @param cstr
|
|
|
|
*
|
|
|
|
* create this UUID from a C style char array
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
void uuid::fromCstring_(const char * cstr)
|
|
|
|
{
|
2021-08-02 16:29:18 -04:00
|
|
|
fromString_(std::string(cstr));
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::fromString
|
|
|
|
* @param str
|
|
|
|
*
|
|
|
|
* create this UUID from a string representation
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
void uuid::fromString_(std::string str)
|
|
|
|
{
|
2021-06-19 15:30:07 -04:00
|
|
|
// remove formating
|
|
|
|
if (str.front() == '{' && str.back() == '}')
|
2021-08-02 10:09:14 -04:00
|
|
|
str = str.substr(1, str.size() - 2);
|
2021-05-27 10:59:22 -04:00
|
|
|
str.erase(std::remove(str.begin(), str.end(), '-'), str.end());
|
|
|
|
if (str.length() != 32) return;
|
|
|
|
// fill buffer
|
|
|
|
uint8_t buf[16];
|
|
|
|
for (unsigned int i = 0; i < sizeof(buf); i++)
|
|
|
|
buf[i] = std::strtoul(str.substr((i*2), 2).c_str(), NULL, 16);
|
|
|
|
// process buffer
|
2021-08-02 16:29:18 -04:00
|
|
|
fromArray_(buf);
|
2021-05-27 10:59:22 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::hex
|
|
|
|
* @return
|
|
|
|
*
|
|
|
|
* output this UUID as a hex string
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
std::string uuid::hex() const
|
|
|
|
{
|
2021-05-27 10:59:22 -04:00
|
|
|
std::ostringstream oss;
|
2021-06-19 15:30:07 -04:00
|
|
|
oss << std::hex;
|
2021-08-02 10:09:14 -04:00
|
|
|
for (int i = 0; i < 16; i++)
|
|
|
|
oss << std::setw(2) << std::setfill('0') << (int)raw_[i];
|
2021-05-27 10:59:22 -04:00
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::string
|
|
|
|
* @return
|
|
|
|
*
|
|
|
|
* output this UUID as formated hex string
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
std::string uuid::string() const
|
|
|
|
{
|
2021-05-27 10:59:22 -04:00
|
|
|
std::string str = hex();
|
2021-06-19 15:30:07 -04:00
|
|
|
str.reserve(str.length() + 6);
|
2021-05-27 10:59:22 -04:00
|
|
|
for (int idx : {8, 13, 18, 23})
|
|
|
|
str.insert(idx, "-");
|
2021-06-19 15:30:07 -04:00
|
|
|
str.insert(0, "{");
|
|
|
|
str.insert(str.length(), "}");
|
2021-05-27 10:59:22 -04:00
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::urn
|
|
|
|
* @return
|
|
|
|
*
|
|
|
|
* output this UUID as a urn specified in RFC 4122
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
std::string uuid::urn() const
|
|
|
|
{
|
2021-05-27 10:59:22 -04:00
|
|
|
return "urn:uuid:" + string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::setBytes
|
|
|
|
* make the raw bytes match the field contents
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setBytes_()
|
2021-07-26 15:41:02 -04:00
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
switch (type_) {
|
2021-07-26 15:41:02 -04:00
|
|
|
case NIL:
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memset(raw_, 0, 16);
|
|
|
|
break;
|
2021-07-26 15:41:02 -04:00
|
|
|
case NCS:
|
2021-08-02 16:29:18 -04:00
|
|
|
setNCSBytes_();
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
2021-07-26 15:41:02 -04:00
|
|
|
case RFC4122:
|
2021-08-02 16:29:18 -04:00
|
|
|
setRFC4122Bytes_();
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
2021-07-26 15:41:02 -04:00
|
|
|
case MS:
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
2021-07-26 15:41:02 -04:00
|
|
|
case RESVERED:
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
2021-07-26 15:41:02 -04:00
|
|
|
}
|
|
|
|
}
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setNCSBytes
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setNCSBytes_()
|
2021-07-26 15:41:02 -04:00
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
NCSFields fields;
|
|
|
|
fields.time = timestamp_;
|
|
|
|
fields.res = 0;
|
|
|
|
fields.family = version_;
|
|
|
|
fields.node = node_;
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memcpy(raw_, &fields, 16);
|
2021-07-26 15:41:02 -04:00
|
|
|
}
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setRFC4122Bytes
|
|
|
|
* TODO: Check endianness
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setRFC4122Bytes_()
|
2021-07-26 15:41:02 -04:00
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
RFC4122Fields fields;
|
|
|
|
fields.time_low = timestamp_;
|
|
|
|
fields.time_mid = timestamp_ >> 32;
|
|
|
|
fields.time_hi_version = timestamp_ >> 48;
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
fields.clock_seq_low = clock_seq_;
|
|
|
|
fields.clock_seq_hi_variant = clock_seq_ >> 8;
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
fields.node_low = node_;
|
|
|
|
fields.node_high = node_ >> 16;
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memcpy(raw_, &fields, 16);
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
// version
|
|
|
|
raw_[7] = (raw_[7] & 0x0f) | (version_ << 4);
|
|
|
|
// type
|
|
|
|
raw_[8] = (raw_[8] & 0b00111111) | (type_ << 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief uuid::setFields
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setFields_()
|
2021-08-02 10:09:14 -04:00
|
|
|
{
|
2021-08-02 16:29:18 -04:00
|
|
|
setType_();
|
2021-08-02 10:09:14 -04:00
|
|
|
switch (type_) {
|
|
|
|
case NCS:
|
2021-08-02 16:29:18 -04:00
|
|
|
setNCSFields_();
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
|
|
|
case RFC4122:
|
2021-08-02 16:29:18 -04:00
|
|
|
setRFC4122Fields_();
|
2021-08-02 10:09:14 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-07-26 15:41:02 -04:00
|
|
|
}
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setNCSFields
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setNCSFields_()
|
2021-07-26 15:41:02 -04:00
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
NCSFields fields;
|
|
|
|
std::memcpy(&fields, raw_, 16);
|
2021-07-26 15:41:02 -04:00
|
|
|
|
2021-08-02 10:09:14 -04:00
|
|
|
timestamp_ = fields.time;
|
|
|
|
version_ = fields.family;
|
|
|
|
node_ = fields.node;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::setRFC4122Fields
|
|
|
|
*/
|
2021-08-02 16:29:18 -04:00
|
|
|
void uuid::setRFC4122Fields_()
|
2021-07-26 15:41:02 -04:00
|
|
|
{
|
|
|
|
RFC4122Fields fields;
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memcpy(&fields, raw_, 16);
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
timestamp_ = fields.time_low;
|
|
|
|
timestamp_ |= (uint64_t)fields.time_mid << 32;
|
|
|
|
timestamp_ |= (uint64_t)(fields.time_hi_version & 0x0fff) << 48;
|
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
switch (raw_[7] >> 4) {
|
|
|
|
case TIME:
|
|
|
|
version_ = TIME;
|
|
|
|
break;
|
|
|
|
case DCE:
|
|
|
|
version_ = DCE;
|
|
|
|
break;
|
|
|
|
case MD5:
|
|
|
|
version_ = MD5;
|
|
|
|
break;
|
|
|
|
case RAND:
|
|
|
|
version_ = RAND;
|
|
|
|
break;
|
|
|
|
case SHA1:
|
|
|
|
version_ = SHA1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
version_ = VOID;
|
|
|
|
}
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
clock_seq_ = fields.clock_seq_low;
|
|
|
|
clock_seq_ |= (fields.clock_seq_hi_variant & 0b00111111) << 8;
|
|
|
|
|
|
|
|
node_ = fields.node_low;
|
|
|
|
node_ |= (fields.node_high << 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::setType
|
|
|
|
*
|
|
|
|
* exctract the type from the raw bytes
|
|
|
|
*/
|
2021-08-15 23:36:29 -04:00
|
|
|
void uuid::setType_()
|
|
|
|
{
|
|
|
|
if ((raw_[8] >> 7) == 0b0)
|
2021-05-27 10:59:22 -04:00
|
|
|
type_ = NCS;
|
2021-08-15 23:36:29 -04:00
|
|
|
else if ((raw_[8] >> 6) == 0b10)
|
2021-05-27 10:59:22 -04:00
|
|
|
type_ = RFC4122;
|
2021-08-15 23:36:29 -04:00
|
|
|
else if ((raw_[8] >> 5) == 0b110)
|
2021-05-27 10:59:22 -04:00
|
|
|
type_ = MS;
|
2021-08-15 23:36:29 -04:00
|
|
|
else
|
2021-05-27 10:59:22 -04:00
|
|
|
type_ = RESVERED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::operator =
|
|
|
|
* @param other
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
uuid& uuid::operator=(const uuid& other)
|
|
|
|
{
|
2021-08-15 23:36:29 -04:00
|
|
|
if (this != &other) // protect against invalid self-assignment
|
|
|
|
{
|
2021-08-02 10:09:14 -04:00
|
|
|
std::memcpy(raw_, other.raw_, 16);
|
2021-08-02 16:29:18 -04:00
|
|
|
setFields_();
|
2021-08-02 10:09:14 -04:00
|
|
|
}
|
|
|
|
return *this;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief operator ==
|
|
|
|
* @param a
|
|
|
|
* @param b
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
bool operator== (const uuid& a, const uuid& b)
|
|
|
|
{
|
|
|
|
if (std::memcmp(a.raw_, b.raw_, 16) == 0)
|
|
|
|
return true;
|
|
|
|
return false;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-02 16:29:18 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::uuid1
|
|
|
|
* @param node
|
|
|
|
* @param clock_seq
|
|
|
|
* @return incrimented clock_sequence
|
|
|
|
*
|
|
|
|
* node ID and clock sequence tracking are the responsibility of the caller
|
|
|
|
*/
|
|
|
|
uint16_t uuid::uuid1(uint64_t node, uint16_t clock_seq)
|
|
|
|
{
|
|
|
|
type_ = RFC4122;
|
|
|
|
version_ = TIME;
|
|
|
|
timestamp_ = now_();
|
|
|
|
clock_seq_ = clock_seq + 1;
|
|
|
|
node_ = node;
|
|
|
|
setBytes_();
|
|
|
|
|
|
|
|
return clock_seq_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
/**
|
2021-08-02 10:09:14 -04:00
|
|
|
* @brief uuid::uuid4
|
|
|
|
*
|
|
|
|
* create this UUID as a Version 4 (RANDOM) UUID
|
|
|
|
*/
|
2021-05-27 10:59:22 -04:00
|
|
|
void uuid::uuid4() {
|
|
|
|
type_ = RFC4122;
|
|
|
|
version_ = RAND;
|
|
|
|
std::srand(std::time(nullptr));
|
|
|
|
timestamp_ = std::rand();
|
|
|
|
timestamp_ |= (uint64_t)std::rand() << 32;
|
|
|
|
clock_seq_ = std::rand();
|
|
|
|
node_ = std::rand();
|
|
|
|
node_ |= (uint64_t)std::rand() << 32;
|
2021-08-02 16:29:18 -04:00
|
|
|
setBytes_();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief uuid::time_ 4.1.4. Timestamp
|
|
|
|
* @return
|
|
|
|
*
|
|
|
|
* The timestamp is a 60-bit value. For UUID version 1, this is represented
|
|
|
|
* by Coordinated Universal Time (UTC) as a count of 100-nanosecond intervals
|
|
|
|
* since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the
|
|
|
|
* Christian calendar).
|
|
|
|
*/
|
|
|
|
uint64_t uuid::now_()
|
|
|
|
{
|
|
|
|
auto now = std::chrono::high_resolution_clock::now();
|
|
|
|
std::chrono::duration<uint64_t, std::nano> since = now.time_since_epoch();
|
|
|
|
uint64_t periods = since.count() / 100;
|
|
|
|
periods += UUID_TICKS_BETWEEK_EPOCH;
|
|
|
|
return periods;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}; // UUID
|