273 lines · 9.9 KB · C++
RawDownload
////  PacketList.cpp//////  Created by Clement on 7/13/15.//  Copyright 2015 High Fidelity, Inc.//  Copyright 2021 Vircadia contributors.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "PacketList.h" #include "../NetworkLogging.h" #include <chrono>#include <QDebug> using namespace udt; int packetListMetaTypeId = qRegisterMetaType<PacketList*>("PacketList*"); std::unique_ptr<PacketList> PacketList::create(PacketType packetType, QByteArray extendedHeader,                                               bool isReliable, bool isOrdered) {    auto packetList = std::unique_ptr<PacketList>(new PacketList(packetType, extendedHeader,                                                                 isReliable, isOrdered));    packetList->open(WriteOnly);    return packetList;} std::unique_ptr<PacketList> PacketList::fromReceivedPackets(std::list<std::unique_ptr<Packet>>&& packets) {    auto packetList = std::unique_ptr<PacketList>(new PacketList(PacketType::Unknown, QByteArray(), true, true));    packetList->_packets = std::move(packets);    packetList->open(ReadOnly);    return packetList;} PacketList::PacketList(PacketType packetType, QByteArray extendedHeader, bool isReliable, bool isOrdered) :    _packetType(packetType),    _isOrdered(isOrdered),    _isReliable(isReliable),    _extendedHeader(extendedHeader){    Q_ASSERT_X(!(!_isReliable && _isOrdered), "PacketList", "Unreliable ordered PacketLists are not currently supported");} PacketList::PacketList(PacketList&& other) :    _packetType(other._packetType),    _packets(std::move(other._packets)),    _isOrdered(other._isOrdered),    _isReliable(other._isReliable),    _extendedHeader(std::move(other._extendedHeader)){} SockAddr PacketList::getSenderSockAddr() const {    return _packets.size() > 0 ? _packets.front()->getSenderSockAddr() : SockAddr();} void PacketList::startSegment() {    _segmentStartIndex = _currentPacket ? _currentPacket->pos() : _extendedHeader.size();} void PacketList::endSegment() {    _segmentStartIndex = -1;} size_t PacketList::getDataSize() const {    size_t totalBytes = 0;    for (const auto& packet : _packets) {        totalBytes += packet->getDataSize();    }     if (_currentPacket) {        totalBytes += _currentPacket->getDataSize();    }     return totalBytes;} size_t PacketList::getMessageSize() const {    size_t totalBytes = 0;    for (const auto& packet: _packets) {        totalBytes += packet->getPayloadSize();    }        if (_currentPacket) {        totalBytes += _currentPacket->getPayloadSize();    }        return totalBytes;} std::unique_ptr<Packet> PacketList::createPacket() {    // use the static create method to create a new packet    // If this packet list is supposed to be ordered then we consider this to be part of a message    bool isPartOfMessage = _isOrdered;    return Packet::create(-1, _isReliable, isPartOfMessage);} std::unique_ptr<Packet> PacketList::createPacketWithExtendedHeader() {    // use the static create method to create a new packet    auto packet = createPacket();     if (!_extendedHeader.isEmpty()) {        // add the extended header to the front of the packet        if (packet->write(_extendedHeader) == -1) {            qCDebug(networking) << "Could not write extendedHeader in PacketList::createPacketWithExtendedHeader"                << "- make sure that _extendedHeader is not larger than the payload capacity.";        }    }     return packet;} void PacketList::closeCurrentPacket(bool shouldSendEmpty) {    if (shouldSendEmpty && !_currentPacket && _packets.empty()) {        _currentPacket = createPacketWithExtendedHeader();    }        if (_currentPacket) {        // move the current packet to our list of packets        _packets.push_back(std::move(_currentPacket));    }} QByteArray PacketList::getMessage() const {    size_t sizeBytes = 0;     for (const auto& packet : _packets) {        sizeBytes += packet->size();    }     QByteArray data;    data.reserve((int)sizeBytes);     for (auto& packet : _packets) {        data.append(packet->getPayload(), packet->getPayloadSize());    }     return data;} void PacketList::preparePackets(MessageNumber messageNumber) {    Q_ASSERT(_packets.size() > 0);        if (_packets.size() == 1) {        _packets.front()->writeMessageNumber(messageNumber, Packet::PacketPosition::ONLY, 0);    } else {        const auto second = ++_packets.begin();        const auto last = --_packets.end();        Packet::MessagePartNumber messagePartNumber = 0;        std::for_each(second, last, [&](const PacketPointer& packet) {            packet->writeMessageNumber(messageNumber, Packet::PacketPosition::MIDDLE, ++messagePartNumber);        });                _packets.front()->writeMessageNumber(messageNumber, Packet::PacketPosition::FIRST, 0);        _packets.back()->writeMessageNumber(messageNumber, Packet::PacketPosition::LAST, ++messagePartNumber);    }} const qint64 PACKET_LIST_WRITE_ERROR = -1; qint64 PacketList::writeString(const QString& string) {    QByteArray data = string.toUtf8();    writePrimitive(static_cast<uint32_t>(data.length()));    return writeData(data.constData(), data.length());} qint64 PacketList::writeData(const char* data, qint64 maxSize) {    auto sizeRemaining = maxSize;     while (sizeRemaining > 0) {        if (!_currentPacket) {            // we don't have a current packet, time to set one up            _currentPacket = createPacketWithExtendedHeader();        }         // check if this block of data can fit into the currentPacket        if (sizeRemaining <= _currentPacket->bytesAvailableForWrite()) {            // it fits, just write it to the current packet            _currentPacket->write(data, sizeRemaining);             sizeRemaining = 0;        } else {            // it does not fit - this may need to be in the next packet             if (!_isOrdered) {                auto newPacket = createPacketWithExtendedHeader();                 if (_segmentStartIndex >= 0) {                    // We in the process of writing a segment for an unordered PacketList.                    // We need to try and pull the first part of the segment out to our new packet                     // check now to see if this is an unsupported write                    int segmentSize = _currentPacket->pos() - _segmentStartIndex;                                        if (segmentSize + sizeRemaining > newPacket->getPayloadCapacity()) {                        // this is an unsupported case - the segment is bigger than the size of an individual packet                        // but the PacketList is not going to be sent ordered                        qCDebug(networking) << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is"                            << "larger than the payload size.";                        Q_ASSERT(false);                                                // we won't be writing this new data to the packet                        // go back before the current segment and return -1 to indicate error                        _currentPacket->seek(_segmentStartIndex);                        _currentPacket->setPayloadSize(_segmentStartIndex);                                                return PACKET_LIST_WRITE_ERROR;                    } else {                        // copy from currentPacket where the segment started to the beginning of the newPacket                        newPacket->write(_currentPacket->getPayload() + _segmentStartIndex, segmentSize);                                                // shrink the current payload to the actual size of the packet                        _currentPacket->setPayloadSize(_segmentStartIndex);                                                // the current segment now starts at the beginning of the new packet                        _segmentStartIndex = _extendedHeader.size();                    }                }                                if (sizeRemaining > newPacket->getPayloadCapacity()) {                    // this is an unsupported case - attempting to write a block of data larger                    // than the capacity of a new packet in an unordered PacketList                    qCDebug(networking) << "Error in PacketList::writeData - attempted to write data to an unordered packet that is"                        << "larger than the payload size.";                    Q_ASSERT(false);                                        return PACKET_LIST_WRITE_ERROR;                }                 // move the current packet to our list of packets                _packets.push_back(std::move(_currentPacket));                 // write the data to the newPacket                newPacket->write(data, sizeRemaining);                 // swap our current packet with the new packet                _currentPacket.swap(newPacket);                 // We've written all of the data, so set sizeRemaining to 0                sizeRemaining = 0;            } else {                // we're an ordered PacketList - let's fit what we can into the current packet and then put the leftover                // into a new packet                 int numBytesToEnd = _currentPacket->bytesAvailableForWrite();                _currentPacket->write(data, numBytesToEnd);                 // Remove number of bytes written from sizeRemaining                sizeRemaining -= numBytesToEnd;                 // Move the data pointer forward                data += numBytesToEnd;                 // move the current packet to our list of packets                _packets.push_back(std::move(_currentPacket));            }        }    }     return maxSize;} p_high_resolution_clock::time_point PacketList::getFirstPacketReceiveTime() const {    using namespace std::chrono;;    if (!_packets.empty()) {        return _packets.front()->getReceiveTime();    }    return p_high_resolution_clock::time_point();}