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