-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pw_bluetooth: Make Uuid a class with constructors and helper methods
The Bluetooth Core Spec defines 16-bit, 32-bit and full 128-bit representations of 128-bit UUID values. In several cases the protocol allows to set a 128-bit value or shorter 16-bit value when using one of the pre-assigned short 16-bit values. This patch makes Uuid a class of the same 128-bit size as before but adds constructors and helper methods to easily handle these representations as well as printing and parsing human-readable hex representations of the same. Test: Added unittests for the new methods. Change-Id: I93607b58a85eb8a3388a8e2f462aa0245b812c00 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/112932 Reviewed-by: Wyatt Hepler <[email protected]> Reviewed-by: Ben Lawson <[email protected]> Reviewed-by: Marie Janssen <[email protected]> Commit-Queue: Alex Deymo <[email protected]>
- Loading branch information
Showing
7 changed files
with
391 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
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
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,46 @@ | ||
// Copyright 2022 The Pigweed Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
// use this file except in compliance with the License. You may obtain a copy of | ||
// the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations under | ||
// the License. | ||
#pragma once | ||
|
||
#include <cstdint> | ||
|
||
namespace pw::bluetooth::internal { | ||
|
||
// Parse a hexadecimal character to a 0-15 number. If the hex char is invalid | ||
// returns 0x100. | ||
constexpr uint16_t HexToNibble(char hex) { | ||
if (hex >= '0' && hex <= '9') { | ||
return hex - '0'; | ||
} | ||
if (hex >= 'A' && hex <= 'F') { | ||
return hex - 'A' + 10; | ||
} | ||
if (hex >= 'a' && hex <= 'f') { | ||
return hex - 'a' + 10; | ||
} | ||
return 0x100; | ||
} | ||
|
||
// Convert a nibble (0 to 15 value) to its lowercase hexadecimal representation. | ||
constexpr char NibbleToHex(uint8_t value) { | ||
if (value < 10) { | ||
return '0' + value; | ||
} | ||
if (value < 16) { | ||
return 'a' + value - 10; | ||
} | ||
return '?'; | ||
} | ||
|
||
} // namespace pw::bluetooth::internal |
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,198 @@ | ||
// Copyright 2022 The Pigweed Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
// use this file except in compliance with the License. You may obtain a copy of | ||
// the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations under | ||
// the License. | ||
#pragma once | ||
|
||
#include <array> | ||
#include <climits> | ||
#include <cstdint> | ||
|
||
#include "pw_assert/assert.h" | ||
#include "pw_bluetooth/internal/hex.h" | ||
#include "pw_span/span.h" | ||
#include "pw_string/string.h" | ||
|
||
namespace pw::bluetooth { | ||
|
||
// A 128-bit Universally Unique Identifier (UUID). | ||
// See Core Spec v5.3 Volume 3, Part B, Section 2.5.1. | ||
// | ||
// Bluetooth defines 16-bit, 32-bit and 128-bit UUID representations for a | ||
// 128-bit UUID, all of which are used in the protocol. 16-bit UUIDs values | ||
// define only the "YYYY" portion in the following UUID pattern (with XXXX set | ||
// as 0), while 32-bit UUID value define the "XXXXYYYY" portion. When using | ||
// these short UUIDs, the remaining bits are set by the Bluetooth_Base_UUID as | ||
// follows: | ||
// XXXXYYYY-0000-1000-8000-00805f9b34fb | ||
// | ||
// This class always stores UUID in their 128-bit representation in little | ||
// endian format. | ||
class Uuid { | ||
public: | ||
// String size of a hexadecimal representation of a UUID, not including the | ||
// null terminator. | ||
static constexpr size_t kHexStringSize = 36; | ||
|
||
// Create a UUID from a span of 128-bit data. UUIDs are represented as | ||
// little endian bytes. | ||
explicit constexpr Uuid(const span<const uint8_t, 16> uuid_span) : uuid_() { | ||
for (size_t i = 0; i < sizeof(uuid_); i++) { | ||
uuid_[i] = uuid_span[i]; | ||
} | ||
} | ||
|
||
// Create a UUID from its string representation. This is parsed manually here | ||
// so it can be parsed at compile time with constexpr. A valid UUID is a hex | ||
// string with hyphen separators at the 9th, 14th, 19th, and 24th | ||
// positions, for example: | ||
// "0000180a-0000-1000-8000-00805f9b34fb" | ||
// The `str` parameter is byte longer than kHexStringSize so it can be | ||
// initialized with literal strings including the null terminator, as in | ||
// BluetoothBase(). | ||
constexpr Uuid(const char (&str)[kHexStringSize + 1]) : uuid_() { | ||
size_t out_hex_index = 2 * sizeof(uuid_); // UUID is stored little-endian. | ||
for (size_t i = 0; i < kHexStringSize; i++) { | ||
// Indices at which we expect to find a hyphen ('-') in a UUID string. | ||
if (i == 8 || i == 13 || i == 18 || i == 23) { | ||
PW_ASSERT(str[i] == '-'); | ||
continue; | ||
} | ||
PW_ASSERT(str[i] != 0); | ||
out_hex_index--; | ||
uint16_t value = internal::HexToNibble(str[i]); | ||
PW_ASSERT(value <= 0xf); | ||
if (out_hex_index % 2 == 0) { | ||
uuid_[out_hex_index / 2] |= value; | ||
} else { | ||
uuid_[out_hex_index / 2] = value << 4; | ||
} | ||
} | ||
} | ||
|
||
// The Bluetooth_Base_UUID defined by the specification. This is the base for | ||
// all 16-bit and 32-bit short UUIDs. | ||
static constexpr const Uuid& BluetoothBase(); | ||
|
||
// Create a UUID combining 96-bits from a base UUID with a 16-bit or 32-bit | ||
// value. 16-bit values will be extended to 32-bit ones, meaning the that the | ||
// 16 most significant bits will be set to 0 regardless of the value on the | ||
// base UUID. | ||
constexpr Uuid(uint32_t short_uuid, const Uuid& base_uuid) | ||
: uuid_(base_uuid.uuid_) { | ||
uuid_[kBaseOffset] = short_uuid & 0xff; | ||
uuid_[kBaseOffset + 1] = (short_uuid >> CHAR_BIT) & 0xff; | ||
uuid_[kBaseOffset + 2] = (short_uuid >> CHAR_BIT * 2) & 0xff; | ||
uuid_[kBaseOffset + 3] = (short_uuid >> CHAR_BIT * 3) & 0xff; | ||
} | ||
|
||
// Create a short UUID (32-bit or 16-bit) using the standard Bluetooth base | ||
// UUID. | ||
explicit constexpr Uuid(uint32_t short_uuid) | ||
: Uuid(short_uuid, BluetoothBase()) {} | ||
|
||
constexpr Uuid(const Uuid&) = default; | ||
constexpr Uuid& operator=(const Uuid&) = default; | ||
|
||
// Return a 2-byte span containing the 16-bit little endian representation of | ||
// the UUID. This is useful when Same112BitBase(BluetoothBase()) is true. | ||
constexpr span<const uint8_t, 2> As16BitSpan() const { | ||
return span<const uint8_t, 2>{uuid_.data() + kBaseOffset, 2u}; | ||
} | ||
|
||
// Return a 4-byte span containing the 32-bit little endian representation of | ||
// the UUID. This is useful when Same96BitBase(BluetoothBase()) is true. | ||
constexpr span<const uint8_t, 4> As32BitSpan() const { | ||
return span<const uint8_t, 4>{uuid_.data() + kBaseOffset, 4u}; | ||
} | ||
|
||
// Return the 128-bit (16-byte) little endian representation of the UUID. | ||
constexpr span<const uint8_t, 16> As128BitSpan() const { | ||
return span<const uint8_t, 16>{uuid_.data(), 16u}; | ||
} | ||
|
||
// Return whether the UUID shares the same 112-bit base with another UUID. | ||
// Sharing the same 112-bit base with BluetoothBase() means that this UUID | ||
// can be resented as a 16-bit UUID. | ||
constexpr bool Same112BitBase(const Uuid& other) const { | ||
return Same96BitBase(other) && uuid_[14] == other.uuid_[14] && | ||
uuid_[15] == other.uuid_[15]; | ||
} | ||
|
||
// Return whether the UUID shares the same 96-bit base with another UUID. | ||
// Sharing the same 96-bit base with BluetoothBase() means that this UUID | ||
// can be resented as a 32-bit UUID. | ||
constexpr bool Same96BitBase(const Uuid& other) const { | ||
for (size_t i = 0; i < 12; i++) { | ||
if (uuid_[i] != other.uuid_[i]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
// Return whether the UUID is a 16-bit UUID represented as 128-bit using the | ||
// BluetoothBase() as the base. | ||
constexpr bool Is16BitUuid() const { return Same112BitBase(BluetoothBase()); } | ||
|
||
// Return whether the UUID is a 32-bit UUID represented as 128-bit using the | ||
// BluetoothBase() as the base. | ||
constexpr bool Is32BitUuid() const { return Same96BitBase(BluetoothBase()); } | ||
|
||
// Return an inline pw_string representation of the UUID in hexadecimal. | ||
constexpr InlineString<kHexStringSize> ToString() const { | ||
InlineString<kHexStringSize> ret; | ||
for (size_t i = uuid_.size(); i-- != 0;) { | ||
ret += internal::NibbleToHex(uuid_[i] >> 4); | ||
ret += internal::NibbleToHex(uuid_[i] & 0xf); | ||
if ((i == 12) || (i == 10) || (i == 8) || (i == 6)) { | ||
ret += '-'; | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
private: | ||
// Offset at which the short 16-bit and 32-bit UUID little-endian data starts | ||
// in the uuid_ array. | ||
static constexpr size_t kBaseOffset = 12; | ||
|
||
std::array<uint8_t, 16> uuid_; | ||
}; | ||
|
||
namespace internal { | ||
// When BluetoothBase() is used in constexpr expressions it would normally be | ||
// evaluated to a final different Uuid, such as when used in Uuid(uint32_t), | ||
// however if a reference to the return value of BluetoothBase() is needed this | ||
// variable would be the only global symbol that provides it even if it is used | ||
// from multiple translation units. | ||
constexpr Uuid kBluetoothBaseUuid{"00000000-0000-1000-8000-00805F9B34FB"}; | ||
} // namespace internal | ||
|
||
inline constexpr const Uuid& Uuid::BluetoothBase() { | ||
return internal::kBluetoothBaseUuid; | ||
} | ||
|
||
// Uuid comparators: | ||
constexpr bool operator==(const Uuid& a, const Uuid& b) { | ||
const auto a_span = a.As128BitSpan(); | ||
const auto b_span = b.As128BitSpan(); | ||
for (size_t i = 0; i < a_span.size(); i++) { | ||
if (a_span[i] != b_span[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
constexpr bool operator!=(const Uuid& a, const Uuid& b) { return !(a == b); } | ||
|
||
} // namespace pw::bluetooth |
Oops, something went wrong.