rx merging on sACN universe priority
This commit is contained in:
parent
f9c01e5ad3
commit
4ce0bf73ea
|
@ -26,12 +26,24 @@
|
|||
|
||||
namespace DMX {
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
Universe::Universe() {
|
||||
* @brief Universe::Universe
|
||||
*/
|
||||
Universe::Universe()
|
||||
{
|
||||
null_start_data_.fill(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Universe::~Universe
|
||||
*/
|
||||
Universe::~Universe()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Get value of a slot.
|
||||
|
||||
|
|
|
@ -54,10 +54,11 @@ using DimmerData = std::array<uint8_t, 513>;
|
|||
class Universe {
|
||||
public:
|
||||
Universe ();
|
||||
virtual ~Universe ();
|
||||
|
||||
const DimmerData * data() const { return &null_start_data_; }
|
||||
const uint8_t slot (const uint16_t) const;
|
||||
const double rxRate();
|
||||
virtual const DimmerData * data() const { return &null_start_data_; }
|
||||
virtual uint8_t slot (const uint16_t) const;
|
||||
virtual double rxRate();
|
||||
|
||||
void setValue (const uint16_t, const uint8_t);
|
||||
void setValue (const uint16_t, const uint16_t, const uint8_t*);
|
||||
|
@ -65,9 +66,11 @@ class Universe {
|
|||
void setData (std::vector<uint8_t>);
|
||||
void onData (const DataHandler);
|
||||
|
||||
protected:
|
||||
std::vector<DataHandler> callbacks_;
|
||||
|
||||
private:
|
||||
DimmerData null_start_data_;
|
||||
std::vector<DataHandler> callbacks_;
|
||||
std::queue<std::chrono::time_point<std::chrono::system_clock>> rx_times_;
|
||||
|
||||
void rx_timeout_(bool add_now = false);
|
||||
|
|
|
@ -44,6 +44,16 @@ Receiver::Receiver(UUID::uuid cid)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Receiver::~Receiver
|
||||
*/
|
||||
Receiver::~Receiver()
|
||||
{
|
||||
for (auto& [_, universe] : universes_)
|
||||
delete universe;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Receiver::subscribe
|
||||
* @param num
|
||||
|
@ -51,7 +61,7 @@ Receiver::Receiver(UUID::uuid cid)
|
|||
void Receiver::subscribe(const uint16_t num) {
|
||||
if (universes_.count(num))
|
||||
return;
|
||||
universes_.emplace(num, new SACN::Universe());
|
||||
universes_.emplace(num, new MergeProxyUniverse());
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,10 +70,11 @@ void Receiver::subscribe(const uint16_t num) {
|
|||
* @param num
|
||||
*/
|
||||
void Receiver::unsubscribe(const uint16_t num) {
|
||||
if (!universes_.count(num))
|
||||
return;
|
||||
delete universes_.at(num);
|
||||
universes_.erase(num);
|
||||
// delete merging universe proxy
|
||||
if (universes_.count(num)) {
|
||||
delete universes_.at(num);
|
||||
universes_.erase(num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,7 +188,7 @@ void Receiver::dataFrameHandler(std::shared_ptr<DATA::Pdu> frame) {
|
|||
|
||||
if (!universes_.count(source->universe()))
|
||||
return;
|
||||
Universe * universe = universes_.at(source->universe());
|
||||
auto universe = universes_.at(source->universe())->sourceUniverse(*source);
|
||||
|
||||
// 6.2.3 E1.31 Data Packet: Priority
|
||||
// No priority outside the range of 0 to 200 shall be transmitted on
|
||||
|
@ -199,7 +210,7 @@ void Receiver::dataFrameHandler(std::shared_ptr<DATA::Pdu> frame) {
|
|||
// Any property values in an E1.31 Data Packet containing this bit
|
||||
// shall be ignored.
|
||||
if (source->isTerminated()) {
|
||||
unsubscribe(source->universe());
|
||||
universes_[source->universe()]->deleteSourceUniverse(*source);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -230,13 +241,6 @@ void Receiver::dataFrameHandler(std::shared_ptr<DATA::Pdu> frame) {
|
|||
/// TODO:: do something to break synchronization
|
||||
}
|
||||
|
||||
// 6.2.3.1 Multiple Sources at Highest Priority
|
||||
// It is possible for there to be multiple sources, all transmitting data at
|
||||
// the highest currently active priority for a given universe. When this
|
||||
// occurs, receivers must handle these sources in some way.
|
||||
|
||||
/// TODO: do something with merging and arbitration
|
||||
|
||||
// PDU data will be a block of DMP
|
||||
auto block = PDU::Block<DMP::Pdu>();
|
||||
block.readBlock(frame->stream(), frame);
|
||||
|
|
|
@ -37,6 +37,8 @@ class Receiver
|
|||
{
|
||||
public:
|
||||
Receiver(UUID::uuid = UUID::uuid());
|
||||
~Receiver();
|
||||
|
||||
virtual void subscribe(const uint16_t);
|
||||
virtual void unsubscribe(const uint16_t);
|
||||
Universe * universe(const uint16_t);
|
||||
|
@ -62,7 +64,7 @@ protected:
|
|||
void discoveryListHanlder(std::shared_ptr<EXTENDED::DISCOVERY::Pdu>);
|
||||
|
||||
private:
|
||||
std::unordered_map <uint16_t, SACN::Universe *> universes_;
|
||||
std::unordered_map<uint16_t, MergeProxyUniverse*> universes_;
|
||||
std::vector<EXTENDED::DISCOVERY::Watcher> discoveryCallbacks_;
|
||||
};
|
||||
|
||||
|
|
|
@ -65,6 +65,36 @@ UniverseSource::UniverseSource(std::shared_ptr<DATA::Pdu> pdu)
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief operator ==
|
||||
* @param a
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
bool operator== (const UniverseSource& a, const UniverseSource& b)
|
||||
{
|
||||
return (std::hash<UniverseSource>{}(a) == std::hash<UniverseSource>{}(b));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Universe::Universe
|
||||
*/
|
||||
Universe::Universe()
|
||||
: DMX::Universe()
|
||||
, synchronized_(false)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Universe::~Universe
|
||||
*/
|
||||
Universe::~Universe()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Set universe data from a DMP PDU from a UniverseSource.
|
||||
*/
|
||||
|
@ -75,7 +105,11 @@ void Universe::set(std::shared_ptr<DMP::Pdu> dmp,
|
|||
// Receivers shall discard the packet if the received value is not 0xa1.
|
||||
if (!dmp->header())
|
||||
return;
|
||||
auto type = (ACN::DMP::address_type*)dmp->header();
|
||||
#if defined(RTTI_ENABLED)
|
||||
auto type = dynamic_cast<ACN::DMP::address_type*>(dmp->header());
|
||||
#else
|
||||
auto type = static_cast<ACN::DMP::address_type*>(dmp->header());
|
||||
#endif
|
||||
if (!type->z_reserved) return; // needs to be true, but why?
|
||||
if (type->relative) return;
|
||||
if (type->type != ACN::DMP::ARRAY) return;
|
||||
|
@ -85,27 +119,30 @@ void Universe::set(std::shared_ptr<DMP::Pdu> dmp,
|
|||
// only act on the first property pair in the data
|
||||
if (!dmp->data())
|
||||
return;
|
||||
auto data = (ACN::DMP::dmp_set_data*)dmp->data();
|
||||
auto prop = data->properties.front();
|
||||
auto pr = prop.first; // property range
|
||||
auto pd = prop.second; // property data
|
||||
|
||||
#if defined(RTTI_ENABLED)
|
||||
auto set_data = dynamic_cast<ACN::DMP::dmp_set_data*>(dmp->data());
|
||||
#else
|
||||
auto set_data = static_cast<ACN::DMP::dmp_set_data*>(dmp->data());
|
||||
#endif
|
||||
const auto& [range, data] = set_data->properties.front();
|
||||
|
||||
// 7.4 First Property Address
|
||||
// Sources shall set the DMP Layer's First Property Address to 0x0000.
|
||||
// Receivers shall discard the packet if the received value is not 0x0000.
|
||||
if (pr.address != 0)
|
||||
if (range.address != 0)
|
||||
return;
|
||||
|
||||
// 7.5 Address Increment
|
||||
// Sources shall set the DMP Layer's Address Increment to 0x0001.
|
||||
// Receivers shall discard the packet if the received value is not 0x0001.
|
||||
if (pr.incriment != 1)
|
||||
if (range.incriment != 1)
|
||||
return;
|
||||
|
||||
// 7.6 Property Value Count
|
||||
// The DMP Layer's Property Value Count is used to encode the number of
|
||||
// DMX512-A [DMX] Slots (including the START Code slot).
|
||||
if (pr.count < 1 || pr.count > 513)
|
||||
if (range.count < 1 || range.count > 513)
|
||||
return;
|
||||
|
||||
setSource(source);
|
||||
|
@ -113,7 +150,7 @@ void Universe::set(std::shared_ptr<DMP::Pdu> dmp,
|
|||
// 7.7 Property Values (DMX512-A Data)
|
||||
// The DMP Layer's Property values field is used to encode the
|
||||
// DMX512-A [DMX] START Code and data.
|
||||
DMX::Universe::setData(pd);
|
||||
DMX::Universe::setData(data);
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,4 +163,211 @@ void Universe::setSource(std::shared_ptr<UniverseSource> source)
|
|||
source_ = source;
|
||||
}
|
||||
|
||||
|
||||
MergeProxyUniverse::MergeProxyUniverse()
|
||||
: SACN::Universe()
|
||||
{
|
||||
}
|
||||
|
||||
MergeProxyUniverse::~MergeProxyUniverse()
|
||||
{
|
||||
for (auto& [_, universe] : sources_)
|
||||
delete universe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::sources
|
||||
* @return
|
||||
*/
|
||||
const std::vector<UniverseSource> MergeProxyUniverse::sources() const
|
||||
{
|
||||
std::vector<UniverseSource> keys;
|
||||
for (const auto& [key, _] : sources_)
|
||||
keys.push_back(key);
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::sourceUniverse
|
||||
* @return
|
||||
*/
|
||||
Universe* MergeProxyUniverse::sourceUniverse(const UniverseSource &src)
|
||||
{
|
||||
if (!hasSourceUniverse(src))
|
||||
newSource_(src);
|
||||
return sources_.at(src);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::hasSourceUniverse
|
||||
* @return
|
||||
*/
|
||||
bool MergeProxyUniverse::hasSourceUniverse(const UniverseSource& src) const
|
||||
{
|
||||
if (sources_.count(src))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::hassources
|
||||
* @return
|
||||
*/
|
||||
bool MergeProxyUniverse::hasSources() const
|
||||
{
|
||||
if (sources_.size())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::deleteSourceUniverse
|
||||
* @return
|
||||
*/
|
||||
void MergeProxyUniverse::deleteSourceUniverse(const UniverseSource& src)
|
||||
{
|
||||
if (sources_.count(src)) {
|
||||
delete sources_.at(src);
|
||||
sources_.erase(src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::addSourceUniverse
|
||||
*/
|
||||
void MergeProxyUniverse::addSourceUniverse(Universe* universe)
|
||||
{
|
||||
sources_.at(*universe->source()) = universe;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::dataChangedNotifier
|
||||
* @param universe
|
||||
*/
|
||||
void MergeProxyUniverse::dataChangedNotifier(DMX::Universe* dmx)
|
||||
{
|
||||
#if defined(RTTI_ENABLED)
|
||||
auto sacn = static_cast<SACN::Universe*>(dmx);
|
||||
#else
|
||||
auto sacn = static_cast<SACN::Universe*>(dmx);
|
||||
#endif
|
||||
auto universe = dominant_();
|
||||
if (!universe)
|
||||
return;
|
||||
if (sacn->source() == universe->source())
|
||||
for (const auto &cb : callbacks_)
|
||||
cb(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::isSyncronized
|
||||
* @return
|
||||
*/
|
||||
bool MergeProxyUniverse::isSyncronized() const
|
||||
{
|
||||
auto universe = dominant_();
|
||||
if (!universe) return false;
|
||||
return universe->isSyncronized();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::source
|
||||
* @return
|
||||
*/
|
||||
std::shared_ptr<UniverseSource> MergeProxyUniverse::source() const
|
||||
{
|
||||
auto universe = dominant_();
|
||||
if (!universe) return nullptr;
|
||||
return universe->source();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::set
|
||||
* @param pdu
|
||||
* @param source
|
||||
*/
|
||||
void MergeProxyUniverse::set(std::shared_ptr<DMP::Pdu> pdu,
|
||||
std::shared_ptr<UniverseSource> src)
|
||||
{
|
||||
if (!sources_.count(*src))
|
||||
newSource_(*src);
|
||||
|
||||
sources_.at(*src)->set(pdu, src);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::data
|
||||
* @return
|
||||
*/
|
||||
const DMX::DimmerData * MergeProxyUniverse::data() const
|
||||
{
|
||||
auto universe = dominant_();
|
||||
if (!universe) return nullptr;
|
||||
return universe->data();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::slot
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
uint8_t MergeProxyUniverse::slot(const uint16_t s) const
|
||||
{
|
||||
auto universe = dominant_();
|
||||
if (!universe) return 0;
|
||||
return universe->slot(s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::rxRate
|
||||
* @return
|
||||
*/
|
||||
double MergeProxyUniverse::rxRate()
|
||||
{
|
||||
auto universe = dominant_();
|
||||
if (!universe) return 0.0;
|
||||
return universe->rxRate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::dominant_
|
||||
* @return
|
||||
*/
|
||||
Universe* MergeProxyUniverse::dominant_() const
|
||||
{
|
||||
Universe* ret = nullptr;
|
||||
for (auto& [_, uni] : sources_) {
|
||||
if (uni->rxRate() < 1) // stale universes cannot be dominant
|
||||
continue;
|
||||
if (!ret || uni->source()->priority() > ret->source()->priority())
|
||||
ret = uni;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief MergeProxyUniverse::newSource_
|
||||
* @param src
|
||||
*/
|
||||
void MergeProxyUniverse::newSource_(const UniverseSource &src)
|
||||
{
|
||||
sources_.emplace(src, new Universe());
|
||||
sources_.at(src)->onData(std::bind(&SACN::MergeProxyUniverse::dataChangedNotifier,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
}; // SACN
|
||||
|
|
|
@ -28,13 +28,13 @@
|
|||
#include "../dmx/universe.h"
|
||||
#include "../uuid/uuid.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace SACN {
|
||||
|
||||
/**
|
||||
universe metadata
|
||||
*/
|
||||
* @brief The UniverseSource class
|
||||
*/
|
||||
class UniverseSource
|
||||
{
|
||||
public:
|
||||
|
@ -53,6 +53,8 @@ public:
|
|||
const bool isForced() const {return options_
|
||||
& DATA::FORCE_SYNCHRONIZATION;}
|
||||
|
||||
friend bool operator== (const UniverseSource& a, const UniverseSource& b);
|
||||
|
||||
void setCID(UUID::uuid cid) { cid_ = cid; }
|
||||
void setDescription(std::string desc) { description_ = desc; }
|
||||
void setOptions(uint8_t o) { options_ = o; }
|
||||
|
@ -68,21 +70,49 @@ private:
|
|||
uint16_t sync_address_;
|
||||
uint8_t options_;
|
||||
};
|
||||
} // SACN namespace
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
/**
|
||||
* @brief The hash struct specilizaton for SACN::UniverseSource
|
||||
*/
|
||||
struct hash<SACN::UniverseSource>
|
||||
{
|
||||
/**
|
||||
* @brief operator ()
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
size_t operator()(SACN::UniverseSource const& src) const noexcept
|
||||
{
|
||||
size_t h1 = hash<string>{}(src.description());
|
||||
size_t h2 = hash<UUID::uuid>{}(src.CID());
|
||||
size_t h3 = hash<int>{}(src.priority());
|
||||
size_t h = h1 ^ h2 ^ h3; // or use boost::hash_combine
|
||||
return h;
|
||||
}
|
||||
};
|
||||
} // std namespace
|
||||
|
||||
namespace SACN {
|
||||
/**
|
||||
universe data
|
||||
*/
|
||||
* @brief The Universe class
|
||||
*/
|
||||
class Universe
|
||||
: public DMX::Universe
|
||||
{
|
||||
public:
|
||||
Universe() : DMX::Universe(), synchronized_(false) {};
|
||||
const bool isSyncronized() const {return synchronized_;};
|
||||
std::shared_ptr<UniverseSource> source() const { return source_; }
|
||||
Universe();
|
||||
~Universe();
|
||||
|
||||
void set(std::shared_ptr<DMP::Pdu>, std::shared_ptr<UniverseSource>);
|
||||
void setSource(std::shared_ptr<UniverseSource>);
|
||||
virtual bool isSyncronized() const { return synchronized_; };
|
||||
virtual std::shared_ptr<UniverseSource> source() const { return source_; }
|
||||
|
||||
virtual void set(std::shared_ptr<DMP::Pdu>, std::shared_ptr<UniverseSource>);
|
||||
virtual void setSource(std::shared_ptr<UniverseSource>);
|
||||
|
||||
private:
|
||||
bool synchronized_;
|
||||
|
@ -90,4 +120,45 @@ private:
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The MergeProxyUniverse class
|
||||
* 6.2.3.1 Multiple Sources at Highest Priority
|
||||
* It is possible for there to be multiple sources, all transmitting data
|
||||
* at the highest currently active priority for a given universe. When this
|
||||
* occurs, receivers must handle these sources in some way.
|
||||
*/
|
||||
class MergeProxyUniverse
|
||||
: public SACN::Universe
|
||||
{
|
||||
public:
|
||||
MergeProxyUniverse();
|
||||
~MergeProxyUniverse();
|
||||
|
||||
// Source universes:
|
||||
const std::vector<UniverseSource> sources() const;
|
||||
Universe* sourceUniverse(const UniverseSource&);
|
||||
bool hasSourceUniverse(const UniverseSource&) const;
|
||||
bool hasSources() const;
|
||||
void deleteSourceUniverse(const UniverseSource&);
|
||||
void addSourceUniverse(Universe *);
|
||||
void dataChangedNotifier(DMX::Universe* universe);
|
||||
|
||||
// SACN::Universe overrides:
|
||||
bool isSyncronized() const override;
|
||||
std::shared_ptr<UniverseSource> source() const override;
|
||||
void set(std::shared_ptr<DMP::Pdu>, std::shared_ptr<UniverseSource>) override;
|
||||
void setSource(std::shared_ptr<UniverseSource>) override {};
|
||||
|
||||
// DMX::Universe Overrides:
|
||||
const DMX::DimmerData *data() const override;
|
||||
uint8_t slot(const uint16_t) const override;
|
||||
double rxRate() override;
|
||||
|
||||
private:
|
||||
std::unordered_map<UniverseSource, SACN::Universe*> sources_;
|
||||
|
||||
Universe* dominant_() const;
|
||||
void newSource_(const UniverseSource&);
|
||||
};
|
||||
|
||||
} // SACN namespace
|
||||
|
|
256
uuid/uuid.cpp
256
uuid/uuid.cpp
|
@ -32,9 +32,11 @@
|
|||
namespace UUID {
|
||||
|
||||
/**
|
||||
construct a NIL UUID
|
||||
*/
|
||||
uuid::uuid() {
|
||||
* @brief uuid::uuid
|
||||
*/
|
||||
uuid::uuid()
|
||||
: raw_(new uint8_t[16])
|
||||
{
|
||||
type_ = NIL;
|
||||
version_ = VOID;
|
||||
timestamp_ = 0;
|
||||
|
@ -45,39 +47,63 @@ uuid::uuid() {
|
|||
|
||||
|
||||
/**
|
||||
create this UUID from a byte array
|
||||
*/
|
||||
void uuid::fromArray(const uint8_t * bytes) {
|
||||
std::memcpy(raw_, bytes, sizeof(raw_));
|
||||
setType();
|
||||
switch (type_) {
|
||||
case NCS:
|
||||
setNCSFields();
|
||||
break;
|
||||
case RFC4122:
|
||||
setRFC4122Fields();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
* @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_;
|
||||
setBytes();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
create this UUID from a C style char array
|
||||
*/
|
||||
* @brief uuid::~uuid
|
||||
*/
|
||||
uuid::~uuid()
|
||||
{
|
||||
delete[] raw_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief uuid::fromArray
|
||||
* @param bytes
|
||||
*
|
||||
* create this UUID from a byte array
|
||||
*/
|
||||
void uuid::fromArray(const uint8_t * bytes) {
|
||||
std::memcpy(raw_, bytes, 16);
|
||||
setFields();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief uuid::fromCstring
|
||||
* @param cstr
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
* @brief uuid::fromString
|
||||
* @param str
|
||||
*
|
||||
* create this UUID from a string representation
|
||||
*/
|
||||
void uuid::fromString(std::string str) {
|
||||
// remove formating
|
||||
if (str.front() == '{' && str.back() == '}')
|
||||
str = str.substr(1, str.size() - 2);
|
||||
str = str.substr(1, str.size() - 2);
|
||||
str.erase(std::remove(str.begin(), str.end(), '-'), str.end());
|
||||
if (str.length() != 32) return;
|
||||
// fill buffer
|
||||
|
@ -89,21 +115,27 @@ void uuid::fromString(std::string str) {
|
|||
};
|
||||
|
||||
|
||||
/*
|
||||
output this UUID as a hex string
|
||||
*/
|
||||
/**
|
||||
* @brief uuid::hex
|
||||
* @return
|
||||
*
|
||||
* output this UUID as a hex string
|
||||
*/
|
||||
std::string uuid::hex() const {
|
||||
std::ostringstream oss;
|
||||
oss << std::hex;
|
||||
for (uint8_t raw : raw_)
|
||||
oss << std::setw(2) << std::setfill('0') << (int)raw;
|
||||
for (int i = 0; i < 16; i++)
|
||||
oss << std::setw(2) << std::setfill('0') << (int)raw_[i];
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
output this UUID as formated hex string
|
||||
*/
|
||||
* @brief uuid::string
|
||||
* @return
|
||||
*
|
||||
* output this UUID as formated hex string
|
||||
*/
|
||||
std::string uuid::string() const {
|
||||
std::string str = hex();
|
||||
str.reserve(str.length() + 6);
|
||||
|
@ -116,32 +148,36 @@ std::string uuid::string() const {
|
|||
|
||||
|
||||
/**
|
||||
output this UUID as a urn specified in RFC 4122
|
||||
*/
|
||||
* @brief uuid::urn
|
||||
* @return
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
* @brief uuid::setBytes
|
||||
* make the raw bytes match the field contents
|
||||
*/
|
||||
void uuid::setBytes()
|
||||
{
|
||||
switch (type_) {
|
||||
switch (type_) {
|
||||
case NIL:
|
||||
std::memset(raw_, 0, sizeof(raw_));
|
||||
break;
|
||||
std::memset(raw_, 0, 16);
|
||||
break;
|
||||
case NCS:
|
||||
setNCSBytes();
|
||||
break;
|
||||
setNCSBytes();
|
||||
break;
|
||||
case RFC4122:
|
||||
setRFC4122Bytes();
|
||||
break;
|
||||
setRFC4122Bytes();
|
||||
break;
|
||||
case MS:
|
||||
break;
|
||||
break;
|
||||
case RESVERED:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,13 +187,13 @@ void uuid::setBytes()
|
|||
*/
|
||||
void uuid::setNCSBytes()
|
||||
{
|
||||
NCSFields fields;
|
||||
fields.time = timestamp_;
|
||||
fields.res = 0;
|
||||
fields.family = version_;
|
||||
fields.node = node_;
|
||||
NCSFields fields;
|
||||
fields.time = timestamp_;
|
||||
fields.res = 0;
|
||||
fields.family = version_;
|
||||
fields.node = node_;
|
||||
|
||||
std::memcpy(raw_, &fields, sizeof(raw_));
|
||||
std::memcpy(raw_, &fields, 16);
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,23 +203,42 @@ void uuid::setNCSBytes()
|
|||
*/
|
||||
void uuid::setRFC4122Bytes()
|
||||
{
|
||||
RFC4122Fields fields;
|
||||
fields.time_low = timestamp_;
|
||||
fields.time_mid = timestamp_ >> 32;
|
||||
fields.time_hi_version = timestamp_ >> 48;
|
||||
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.clock_seq_low = clock_seq_;
|
||||
fields.clock_seq_hi_variant = clock_seq_ >> 8;
|
||||
|
||||
fields.node_low = node_;
|
||||
fields.node_high = node_ >> 16;
|
||||
fields.node_low = node_;
|
||||
fields.node_high = node_ >> 16;
|
||||
|
||||
std::memcpy(raw_, &fields, sizeof(raw_));
|
||||
std::memcpy(raw_, &fields, 16);
|
||||
|
||||
// version
|
||||
raw_[7] = (raw_[7] & 0x0f) | (version_ << 4);
|
||||
// type
|
||||
raw_[8] = (raw_[8] & 0b00111111) | (type_ << 6);
|
||||
// version
|
||||
raw_[7] = (raw_[7] & 0x0f) | (version_ << 4);
|
||||
// type
|
||||
raw_[8] = (raw_[8] & 0b00111111) | (type_ << 6);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief uuid::setFields
|
||||
*/
|
||||
void uuid::setFields()
|
||||
{
|
||||
setType();
|
||||
switch (type_) {
|
||||
case NCS:
|
||||
setNCSFields();
|
||||
break;
|
||||
case RFC4122:
|
||||
setRFC4122Fields();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -192,23 +247,22 @@ void uuid::setRFC4122Bytes()
|
|||
*/
|
||||
void uuid::setNCSFields()
|
||||
{
|
||||
NCSFields fields;
|
||||
std::memcpy(&fields, raw_, sizeof (raw_));
|
||||
NCSFields fields;
|
||||
std::memcpy(&fields, raw_, 16);
|
||||
|
||||
timestamp_ = fields.time;
|
||||
version_ = fields.family;
|
||||
node_ = fields.node;
|
||||
timestamp_ = fields.time;
|
||||
version_ = fields.family;
|
||||
node_ = fields.node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
populate data fields from byte array
|
||||
*/
|
||||
// TODO: Check endianness
|
||||
* @brief uuid::setRFC4122Fields
|
||||
*/
|
||||
void uuid::setRFC4122Fields()
|
||||
{
|
||||
RFC4122Fields fields;
|
||||
std::memcpy(&fields, raw_, sizeof(raw_));
|
||||
std::memcpy(&fields, raw_, 16);
|
||||
|
||||
timestamp_ = fields.time_low;
|
||||
timestamp_ |= (uint64_t)fields.time_mid << 32;
|
||||
|
@ -243,8 +297,10 @@ void uuid::setRFC4122Fields()
|
|||
|
||||
|
||||
/**
|
||||
exctract the type from the raw bytes
|
||||
*/
|
||||
* @brief uuid::setType
|
||||
*
|
||||
* exctract the type from the raw bytes
|
||||
*/
|
||||
void uuid::setType() {
|
||||
if ((raw_[8] >> 7) == 0b0) {
|
||||
type_ = NCS;
|
||||
|
@ -257,43 +313,41 @@ void uuid::setType() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
compare to another UUID
|
||||
*/
|
||||
bool uuid::operator== (const uuid & other) const {
|
||||
return std::memcmp(raw_, other.bytes(), sizeof(raw_));
|
||||
* @brief uuid::operator =
|
||||
* @param other
|
||||
* @return
|
||||
*/
|
||||
uuid& uuid::operator=(const uuid& other)
|
||||
{
|
||||
if (this != &other) { // protect against invalid self-assignment
|
||||
std::memcpy(raw_, other.raw_, 16);
|
||||
setFields();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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_));
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
* @brief uuid::uuid4
|
||||
*
|
||||
* create this UUID as a Version 4 (RANDOM) UUID
|
||||
*/
|
||||
void uuid::uuid4() {
|
||||
type_ = RFC4122;
|
||||
version_ = RAND;
|
||||
|
|
174
uuid/uuid.h
174
uuid/uuid.h
|
@ -30,71 +30,99 @@
|
|||
// RFC4122 A Universally Unique IDentifier (UUID) URN Namespace
|
||||
namespace UUID {
|
||||
|
||||
/**
|
||||
* @brief The Type enum
|
||||
*/
|
||||
enum Type {
|
||||
NCS, // Reserved, NCS backward compatibility.
|
||||
RFC4122, // The variant specified in this document.
|
||||
MS, // Reserved, Microsoft Corporation backward compatibility
|
||||
RESVERED, // Reserved for future definition.
|
||||
NIL
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The RFC4122Version enum
|
||||
*/
|
||||
enum RFC4122Version {
|
||||
VOID = 0b0000,
|
||||
TIME = 0b0001, // The time-based version specified in this document.
|
||||
DCE = 0b0010, // DCE Security version, with embedded POSIX UIDs.
|
||||
MD5 = 0b0011, // The name-based version that uses MD5 hashing.
|
||||
RAND = 0b0100, // The randomly or pseudo-randomly generated version.
|
||||
SHA1 = 0b0101 // The name-based version that uses SHA-1 hashing.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The Namespace enum
|
||||
*/
|
||||
enum Namespace {
|
||||
DNS, // the name string is a fully-qualified domain name.
|
||||
URL, // the name string is a URL.
|
||||
OID, // the name string is an ISO OID.
|
||||
X500 // the name string is an X.500 DN in DER or a text output format.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The NCSFields struct
|
||||
*/
|
||||
struct NCSFields {
|
||||
uint64_t time : 48;
|
||||
uint16_t res;
|
||||
uint8_t family;
|
||||
uint64_t node : 56;
|
||||
}__attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* @brief The RFC4122Fields struct
|
||||
*/
|
||||
struct RFC4122Fields {
|
||||
uint32_t time_low;
|
||||
uint16_t time_mid;
|
||||
uint16_t time_hi_version;
|
||||
uint8_t clock_seq_hi_variant;
|
||||
uint8_t clock_seq_low;
|
||||
uint16_t node_low;
|
||||
uint32_t node_high;
|
||||
}__attribute__((packed));
|
||||
|
||||
|
||||
// extern const std::string NAMESPACE_DNS;
|
||||
// extern const std::string NAMESPACE_URL;
|
||||
// extern const std::string NAMESPACE_OID;
|
||||
// extern const std::string NAMESPACE_X500;
|
||||
|
||||
|
||||
/**
|
||||
* @brief The uuid class
|
||||
*/
|
||||
class uuid {
|
||||
public:
|
||||
enum Type {
|
||||
NCS, // Reserved, NCS backward compatibility.
|
||||
RFC4122, // The variant specified in this document.
|
||||
MS, // Reserved, Microsoft Corporation backward compatibility
|
||||
RESVERED, // Reserved for future definition.
|
||||
NIL
|
||||
};
|
||||
|
||||
enum RFC4122Version {
|
||||
VOID = 0b0000,
|
||||
TIME = 0b0001, // The time-based version specified in this document.
|
||||
DCE = 0b0010, // DCE Security version, with embedded POSIX UIDs.
|
||||
MD5 = 0b0011, // The name-based version that uses MD5 hashing.
|
||||
RAND = 0b0100, // The randomly or pseudo-randomly generated version.
|
||||
SHA1 = 0b0101 // The name-based version that uses SHA-1 hashing.
|
||||
};
|
||||
|
||||
enum Namespace {
|
||||
DNS, // the name string is a fully-qualified domain name.
|
||||
URL, // the name string is a URL.
|
||||
OID, // the name string is an ISO OID.
|
||||
X500 // the name string is an X.500 DN in DER or a text output format.
|
||||
};
|
||||
|
||||
struct NCSFields {
|
||||
uint64_t time : 48;
|
||||
uint16_t res;
|
||||
uint8_t family;
|
||||
uint64_t node : 56;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct RFC4122Fields {
|
||||
uint32_t time_low;
|
||||
uint16_t time_mid;
|
||||
uint16_t time_hi_version;
|
||||
uint8_t clock_seq_hi_variant;
|
||||
uint8_t clock_seq_low;
|
||||
uint16_t node_low;
|
||||
uint32_t node_high;
|
||||
}__attribute__((packed));
|
||||
|
||||
// constructor
|
||||
uuid();
|
||||
uuid(const uuid& other);
|
||||
uuid(const uint8_t * raw) : uuid() { fromArray(raw); };
|
||||
uuid(const char * cstr) : uuid() { fromCstring(cstr); };
|
||||
uuid(std::string str) : uuid() { fromString(str); };
|
||||
~uuid();
|
||||
|
||||
// accessors
|
||||
const Type type() const { return type_; };
|
||||
const uint16_t version() const { return version_; };
|
||||
const uint64_t time() const { return timestamp_; };
|
||||
const uint16_t sequence() const { return clock_seq_; };
|
||||
const uint64_t node() const { return node_; };
|
||||
const uint8_t * bytes() const { return raw_; };
|
||||
Type type() const { return type_; };
|
||||
uint16_t version() const { return version_; };
|
||||
uint64_t time() const { return timestamp_; };
|
||||
uint16_t sequence() const { return clock_seq_; };
|
||||
uint64_t node() const { return node_; };
|
||||
const uint8_t* bytes() const { return raw_; };
|
||||
|
||||
// comparitor overload
|
||||
bool operator== (const uuid &) const;
|
||||
bool operator== (const uint8_t *) const;
|
||||
bool operator== (const char *) const;
|
||||
bool operator== (const std::string &) const;
|
||||
// operator overloads
|
||||
uuid& operator= (const uuid& other);
|
||||
friend bool operator== (const uuid& a, const uuid& b);
|
||||
|
||||
// typecast overload
|
||||
operator const uint8_t * () const { return raw_; };
|
||||
operator const uint8_t* () const { return raw_; };
|
||||
operator const std::string () const { return string(); };
|
||||
|
||||
// output
|
||||
|
@ -103,13 +131,13 @@ public:
|
|||
std::string urn() const; // 'urn:uuid:12345678-1234-5678-1234-567812345678'
|
||||
|
||||
// creators
|
||||
virtual void uuid1(uint64_t node, uint16_t clock_seq) {};
|
||||
virtual void uuid3(Namespace, std::string) {};
|
||||
// virtual void uuid1(uint64_t node, uint16_t clock_seq) {};
|
||||
// virtual void uuid3(Namespace, std::string) {};
|
||||
virtual void uuid4(); // very low quality of random
|
||||
virtual void uuid5(Namespace, std::string) {};
|
||||
// virtual void uuid5(Namespace, std::string) {};
|
||||
|
||||
private:
|
||||
uint8_t raw_[16];
|
||||
uint8_t* raw_;
|
||||
Type type_;
|
||||
uint16_t version_;
|
||||
uint64_t timestamp_;
|
||||
|
@ -125,14 +153,30 @@ private:
|
|||
void setNCSBytes();
|
||||
void setRFC4122Bytes();
|
||||
|
||||
void setFields();
|
||||
void setNCSFields();
|
||||
void setRFC4122Fields();
|
||||
}; // uuid
|
||||
};
|
||||
|
||||
} // namespace UUID
|
||||
|
||||
|
||||
// extern const std::string NAMESPACE_DNS;
|
||||
// extern const std::string NAMESPACE_URL;
|
||||
// extern const std::string NAMESPACE_OID;
|
||||
// extern const std::string NAMESPACE_X500;
|
||||
|
||||
}; // UUID
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
/**
|
||||
* @brief The hash struct specialization for UUID::uuid
|
||||
*/
|
||||
struct hash<UUID::uuid>
|
||||
{
|
||||
/**
|
||||
* @brief operator ()
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
size_t operator()(UUID::uuid const& id) const noexcept
|
||||
{
|
||||
return hash<string>{}(id.hex());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
|
Loading…
Reference in New Issue