UUID
The UUID class is taken from the Poco project. The current implementation supports generating only time based UUID values. This class depends upon the DateTime class for time. The class needs to be initialised before first use with the MAC address associated with the device network shield. Initialisation is necessary since the Arduino ethernet library does not expose a way to programmatically determine the MAC address (the WiFi library does however). Unique MAC addresses are essential for generating unique UUID values.
UUID.h
The header that defines the UUID class.
/* Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef SPT_UUID_H #define SPT_UUID_H #if defined( ARDUINO ) #include "../StandardCplusplus/iostream" #include "../StandardCplusplus/string" #else #include <iostream> #include <string> #endif namespace spt { /** * @brief A class that represents a UUID/GUID * * A UUID is an identifier that is unique across both space and time, * with respect to the space of all UUIDs. Since a UUID is a fixed * size and contains a time field, it is possible for values to * rollover (around A.D. 3400, depending on the specific algorithm * used). A UUID can be used for multiple purposes, from tagging * objects with an extremely short lifetime, to reliably identifying * very persistent objects across a network. * * This class implements a Universal Unique Identifier, * as specified in Appendix A of the DCE 1.1 Remote Procedure * Call Specification (http://www.opengroup.org/onlinepubs/9629399/), * RFC 2518 (WebDAV), section 6.4.1 and the UUIDs and GUIDs internet * draft by Leach/Salz from February, 1998 * (http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt) * and also http://tools.ietf.org/html/draft-mealling-uuid-urn-05 */ class UUID { public: enum Version { UUID_TIME_BASED = 0x01, UUID_DCE_UID = 0x02, UUID_NAME_BASED = 0x03, UUID_RANDOM = 0x04 }; /// Creates a nil (all zero) UUID. UUID(); /// Copy constructor. UUID( const UUID& uuid ); /// Parses the UUID from a string. explicit UUID( const std::string& uuid ); /// Parses the UUID from a char array. explicit UUID( const char* uuid ); /// Destroys the UUID. ~UUID(); /// Assignment operator. UUID& operator = ( const UUID& uuid ); /** * @brief Tries to interpret the given string as an UUID. * @param uuid The value to parse * @return If the UUID is syntactically valid, assigns the * members and returns true. Otherwise leaves the * object unchanged and returns false. */ bool parse( const std::string& uuid ); /** * @brief Returns a string representation of the UUID * consisting of groups of hexadecimal digits separated by hyphens. */ std::string toString() const; /** * @brief Copies the UUID (16 bytes) from a buffer or byte array. * The UUID fields are expected to be stored in network byte order. * @param buffer The buffer need not be aligned. */ void copyFrom( const char* buffer ); /** * @brief Copies the UUID to the buffer. * The fields are in network byte order. The buffer need not be aligned. * @param buffer There must be room for at least 16 bytes. */ void copyTo( char* buffer ) const; /// Returns the version of the UUID. Version version() const; /** * @brief Returns the variant number of the UUID: * @return * - 0 reserved for NCS backward compatibility * - 2 the Leach-Salz variant (used by this class) * - 6 reserved, Microsoft Corporation backward compatibility * - 7 reserved for future definition */ int variant() const; bool operator == ( const UUID& uuid ) const; bool operator != ( const UUID& uuid ) const; bool operator < ( const UUID& uuid ) const; bool operator <= ( const UUID& uuid ) const; bool operator > ( const UUID& uuid ) const; bool operator >= ( const UUID& uuid ) const; /// @return Returns true if the UUID is nil (in other words, consists of all zeros). bool isNull() const; /// Returns a null/nil UUID. static const UUID& null(); /// Returns the namespace identifier for the DNS namespace. static const UUID& dns(); /// Returns the namespace identifier for the URI (former URL) namespace. static const UUID& uri(); /// Returns the namespace identifier for the OID namespace. static const UUID& oid(); /// Returns the namespace identifier for the X500 namespace. static const UUID& x500(); /// Generate a time based UUID instance static const UUID create(); /** * @brief Initialise the UUID engine. On application start, * invoke with the current MAC address. * @param node The MAC address. */ static void init( uint8_t node[6] ); protected: UUID( uint32_t timeLow, uint32_t timeMid, uint32_t timeHiAndVersion, uint16_t clockSeq, uint8_t node[6] ); UUID( const char* bytes, Version version ); int compare( const UUID& uuid ) const; static void appendHex( std::string& str, uint8_t n ); static void appendHex( std::string& str, uint16_t n ); static void appendHex( std::string& str, uint32_t n ); static uint8_t nibble( char hex ); void fromNetwork(); void toNetwork(); static uint32_t randomNumber( int32_t input ); private: uint32_t timeLow; uint16_t timeMid; uint16_t timeHiAndVersion; uint16_t clockSeq; uint8_t node[6]; }; /// Serialise the string representation of the uuid into the output stream inline std::ostream& operator << ( std::ostream& os, const UUID& uuid ) { os << uuid.toString(); return os; } /// Append the string representation of the uuid to the specified string inline std::string& operator += ( std::string& str, const UUID& uuid ) { return str.append( uuid.toString() ); } inline bool UUID::operator == (const UUID& uuid) const { return compare(uuid) == 0; } inline bool UUID::operator != (const UUID& uuid) const { return compare(uuid) != 0; } inline bool UUID::operator < (const UUID& uuid) const { return compare(uuid) < 0; } inline bool UUID::operator <= (const UUID& uuid) const { return compare(uuid) <= 0; } inline bool UUID::operator > (const UUID& uuid) const { return compare(uuid) > 0; } inline bool UUID::operator >= (const UUID& uuid) const { return compare(uuid) >= 0; } inline UUID::Version UUID::version() const { return Version( timeHiAndVersion >> 12 ); } inline bool UUID::isNull() const { return compare( null() ) == 0; } } // namespace spt #endif // SPT_UUID_H
UUID.cpp
The implementation of the UUID class.
/* Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. and Contributors. Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 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 "ByteOrder.h" #if defined( ARDUINO ) #include "../StandardCplusplus/algorithm" #include "../StandardCplusplus/cstring" #include "../StandardCplusplus/string" #include "DateTime.h" #else #include <algorithm> #include <cstring> #include <string> #include <net/DateTime.h> #endif namespace spt { namespace data { static uint8_t mac[6]; static bool UUIDInitialised = false; } } using spt::ByteOrder; using spt::UUID; using std::string; UUID::UUID(): timeLow( 0 ), timeMid( 0 ), timeHiAndVersion( 0 ), clockSeq( 0 ) { memset( node, 0, sizeof( node ) ); } UUID::UUID( const UUID& uuid ): timeLow( uuid.timeLow ), timeMid( uuid.timeMid ), timeHiAndVersion( uuid.timeHiAndVersion ), clockSeq( uuid.clockSeq ) { memcpy( node, uuid.node, sizeof( node ) ); } UUID::UUID( const string& uuid ) { parse( uuid ); } UUID::UUID( const char* uuid ) { parse( string( uuid ) ); } UUID::UUID( uint32_t tl, uint32_t tm, uint32_t thv, uint16_t cs, uint8_t n[6] ): timeLow( tl ), timeMid( tm ), timeHiAndVersion( thv ), clockSeq( cs ) { memcpy( node, n, sizeof( node ) ); } UUID::UUID( const char* bytes, Version version ) { uint32_t i32; uint16_t i16; memcpy( &i32, bytes, sizeof(i32) ); timeLow = ByteOrder::fromNetwork(i32); bytes += sizeof(i32); memcpy( &i16, bytes, sizeof(i16) ); timeMid = ByteOrder::fromNetwork(i16); bytes += sizeof(i16); memcpy( &i16, bytes, sizeof(i16) ); timeHiAndVersion = ByteOrder::fromNetwork(i16); bytes += sizeof(i16); memcpy( &i16, bytes, sizeof(i16) ); clockSeq = ByteOrder::fromNetwork(i16); bytes += sizeof(i16); memcpy( node, bytes, sizeof(node) ); timeHiAndVersion &= 0x0FFF; timeHiAndVersion |= ( version << 12 ); clockSeq &= 0x3FFF; clockSeq |= 0x8000; } UUID::~UUID() {} UUID& UUID::operator = ( const UUID& uuid ) { if ( &uuid != this ) { timeLow = uuid.timeLow; timeMid = uuid.timeMid; timeHiAndVersion = uuid.timeHiAndVersion; clockSeq = uuid.clockSeq; memcpy( node, uuid.node, sizeof( node ) ); } return *this; } bool UUID::parse( const string& uuid ) { if ( uuid.size() < 32 ) return false; bool haveHyphens = false; if ( uuid[8] == '-' && uuid[13] == '-' && uuid[18] == '-' && uuid[23] == '-' ) { if ( uuid.size() >= 36 ) haveHyphens = true; else return false; } string::const_iterator it = uuid.begin(); timeLow = 0; for ( int i = 0; i < 8; ++i ) { timeLow = (timeLow << 4) | nibble(*it++); } if ( haveHyphens ) ++it; timeMid = 0; for ( int i = 0; i < 4; ++i ) { timeMid = (timeMid << 4) | nibble(*it++); } if ( haveHyphens ) ++it; timeHiAndVersion = 0; for ( int i = 0; i < 4; ++i ) { timeHiAndVersion = (timeHiAndVersion << 4) | nibble(*it++); } if ( haveHyphens ) ++it; clockSeq = 0; for ( int i = 0; i < 4; ++i ) { clockSeq = (clockSeq << 4) | nibble(*it++); } if ( haveHyphens ) ++it; for ( int i = 0; i < 6; ++i ) { node[i] = (nibble(*it++) << 4) | nibble(*it++) ; } return true; } string UUID::toString() const { string result; result.reserve( 36 ); appendHex( result, timeLow ); result += '-'; appendHex( result, timeMid ); result += '-'; appendHex( result, timeHiAndVersion ); result += '-'; appendHex( result, clockSeq ); result += '-'; for ( unsigned int i = 0; i < sizeof(node); ++i ) appendHex( result, node[i] ); return result; } void UUID::copyFrom( const char* buffer ) { uint32_t i32; uint16_t i16; memcpy( &i32, buffer, sizeof(i32) ); timeLow = ByteOrder::fromNetwork( i32 ); buffer += sizeof(i32); memcpy( &i16, buffer, sizeof(i16) ); timeMid = ByteOrder::fromNetwork( i16 ); buffer += sizeof(i16); memcpy( &i16, buffer, sizeof(i16) ); timeHiAndVersion = ByteOrder::fromNetwork(i16); buffer += sizeof(i16); memcpy( &i16, buffer, sizeof(i16) ); clockSeq = ByteOrder::fromNetwork(i16); buffer += sizeof(i16); memcpy( node, buffer, sizeof(node) ); } void UUID::copyTo( char* buffer ) const { uint32_t i32 = ByteOrder::toNetwork( timeLow ); memcpy( buffer, &i32, sizeof(i32) ); buffer += sizeof(i32); uint16_t i16 = ByteOrder::toNetwork( timeMid ); memcpy( buffer, &i16, sizeof(i16) ); buffer += sizeof(i16); i16 = ByteOrder::toNetwork( timeHiAndVersion ); memcpy( buffer, &i16, sizeof(i16) ); buffer += sizeof(i16); i16 = ByteOrder::toNetwork( clockSeq ); memcpy( buffer, &i16, sizeof(i16) ); buffer += sizeof(i16); memcpy( buffer, node, sizeof(node) ); } int UUID::variant() const { int v = clockSeq >> 13; if ( (v & 6) == 6 ) return v; else if ( v & 4 ) return 2; else return 0; } int UUID::compare( const UUID& uuid ) const { if ( timeLow != uuid.timeLow ) return timeLow < uuid.timeLow ? -1 : 1; if ( timeMid != uuid.timeMid ) return timeMid < uuid.timeMid ? -1 : 1; if ( timeHiAndVersion != uuid.timeHiAndVersion ) return timeHiAndVersion < uuid.timeHiAndVersion ? -1 : 1; if ( clockSeq != uuid.clockSeq ) return clockSeq < uuid.clockSeq ? -1 : 1; for ( unsigned int i = 0; i < sizeof(node); ++i ) { if (node[i] < uuid.node[i]) return -1; else if (node[i] > uuid.node[i]) return 1; } return 0; } void UUID::appendHex( string& str, uint8_t n ) { static const char* digits = "0123456789abcdef"; str += digits[(n >> 4) & 0xF]; str += digits[n & 0xF]; } void UUID::appendHex( string& str, uint16_t n ) { appendHex( str, uint8_t( n >> 8 ) ); appendHex( str, uint8_t( n & 0xFF ) ); } void UUID::appendHex( string& str, uint32_t n ) { appendHex( str, uint16_t( n >> 16 ) ); appendHex( str, uint16_t( n & 0xFFFF ) ); } uint8_t UUID::nibble( char hex ) { if ( hex >= 'a' && hex <= 'f' ) return uint8_t( hex - 'a' + 10 ); else if ( hex >= 'A' && hex <= 'F' ) return uint8_t( hex - 'A' + 10 ); else if ( hex >= '0' && hex <= '9' ) return uint8_t( hex - '0' ); else return uint8_t( 0 ); } void UUID::fromNetwork() { timeLow = ByteOrder::fromNetwork( timeLow ); timeMid = ByteOrder::fromNetwork( timeMid ); timeHiAndVersion = ByteOrder::fromNetwork( timeHiAndVersion ); clockSeq = ByteOrder::fromNetwork( clockSeq ); } void UUID::toNetwork() { timeLow = ByteOrder::toNetwork( timeLow ); timeMid = ByteOrder::toNetwork( timeMid ); timeHiAndVersion = ByteOrder::toNetwork( timeHiAndVersion ); clockSeq = ByteOrder::toNetwork( clockSeq ); } uint32_t UUID::randomNumber( int32_t x ) { int32_t hi, lo; if ( x == 0 ) x = 123459876; hi = x / 127773; lo = x % 127773; x = 16807 * lo - 2836 * hi; if ( x < 0 ) x += 0x7FFFFFFF; return x; } namespace { static UUID uuidNull; static UUID uuidDNS( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); static UUID uuidURI( "6ba7b811-9dad-11d1-80b4-00c04fd430c8" ); static UUID uuidOID( "6ba7b812-9dad-11d1-80b4-00c04fd430c8" ); static UUID uuidX500( "6ba7b814-9dad-11d1-80b4-00c04fd430c8" ); } const UUID& UUID::null() { return uuidNull; } const UUID& UUID::dns() { return uuidDNS; } const UUID& UUID::uri() { return uuidURI; } const UUID& UUID::oid() { return uuidOID; } const UUID& UUID::x500() { return uuidX500; } const UUID UUID::create() { using spt::net::DateTime; int64_t millis = DateTime::singleton().currentTimeMillis(); uint32_t timeLow = uint32_t( millis & 0xFFFFFFFF ); uint16_t timeMid = uint16_t( ( millis >> 32 ) & 0xFFFF ); uint16_t timeHiAndVersion = uint16_t( ( millis >> 48 ) & 0x0FFF ) + ( UUID::UUID_TIME_BASED << 12 ); uint16_t clockSeq = ( uint16_t( randomNumber( timeLow ) >> 4 ) & 0x3FFF) | 0x8000; return UUID( timeLow, timeMid, timeHiAndVersion, clockSeq, spt::data::mac ); } void UUID::init( uint8_t node[6] ) { if ( ! spt::data::UUIDInitialised ) { for ( int i = 0; i < 6; ++i ) spt::data::mac[i] = node[i]; spt::data::UUIDInitialised = true; } }
UUIDTest.cpp
The unit test suite for the UUID class that runs on the Arduino board.
#if defined( ARDUINO ) #include "tut.hpp" #include "UUID.h" #else #include <tut/tut.hpp> #include <UUID.h> #endif using spt::UUID; namespace tut { struct UUIDTestData {}; typedef test_group<UUIDTestData> UUIDTestGroup; typedef UUIDTestGroup::object UUIDTest; UUIDTestGroup uuidTestGroup( "UUID test suite" ); template<> template<> void UUIDTest::test<1>() { set_test_name( "empty" ); UUID uuid; ensure( "Empty UUID invalid", uuid.toString() == "00000000-0000-0000-0000-000000000000" ); } template<> template<> void UUIDTest::test<2>() { set_test_name( "parse" ); UUID uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); ensure( "Constructed uuid not same", uuid.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); uuid.parse( "6BA7B810-9DAD-11D1-80B4-00C04FD430C8" ); ensure( "Second parsed uuid not same", uuid.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); uuid.parse( "6BA7B8109DAD11D180B400C04FD430C8" ); ensure( "UUID without dashes not parsed", uuid.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); } template<> template<> void UUIDTest::test<3>() { set_test_name( "buffer" ); UUID uuid( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); char buffer[16]; uuid.copyTo( buffer ); UUID uuid2; uuid2.copyFrom( buffer ); ensure( "UUID from buffer not same", uuid2.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); } template<> template<> void UUIDTest::test<4>() { set_test_name( "compare" ); UUID null; ensure( null.isNull()); ensure( UUID::null().isNull() ); UUID uuid1 = null; UUID uuid2; ensure( uuid1.isNull() ); ensure( uuid1 == null ); ensure( !(uuid1 != null ) ); ensure( uuid1 >= null ); ensure( uuid1 <= null ); ensure( !(uuid1 > null ) ); ensure( !(uuid1 < null ) ); ensure( uuid1.toString() == "00000000-0000-0000-0000-000000000000" ); uuid1 = UUID::dns(); ensure( !uuid1.isNull() ); ensure( uuid1 != null ); ensure( !(uuid1 == null ) ); ensure( uuid1 >= null ); ensure( !(uuid1 <= null ) ); ensure( uuid1 > null ); ensure( !(uuid1 < null ) ); ensure( uuid1.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); ensure( null != uuid1 ); ensure( !(null == uuid1 ) ); ensure( !(null >= uuid1 ) ); ensure( null <= uuid1 ); ensure( !(null > uuid1 ) ); ensure( null < uuid1 ); uuid2 = uuid1; ensure( uuid2 == uuid1 ); ensure( !(uuid2 != uuid1 ) ); ensure( uuid2 >= uuid1 ); ensure( uuid2 <= uuid1 ); ensure( !(uuid2 > uuid1 ) ); ensure( !(uuid2 < uuid1 ) ); } template<> template<> void UUIDTest::test<5>() { set_test_name( "version" ); UUID uuid( "db4fa7e9-9e62-4597-99e0-b1ec0b59800e" ); UUID::Version v = uuid.version(); ensure( v == UUID::UUID_RANDOM ); uuid.parse( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); v = uuid.version(); ensure( v == UUID::UUID_TIME_BASED ); uuid.parse( "d2ee4220-3625-11d9-9669-0800200c9a66" ); v = uuid.version(); ensure( v == UUID::UUID_TIME_BASED ); uuid.parse( "360d3652-4411-4786-bbe6-b9675b548559" ); v = uuid.version(); ensure( v == UUID::UUID_RANDOM ); } template<> template<> void UUIDTest::test<6>() { set_test_name( "variant" ); UUID uuid( "db4fa7e9-9e62-4597-99e0-b1ec0b59800e" ); int v = uuid.variant(); ensure( v == 2 ); uuid.parse( "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); v = uuid.variant(); ensure( v == 2 ); uuid.parse( "d2ee4220-3625-11d9-9669-0800200c9a66" ); v = uuid.variant(); ensure( v == 2 ); uuid.parse( "360d3652-4411-4786-bbe6-b9675b548559" ); v = uuid.variant(); ensure( v == 2 ); } template<> template<> void UUIDTest::test<7>() { set_test_name( "invalid" ); UUID uuid; ensure( uuid.parse("6BA7B810-9DAD-11D1-80B4-00C04FD430C8" ) ); ensure( uuid.toString() == "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ); UUID notUuid; ensure( !notUuid.parse( "not a uuid" ) ); ensure( notUuid.isNull() ); } }
The UUID class may be initialised as part of the sketch setup function.
// A proper unique mac address is critical to having a stable UUID generated
// for events published to Sidecar service. Please use the MAC address
// printed on the sticker on the sheild
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
qsense::UUID::init( mac );
qsense::UUID uuid = qsense::UUID::create();