blob: ac052bd8f038334aaf29aa4f2b1bf8b2831c6bc8 [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 "pdu.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_packet.h"
namespace bt {
namespace l2cap {
PDU::PDU() : fragment_count_(0u) {}
// NOTE: The order in which these are initialized matters, as
// other.ReleaseFragments() resets |other.fragment_count_|.
PDU::PDU(PDU&& other)
: fragment_count_(other.fragment_count_),
fragments_(other.ReleaseFragments()) {}
PDU& PDU::operator=(PDU&& other) {
// NOTE: The order in which these are initialized matters, as
// other.ReleaseFragments() resets |other.fragment_count_|.
fragment_count_ = other.fragment_count_;
fragments_ = other.ReleaseFragments();
return *this;
}
size_t PDU::Copy(MutableByteBuffer* out_buffer, size_t pos, size_t size) const {
ZX_DEBUG_ASSERT(out_buffer);
ZX_DEBUG_ASSERT(pos <= length());
ZX_DEBUG_ASSERT(is_valid());
size_t remaining = std::min(size, length() - pos);
ZX_DEBUG_ASSERT(out_buffer->size() >= remaining);
if (!remaining) {
return 0;
}
bool found = false;
size_t offset = 0u;
for (auto iter = fragments_.begin(); iter != fragments_.end() && remaining;
++iter) {
auto payload = iter->view().payload_data();
// Skip the Basic L2CAP header for the first fragment.
if (iter == fragments_.begin()) {
payload = payload.view(sizeof(BasicHeader));
}
// We first find the beginning fragment based on |pos|.
if (!found) {
size_t fragment_size = payload.size();
if (pos >= fragment_size) {
pos -= fragment_size;
continue;
}
// The beginning fragment has been found.
found = true;
}
// Calculate how much to read from the current fragment
size_t write_size = std::min(payload.size() - pos, remaining);
// Read the fragment into out_buffer->mutable_data() + offset.
out_buffer->Write(payload.data() + pos, write_size, offset);
// Clear |pos| after using it on the first fragment as all successive
// fragments are read from the beginning.
if (pos)
pos = 0u;
offset += write_size;
remaining -= write_size;
}
return offset;
}
PDU::FragmentList PDU::ReleaseFragments() {
auto out_list = std::move(fragments_);
fragment_count_ = 0u;
ZX_DEBUG_ASSERT(!is_valid());
return out_list;
}
const BasicHeader& PDU::basic_header() const {
ZX_DEBUG_ASSERT(!fragments_.is_empty());
const auto& fragment = *fragments_.begin();
ZX_DEBUG_ASSERT(fragment.packet_boundary_flag() !=
hci::ACLPacketBoundaryFlag::kContinuingFragment);
return fragment.view().payload<BasicHeader>();
}
void PDU::AppendFragment(hci::ACLDataPacketPtr fragment) {
ZX_DEBUG_ASSERT(fragment);
ZX_DEBUG_ASSERT(!is_valid() || fragments_.begin()->connection_handle() ==
fragment->connection_handle());
fragments_.push_back(std::move(fragment));
fragment_count_++;
}
} // namespace l2cap
} // namespace bt