| // 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 |