| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/uuid.h" |
| |
| #include <endian.h> |
| |
| #include "pw_string/format.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/random.h" |
| |
| namespace bt { |
| bool UUID::FromBytes(const ByteBuffer& bytes, UUID* out_uuid) { |
| switch (bytes.size()) { |
| case UUIDElemSize::k16Bit: { |
| uint16_t dst; |
| memcpy(&dst, bytes.data(), sizeof(dst)); |
| *out_uuid = UUID(le16toh(dst)); |
| return true; |
| } |
| case UUIDElemSize::k32Bit: { |
| uint32_t dst; |
| memcpy(&dst, bytes.data(), sizeof(dst)); |
| *out_uuid = UUID(le32toh(dst)); |
| return true; |
| } |
| case UUIDElemSize::k128Bit: { |
| UInt128 dst; |
| memcpy(dst.data(), bytes.data(), sizeof(dst)); |
| *out_uuid = UUID(dst); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| UUID UUID::Generate() { |
| // We generate a 128-bit random UUID in the form of version 4 as described in |
| // ITU-T Rec. X.667(10/2012) Sec 15.1. This is the same as RFC 4122. |
| UInt128 uuid = Random<UInt128>(); |
| // Set the four most significant bits (bits 15 through 12) of the |
| // "VersionAndTimeHigh" field to 4. |
| constexpr uint8_t version_number = 0b0100'0000; |
| uuid[6] = (uuid[6] & 0b0000'1111) | version_number; |
| // Set the two most significant bits (bits 7 and 6) of the |
| // "VariantAndClockSeqHigh" field to 1 and 0, respectively. |
| uuid[8] = (uuid[8] & 0b0011'1111) | 0b1000'0000; |
| return UUID(uuid); |
| } |
| |
| UUID::UUID(const ByteBuffer& bytes) { |
| bool result = FromBytes(bytes, this); |
| BT_ASSERT_MSG(result, "|bytes| must contain a 16, 32, or 128-bit UUID"); |
| } |
| |
| bool UUID::operator==(const UUID& uuid) const { return value_ == uuid.value_; } |
| |
| bool UUID::operator==(uint16_t uuid16) const { |
| if (type_ == Type::k16Bit) |
| return uuid16 == ValueAs16Bit(); |
| |
| // Quick conversion is not possible; compare as two 128-bit UUIDs. |
| return *this == UUID(uuid16); |
| } |
| |
| bool UUID::operator==(uint32_t uuid32) const { |
| if (type_ != Type::k128Bit) |
| return uuid32 == ValueAs32Bit(); |
| |
| // Quick conversion is not possible; compare as two 128-bit UUIDs. |
| return *this == UUID(uuid32); |
| } |
| |
| bool UUID::operator==(const UInt128& uuid128) const { |
| return value_ == uuid128; |
| } |
| |
| bool UUID::CompareBytes(const ByteBuffer& bytes) const { |
| UUID other; |
| if (!FromBytes(bytes, &other)) { |
| return false; |
| } |
| return *this == other; |
| } |
| |
| std::string UUID::ToString() const { |
| char out[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]; |
| pw::StatusWithSize result = pw::string::Format( |
| {out, sizeof(out)}, |
| "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
| value_[15], |
| value_[14], |
| value_[13], |
| value_[12], |
| value_[11], |
| value_[10], |
| value_[9], |
| value_[8], |
| value_[7], |
| value_[6], |
| value_[5], |
| value_[4], |
| value_[3], |
| value_[2], |
| value_[1], |
| value_[0]); |
| BT_DEBUG_ASSERT(result.ok()); |
| return out; |
| } |
| |
| UUIDElemSize UUID::CompactSize(bool allow_32bit) const { |
| switch (type_) { |
| case Type::k16Bit: |
| return UUIDElemSize::k16Bit; |
| case Type::k32Bit: |
| if (allow_32bit) |
| return UUIDElemSize::k32Bit; |
| |
| // Fall through if 32-bit UUIDs are not allowed. |
| [[fallthrough]]; |
| case Type::k128Bit: |
| return UUIDElemSize::k128Bit; |
| }; |
| BT_PANIC("uuid type of %du is invalid", static_cast<uint8_t>(type_)); |
| } |
| |
| size_t UUID::ToBytes(MutableByteBuffer* bytes, bool allow_32bit) const { |
| size_t size = CompactSize(allow_32bit); |
| size_t offset = (size == UUIDElemSize::k128Bit) ? 0u : kBaseOffset; |
| bytes->Write(value_.data() + offset, size); |
| return size; |
| } |
| |
| BufferView UUID::CompactView(bool allow_32bit) const { |
| size_t size = CompactSize(allow_32bit); |
| size_t offset = (size == UUIDElemSize::k128Bit) ? 0u : kBaseOffset; |
| return BufferView(value_.data() + offset, size); |
| } |
| |
| std::size_t UUID::Hash() const { |
| static_assert(sizeof(value_) % sizeof(size_t) == 0); |
| // Morally we'd like to assert this, but: |
| // |
| // 'alignof' applied to an expression is a GNU extension. |
| // |
| // static_assert(alignof(value_) % alignof(size_t) == 0); |
| size_t hash = 0; |
| for (size_t i = 0; i < (sizeof(value_) / sizeof(size_t)); i++) { |
| hash ^= |
| *reinterpret_cast<const size_t*>(value_.data() + (i * sizeof(size_t))); |
| } |
| return hash; |
| } |
| |
| std::optional<uint16_t> UUID::As16Bit() const { |
| std::optional<uint16_t> ret; |
| if (type_ == Type::k16Bit) { |
| ret = ValueAs16Bit(); |
| } |
| return ret; |
| } |
| |
| uint16_t UUID::ValueAs16Bit() const { |
| BT_DEBUG_ASSERT(type_ == Type::k16Bit); |
| |
| return le16toh( |
| *reinterpret_cast<const uint16_t*>(value_.data() + kBaseOffset)); |
| } |
| |
| uint32_t UUID::ValueAs32Bit() const { |
| BT_DEBUG_ASSERT(type_ != Type::k128Bit); |
| |
| return le32toh( |
| *reinterpret_cast<const uint32_t*>(value_.data() + kBaseOffset)); |
| } |
| |
| } // namespace bt |