#include "platform_sys.h"
#include "list.h"
#include "packet.h"
#include "logging.h"
namespace srt_logging
{
extern Logger qrlog;
extern Logger qslog;
}
using srt_logging::qrlog;
using srt_logging::qslog;
using namespace srt::sync;
CSndLossList::CSndLossList(int size)
: m_caSeq()
, m_iHead(-1)
, m_iLength(0)
, m_iSize(size)
, m_iLastInsertPos(-1)
, m_ListLock()
{
m_caSeq = new Seq[size];
for (int i = 0; i < size; ++i)
{
m_caSeq[i].seqstart = SRT_SEQNO_NONE;
m_caSeq[i].seqend = SRT_SEQNO_NONE;
}
setupMutex(m_ListLock, "LossList");
}
CSndLossList::~CSndLossList()
{
delete[] m_caSeq;
releaseMutex(m_ListLock);
}
void CSndLossList::traceState() const
{
int pos = m_iHead;
while (pos != SRT_SEQNO_NONE)
{
::cout << pos << ":[" << m_caSeq[pos].seqstart;
if (m_caSeq[pos].seqend != SRT_SEQNO_NONE)
::cout << ", " << m_caSeq[pos].seqend;
::cout << "], ";
pos = m_caSeq[pos].inext;
}
::cout << "\n";
}
int CSndLossList::insert(int32_t seqno1, int32_t seqno2)
{
ScopedLock listguard(m_ListLock);
if (m_iLength == 0)
{
insertHead(0, seqno1, seqno2);
return m_iLength;
}
const int origlen = m_iLength;
const int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1);
int loc = (m_iHead + offset + m_iSize) % m_iSize;
if (loc < 0)
{
const int offset_seqno2 = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno2);
const int loc_seqno2 = (m_iHead + offset_seqno2 + m_iSize) % m_iSize;
if (loc_seqno2 < 0)
{
LOGC(qslog.Error, log << "IPE: New loss record is too old. Ignoring. "
<< "First loss seqno " << m_caSeq[m_iHead].seqstart
<< ", insert seqno " << seqno1 << ":" << seqno2);
return 0;
}
loc = loc_seqno2;
}
if (offset < 0)
{
insertHead(loc, seqno1, seqno2);
}
else if (offset > 0)
{
if (seqno1 == m_caSeq[loc].seqstart)
{
const bool updated = updateElement(loc, seqno1, seqno2);
if (!updated)
return 0;
}
else
{
int i = m_iHead;
if ((m_iLastInsertPos != -1) && (CSeqNo::seqcmp(m_caSeq[m_iLastInsertPos].seqstart, seqno1) < 0))
i = m_iLastInsertPos;
while (m_caSeq[i].inext != -1 && CSeqNo::seqcmp(m_caSeq[m_caSeq[i].inext].seqstart, seqno1) < 0)
i = m_caSeq[i].inext;
const int seqend = m_caSeq[i].seqend == SRT_SEQNO_NONE ? m_caSeq[i].seqstart : m_caSeq[i].seqend;
if (CSeqNo::seqcmp(seqend, seqno1) < 0 && CSeqNo::incseq(seqend) != seqno1)
{
insertAfter(loc, i, seqno1, seqno2);
}
else
{
m_iLastInsertPos = i;
if (CSeqNo::seqcmp(seqend, seqno2) >= 0)
return 0;
m_iLength += CSeqNo::seqlen(seqend, seqno2) - 1;
m_caSeq[i].seqend = seqno2;
loc = i;
}
}
}
else {
const bool updated = updateElement(m_iHead, seqno1, seqno2);
if (!updated)
return 0;
}
coalesce(loc);
return m_iLength - origlen;
}
void CSndLossList::removeUpTo(int32_t seqno)
{
ScopedLock listguard(m_ListLock);
if (0 == m_iLength)
return;
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno);
int loc = (m_iHead + offset + m_iSize) % m_iSize;
if (0 == offset)
{
loc = (loc + 1) % m_iSize;
if (SRT_SEQNO_NONE == m_caSeq[m_iHead].seqend)
loc = m_caSeq[m_iHead].inext;
else
{
m_caSeq[loc].seqstart = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[m_iHead].seqend, CSeqNo::incseq(seqno)) > 0)
m_caSeq[loc].seqend = m_caSeq[m_iHead].seqend;
m_caSeq[m_iHead].seqend = SRT_SEQNO_NONE;
m_caSeq[loc].inext = m_caSeq[m_iHead].inext;
}
m_caSeq[m_iHead].seqstart = SRT_SEQNO_NONE;
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
m_iHead = loc;
m_iLength--;
}
else if (offset > 0)
{
int h = m_iHead;
if (seqno == m_caSeq[loc].seqstart)
{
int temp = loc;
loc = (loc + 1) % m_iSize;
if (SRT_SEQNO_NONE == m_caSeq[temp].seqend)
m_iHead = m_caSeq[temp].inext;
else
{
m_caSeq[loc].seqstart = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[temp].seqend, m_caSeq[loc].seqstart) > 0)
m_caSeq[loc].seqend = m_caSeq[temp].seqend;
m_iHead = loc;
m_caSeq[loc].inext = m_caSeq[temp].inext;
m_caSeq[temp].inext = loc;
m_caSeq[temp].seqend = SRT_SEQNO_NONE;
}
}
else
{
int i = m_iHead;
while ((-1 != m_caSeq[i].inext) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].inext].seqstart, seqno) < 0))
i = m_caSeq[i].inext;
loc = (loc + 1) % m_iSize;
if (SRT_SEQNO_NONE == m_caSeq[i].seqend)
m_iHead = m_caSeq[i].inext;
else if (CSeqNo::seqcmp(m_caSeq[i].seqend, seqno) > 0)
{
m_caSeq[loc].seqstart = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[i].seqend, m_caSeq[loc].seqstart) > 0)
m_caSeq[loc].seqend = m_caSeq[i].seqend;
m_caSeq[i].seqend = seqno;
m_caSeq[loc].inext = m_caSeq[i].inext;
m_caSeq[i].inext = loc;
m_iHead = loc;
}
else
m_iHead = m_caSeq[i].inext;
}
while (h != m_iHead)
{
if (m_caSeq[h].seqend != SRT_SEQNO_NONE)
{
m_iLength -= CSeqNo::seqlen(m_caSeq[h].seqstart, m_caSeq[h].seqend);
m_caSeq[h].seqend = SRT_SEQNO_NONE;
}
else
m_iLength--;
m_caSeq[h].seqstart = SRT_SEQNO_NONE;
if (m_iLastInsertPos == h)
m_iLastInsertPos = -1;
h = m_caSeq[h].inext;
}
}
}
int CSndLossList::getLossLength() const
{
ScopedLock listguard(m_ListLock);
return m_iLength;
}
int32_t CSndLossList::popLostSeq()
{
ScopedLock listguard(m_ListLock);
if (0 == m_iLength)
{
SRT_ASSERT(m_iHead == -1);
return SRT_SEQNO_NONE;
}
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
const int32_t seqno = m_caSeq[m_iHead].seqstart;
if (SRT_SEQNO_NONE == m_caSeq[m_iHead].seqend)
{
m_caSeq[m_iHead].seqstart = SRT_SEQNO_NONE;
m_iHead = m_caSeq[m_iHead].inext;
}
else
{
int loc = (m_iHead + 1) % m_iSize;
m_caSeq[loc].seqstart = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[m_iHead].seqend, m_caSeq[loc].seqstart) > 0)
m_caSeq[loc].seqend = m_caSeq[m_iHead].seqend;
m_caSeq[m_iHead].seqstart = SRT_SEQNO_NONE;
m_caSeq[m_iHead].seqend = SRT_SEQNO_NONE;
m_caSeq[loc].inext = m_caSeq[m_iHead].inext;
m_iHead = loc;
}
m_iLength--;
return seqno;
}
void CSndLossList::insertHead(int pos, int32_t seqno1, int32_t seqno2)
{
SRT_ASSERT(pos >= 0);
m_caSeq[pos].seqstart = seqno1;
SRT_ASSERT(m_caSeq[pos].seqend == SRT_SEQNO_NONE);
if (seqno2 != seqno1)
m_caSeq[pos].seqend = seqno2;
m_caSeq[pos].inext = m_iHead;
m_iHead = pos;
m_iLastInsertPos = pos;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
void CSndLossList::insertAfter(int pos, int pos_after, int32_t seqno1, int32_t seqno2)
{
m_caSeq[pos].seqstart = seqno1;
SRT_ASSERT(m_caSeq[pos].seqend == SRT_SEQNO_NONE);
if (seqno2 != seqno1)
m_caSeq[pos].seqend = seqno2;
m_caSeq[pos].inext = m_caSeq[pos_after].inext;
m_caSeq[pos_after].inext = pos;
m_iLastInsertPos = pos;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
void CSndLossList::coalesce(int loc)
{
while ((m_caSeq[loc].inext != -1) && (m_caSeq[loc].seqend != SRT_SEQNO_NONE))
{
const int i = m_caSeq[loc].inext;
if (CSeqNo::seqcmp(m_caSeq[i].seqstart, CSeqNo::incseq(m_caSeq[loc].seqend)) > 0)
break;
if (m_caSeq[i].seqend != SRT_SEQNO_NONE)
{
if (CSeqNo::seqcmp(m_caSeq[i].seqend, m_caSeq[loc].seqend) > 0)
{
if (CSeqNo::seqcmp(m_caSeq[loc].seqend, m_caSeq[i].seqstart) >= 0)
m_iLength -= CSeqNo::seqlen(m_caSeq[i].seqstart, m_caSeq[loc].seqend);
m_caSeq[loc].seqend = m_caSeq[i].seqend;
}
else
m_iLength -= CSeqNo::seqlen(m_caSeq[i].seqstart, m_caSeq[i].seqend);
}
else
{
if (m_caSeq[i].seqstart == CSeqNo::incseq(m_caSeq[loc].seqend))
m_caSeq[loc].seqend = m_caSeq[i].seqstart;
else
m_iLength--;
}
m_caSeq[i].seqstart = SRT_SEQNO_NONE;
m_caSeq[i].seqend = SRT_SEQNO_NONE;
m_caSeq[loc].inext = m_caSeq[i].inext;
}
}
bool CSndLossList::updateElement(int pos, int32_t seqno1, int32_t seqno2)
{
m_iLastInsertPos = pos;
if (seqno2 == SRT_SEQNO_NONE || seqno2 == seqno1)
return false;
if (m_caSeq[pos].seqend == SRT_SEQNO_NONE)
{
m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
m_caSeq[pos].seqend = seqno2;
return true;
}
if (CSeqNo::seqcmp(seqno2, m_caSeq[pos].seqend) <= 0)
return false;
m_iLength += CSeqNo::seqlen(m_caSeq[pos].seqend, seqno2) - 1;
m_caSeq[pos].seqend = seqno2;
return true;
}
CRcvLossList::CRcvLossList(int size)
: m_caSeq()
, m_iHead(-1)
, m_iTail(-1)
, m_iLength(0)
, m_iSize(size)
{
m_caSeq = new Seq[m_iSize];
for (int i = 0; i < size; ++i)
{
m_caSeq[i].seqstart = SRT_SEQNO_NONE;
m_caSeq[i].seqend = SRT_SEQNO_NONE;
}
}
CRcvLossList::~CRcvLossList()
{
delete[] m_caSeq;
}
void CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
{
if (0 == m_iLength)
{
m_iHead = 0;
m_iTail = 0;
m_caSeq[m_iHead].seqstart = seqno1;
if (seqno2 != seqno1)
m_caSeq[m_iHead].seqend = seqno2;
m_caSeq[m_iHead].inext = -1;
m_caSeq[m_iHead].iprior = -1;
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
return;
}
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno1);
if (offset < 0)
{
LOGC(qrlog.Error,
log << "RCV-LOSS/insert: IPE: new LOSS %(" << seqno1 << "-" << seqno2 << ") PREDATES HEAD %"
<< m_caSeq[m_iHead].seqstart << " -- REJECTING");
return;
}
int loc = (m_iHead + offset) % m_iSize;
if ((SRT_SEQNO_NONE != m_caSeq[m_iTail].seqend) && (CSeqNo::incseq(m_caSeq[m_iTail].seqend) == seqno1))
{
loc = m_iTail;
m_caSeq[loc].seqend = seqno2;
}
else
{
m_caSeq[loc].seqstart = seqno1;
if (seqno2 != seqno1)
m_caSeq[loc].seqend = seqno2;
m_caSeq[m_iTail].inext = loc;
m_caSeq[loc].iprior = m_iTail;
m_caSeq[loc].inext = -1;
m_iTail = loc;
}
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
}
bool CRcvLossList::remove(int32_t seqno)
{
if (0 == m_iLength)
return false;
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].seqstart, seqno);
if (offset < 0)
return false;
int loc = (m_iHead + offset) % m_iSize;
if (seqno == m_caSeq[loc].seqstart)
{
if (SRT_SEQNO_NONE == m_caSeq[loc].seqend)
{
if (m_iHead == loc)
{
m_iHead = m_caSeq[m_iHead].inext;
if (-1 != m_iHead)
m_caSeq[m_iHead].iprior = -1;
}
else
{
m_caSeq[m_caSeq[loc].iprior].inext = m_caSeq[loc].inext;
if (-1 != m_caSeq[loc].inext)
m_caSeq[m_caSeq[loc].inext].iprior = m_caSeq[loc].iprior;
else
m_iTail = m_caSeq[loc].iprior;
}
m_caSeq[loc].seqstart = SRT_SEQNO_NONE;
}
else
{
int i = (loc + 1) % m_iSize;
m_caSeq[i].seqstart = CSeqNo::incseq(m_caSeq[loc].seqstart);
if (CSeqNo::seqcmp(m_caSeq[loc].seqend, CSeqNo::incseq(m_caSeq[loc].seqstart)) > 0)
m_caSeq[i].seqend = m_caSeq[loc].seqend;
m_caSeq[loc].seqstart = SRT_SEQNO_NONE;
m_caSeq[loc].seqend = SRT_SEQNO_NONE;
m_caSeq[i].inext = m_caSeq[loc].inext;
m_caSeq[i].iprior = m_caSeq[loc].iprior;
if (m_iHead == loc)
m_iHead = i;
else
m_caSeq[m_caSeq[i].iprior].inext = i;
if (m_iTail == loc)
m_iTail = i;
else
m_caSeq[m_caSeq[i].inext].iprior = i;
}
m_iLength--;
return true;
}
int i = (loc - 1 + m_iSize) % m_iSize;
while (SRT_SEQNO_NONE == m_caSeq[i].seqstart)
i = (i - 1 + m_iSize) % m_iSize;
if ((SRT_SEQNO_NONE == m_caSeq[i].seqend) || (CSeqNo::seqcmp(seqno, m_caSeq[i].seqend) > 0))
return false;
if (seqno == m_caSeq[i].seqend)
{
if (seqno == CSeqNo::incseq(m_caSeq[i].seqstart))
m_caSeq[i].seqend = SRT_SEQNO_NONE;
else
m_caSeq[i].seqend = CSeqNo::decseq(seqno);
}
else
{
loc = (loc + 1) % m_iSize;
m_caSeq[loc].seqstart = CSeqNo::incseq(seqno);
if (CSeqNo::seqcmp(m_caSeq[i].seqend, m_caSeq[loc].seqstart) > 0)
m_caSeq[loc].seqend = m_caSeq[i].seqend;
if (seqno == CSeqNo::incseq(m_caSeq[i].seqstart))
m_caSeq[i].seqend = SRT_SEQNO_NONE;
else
m_caSeq[i].seqend = CSeqNo::decseq(seqno);
m_caSeq[loc].inext = m_caSeq[i].inext;
m_caSeq[i].inext = loc;
m_caSeq[loc].iprior = i;
if (m_iTail == i)
m_iTail = loc;
else
m_caSeq[m_caSeq[loc].inext].iprior = loc;
}
m_iLength--;
return true;
}
bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2)
{
if (seqno1 <= seqno2)
{
for (int32_t i = seqno1; i <= seqno2; ++i)
remove(i);
}
else
{
for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++j)
remove(j);
for (int32_t k = 0; k <= seqno2; ++k)
remove(k);
}
return true;
}
bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const
{
if (0 == m_iLength)
return false;
int p = m_iHead;
while (-1 != p)
{
if ((CSeqNo::seqcmp(m_caSeq[p].seqstart, seqno1) == 0) ||
((CSeqNo::seqcmp(m_caSeq[p].seqstart, seqno1) > 0) && (CSeqNo::seqcmp(m_caSeq[p].seqstart, seqno2) <= 0)) ||
((CSeqNo::seqcmp(m_caSeq[p].seqstart, seqno1) < 0) && (m_caSeq[p].seqend != SRT_SEQNO_NONE) &&
CSeqNo::seqcmp(m_caSeq[p].seqend, seqno1) >= 0))
return true;
p = m_caSeq[p].inext;
}
return false;
}
int CRcvLossList::getLossLength() const
{
return m_iLength;
}
int32_t CRcvLossList::getFirstLostSeq() const
{
if (0 == m_iLength)
return SRT_SEQNO_NONE;
return m_caSeq[m_iHead].seqstart;
}
void CRcvLossList::getLossArray(int32_t* array, int& len, int limit)
{
len = 0;
int i = m_iHead;
while ((len < limit - 1) && (-1 != i))
{
array[len] = m_caSeq[i].seqstart;
if (SRT_SEQNO_NONE != m_caSeq[i].seqend)
{
array[len] |= LOSSDATA_SEQNO_RANGE_FIRST;
++len;
array[len] = m_caSeq[i].seqend;
}
++len;
i = m_caSeq[i].inext;
}
}
CRcvFreshLoss::CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_age)
: ttl(initial_age)
, timestamp(steady_clock::now())
{
seq[0] = seqlo;
seq[1] = seqhi;
}
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t sequence)
{
int32_t diffbegin = CSeqNo::seqcmp(sequence, seq[0]);
int32_t diffend = CSeqNo::seqcmp(sequence, seq[1]);
if (diffbegin < 0 || diffend > 0)
{
return NONE; }
if (diffbegin == 0)
{
if (diffend == 0) {
return DELETE;
}
seq[0] = CSeqNo::incseq(seq[0]);
return STRIPPED;
}
if (diffend == 0) {
seq[1] = CSeqNo::decseq(seq[1]);
return STRIPPED;
}
return SPLIT;
}
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t lo, int32_t hi)
{
if (CSeqNo::seqcmp(lo, seq[1]) > 0)
return DELETE;
if (CSeqNo::seqcmp(hi, seq[0]) < 0)
return NONE;
if (CSeqNo::seqcmp(hi, seq[1]) < 0)
{
seq[0] = CSeqNo::incseq(hi);
return STRIPPED;
}
return DELETE;
}