blob: 7d84b23d19a6af9db7244ef690412eb4652bce3e [file] [log] [blame]
// Copyright 2020 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/supplement_data.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/log.h"
#pragma clang diagnostic ignored "-Wswitch-enum"
namespace bt {
bool ParseUuids(const BufferView& data,
UUIDElemSize uuid_size,
UuidFunction func) {
BT_ASSERT(func);
if (data.size() % uuid_size) {
return false;
}
size_t uuid_count = data.size() / uuid_size;
for (size_t i = 0; i < uuid_count; i++) {
const BufferView uuid_bytes(data.data() + (i * uuid_size), uuid_size);
UUID uuid;
if (!UUID::FromBytes(uuid_bytes, &uuid) || !func(uuid)) {
return false;
}
}
return true;
}
UUIDElemSize SizeForType(DataType type) {
switch (type) {
case DataType::kIncomplete16BitServiceUuids:
case DataType::kComplete16BitServiceUuids:
case DataType::kServiceData16Bit:
return UUIDElemSize::k16Bit;
case DataType::kIncomplete32BitServiceUuids:
case DataType::kComplete32BitServiceUuids:
case DataType::kServiceData32Bit:
return UUIDElemSize::k32Bit;
case DataType::kIncomplete128BitServiceUuids:
case DataType::kComplete128BitServiceUuids:
case DataType::kServiceData128Bit:
return UUIDElemSize::k128Bit;
default:
break;
};
BT_PANIC("called SizeForType with non-UUID DataType %du",
static_cast<uint8_t>(type));
return UUIDElemSize::k16Bit;
}
SupplementDataReader::SupplementDataReader(const ByteBuffer& data)
: is_valid_(true), remaining_(data) {
if (!remaining_.size()) {
is_valid_ = false;
return;
}
// Do a validity check.
BufferView tmp(remaining_);
while (tmp.size()) {
size_t tlv_len = tmp[0];
// A struct can have 0 as its length. In that case its valid to terminate.
if (!tlv_len)
break;
// The full struct includes the length octet itself.
size_t struct_size = tlv_len + 1;
if (struct_size > tmp.size()) {
is_valid_ = false;
break;
}
tmp = tmp.view(struct_size);
}
}
bool SupplementDataReader::GetNextField(DataType* out_type,
BufferView* out_data) {
BT_DEBUG_ASSERT(out_type);
BT_DEBUG_ASSERT(out_data);
if (!HasMoreData())
return false;
size_t tlv_len = remaining_[0];
size_t cur_struct_size = tlv_len + 1;
BT_DEBUG_ASSERT(cur_struct_size <= remaining_.size());
*out_type = static_cast<DataType>(remaining_[1]);
*out_data = remaining_.view(2, tlv_len - 1);
// Update |remaining_|.
remaining_ = remaining_.view(cur_struct_size);
return true;
}
bool SupplementDataReader::HasMoreData() const {
if (!is_valid_ || !remaining_.size())
return false;
// If the buffer is valid and has remaining bytes but the length of the next
// segment is zero, then we terminate.
return !!remaining_[0];
}
SupplementDataWriter::SupplementDataWriter(MutableByteBuffer* buffer)
: buffer_(buffer), bytes_written_(0u) {
BT_DEBUG_ASSERT(buffer_);
}
bool SupplementDataWriter::WriteField(DataType type, const ByteBuffer& data) {
size_t next_size = data.size() + 2; // 2 bytes for [length][type].
if (bytes_written_ + next_size > buffer_->size() || next_size > 255)
return false;
(*buffer_)[bytes_written_++] = static_cast<uint8_t>(next_size) - 1;
(*buffer_)[bytes_written_++] = static_cast<uint8_t>(type);
// Get a view into the offset we want to write to.
auto target = buffer_->mutable_view(bytes_written_);
// Copy the data into the view.
data.Copy(&target);
bytes_written_ += data.size();
return true;
}
} // namespace bt