mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-05 21:12:42 +02:00
273 lines
No EOL
9.9 KiB
C++
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();
|
|
} |