xref: /aosp_15_r20/external/pigweed/pw_bluetooth/public/pw_bluetooth/uuid.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 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 #pragma once
15 
16 #include <array>
17 #include <climits>
18 #include <cstdint>
19 
20 #include "pw_assert/assert.h"
21 #include "pw_bluetooth/internal/hex.h"
22 #include "pw_span/span.h"
23 #include "pw_string/string.h"
24 
25 namespace pw::bluetooth {
26 
27 // A 128-bit Universally Unique Identifier (UUID).
28 // See Core Spec v5.3 Volume 3, Part B, Section 2.5.1.
29 //
30 // Bluetooth defines 16-bit, 32-bit and 128-bit UUID representations for a
31 // 128-bit UUID, all of which are used in the protocol. 16-bit UUIDs values
32 // define only the "YYYY" portion in the following UUID pattern (with XXXX set
33 // as 0), while 32-bit UUID value define the "XXXXYYYY" portion. When using
34 // these short UUIDs, the remaining bits are set by the Bluetooth_Base_UUID as
35 // follows:
36 //   XXXXYYYY-0000-1000-8000-00805f9b34fb
37 //
38 // This class always stores UUID in their 128-bit representation in little
39 // endian format.
40 class Uuid {
41  public:
42   // String size of a hexadecimal representation of a UUID, not including the
43   // null terminator.
44   static constexpr size_t kHexStringSize = 36;
45 
46   // Create a UUID from a span of 128-bit data. UUIDs are represented as
47   // little endian bytes.
Uuid(const span<const uint8_t,16> uuid_span)48   explicit constexpr Uuid(const span<const uint8_t, 16> uuid_span) : uuid_() {
49     for (size_t i = 0; i < sizeof(uuid_); i++) {
50       uuid_[i] = uuid_span[i];
51     }
52   }
53 
54   // Create a UUID from its string representation. This is parsed manually here
55   // so it can be parsed at compile time with constexpr. A valid UUID is a hex
56   // string with hyphen separators at the 9th, 14th, 19th, and 24th
57   // positions, for example:
58   //   "0000180a-0000-1000-8000-00805f9b34fb"
59   // The `str` parameter is byte longer than kHexStringSize so it can be
60   // initialized with literal strings including the null terminator, as in
61   // BluetoothBase().
Uuid(const char (& str)[kHexStringSize+1])62   constexpr Uuid(const char (&str)[kHexStringSize + 1]) : uuid_() {
63     size_t out_hex_index = 2 * sizeof(uuid_);  // UUID is stored little-endian.
64     for (size_t i = 0; i < kHexStringSize; i++) {
65       // Indices at which we expect to find a hyphen ('-') in a UUID string.
66       if (i == 8 || i == 13 || i == 18 || i == 23) {
67         PW_ASSERT(str[i] == '-');
68         continue;
69       }
70       PW_ASSERT(str[i] != 0);
71       out_hex_index--;
72       uint16_t value = internal::HexToNibble(str[i]);
73       PW_ASSERT(value <= 0xf);
74       if (out_hex_index % 2 == 0) {
75         uuid_[out_hex_index / 2] |= value;
76       } else {
77         uuid_[out_hex_index / 2] = value << 4;
78       }
79     }
80   }
81 
82   // The Bluetooth_Base_UUID defined by the specification. This is the base for
83   // all 16-bit and 32-bit short UUIDs.
84   static constexpr const Uuid& BluetoothBase();
85 
Uuid()86   constexpr Uuid() : uuid_() {}
87 
88   // Create a UUID combining 96-bits from a base UUID with a 16-bit or 32-bit
89   // value. 16-bit values will be extended to 32-bit ones, meaning the that the
90   // 16 most significant bits will be set to 0 regardless of the value on the
91   // base UUID.
Uuid(uint32_t short_uuid,const Uuid & base_uuid)92   constexpr Uuid(uint32_t short_uuid, const Uuid& base_uuid)
93       : uuid_(base_uuid.uuid_) {
94     uuid_[kBaseOffset] = short_uuid & 0xff;
95     uuid_[kBaseOffset + 1] = (short_uuid >> CHAR_BIT) & 0xff;
96     uuid_[kBaseOffset + 2] = (short_uuid >> CHAR_BIT * 2) & 0xff;
97     uuid_[kBaseOffset + 3] = (short_uuid >> CHAR_BIT * 3) & 0xff;
98   }
99 
100   // Create a short UUID (32-bit or 16-bit) using the standard Bluetooth base
101   // UUID.
Uuid(uint32_t short_uuid)102   explicit constexpr Uuid(uint32_t short_uuid)
103       : Uuid(short_uuid, BluetoothBase()) {}
104 
105   constexpr Uuid(const Uuid&) = default;
106   constexpr Uuid& operator=(const Uuid&) = default;
107 
108   // Return a 2-byte span containing the 16-bit little endian representation of
109   // the UUID. This is useful when Same112BitBase(BluetoothBase()) is true.
As16BitSpan()110   constexpr span<const uint8_t, 2> As16BitSpan() const {
111     return span<const uint8_t, 2>{uuid_.data() + kBaseOffset, 2u};
112   }
113 
114   // Return a 4-byte span containing the 32-bit little endian representation of
115   // the UUID. This is useful when Same96BitBase(BluetoothBase()) is true.
As32BitSpan()116   constexpr span<const uint8_t, 4> As32BitSpan() const {
117     return span<const uint8_t, 4>{uuid_.data() + kBaseOffset, 4u};
118   }
119 
120   // Return the 128-bit (16-byte) little endian representation of the UUID.
As128BitSpan()121   constexpr span<const uint8_t, 16> As128BitSpan() const {
122     return span<const uint8_t, 16>{uuid_.data(), 16u};
123   }
124 
125   // Return whether the UUID shares the same 112-bit base with another UUID.
126   // Sharing the same 112-bit base with BluetoothBase() means that this UUID
127   // can be resented as a 16-bit UUID.
Same112BitBase(const Uuid & other)128   constexpr bool Same112BitBase(const Uuid& other) const {
129     return Same96BitBase(other) && uuid_[14] == other.uuid_[14] &&
130            uuid_[15] == other.uuid_[15];
131   }
132 
133   // Return whether the UUID shares the same 96-bit base with another UUID.
134   // Sharing the same 96-bit base with BluetoothBase() means that this UUID
135   // can be resented as a 32-bit UUID.
Same96BitBase(const Uuid & other)136   constexpr bool Same96BitBase(const Uuid& other) const {
137     for (size_t i = 0; i < 12; i++) {
138       if (uuid_[i] != other.uuid_[i])
139         return false;
140     }
141     return true;
142   }
143 
144   // Return whether the UUID is a 16-bit UUID represented as 128-bit using the
145   // BluetoothBase() as the base.
Is16BitUuid()146   constexpr bool Is16BitUuid() const { return Same112BitBase(BluetoothBase()); }
147 
148   // Return whether the UUID is a 32-bit UUID represented as 128-bit using the
149   // BluetoothBase() as the base.
Is32BitUuid()150   constexpr bool Is32BitUuid() const { return Same96BitBase(BluetoothBase()); }
151 
152   // Return an inline pw_string representation of the UUID in hexadecimal.
ToString()153   constexpr InlineString<kHexStringSize> ToString() const {
154     InlineString<kHexStringSize> ret;
155     for (size_t i = uuid_.size(); i-- != 0;) {
156       ret += internal::NibbleToHex(uuid_[i] >> 4);
157       ret += internal::NibbleToHex(uuid_[i] & 0xf);
158       if ((i == 12) || (i == 10) || (i == 8) || (i == 6)) {
159         ret += '-';
160       }
161     }
162     return ret;
163   }
164 
165  private:
166   // Offset at which the short 16-bit and 32-bit UUID little-endian data starts
167   // in the uuid_ array.
168   static constexpr size_t kBaseOffset = 12;
169 
170   std::array<uint8_t, 16> uuid_;
171 };
172 
173 namespace internal {
174 // When BluetoothBase() is used in constexpr expressions it would normally be
175 // evaluated to a final different Uuid, such as when used in Uuid(uint32_t),
176 // however if a reference to the return value of BluetoothBase() is needed this
177 // variable would be the only global symbol that provides it even if it is used
178 // from multiple translation units.
179 constexpr Uuid kBluetoothBaseUuid{"00000000-0000-1000-8000-00805F9B34FB"};
180 }  // namespace internal
181 
BluetoothBase()182 inline constexpr const Uuid& Uuid::BluetoothBase() {
183   return internal::kBluetoothBaseUuid;
184 }
185 
186 // Uuid comparators:
187 constexpr bool operator==(const Uuid& a, const Uuid& b) {
188   const auto a_span = a.As128BitSpan();
189   const auto b_span = b.As128BitSpan();
190   for (size_t i = 0; i < a_span.size(); i++) {
191     if (a_span[i] != b_span[i]) {
192       return false;
193     }
194   }
195   return true;
196 }
197 
198 constexpr bool operator!=(const Uuid& a, const Uuid& b) { return !(a == b); }
199 
200 }  // namespace pw::bluetooth
201