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