1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include <optional> 18 #include <string> 19 #include <unordered_set> 20 21 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 22 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h" 23 24 namespace bt { 25 26 // Use raw, non-class enum to explicitly enable usage of enum values as numeric 27 // sizes. 28 enum UUIDElemSize : uint8_t { k16Bit = 2, k32Bit = 4, k128Bit = 16 }; 29 30 // Represents a 128-bit Bluetooth UUID. This class allows UUID values to be 31 // constructed in the official Bluetooth 16-bit, 32-bit, and 128-bit formats and 32 // to be compared against any other Bluetooth UUID. 33 class UUID final { 34 public: 35 // Constructs a UUID from |bytes|. |bytes| should contain a 16-, 32-, or 36 // 128-bit UUID in little-endian byte order. Returns false if |bytes| contains 37 // an unsupported size. 38 static bool FromBytes(const ByteBuffer& bytes, UUID* out_uuid); 39 40 // Returns a random (Version 4) UUID. 41 static UUID Generate(); 42 43 // The default constructor initializes all values to zero. 44 constexpr UUID() = default; 45 46 // Constructs a UUID from |bytes|. This is similar to FromBytes, except it 47 // asserts if |bytes| has an unsupported size. 48 explicit UUID(const ByteBuffer& bytes); 49 UUID(const UInt128 & uuid128)50 constexpr explicit UUID(const UInt128& uuid128) : value_(uuid128) { 51 if (!IsValueCompressable()) 52 return; 53 54 if (value_[kBaseOffset + 2] == 0 && value_[kBaseOffset + 3] == 0) { 55 type_ = Type::k16Bit; 56 } else { 57 type_ = Type::k32Bit; 58 } 59 } 60 UUID(const uint16_t uuid16)61 constexpr explicit UUID(const uint16_t uuid16) 62 : type_(Type::k16Bit), value_(BuildSIGUUID(uuid16)) {} 63 UUID(const uint32_t uuid32)64 constexpr explicit UUID(const uint32_t uuid32) 65 : type_(uuid32 > std::numeric_limits<uint16_t>::max() ? Type::k32Bit 66 : Type::k16Bit), 67 value_(BuildSIGUUID(uuid32)) {} 68 69 // Equality operators. 70 bool operator==(const UUID& uuid) const; 71 bool operator==(uint16_t uuid16) const; 72 bool operator==(uint32_t uuid32) const; 73 bool operator==(const UInt128& uuid128) const; 74 bool operator!=(const UUID& uuid) const { return !(*this == uuid); } 75 bool operator!=(uint16_t uuid16) const { return !(*this == uuid16); } 76 bool operator!=(uint32_t uuid32) const { return !(*this == uuid32); } 77 bool operator!=(const UInt128& uuid128) const { return !(*this == uuid128); } 78 79 // Compares a UUID with the contents of a raw buffer in little-endian byte 80 // order. This is useful for making a direct comparison with UUIDs received 81 // over PDUs. Returns false if |bytes| has an unaccepted size; the only 82 // accepted sizes for are 2, 4, and 16 for 16-bit, 32-bit, and 128-bit 83 // formats, respectively. 84 bool CompareBytes(const ByteBuffer& bytes) const; 85 86 // Returns a string representation of this UUID in the following format: 87 // 88 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 89 // 90 // where x is one of the alphanumeric characters in the string 91 // 0123456789abcdef. 92 std::string ToString() const; 93 94 // Returns the number of bytes required to store this UUID. Returns 16 (i.e. 95 // 128 bits) if |allow_32bit| is false and the compact size is 4 bytes (i.e. 96 // 32 bits). 97 UUIDElemSize CompactSize(bool allow_32bit = true) const; 98 99 // Writes a little-endian representation of this UUID to |buffer|. Returns 100 // the number of bytes used. there must be enough space in |buffer| to store 101 // |CompactSize()| bytes. 102 size_t ToBytes(MutableByteBuffer* bytes, bool allow_32bit = true) const; 103 104 // Returns the most compact representation of this UUID. If |allow_32bit| is 105 // false, then a 32-bit UUIDs will default to 128-bit. The contents will be in 106 // little-endian order. 107 // 108 // Unlike ToBytes(), this does not copy. Since the returned view does not own 109 // its data, it should not outlive this UUID instance. 110 BufferView CompactView(bool allow_32bit = true) const; 111 112 // Returns a hash of this UUID. 113 std::size_t Hash() const; 114 115 // Returns the underlying value in little-endian byte order. value()116 const UInt128& value() const { return value_; } 117 118 std::optional<uint16_t> As16Bit() const; 119 std::optional<uint32_t> As32Bit() const; 120 121 // We store the type that this was initialized with to allow quick comparison 122 // with short Bluetooth SIG UUIDs. 123 enum class Type : uint8_t { 124 k16Bit, 125 k32Bit, 126 k128Bit, 127 }; 128 129 // The type (size) of this UUID type()130 Type type() const { return type_; } 131 132 private: 133 // The Bluetooth Base UUID defines the first value in the range reserved 134 // by the Bluetooth SIG for often-used and officially registered UUIDs. This 135 // UUID is defined as 136 // 137 // "00000000-0000-1000-8000-00805F9B34FB" 138 // 139 // (see Core Spec v5.0, Vol 3, Part B, Section 2.5.1) 140 static constexpr UInt128 kBaseUuid = {{0xFB, 141 0x34, 142 0x9B, 143 0x5F, 144 0x80, 145 0x00, 146 0x00, 147 0x80, 148 0x00, 149 0x10, 150 0x00, 151 0x00, 152 0x00, 153 0x00, 154 0x00, 155 0x00}}; 156 157 // A 16-bit or 32-bit UUID can be converted to a 128-bit UUID using the 158 // following formula: 159 // 160 // 16-/32-bit value * 2^96 + Bluetooth_Base_UUID 161 // 162 // This is the equivalent of modifying the higher order bytes of the base UUID 163 // starting at octet 12 (96 bits = 12 bytes). 164 // 165 // (see Core Spec v5.0, Vol 3, Part B, Section 2.5.1) 166 static constexpr size_t kBaseOffset = 12; 167 168 // Returns a 128-bit SIG UUID from the given 16-bit value. BuildSIGUUID(const uint16_t uuid16)169 static constexpr UInt128 BuildSIGUUID(const uint16_t uuid16) { 170 return BuildSIGUUID(static_cast<uint32_t>(uuid16)); 171 } 172 173 // Returns a 128-bit SIG UUID from the given 32-bit value. BuildSIGUUID(const uint32_t uuid32)174 static constexpr UInt128 BuildSIGUUID(const uint32_t uuid32) { 175 UInt128 result(kBaseUuid); 176 result[kBaseOffset] = static_cast<uint8_t>(uuid32); 177 result[kBaseOffset + 1] = static_cast<uint8_t>(uuid32 >> 8); 178 result[kBaseOffset + 2] = static_cast<uint8_t>(uuid32 >> 16); 179 result[kBaseOffset + 3] = static_cast<uint8_t>(uuid32 >> 24); 180 return result; 181 } 182 183 // Returns true if the contents of |value_| represents a UUID in the SIG 184 // reserved range. IsValueCompressable()185 constexpr bool IsValueCompressable() const { 186 // C++14 allows for-loops in constexpr functions. 187 for (size_t i = 0; i < kBaseOffset; i++) { 188 if (kBaseUuid[i] != value_[i]) 189 return false; 190 } 191 return true; 192 } 193 194 // If a quick conversion is possible, these return the 16 or 32 bit values of 195 // the UUID in host byte order. 196 uint16_t ValueAs16Bit() const; 197 uint32_t ValueAs32Bit() const; 198 199 Type type_ = Type::k128Bit; 200 UInt128 value_ alignas(size_t) = {}; 201 }; 202 203 // Returns true if the given |uuid_string| contains a valid UUID in the 204 // following format: 205 // 206 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 207 // 208 // where x is one of the alphanumeric characters in the string 209 // 0123456789abcdefABCDEF. 210 bool IsStringValidUuid(const std::string& uuid_string); 211 212 // Constructs a 128-bit UUID from a string representation in one of the 213 // following formats: 214 // 215 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (full UUID string) 216 // xxxx (abbreviated 16-bit UUID) 217 // 218 // where x is one of the alphanumeric characters in the string 219 // 0123456789abcdefABCDEF. 220 // 221 // Returns false if the string does not represent a valid Bluetooth UUID. 222 // Otherwise returns true and populates |out_uuid|. 223 bool StringToUuid(const std::string& uuid_string, UUID* out_uuid); 224 225 // Equality operators 226 inline bool operator==(uint16_t lhs, const UUID& rhs) { return rhs == lhs; } 227 228 inline bool operator==(uint32_t lhs, const UUID& rhs) { return rhs == lhs; } 229 230 inline bool operator==(const UInt128& lhs, const UUID& rhs) { 231 return rhs == lhs; 232 } 233 234 inline bool operator!=(uint16_t lhs, const UUID& rhs) { return rhs != lhs; } 235 236 inline bool operator!=(uint32_t lhs, const UUID& rhs) { return rhs != lhs; } 237 238 inline bool operator!=(const UInt128& lhs, const UUID& rhs) { 239 return rhs != lhs; 240 } 241 242 } // namespace bt 243 244 // Specialization of std::hash for std::unordered_set, std::unordered_map, etc. 245 namespace std { 246 247 template <> 248 struct hash<bt::UUID> { 249 size_t operator()(const bt::UUID& k) const { return k.Hash(); } 250 }; 251 252 } // namespace std 253