260 lines
5.7 KiB
C++
260 lines
5.7 KiB
C++
|
/*
|
||
|
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;
|
||
|
timestamp_ = 0;
|
||
|
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();
|
||
|
if (type_ != RFC4122) return;
|
||
|
setVersion();
|
||
|
setFields();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
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) {
|
||
|
// remove dashes
|
||
|
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;
|
||
|
oss << std::hex << std::setfill('0') << std::setw(2);
|
||
|
for (uint8_t raw : raw_)
|
||
|
oss << raw;
|
||
|
return oss.str();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
output this UUID as formated hex string
|
||
|
*/
|
||
|
std::string uuid::string() const {
|
||
|
std::string str = hex();
|
||
|
str.reserve(str.length() + 4);
|
||
|
for (int idx : {8, 13, 18, 23})
|
||
|
str.insert(idx, "-");
|
||
|
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
|
||
|
*/
|
||
|
// TODO: Check endianness
|
||
|
void uuid::setBytes() {
|
||
|
if (type_ == NIL)
|
||
|
std::memset(raw_, 0, sizeof(raw_));
|
||
|
if (type_ != RFC4122) return;
|
||
|
|
||
|
Fields 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);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
populate data fields from byte array
|
||
|
*/
|
||
|
// TODO: Check endianness
|
||
|
void uuid::setFields() {
|
||
|
Fields fields;
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
exctract the version from the raw bytes
|
||
|
*/
|
||
|
void uuid::setVersion() {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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
|