-
-
Notifications
You must be signed in to change notification settings - Fork 345
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add general UUID / GUID support (#2552)
This PR moves the `Uuid` object out of the `SSDP` library and makes some improvements. UUIDs/GUIDs are not exclusively network entities. They're required for filesystems, for example. - Add template constructor for arbitrary 16-byte entities - Add comparison operators - Add Uuid test module - Fix test message truncation by updating SmingTest to write messages directly to Serial
- Loading branch information
Showing
7 changed files
with
378 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/**** | ||
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. | ||
* Created 2015 by Skurydin Alexey | ||
* http://github.com/SmingHub/Sming | ||
* All files of the Sming Core are provided under the LGPL v3 license. | ||
* | ||
* Uuid.cpp - Universal Unique Identifier | ||
* | ||
* See https://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm. | ||
* | ||
* @author mikee47 <mike@sillyhouse.net> | ||
* | ||
****/ | ||
|
||
#include "Uuid.h" | ||
#include <SystemClock.h> | ||
#include <stringconversion.h> | ||
|
||
extern "C" { | ||
uint32_t os_random(); | ||
void os_get_random(void* buf, size_t n); | ||
} | ||
|
||
bool Uuid::generate(MacAddress mac) | ||
{ | ||
uint8_t version = 1; // DCE version | ||
uint8_t variant = 2; // DCE variant | ||
uint16_t clock_seq = os_random(); | ||
uint32_t time; | ||
if(SystemClock.isSet()) { | ||
time = SystemClock.now(eTZ_UTC); | ||
} else { | ||
time = os_random(); | ||
} | ||
// Time only provides 32 bits, we need 60 | ||
time_low = (os_random() & 0xFFFFFFFC) | (time & 0x00000003); | ||
time_mid = (time >> 2) & 0xFFFF; | ||
time_hi_and_version = (version << 12) | ((time >> 18) << 2); | ||
clock_seq_hi_and_reserved = (variant << 6) | ((clock_seq >> 8) & 0x3F); | ||
clock_seq_low = clock_seq & 0xFF; | ||
mac.getOctets(node); | ||
|
||
return SystemClock.isSet(); | ||
} | ||
|
||
bool Uuid::generate() | ||
{ | ||
MacAddress::Octets mac; | ||
os_get_random(mac, sizeof(mac)); | ||
// RFC4122 requires LSB of first octet to be 1 | ||
mac[0] |= 0x01; | ||
return generate(mac); | ||
} | ||
|
||
bool Uuid::decompose(const char* s, size_t len) | ||
{ | ||
if(len != stringSize) { | ||
return false; | ||
} | ||
|
||
char* p; | ||
time_low = strtoul(s, &p, 16); | ||
if(*p != '-' || p - s != 8) { | ||
return false; | ||
} | ||
s = ++p; | ||
|
||
time_mid = strtoul(s, &p, 16); | ||
if(*p != '-' || p - s != 4) { | ||
return false; | ||
} | ||
s = ++p; | ||
|
||
time_hi_and_version = strtoul(s, &p, 16); | ||
if(*p != '-' || p - s != 4) { | ||
return false; | ||
} | ||
s = ++p; | ||
|
||
uint16_t x = strtoul(s, &p, 16); | ||
if(*p != '-' || p - s != 4) { | ||
return false; | ||
} | ||
clock_seq_hi_and_reserved = x >> 8; | ||
clock_seq_low = x & 0xff; | ||
s = ++p; | ||
|
||
for(unsigned i = 0; i < sizeof(node); ++i) { | ||
uint8_t c = unhex(*s++) << 4; | ||
c |= unhex(*s++); | ||
node[i] = c; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
size_t Uuid::toString(char* buffer, size_t bufSize) const | ||
{ | ||
if(isFlashPtr(this)) { | ||
return Uuid(*this).toString(buffer, bufSize); | ||
} | ||
|
||
if(buffer == nullptr || bufSize < stringSize) { | ||
return 0; | ||
} | ||
|
||
auto set = [&](unsigned offset, uint32_t value, unsigned digits) { | ||
ultoa_wp(value, &buffer[offset], 16, digits, '0'); | ||
}; | ||
|
||
// 2fac1234-31f8-11b4-a222-08002b34c003 | ||
// 0 9 14 19 24 36 | ||
|
||
set(0, time_low, 8); | ||
buffer[8] = '-'; | ||
set(9, time_mid, 4); | ||
buffer[13] = '-'; | ||
set(14, time_hi_and_version, 4); | ||
buffer[18] = '-'; | ||
set(19, clock_seq_hi_and_reserved, 2); | ||
set(21, clock_seq_low, 2); | ||
buffer[23] = '-'; | ||
|
||
unsigned pos = 24; | ||
for(unsigned i = 0; i < 6; ++i) { | ||
buffer[pos++] = hexchar(node[i] >> 4); | ||
buffer[pos++] = hexchar(node[i] & 0x0f); | ||
} | ||
|
||
return stringSize; | ||
} | ||
|
||
String Uuid::toString() const | ||
{ | ||
String s; | ||
if(s.setLength(stringSize)) { | ||
toString(s.begin(), stringSize); | ||
} | ||
return s; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/**** | ||
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. | ||
* Created 2015 by Skurydin Alexey | ||
* http://github.com/SmingHub/Sming | ||
* All files of the Sming Core are provided under the LGPL v3 license. | ||
* | ||
* Uuid.h - Universal Unique Identifier | ||
* | ||
* @author mikee47 <mike@sillyhouse.net> | ||
* | ||
****/ | ||
|
||
#pragma once | ||
|
||
#include <WString.h> | ||
#include <MacAddress.h> | ||
|
||
/** | ||
* @brief Class for manipulating UUID (aka GUID) entities | ||
* | ||
* UUID: Universally Unique IDentifier | ||
* GUID: Globally Unique IDentifier | ||
* | ||
* See https://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm. | ||
*/ | ||
struct Uuid { | ||
uint32_t time_low{0}; // 0-3 | ||
uint16_t time_mid{0}; // 4-5 | ||
uint16_t time_hi_and_version{0}; // 6-7, version = top 4 bits | ||
uint8_t clock_seq_hi_and_reserved{0}; // 8, variant = top 2 bits | ||
uint8_t clock_seq_low{0}; // 9 | ||
uint8_t node[6]{}; // 10-15 | ||
|
||
/** | ||
* @brief Number of characters in a UUID string (excluding NUL terminator) | ||
*/ | ||
static constexpr size_t stringSize = 36; | ||
|
||
Uuid() | ||
{ | ||
} | ||
|
||
explicit Uuid(const char* s) | ||
{ | ||
decompose(s); | ||
} | ||
|
||
explicit Uuid(const char* s, size_t len) | ||
{ | ||
decompose(s, len); | ||
} | ||
|
||
explicit Uuid(const String& s) : Uuid(s.c_str(), s.length()) | ||
{ | ||
} | ||
|
||
explicit Uuid(const FlashString& s) : Uuid(String(s)) | ||
{ | ||
} | ||
|
||
explicit constexpr Uuid(uint32_t time_low, uint16_t time_mid, uint16_t time_hi_and_version, | ||
uint8_t clock_seq_hi_and_reserved, uint8_t clock_seq_low, uint8_t n1, uint8_t n2, | ||
uint8_t n3, uint8_t n4, uint8_t n5, uint8_t n6) | ||
: time_low(time_low), time_mid(time_mid), time_hi_and_version(time_hi_and_version), | ||
clock_seq_hi_and_reserved(clock_seq_hi_and_reserved), | ||
clock_seq_low(clock_seq_low), node{n1, n2, n3, n4, n5, n6} | ||
{ | ||
} | ||
|
||
explicit operator bool() const | ||
{ | ||
Uuid Null{}; | ||
return memcmp(this, &Null, sizeof(Null)) != 0; | ||
} | ||
|
||
bool operator==(const Uuid& other) const | ||
{ | ||
return memcmp(this, &other, sizeof(Uuid)) == 0; | ||
} | ||
|
||
bool operator!=(const Uuid& other) const | ||
{ | ||
return !operator==(other); | ||
} | ||
|
||
/** | ||
* @brief Generate a UUID using a MAC node address | ||
* @param mac Node address to use in generating the UUID, typically from WifiStation | ||
* @retval bool true if system clock time was used, false if substituted with random number | ||
*/ | ||
bool generate(MacAddress mac); | ||
|
||
/** | ||
* @brief Generate UUID using random number instead of MAC | ||
* @retval bool true if system clock time was used, false if substituted with random number | ||
* | ||
* Used where MAC address is not available or it is not desirable to expose it. | ||
*/ | ||
bool generate(); | ||
|
||
/** | ||
* @name Decompse string into UUID | ||
* @{ | ||
*/ | ||
bool decompose(const char* s, size_t len); | ||
|
||
bool decompose(const char* s) | ||
{ | ||
return s ? decompose(s, strlen(s)) : false; | ||
} | ||
|
||
bool decompose(const String& s) | ||
{ | ||
return decompose(s.c_str(), s.length()); | ||
} | ||
/** @} */ | ||
|
||
/** | ||
* @name Get string representation of UUID | ||
* @{ | ||
*/ | ||
|
||
/** | ||
* @param uuid | ||
* @param buffer | ||
* @param bufSize Must be at least UUID_STRING_SIZE | ||
* @retval size_t number of characters written (either 0 or UUID_STRING_SIZE) | ||
* @note Converts UUID into a string of the form | ||
* | ||
* <time_low>-<time_mid>-<time_high_and_version>-<clock_seq_and_reserved><clock_seq_low>-<node> | ||
* | ||
* e.g. 2fac1234-31f8-11b4-a222-08002b34c003 | ||
*/ | ||
size_t toString(char* buffer, size_t bufSize) const; | ||
|
||
String toString() const; | ||
|
||
operator String() const | ||
{ | ||
return toString(); | ||
} | ||
|
||
/** @} */ | ||
}; | ||
|
||
static_assert(sizeof(Uuid) == 16, "Bad Uuid"); | ||
|
||
inline String toString(const Uuid& uuid) | ||
{ | ||
return uuid.toString(); | ||
} | ||
|
||
inline bool fromString(const char* s, Uuid& uuid) | ||
{ | ||
return uuid.decompose(s); | ||
} | ||
|
||
inline bool fromString(const String& s, Uuid& uuid) | ||
{ | ||
return uuid.decompose(s); | ||
} | ||
|
||
/** | ||
* @deprecated Use `Uuid` instead. | ||
*/ | ||
typedef Uuid UUID SMING_DEPRECATED; |
Submodule SSDP
updated
3 files
+0 −131 | src/Uuid.cpp | |
+1 −1 | src/include/Network/SSDP/Urn.h | |
+0 −121 | src/include/Network/SSDP/Uuid.h |
Submodule SmingTest
updated
4 files
+10 −6 | README.rst | |
+13 −13 | src/SmingTest.cpp | |
+5 −9 | src/TestBase.cpp | |
+1 −1 | src/TestGroup.cpp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#include <HostTests.h> | ||
|
||
#include <Data/Uuid.h> | ||
#include <Data/Stream/MemoryDataStream.h> | ||
|
||
namespace | ||
{ | ||
using guid_t = Uuid; | ||
|
||
#define DEFINE_GUID(name, a, b, c, d...) static constexpr guid_t name PROGMEM{a, b, c, d}; | ||
|
||
DEFINE_GUID(PARTITION_SYSTEM_GUID, 0xc12a7328, 0xf81f, 0x11d2, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b) | ||
#define PARTITION_SYSTEM_GUID_PSTR "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" | ||
DEFINE_FSTR_LOCAL(PARTITION_SYSTEM_GUID_FSTR, PARTITION_SYSTEM_GUID_PSTR) | ||
|
||
} // namespace | ||
|
||
class UuidTest : public TestGroup | ||
{ | ||
public: | ||
UuidTest() : TestGroup(_F("UUID")) | ||
{ | ||
} | ||
|
||
void execute() override | ||
{ | ||
TEST_CASE("NULL GUID") | ||
{ | ||
Uuid uuid; | ||
uint8_t empty[16]{}; | ||
REQUIRE(memcmp(&uuid, empty, 16) == 0); | ||
} | ||
|
||
TEST_CASE("Struct") | ||
{ | ||
REQUIRE_EQ(String(PARTITION_SYSTEM_GUID_FSTR), Uuid(PARTITION_SYSTEM_GUID)); | ||
} | ||
|
||
TEST_CASE("Decomposition") | ||
{ | ||
REQUIRE_EQ(String(PARTITION_SYSTEM_GUID_FSTR), Uuid(PARTITION_SYSTEM_GUID_PSTR)); | ||
REQUIRE_EQ(String(PARTITION_SYSTEM_GUID_FSTR), Uuid(PARTITION_SYSTEM_GUID_FSTR)); | ||
} | ||
|
||
TEST_CASE("Copy") | ||
{ | ||
Uuid u1; | ||
Uuid u2(PARTITION_SYSTEM_GUID); | ||
u1 = u2; | ||
REQUIRE_EQ(u1, u2); | ||
} | ||
|
||
TEST_CASE("Printing") | ||
{ | ||
MemoryDataStream str; | ||
const Uuid& u1(PARTITION_SYSTEM_GUID); | ||
str << u1; | ||
String s = str.readString(Uuid::stringSize); | ||
REQUIRE_EQ(str.available(), 0); | ||
REQUIRE_EQ(s, u1); | ||
} | ||
} | ||
}; | ||
|
||
void REGISTER_TEST(Uuid) | ||
{ | ||
registerGroup<UuidTest>(); | ||
} |