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