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>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <ctime>
|
|
|
|
|
|
|
|
namespace UUID {
|
|
|
|
|
|
|
|
/**
|
|
|
|
construct a NIL UUID
|
|
|
|
*/
|
|
|
|
uuid::uuid() {
|
|
|
|
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;
|
|
|
|
setBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
create this UUID from a byte array
|
|
|
|
*/
|
|
|
|
void uuid::fromArray(const uint8_t * bytes) {
|
|
|
|
std::memcpy(raw_, bytes, sizeof(raw_));
|
|
|
|
setType();
|
2021-07-26 15:41:02 -04:00
|
|
|
switch (type_) {
|
|
|
|
case NCS:
|
|
|
|
setNCSFields();
|
|
|
|
break;
|
|
|
|
case RFC4122:
|
|
|
|
setRFC4122Fields();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
create this UUID from a C style char array
|
|
|
|
*/
|
|
|
|
void uuid::fromCstring(const char * cstr) {
|
|
|
|
fromString(std::string(cstr));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
create this UUID from a string representation
|
|
|
|
*/
|
|
|
|
void uuid::fromString(std::string str) {
|
2021-06-19 15:30:07 -04:00
|
|
|
// remove formating
|
|
|
|
if (str.front() == '{' && str.back() == '}')
|
|
|
|
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
|
|
|
|
fromArray(buf);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
output this UUID as a hex string
|
|
|
|
*/
|
|
|
|
std::string uuid::hex() const {
|
|
|
|
std::ostringstream oss;
|
2021-06-19 15:30:07 -04:00
|
|
|
oss << std::hex;
|
2021-05-27 10:59:22 -04:00
|
|
|
for (uint8_t raw : raw_)
|
2021-06-19 15:30:07 -04:00
|
|
|
oss << std::setw(2) << std::setfill('0') << (int)raw;
|
2021-05-27 10:59:22 -04:00
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
output this UUID as formated hex string
|
|
|
|
*/
|
|
|
|
std::string uuid::string() const {
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
output this UUID as a urn specified in RFC 4122
|
|
|
|
*/
|
|
|
|
std::string uuid::urn() const {
|
|
|
|
return "urn:uuid:" + string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
make the raw bytes match the field contents
|
|
|
|
*/
|
2021-07-26 15:41:02 -04:00
|
|
|
void uuid::setBytes()
|
|
|
|
{
|
|
|
|
switch (type_) {
|
|
|
|
case NIL:
|
|
|
|
std::memset(raw_, 0, sizeof(raw_));
|
|
|
|
break;
|
|
|
|
case NCS:
|
|
|
|
setNCSBytes();
|
|
|
|
break;
|
|
|
|
case RFC4122:
|
|
|
|
setRFC4122Bytes();
|
|
|
|
break;
|
|
|
|
case MS:
|
|
|
|
break;
|
|
|
|
case RESVERED:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setNCSBytes
|
|
|
|
*/
|
|
|
|
void uuid::setNCSBytes()
|
|
|
|
{
|
|
|
|
NCSFields fields;
|
|
|
|
fields.time = timestamp_;
|
|
|
|
fields.res = 0;
|
|
|
|
fields.family = version_;
|
|
|
|
fields.node = node_;
|
|
|
|
|
|
|
|
std::memcpy(raw_, &fields, sizeof(raw_));
|
|
|
|
}
|
|
|
|
|
2021-05-27 10:59:22 -04:00
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setRFC4122Bytes
|
|
|
|
* TODO: Check endianness
|
|
|
|
*/
|
|
|
|
void uuid::setRFC4122Bytes()
|
|
|
|
{
|
|
|
|
RFC4122Fields fields;
|
|
|
|
fields.time_low = timestamp_;
|
|
|
|
fields.time_mid = timestamp_ >> 32;
|
|
|
|
fields.time_hi_version = timestamp_ >> 48;
|
|
|
|
|
|
|
|
fields.clock_seq_low = clock_seq_;
|
|
|
|
fields.clock_seq_hi_variant = clock_seq_ >> 8;
|
|
|
|
|
|
|
|
fields.node_low = node_;
|
|
|
|
fields.node_high = node_ >> 16;
|
|
|
|
|
|
|
|
std::memcpy(raw_, &fields, sizeof(raw_));
|
|
|
|
|
|
|
|
// version
|
|
|
|
raw_[7] = (raw_[7] & 0x0f) | (version_ << 4);
|
|
|
|
// type
|
|
|
|
raw_[8] = (raw_[8] & 0b00111111) | (type_ << 6);
|
|
|
|
}
|
2021-05-27 10:59:22 -04:00
|
|
|
|
|
|
|
|
2021-07-26 15:41:02 -04:00
|
|
|
/**
|
|
|
|
* @brief uuid::setNCSFields
|
|
|
|
*/
|
|
|
|
void uuid::setNCSFields()
|
|
|
|
{
|
|
|
|
NCSFields fields;
|
|
|
|
std::memcpy(&fields, raw_, sizeof (raw_));
|
|
|
|
|
|
|
|
timestamp_ = fields.time;
|
|
|
|
version_ = fields.family;
|
|
|
|
node_ = fields.node;
|
2021-05-27 10:59:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
populate data fields from byte array
|
|
|
|
*/
|
|
|
|
// TODO: Check endianness
|
2021-07-26 15:41:02 -04:00
|
|
|
void uuid::setRFC4122Fields()
|
|
|
|
{
|
|
|
|
RFC4122Fields fields;
|
2021-05-27 10:59:22 -04:00
|
|
|
std::memcpy(&fields, raw_, sizeof(raw_));
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
exctract the type from the raw bytes
|
|
|
|
*/
|
|
|
|
void uuid::setType() {
|
|
|
|
if ((raw_[8] >> 7) == 0b0) {
|
|
|
|
type_ = NCS;
|
|
|
|
} else if ((raw_[8] >> 6) == 0b10) {
|
|
|
|
type_ = RFC4122;
|
|
|
|
} else if ((raw_[8] >> 5) == 0b110) {
|
|
|
|
type_ = MS;
|
|
|
|
} else {
|
|
|
|
type_ = RESVERED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
compare to another UUID
|
|
|
|
*/
|
|
|
|
bool uuid::operator== (const uuid & other) const {
|
|
|
|
return std::memcmp(raw_, other.bytes(), sizeof(raw_));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
compare to another byte array
|
|
|
|
*/
|
|
|
|
bool uuid::operator== (const uint8_t * other) const {
|
|
|
|
if (sizeof(raw_) != sizeof(other))
|
|
|
|
return false;
|
|
|
|
return std::memcmp(raw_, other, sizeof(raw_));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
compare to another formatted C string
|
|
|
|
*/
|
|
|
|
bool uuid::operator== (const char* other) const {
|
|
|
|
return std::strcmp(string().c_str(), other);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
compare to another formatted std::string
|
|
|
|
*/
|
|
|
|
bool uuid::operator== (const std::string & other) const {
|
|
|
|
return string().compare(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
create this UUID as a Version 4 (RANDOM) UUID
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
setBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
// const std::string NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
|
|
|
|
// const std::string NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
|
|
|
|
// const std::string NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
|
|
|
|
// const std::string NAMESPACE_X500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8";
|
|
|
|
|
|
|
|
}; // UUID
|