blob: 1241199a36f77345208f6e438c711967a4f5191f [file] [log] [blame]
// 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