• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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