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 120 private: 121 // The Bluetooth Base UUID defines the first value in the range reserved 122 // by the Bluetooth SIG for often-used and officially registered UUIDs. This 123 // UUID is defined as 124 // 125 // "00000000-0000-1000-8000-00805F9B34FB" 126 // 127 // (see Core Spec v5.0, Vol 3, Part B, Section 2.5.1) 128 static constexpr UInt128 kBaseUuid = {{0xFB, 129 0x34, 130 0x9B, 131 0x5F, 132 0x80, 133 0x00, 134 0x00, 135 0x80, 136 0x00, 137 0x10, 138 0x00, 139 0x00, 140 0x00, 141 0x00, 142 0x00, 143 0x00}}; 144 145 // A 16-bit or 32-bit UUID can be converted to a 128-bit UUID using the 146 // following formula: 147 // 148 // 16-/32-bit value * 2^96 + Bluetooth_Base_UUID 149 // 150 // This is the equivalent of modifying the higher order bytes of the base UUID 151 // starting at octet 12 (96 bits = 12 bytes). 152 // 153 // (see Core Spec v5.0, Vol 3, Part B, Section 2.5.1) 154 static constexpr size_t kBaseOffset = 12; 155 156 // Returns a 128-bit SIG UUID from the given 16-bit value. BuildSIGUUID(const uint16_t uuid16)157 static constexpr UInt128 BuildSIGUUID(const uint16_t uuid16) { 158 return BuildSIGUUID(static_cast<uint32_t>(uuid16)); 159 } 160 161 // Returns a 128-bit SIG UUID from the given 32-bit value. BuildSIGUUID(const uint32_t uuid32)162 static constexpr UInt128 BuildSIGUUID(const uint32_t uuid32) { 163 UInt128 result(kBaseUuid); 164 result[kBaseOffset] = static_cast<uint8_t>(uuid32); 165 result[kBaseOffset + 1] = static_cast<uint8_t>(uuid32 >> 8); 166 result[kBaseOffset + 2] = static_cast<uint8_t>(uuid32 >> 16); 167 result[kBaseOffset + 3] = static_cast<uint8_t>(uuid32 >> 24); 168 return result; 169 } 170 171 // Returns true if the contents of |value_| represents a UUID in the SIG 172 // reserved range. IsValueCompressable()173 constexpr bool IsValueCompressable() const { 174 // C++14 allows for-loops in constexpr functions. 175 for (size_t i = 0; i < kBaseOffset; i++) { 176 if (kBaseUuid[i] != value_[i]) 177 return false; 178 } 179 return true; 180 } 181 182 // We store the type that this was initialized with to allow quick comparison 183 // with short Bluetooth SIG UUIDs. 184 enum class Type : uint8_t { 185 k16Bit, 186 k32Bit, 187 k128Bit, 188 }; 189 190 // If a quick conversion is possible, these return the 16 or 32 bit values of 191 // the UUID in host byte order. 192 uint16_t ValueAs16Bit() const; 193 uint32_t ValueAs32Bit() const; 194 195 Type type_ = Type::k128Bit; 196 UInt128 value_ alignas(size_t) = {}; 197 }; 198 199 // Returns true if the given |uuid_string| contains a valid UUID in the 200 // following format: 201 // 202 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 203 // 204 // where x is one of the alphanumeric characters in the string 205 // 0123456789abcdefABCDEF. 206 bool IsStringValidUuid(const std::string& uuid_string); 207 208 // Constructs a 128-bit UUID from a string representation in one of the 209 // following formats: 210 // 211 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (full UUID string) 212 // xxxx (abbreviated 16-bit UUID) 213 // 214 // where x is one of the alphanumeric characters in the string 215 // 0123456789abcdefABCDEF. 216 // 217 // Returns false if the string does not represent a valid Bluetooth UUID. 218 // Otherwise returns true and populates |out_uuid|. 219 bool StringToUuid(const std::string& uuid_string, UUID* out_uuid); 220 221 // Equality operators 222 inline bool operator==(uint16_t lhs, const UUID& rhs) { return rhs == lhs; } 223 224 inline bool operator==(uint32_t lhs, const UUID& rhs) { return rhs == lhs; } 225 226 inline bool operator==(const UInt128& lhs, const UUID& rhs) { 227 return rhs == lhs; 228 } 229 230 inline bool operator!=(uint16_t lhs, const UUID& rhs) { return rhs != lhs; } 231 232 inline bool operator!=(uint32_t lhs, const UUID& rhs) { return rhs != lhs; } 233 234 inline bool operator!=(const UInt128& lhs, const UUID& rhs) { 235 return rhs != lhs; 236 } 237 238 } // namespace bt 239 240 // Specialization of std::hash for std::unordered_set, std::unordered_map, etc. 241 namespace std { 242 243 template <> 244 struct hash<bt::UUID> { 245 size_t operator()(const bt::UUID& k) const { return k.Hash(); } 246 }; 247 248 } // namespace std 249