overte-HifiExperiments/libraries/networking/src/udt/PacketList.cpp
2021-07-06 13:23:57 +12:00

273 lines
No EOL
9.9 KiB
C++

//
// 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();
}