overte/libraries/networking/src/udt/LossList.cpp
2015-08-25 17:12:03 -07:00

184 lines
6.4 KiB
C++

//
// LossList.cpp
// libraries/networking/src/udt
//
// Created by Clement on 7/27/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "LossList.h"
#include "ControlPacket.h"
using namespace udt;
using namespace std;
void LossList::append(SequenceNumber seq) {
Q_ASSERT_X(_lossList.empty() || (_lossList.back().second < seq), "LossList::append(SequenceNumber)",
"SequenceNumber appended is not greater than the last SequenceNumber in the list");
if (getLength() > 0 && _lossList.back().second + 1 == seq) {
++_lossList.back().second;
} else {
_lossList.push_back(make_pair(seq, seq));
}
_length += 1;
}
void LossList::append(SequenceNumber start, SequenceNumber end) {
Q_ASSERT_X(_lossList.empty() || (_lossList.back().second < start),
"LossList::append(SequenceNumber, SequenceNumber)",
"SequenceNumber range appended is not greater than the last SequenceNumber in the list");
Q_ASSERT_X(start <= end,
"LossList::append(SequenceNumber, SequenceNumber)", "Range start greater than range end");
if (getLength() > 0 && _lossList.back().second + 1 == start) {
_lossList.back().second = end;
} else {
_lossList.push_back(make_pair(start, end));
}
_length += seqlen(start, end);
}
void LossList::insert(SequenceNumber start, SequenceNumber end) {
Q_ASSERT_X(start <= end,
"LossList::insert(SequenceNumber, SequenceNumber)", "Range start greater than range end");
auto it = find_if_not(_lossList.begin(), _lossList.end(), [&start](pair<SequenceNumber, SequenceNumber> pair){
return pair.second < start;
});
if (it == _lossList.end() || end < it->first) {
// No overlap, simply insert
_length += seqlen(start, end);
_lossList.insert(it, make_pair(start, end));
} else {
// If it starts before segment, extend segment
if (start < it->first) {
_length += seqlen(start, it->first - 1);
it->first = start;
}
// If it ends after segment, extend segment
if (end > it->second) {
_length += seqlen(it->second + 1, end);
it->second = end;
}
auto it2 = it;
++it2;
// For all ranges touching the current range
while (it2 != _lossList.end() && it->second >= it2->first - 1) {
// extend current range if necessary
if (it->second < it2->second) {
_length += seqlen(it->second + 1, it2->second);
it->second = it2->second;
}
// Remove overlapping range
_length -= seqlen(it2->first, it2->second);
it2 = _lossList.erase(it2);
}
}
}
bool LossList::remove(SequenceNumber seq) {
auto it = find_if(_lossList.begin(), _lossList.end(), [&seq](pair<SequenceNumber, SequenceNumber> pair) {
return pair.first <= seq && seq <= pair.second;
});
if (it != end(_lossList)) {
if (it->first == it->second) {
_lossList.erase(it);
} else if (seq == it->first) {
++it->first;
} else if (seq == it->second) {
--it->second;
} else {
auto temp = it->second;
it->second = seq - 1;
_lossList.insert(++it, make_pair(seq + 1, temp));
}
_length -= 1;
// this sequence number was found in the loss list, return true
return true;
} else {
// this sequence number was not found in the loss list, return false
return false;
}
}
void LossList::remove(SequenceNumber start, SequenceNumber end) {
Q_ASSERT_X(start <= end,
"LossList::remove(SequenceNumber, SequenceNumber)", "Range start greater than range end");
// Find the first segment sharing sequence numbers
auto it = find_if(_lossList.begin(), _lossList.end(), [&start, &end](pair<SequenceNumber, SequenceNumber> pair) {
return (pair.first <= start && start <= pair.second) || (start <= pair.first && pair.first <= end);
});
// If we found one
if (it != _lossList.end()) {
// While the end of the current segment is contained, either shorten it (first one only - sometimes)
// or remove it altogether since it is fully contained it the range
while (it != _lossList.end() && end >= it->second) {
if (start <= it->first) {
// Segment is contained, update new length and erase it.
_length -= seqlen(it->first, it->second);
it = _lossList.erase(it);
} else {
// Beginning of segment not contained, modify end of segment.
// Will only occur sometimes one the first loop
_length -= seqlen(start, it->second);
it->second = start - 1;
++it;
}
}
// There might be more to remove
if (it != _lossList.end() && it->first <= end) {
if (start <= it->first) {
// Truncate beginning of segment
_length -= seqlen(it->first, end);
it->first = end + 1;
} else {
// Cut it in half if the range we are removing is contained within one segment
_length -= seqlen(start, end);
auto temp = it->second;
it->second = start - 1;
_lossList.insert(++it, make_pair(end + 1, temp));
}
}
}
}
SequenceNumber LossList::getFirstSequenceNumber() const {
Q_ASSERT_X(getLength() > 0, "LossList::getFirstSequenceNumber()", "Trying to get first element of an empty list");
return _lossList.front().first;
}
SequenceNumber LossList::popFirstSequenceNumber() {
auto front = getFirstSequenceNumber();
remove(front);
return front;
}
void LossList::write(ControlPacket& packet, int maxPairs) {
int writtenPairs = 0;
for (const auto& pair : _lossList) {
packet.writePrimitive(pair.first);
packet.writePrimitive(pair.second);
++writtenPairs;
// check if we've written the maximum number we were told to write
if (maxPairs != -1 && writtenPairs >= maxPairs) {
break;
}
}
}