xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/common/uuid.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
16 
17 #include <pw_bytes/endian.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
22 #include "pw_string/format.h"
23 
24 namespace bt {
FromBytes(const ByteBuffer & bytes,UUID * out_uuid)25 bool UUID::FromBytes(const ByteBuffer& bytes, UUID* out_uuid) {
26   switch (bytes.size()) {
27     case UUIDElemSize::k16Bit: {
28       uint16_t dst;
29       memcpy(&dst, bytes.data(), sizeof(dst));
30       *out_uuid = UUID(pw::bytes::ConvertOrderFrom(cpp20::endian::little, dst));
31       return true;
32     }
33     case UUIDElemSize::k32Bit: {
34       uint32_t dst;
35       memcpy(&dst, bytes.data(), sizeof(dst));
36       *out_uuid = UUID(pw::bytes::ConvertOrderFrom(cpp20::endian::little, dst));
37       return true;
38     }
39     case UUIDElemSize::k128Bit: {
40       UInt128 dst;
41       memcpy(dst.data(), bytes.data(), sizeof(dst));
42       *out_uuid = UUID(dst);
43       return true;
44     }
45   }
46 
47   return false;
48 }
49 
Generate()50 UUID UUID::Generate() {
51   // We generate a 128-bit random UUID in the form of version 4 as described in
52   // ITU-T Rec. X.667(10/2012) Sec 15.1. This is the same as RFC 4122.
53   UInt128 uuid = Random<UInt128>();
54   //  Set the four most significant bits (bits 15 through 12) of the
55   //  "VersionAndTimeHigh" field to 4.
56   constexpr uint8_t version_number = 0b0100'0000;
57   uuid[6] = (uuid[6] & 0b0000'1111) | version_number;
58   // Set the two most significant bits (bits 7 and 6) of the
59   // "VariantAndClockSeqHigh" field to 1 and 0, respectively.
60   uuid[8] = (uuid[8] & 0b0011'1111) | 0b1000'0000;
61   return UUID(uuid);
62 }
63 
UUID(const ByteBuffer & bytes)64 UUID::UUID(const ByteBuffer& bytes) {
65   bool result = FromBytes(bytes, this);
66   PW_CHECK(result, "|bytes| must contain a 16, 32, or 128-bit UUID");
67 }
68 
operator ==(const UUID & uuid) const69 bool UUID::operator==(const UUID& uuid) const { return value_ == uuid.value_; }
70 
operator ==(uint16_t uuid16) const71 bool UUID::operator==(uint16_t uuid16) const {
72   if (type_ == Type::k16Bit)
73     return uuid16 == ValueAs16Bit();
74 
75   // Quick conversion is not possible; compare as two 128-bit UUIDs.
76   return *this == UUID(uuid16);
77 }
78 
operator ==(uint32_t uuid32) const79 bool UUID::operator==(uint32_t uuid32) const {
80   if (type_ != Type::k128Bit)
81     return uuid32 == ValueAs32Bit();
82 
83   // Quick conversion is not possible; compare as two 128-bit UUIDs.
84   return *this == UUID(uuid32);
85 }
86 
operator ==(const UInt128 & uuid128) const87 bool UUID::operator==(const UInt128& uuid128) const {
88   return value_ == uuid128;
89 }
90 
CompareBytes(const ByteBuffer & bytes) const91 bool UUID::CompareBytes(const ByteBuffer& bytes) const {
92   UUID other;
93   if (!FromBytes(bytes, &other)) {
94     return false;
95   }
96   return *this == other;
97 }
98 
ToString() const99 std::string UUID::ToString() const {
100   char out[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
101   pw::StatusWithSize result = pw::string::Format(
102       {out, sizeof(out)},
103       "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
104       value_[15],
105       value_[14],
106       value_[13],
107       value_[12],
108       value_[11],
109       value_[10],
110       value_[9],
111       value_[8],
112       value_[7],
113       value_[6],
114       value_[5],
115       value_[4],
116       value_[3],
117       value_[2],
118       value_[1],
119       value_[0]);
120   PW_DCHECK(result.ok());
121   return out;
122 }
123 
CompactSize(bool allow_32bit) const124 UUIDElemSize UUID::CompactSize(bool allow_32bit) const {
125   switch (type_) {
126     case Type::k16Bit:
127       return UUIDElemSize::k16Bit;
128     case Type::k32Bit:
129       if (allow_32bit)
130         return UUIDElemSize::k32Bit;
131 
132       // Fall through if 32-bit UUIDs are not allowed.
133       [[fallthrough]];
134     case Type::k128Bit:
135       return UUIDElemSize::k128Bit;
136   };
137   BT_PANIC("uuid type of %du is invalid", static_cast<uint8_t>(type_));
138 }
139 
ToBytes(MutableByteBuffer * bytes,bool allow_32bit) const140 size_t UUID::ToBytes(MutableByteBuffer* bytes, bool allow_32bit) const {
141   size_t size = CompactSize(allow_32bit);
142   size_t offset = (size == UUIDElemSize::k128Bit) ? 0u : kBaseOffset;
143   bytes->Write(value_.data() + offset, size);
144   return size;
145 }
146 
CompactView(bool allow_32bit) const147 BufferView UUID::CompactView(bool allow_32bit) const {
148   size_t size = CompactSize(allow_32bit);
149   size_t offset = (size == UUIDElemSize::k128Bit) ? 0u : kBaseOffset;
150   return BufferView(value_.data() + offset, size);
151 }
152 
Hash() const153 std::size_t UUID::Hash() const {
154   static_assert(sizeof(value_) % sizeof(size_t) == 0);
155   // Morally we'd like to assert this, but:
156   //
157   // 'alignof' applied to an expression is a GNU extension.
158   //
159   // static_assert(alignof(value_) % alignof(size_t) == 0);
160   size_t hash = 0;
161   for (size_t i = 0; i < (sizeof(value_) / sizeof(size_t)); i++) {
162     hash ^=
163         *reinterpret_cast<const size_t*>(value_.data() + (i * sizeof(size_t)));
164   }
165   return hash;
166 }
167 
As16Bit() const168 std::optional<uint16_t> UUID::As16Bit() const {
169   std::optional<uint16_t> ret;
170   if (type_ == Type::k16Bit) {
171     ret = ValueAs16Bit();
172   }
173   return ret;
174 }
175 
ValueAs16Bit() const176 uint16_t UUID::ValueAs16Bit() const {
177   PW_DCHECK(type_ == Type::k16Bit);
178 
179   return pw::bytes::ConvertOrderFrom(
180       cpp20::endian::little,
181       *reinterpret_cast<const uint16_t*>(value_.data() + kBaseOffset));
182 }
183 
ValueAs32Bit() const184 uint32_t UUID::ValueAs32Bit() const {
185   PW_DCHECK(type_ != Type::k128Bit);
186 
187   return pw::bytes::ConvertOrderFrom(
188       cpp20::endian::little,
189       *reinterpret_cast<const uint32_t*>(value_.data() + kBaseOffset));
190 }
191 
192 }  // namespace bt
193