From 2dddcb8d634013307cb8bb4b01c0f0641501a242 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 5 Nov 2026 17:17:49 -0600 Subject: [PATCH] Improve NimBLEUUID construction from literals and strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace the separate 16-bit and 32-bit integer constructors with a single uint32_t constructor. Values up to 0xFFFF create 16-bit UUIDs; larger values create 32-bit UUIDs, allowing convenient construction such as NimBLEUUID(0x1234) and NimBLEUUID(0x12345678). * Add a const char* constructor supporting 16-bit, 32-bit, and hyphenated 128-bit UUID strings, with optional 0x prefixes for short UUIDs. * Handle null C-string input and check the expected hyphen positions in 128-bit UUID strings. * Route fromString() through the shared string parser while preserving the std::string constructor’s 16-byte binary UUID handling. --- examples/NimBLE_Server/main/main.cpp | 6 +- src/NimBLEUUID.cpp | 144 ++++++++++++++++----------- src/NimBLEUUID.h | 2 +- src/NimBLEUtils.h | 6 ++ 4 files changed, 95 insertions(+), 63 deletions(-) diff --git a/examples/NimBLE_Server/main/main.cpp b/examples/NimBLE_Server/main/main.cpp index e76958c5..ac50a50a 100644 --- a/examples/NimBLE_Server/main/main.cpp +++ b/examples/NimBLE_Server/main/main.cpp @@ -150,7 +150,7 @@ extern "C" void app_main(void) { pServer = NimBLEDevice::createServer(); pServer->setCallbacks(serverCallbacks); - NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLEService* pDeadService = pServer->createService(0xDEAD); NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic("BEEF", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | @@ -172,7 +172,7 @@ extern "C" void app_main(void) { pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); pBeef2904->setCallbacks(dscCallbacks); - NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLEService* pBaadService = pServer->createService(0xBAAD); NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic("F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); @@ -204,7 +204,7 @@ extern "C" void app_main(void) { /** Loop here and send notifications to connected peers */ for (;;) { if (pServer->getConnectedCount()) { - NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); + NimBLEService* pSvc = pServer->getServiceByUUID(0xBAAD); if (pSvc) { NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); if (pChr) { diff --git a/src/NimBLEUUID.cpp b/src/NimBLEUUID.cpp index 6f7792ab..e4250592 100644 --- a/src/NimBLEUUID.cpp +++ b/src/NimBLEUUID.cpp @@ -69,29 +69,83 @@ NimBLEUUID::NimBLEUUID(const ble_uuid_t* uuid) { * * @param [in] value The string to build a UUID from. */ -NimBLEUUID::NimBLEUUID(const std::string& value) { - if (value.length() == 4) { - m_uuid.u.type = BLE_UUID_TYPE_16; - m_uuid.u16.value = strtoul(value.c_str(), nullptr, 16); - } else if (value.length() == 8) { - m_uuid.u.type = BLE_UUID_TYPE_32; - m_uuid.u32.value = strtoul(value.c_str(), nullptr, 16); - } else if (value.length() == 16) { - memcpy(m_uuid.u128.value, &value[0], 16); - m_uuid.u.type = BLE_UUID_TYPE_128; - } else if (value.length() == 36) { - char* position; - uint64_t first_half = (strtoull(&value[0], &position, 16) << 32) + - (strtoull(position + 1, &position, 16) << 16) + strtoull(position + 1, &position, 16); - uint64_t second_half = (strtoull(position + 1, &position, 16) << 48) + strtoull(position + 1, nullptr, 16); - memcpy(m_uuid.u128.value + 8, &first_half, 8); - memcpy(m_uuid.u128.value, &second_half, 8); +NimBLEUUID::NimBLEUUID(const char* value) { + uint8_t* dest = nullptr; + size_t byteIdx = 0; + bool lowNibble = true; + size_t len = 0; + + if (value == nullptr) { + goto error; + } + + len = strlen(value); + if (len < 4) { + goto error; + } + + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + value += 2; + len -= 2; + } + + // NimBLE stores UUIDs little endian, so write the bytes directly into the value field. + if (len == 4) { + m_uuid.u.type = BLE_UUID_TYPE_16; + dest = reinterpret_cast(&m_uuid.u16.value); + } else if (len == 8) { + m_uuid.u.type = BLE_UUID_TYPE_32; + dest = reinterpret_cast(&m_uuid.u32.value); + } else if (len == 36) { m_uuid.u.type = BLE_UUID_TYPE_128; + dest = m_uuid.u128.value; } else { - NIMBLE_LOGE(LOG_TAG, "Invalid UUID length"); - m_uuid.u.type = 0; + goto error; + } + + // Parse the string from the end so the least significant byte is stored first. + for (size_t i = len; i-- > 0;) { + // Ensure hyphens are in the correct positions for 36-character UUIDs. + if (len == 36 && (i == 8 || i == 13 || i == 18 || i == 23)) { + if (value[i] != '-') { + goto error; + } + continue; + } + + uint8_t nibble = NimBLEUtils::hexToByte(value[i]); + if (nibble == 0xFF) { + goto error; + } + + if (lowNibble) { + dest[byteIdx] = nibble; + } else { + dest[byteIdx++] |= nibble << 4; + } + lowNibble = !lowNibble; + } + + return; + +error: + m_uuid = ble_uuid_any_t{}; + NIMBLE_LOGE(LOG_TAG, "Invalid UUID"); +} // NimBLEUUID(const char*) + +/** + * @brief Create a UUID from a std::string. + * @param [in] value The string to build a UUID from. + */ +NimBLEUUID::NimBLEUUID(const std::string& value) { + if (value.length() == 16) { + m_uuid.u.type = BLE_UUID_TYPE_128; + memcpy(m_uuid.u128.value, value.data(), 16); + return; } -} // NimBLEUUID(std::string) + + *this = NimBLEUUID(value.c_str()); +} // NimBLEUUID(const std::string& value) /** * @brief Create a UUID from 2, 4, 16 bytes of memory. @@ -106,22 +160,17 @@ NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size) { } // NimBLEUUID(const uint8_t* pData, size_t size) /** - * @brief Create a UUID from the 16bit value. - * @param [in] uuid The 16bit short form UUID. - */ -NimBLEUUID::NimBLEUUID(uint16_t uuid) { - m_uuid.u.type = BLE_UUID_TYPE_16; - m_uuid.u16.value = uuid; -} // NimBLEUUID(uint16_t uuid) - -/** - * @brief Create a UUID from the 32bit value. - * @param [in] uuid The 32bit short form UUID. + * @brief Create a UUID from an int value. + * @param [in] uuid The int short form UUID. */ NimBLEUUID::NimBLEUUID(uint32_t uuid) { - m_uuid.u.type = BLE_UUID_TYPE_32; - m_uuid.u32.value = uuid; -} // NimBLEUUID(uint32_t uuid) + m_uuid.u.type = (uuid > UINT16_MAX) ? BLE_UUID_TYPE_32 : BLE_UUID_TYPE_16; + if (m_uuid.u.type == BLE_UUID_TYPE_16) { + m_uuid.u16.value = static_cast(uuid); + } else { + m_uuid.u32.value = static_cast(uuid); + } +} // NimBLEUUID(int uuid) /** * @brief Create a UUID from the native UUID. @@ -196,34 +245,11 @@ bool NimBLEUUID::equals(const NimBLEUUID& uuid) const { } // equals /** - * Create a NimBLEUUID from a string of the form: - * 0xNNNN - * 0xNNNNNNNN - * 0x - * NNNN - * NNNNNNNN - * - * + * Create a NimBLEUUID from a string. * @param [in] uuid The string to create the UUID from. */ NimBLEUUID NimBLEUUID::fromString(const std::string& uuid) { - uint8_t start = 0; - if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters. - start = 2; - } - - uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use. - if (len == 4) { - uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16); - return NimBLEUUID(x); - } else if (len == 8) { - uint32_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16); - return NimBLEUUID(x); - } else if (len == 36) { - return NimBLEUUID(uuid); - } - - return NimBLEUUID(); + return NimBLEUUID(uuid.c_str()); } // fromString /** diff --git a/src/NimBLEUUID.h b/src/NimBLEUUID.h index 10844f84..46719741 100644 --- a/src/NimBLEUUID.h +++ b/src/NimBLEUUID.h @@ -46,8 +46,8 @@ class NimBLEUUID { NimBLEUUID() = default; NimBLEUUID(const ble_uuid_any_t& uuid); NimBLEUUID(const ble_uuid_t* uuid); + NimBLEUUID(const char* value); NimBLEUUID(const std::string& uuid); - NimBLEUUID(uint16_t uuid); NimBLEUUID(uint32_t uuid); NimBLEUUID(const ble_uuid128_t* uuid); NimBLEUUID(const uint8_t* pData, size_t size); diff --git a/src/NimBLEUtils.h b/src/NimBLEUtils.h index 0bf780c2..a107d7d2 100644 --- a/src/NimBLEUtils.h +++ b/src/NimBLEUtils.h @@ -94,6 +94,12 @@ class NimBLEUtils { static NimBLEAddress generateAddr(bool nrpa); static bool taskWait(const TaskData& taskData, uint32_t timeout); static void taskRelease(const TaskData& taskData, int rc = 0); + static constexpr uint8_t hexToByte(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return 0xFF; + } private: friend class NimBLEDevice;