www.pudn.com > udt.rar > list.cpp
/*****************************************************************************
This file contains the implementation of UDT loss lists and irregular packet
list management modules.
All the lists are static linked lists in ascending order of sequence numbers.
*****************************************************************************/
#include "udt.h"
// Definition of >, <, >=, and <= with sequence number wrap
inline const bool CList::greaterthan(const __int32& seqno1, const __int32& seqno2) const
{
if ((seqno1 > seqno2) && (seqno1 - seqno2 < m_iSeqNoTH))
return true;
if (seqno1 < seqno2 - m_iSeqNoTH)
return true;
return false;
}
inline const bool CList::lessthan(const __int32& seqno1, const __int32& seqno2) const
{
return greaterthan(seqno2, seqno1);
}
inline const bool CList::notlessthan(const __int32& seqno1, const __int32& seqno2) const
{
if (seqno1 == seqno2)
return true;
return greaterthan(seqno1, seqno2);
}
inline const bool CList::notgreaterthan(const __int32& seqno1, const __int32& seqno2) const
{
if (seqno1 == seqno2)
return true;
return lessthan(seqno1, seqno2);
}
// return the distance between two sequence numbers, parameters are pre-checked
inline const __int32 CList::getLength(const __int32& seqno1, const __int32& seqno2) const
{
if (seqno2 >= seqno1)
return seqno2 - seqno1 + 1;
else if (seqno2 < seqno1 - m_iSeqNoTH)
return seqno2 - seqno1 + m_iMaxSeqNo + 1;
else
return 0;
}
//Definition of ++, and -- with sequence number wrap
inline const __int32 CList::incSeqNo(const __int32& seqno) const
{
return (seqno + 1) % m_iMaxSeqNo;
}
inline const __int32 CList::decSeqNo(const __int32& seqno) const
{
return (seqno - 1 + m_iMaxSeqNo) % m_iMaxSeqNo;
}
CSndLossList::CSndLossList(const __int32& size, const __int32& th, const __int32& max):
m_iSize(size)
{
m_iSeqNoTH = th;
m_iMaxSeqNo = max;
m_piData1 = new __int32 [m_iSize];
m_piData2 = new __int32 [m_iSize];
m_piNext = new __int32 [m_iSize];
// -1 means there is no data in the node
for (__int32 i = 0; i < size; ++ i)
{
m_piData1[i] = -1;
m_piData2[i] = -1;
}
m_iLength = 0;
m_iHead = -1;
m_iLastInsertPos = -1;
// sender list needs mutex protection
#ifndef WIN32
pthread_mutex_init(&m_ListLock, 0);
#else
m_ListLock = CreateMutex(NULL, false, NULL);
#endif
}
CSndLossList::~CSndLossList()
{
delete [] m_piData1;
delete [] m_piData2;
delete [] m_piNext;
#ifndef WIN32
pthread_mutex_destroy(&m_ListLock);
#else
CloseHandle(m_ListLock);
#endif
}
__int32 CSndLossList::insert(const __int32& seqno1, const __int32& seqno2)
{
CGuard listguard(m_ListLock);
if (0 == m_iLength)
{
// insert data into an empty list
m_iHead = 0;
m_piData1[m_iHead] = seqno1;
if (seqno2 != seqno1)
m_piData2[m_iHead] = seqno2;
m_piNext[m_iHead] = -1;
m_iLastInsertPos = m_iHead;
m_iLength += getLength(seqno1, seqno2);
return m_iLength;
}
// otherwise find the position where the data can be inserted
__int32 origlen = m_iLength;
__int32 offset = seqno1 - m_piData1[m_iHead];
if (offset < -m_iSeqNoTH)
offset += m_iMaxSeqNo;
else if (offset > m_iSeqNoTH)
offset -= m_iMaxSeqNo;
__int32 loc = (m_iHead + offset + m_iSize) % m_iSize;
if (offset < 0)
{
// Insert data prior to the head pointer
m_piData1[loc] = seqno1;
if (seqno2 != seqno1)
m_piData2[loc] = seqno2;
// new node becomes head
m_piNext[loc] = m_iHead;
m_iHead = loc;
m_iLastInsertPos = loc;
m_iLength += getLength(seqno1, seqno2);
}
else if (offset > 0)
{
if (seqno1 == m_piData1[loc])
{
m_iLastInsertPos = loc;
// first seqno is equivlent, compare the second
if (-1 == m_piData2[loc])
{
if (seqno2 != seqno1)
{
m_iLength += getLength(seqno1, seqno2) - 1;
m_piData2[loc] = seqno2;
}
}
else if (greaterthan(seqno2, m_piData2[loc]))
{
// new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7]
m_iLength += getLength(m_piData2[loc], seqno2) - 1;
m_piData2[loc] = seqno2;
}
else
// Do nothing if it is already there
return 0;
}
else
{
// searching the prior node
__int32 i;
if ((-1 != m_iLastInsertPos) && lessthan(m_piData1[m_iLastInsertPos], seqno1))
i = m_iLastInsertPos;
else
i = m_iHead;
while ((-1 != m_piNext[i]) && lessthan(m_piData1[m_piNext[i]], seqno1))
i = m_piNext[i];
if ((-1 == m_piData2[i]) || lessthan(m_piData2[i], seqno1))
{
m_iLastInsertPos = loc;
// no overlap, create new node
m_piData1[loc] = seqno1;
if (seqno2 != seqno1)
m_piData2[loc] = seqno2;
m_piNext[loc] = m_piNext[i];
m_piNext[i] = loc;
m_iLength += getLength(seqno1, seqno2);
}
else
{
m_iLastInsertPos = i;
// overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7]
if (lessthan(m_piData2[i], seqno2))
{
m_iLength += getLength(m_piData2[i], seqno2) - 1;
m_piData2[i] = seqno2;
loc = i;
}
else
return 0;
}
}
}
else
{
m_iLastInsertPos = m_iHead;
// insert to head node
if (seqno2 != seqno1)
{
if (-1 == m_piData2[loc])
{
m_iLength += getLength(seqno1, seqno2) - 1;
m_piData2[loc] = seqno2;
}
else if (greaterthan(seqno2, m_piData2[loc]))
{
m_iLength += getLength(m_piData2[loc], seqno2) - 1;
m_piData2[loc] = seqno2;
}
else
return 0;
}
else
return 0;
}
// coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9]
while ((-1 != m_piNext[loc]) && (-1 != m_piData2[loc]))
{
__int32 i = m_piNext[loc];
if (notgreaterthan(m_piData1[i], incSeqNo(m_piData2[loc])))
{
// coalesce if there is overlap
if (-1 != m_piData2[i])
{
if (greaterthan(m_piData2[i], m_piData2[loc]))
{
if (notlessthan(m_piData2[loc], m_piData1[i]))
m_iLength -= getLength(m_piData1[i], m_piData2[loc]);
m_piData2[loc] = m_piData2[i];
}
else
m_iLength -= getLength(m_piData1[i], m_piData2[i]);
}
else
{
if (m_piData1[i] == incSeqNo(m_piData2[loc]))
m_piData2[loc] = m_piData1[i];
else
m_iLength --;
}
m_piData1[i] = -1;
m_piData2[i] = -1;
m_piNext[loc] = m_piNext[i];
}
else
break;
}
return m_iLength - origlen;
}
void CSndLossList::remove(const __int32& seqno)
{
CGuard listguard(m_ListLock);
if (0 == m_iLength)
return;
// Remove all from the head pointer to a node with a larger seq. no. or the list is empty
__int32 offset = seqno - m_piData1[m_iHead];
if (offset < -m_iSeqNoTH)
offset += m_iMaxSeqNo;
else if (offset > m_iSeqNoTH)
offset -= m_iMaxSeqNo;
__int32 loc = (m_iHead + offset + m_iSize) % m_iSize;
if (0 == offset)
{
// It is the head. Remove the head and point to the next node
loc = (loc + 1) % m_iSize;
if (-1 == m_piData2[m_iHead])
loc = m_piNext[m_iHead];
else
{
m_piData1[loc] = incSeqNo(seqno);
if (greaterthan(m_piData2[m_iHead], incSeqNo(seqno)))
m_piData2[loc] = m_piData2[m_iHead];
m_piData2[m_iHead] = -1;
m_piNext[loc] = m_piNext[m_iHead];
}
m_piData1[m_iHead] = -1;
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
m_iHead = loc;
m_iLength --;
}
else if (offset > 0)
{
__int32 h = m_iHead;
if (seqno == m_piData1[loc])
{
// target node is not empty, remove part/all of the seqno in the node.
__int32 temp = loc;
loc = (loc + 1) % m_iSize;
if (-1 == m_piData2[temp])
m_iHead = m_piNext[temp];
else
{
// remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3)
m_piData1[loc] = incSeqNo(seqno);
if (greaterthan(m_piData2[temp], incSeqNo(seqno)))
m_piData2[loc] = m_piData2[temp];
m_iHead = loc;
m_piNext[loc] = m_piNext[temp];
m_piNext[temp] = loc;
m_piData2[temp] = -1;
}
}
else
{
// targe node is empty, check prior node
__int32 i = m_iHead;
while ((-1 != m_piNext[i]) && lessthan(m_piData1[m_piNext[i]], seqno))
i = m_piNext[i];
loc = (loc + 1) % m_iSize;
if (-1 == m_piData2[i])
m_iHead = m_piNext[i];
else if (greaterthan(m_piData2[i], seqno))
{
// remove part/all seqno in the prior node
m_piData1[loc] = incSeqNo(seqno);
if (greaterthan(m_piData2[i], incSeqNo(seqno)))
m_piData2[loc] = m_piData2[i];
m_piData2[i] = seqno;
m_piNext[loc] = m_piNext[i];
m_piNext[i] = loc;
m_iHead = loc;
}
else
m_iHead = m_piNext[i];
}
// Remove all nodes prior to the new head
while (h != m_iHead)
{
if (m_piData2[h] != -1)
{
m_iLength -= getLength(m_piData1[h], m_piData2[h]);
m_piData2[h] = -1;
}
else
m_iLength --;
m_piData1[h] = -1;
if (m_iLastInsertPos == h)
m_iLastInsertPos = -1;
h = m_piNext[h];
}
}
}
__int32 CSndLossList::getLossLength()
{
CGuard listguard(m_ListLock);
return m_iLength;
}
__int32 CSndLossList::getLostSeq()
{
if (0 == m_iLength)
return -1;
CGuard listguard(m_ListLock);
if (0 == m_iLength)
return -1;
if (m_iLastInsertPos == m_iHead)
m_iLastInsertPos = -1;
// return the first loss seq. no.
__int32 seqno = m_piData1[m_iHead];
// head moves to the next node
if (-1 == m_piData2[m_iHead])
{
//[3, -1] becomes [], and head moves to next node in the list
m_piData1[m_iHead] = -1;
m_iHead = m_piNext[m_iHead];
}
else
{
// shift to next node, e.g., [3, 7] becomes [], [4, 7]
__int32 loc = (m_iHead + 1) % m_iSize;
m_piData1[loc] = incSeqNo(seqno);
if (greaterthan(m_piData2[m_iHead], incSeqNo(seqno)))
m_piData2[loc] = m_piData2[m_iHead];
m_piData1[m_iHead] = -1;
m_piData2[m_iHead] = -1;
m_piNext[loc] = m_piNext[m_iHead];
m_iHead = loc;
}
m_iLength --;
return seqno;
}
//
CRcvLossList::CRcvLossList(const __int32& size, const __int32& th, const __int32& max):
m_iSize(size)
{
m_iSeqNoTH = th;
m_iMaxSeqNo = max;
m_piData1 = new __int32 [m_iSize];
m_piData2 = new __int32 [m_iSize];
m_pLastFeedbackTime = new timeval [m_iSize];
m_piCount = new __int32 [m_iSize];
m_piNext = new __int32 [m_iSize];
m_piPrior = new __int32 [m_iSize];
// -1 means there is no data in the node
for (__int32 i = 0; i < size; ++ i)
{
m_piData1[i] = -1;
m_piData2[i] = -1;
}
m_iLength = 0;
m_iHead = -1;
m_iTail = -1;
}
CRcvLossList::~CRcvLossList()
{
delete [] m_piData1;
delete [] m_piData2;
delete [] m_pLastFeedbackTime;
delete [] m_piCount;
delete [] m_piNext;
delete [] m_piPrior;
}
void CRcvLossList::insert(const __int32& seqno1, const __int32& seqno2)
{
// Data to be inserted must be larger than all those in the list
// guaranteed by the UDT receiver
if (0 == m_iLength)
{
// insert data into an empty list
m_iHead = 0;
m_iTail = 0;
m_piData1[m_iHead] = seqno1;
if (seqno2 != seqno1)
m_piData2[m_iHead] = seqno2;
gettimeofday(m_pLastFeedbackTime + m_iHead, 0);
m_piCount[m_iHead] = 2;
m_piNext[m_iHead] = -1;
m_piPrior[m_iHead] = -1;
m_iLength += getLength(seqno1, seqno2);
return;
}
// otherwise searching for the position where the node should be
__int32 offset = seqno1 - m_piData1[m_iHead];
if (offset < -m_iSeqNoTH)
offset += m_iMaxSeqNo;
__int32 loc = (m_iHead + offset) % m_iSize;
if ((-1 != m_piData2[m_iTail]) && (incSeqNo(m_piData2[m_iTail]) == seqno1))
{
// coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7]
loc = m_iTail;
m_piData2[loc] = seqno2;
}
else
{
// create new node
m_piData1[loc] = seqno1;
if (seqno2 != seqno1)
m_piData2[loc] = seqno2;
m_piNext[m_iTail] = loc;
m_piPrior[loc] = m_iTail;
m_piNext[loc] = -1;
m_iTail = loc;
}
// Initilize time stamp
gettimeofday(m_pLastFeedbackTime + loc, 0);
m_piCount[loc] = 2;
m_iLength += getLength(seqno1, seqno2);
}
bool CRcvLossList::remove(const __int32& seqno)
{
if (0 == m_iLength)
return false;
// locate the position of "seqno" in the list
__int32 offset = seqno - m_piData1[m_iHead];
if (offset < -m_iSeqNoTH)
offset += m_iMaxSeqNo;
if (offset < 0)
return false;
__int32 loc = (m_iHead + offset) % m_iSize;
if (seqno == m_piData1[loc])
{
// This is a seq. no. that starts the loss sequence
if (-1 == m_piData2[loc])
{
// there is only 1 loss in the sequence, delete it from the node
if (m_iHead == loc)
{
m_iHead = m_piNext[m_iHead];
if (-1 != m_iHead)
m_piPrior[m_iHead] = -1;
}
else
{
m_piNext[m_piPrior[loc]] = m_piNext[loc];
if (-1 != m_piNext[loc])
m_piPrior[m_piNext[loc]] = m_piPrior[loc];
else
m_iTail = m_piPrior[loc];
}
m_piData1[loc] = -1;
}
else
{
// there are more than 1 loss in the sequence
// move the node to the next and update the starter as the next loss inSeqNo(seqno)
// find next node
__int32 i = (loc + 1) % m_iSize;
// remove the "seqno" and change the starter as next seq. no.
m_piData1[i] = incSeqNo(m_piData1[loc]);
// process the sequence end
if (greaterthan(m_piData2[loc], incSeqNo(m_piData1[loc])))
m_piData2[i] = m_piData2[loc];
// replicate the time stamp and report counter
m_pLastFeedbackTime[i] = m_pLastFeedbackTime[loc];
m_piCount[i] = m_piCount[loc];
// remove the current node
m_piData1[loc] = -1;
m_piData2[loc] = -1;
// update list pointer
m_piNext[i] = m_piNext[loc];
m_piPrior[i] = m_piPrior[loc];
if (m_iHead == loc)
m_iHead = i;
else
m_piNext[m_piPrior[i]] = i;
if (m_iTail == loc)
m_iTail = i;
else
m_piPrior[m_piNext[i]] = i;
}
m_iLength --;
return true;
}
// There is no loss sequence in the current position
// the "seqno" may be contained in a previous node
// searching previous node
__int32 i = (loc - 1 + m_iSize) % m_iSize;
while (-1 == m_piData1[i])
i = (i - 1 + m_iSize) % m_iSize;
// not contained in this node, return
if ((-1 == m_piData2[i]) || greaterthan(seqno, m_piData2[i]))
return false;
if (seqno == m_piData2[i])
{
// it is the sequence end
if (seqno == incSeqNo(m_piData1[i]))
m_piData2[i] = -1;
else
m_piData2[i] = decSeqNo(seqno);
}
else
{
// split the sequence
// construct the second sequence from incSeqNo(seqno) to the original sequence end
// located at "loc + 1"
loc = (loc + 1) % m_iSize;
m_piData1[loc] = incSeqNo(seqno);
if (greaterthan(m_piData2[i], incSeqNo(seqno)))
m_piData2[loc] = m_piData2[i];
// the first (original) sequence is between the original sequence start to decSeqNo(seqno)
if (seqno == incSeqNo(m_piData1[i]))
m_piData2[i] = -1;
else
m_piData2[i] = decSeqNo(seqno);
// replicate the time stamp and report counter
m_pLastFeedbackTime[loc] = m_pLastFeedbackTime[i];
m_piCount[loc] = m_piCount[i];
// update the list pointer
m_piNext[loc] = m_piNext[i];
m_piNext[i] = loc;
m_piPrior[loc] = i;
if (m_iTail == i)
m_iTail = loc;
else
m_piPrior[m_piNext[loc]] = loc;
}
m_iLength --;
return true;
}
__int32 CRcvLossList::getLossLength() const
{
return m_iLength;
}
__int32 CRcvLossList::getFirstLostSeq() const
{
if (0 == m_iLength)
return -1;
return m_piData1[m_iHead];
}
void CRcvLossList::getLossArray(__int32* array, __int32& len, const __int32& limit, const __int32& threshold)
{
timeval currtime;
gettimeofday(&currtime, 0);
__int32 i = m_iHead;
len = 0;
while ((len < limit - 1) && (-1 != i))
{
if ((currtime.tv_sec - m_pLastFeedbackTime[i].tv_sec) * 1000000 + currtime.tv_usec - m_pLastFeedbackTime[i].tv_usec > m_piCount[i] * threshold)
{
array[len] = m_piData1[i];
if (-1 != m_piData2[i])
{
// there are more than 1 loss in the sequence
array[len] |= 0x80000000;
++ len;
array[len] = m_piData2[i];
}
++ len;
// update the timestamp
gettimeofday(m_pLastFeedbackTime + i, 0);
// update how many times this loss has been fed back, the "k" in UDT paper
++ m_piCount[i];
}
i = m_piNext[i];
}
}
//
CIrregularPktList::CIrregularPktList(const __int32& size, const __int32& th, const __int32& max):
m_iSize(size)
{
m_iSeqNoTH = th;
m_iMaxSeqNo = max;
m_piData = new __int32 [m_iSize];
m_piErrorSize = new __int32 [m_iSize];
m_piNext = new __int32 [m_iSize];
// -1 means there is no data in the node
for (__int32 i = 0; i < size; ++ i)
m_piData[i] = -1;
m_iLength = 0;
m_iHead = -1;
m_iInsertPos = -1;
}
CIrregularPktList::~CIrregularPktList()
{
delete [] m_piData;
delete [] m_piErrorSize;
delete [] m_piNext;
}
__int32 CIrregularPktList::currErrorSize(const __int32& seqno) const
{
if (0 == m_iLength)
return 0;
__int32 size = 0;
__int32 i = m_iHead;
// calculate the sum of the size error until the node with a seq. no. not less than "seqno"
while ((-1 != i) && lessthan(m_piData[i], seqno))
{
size += m_piErrorSize[i];
i = m_piNext[i];
}
return size;
}
void CIrregularPktList::addIrregularPkt(const __int32& seqno, const __int32& errsize)
{
if (0 == m_iLength)
{
// insert into an empty list
m_iHead = 0;
m_piData[m_iHead] = seqno;
m_piErrorSize[m_iHead] = errsize;
m_piNext[m_iHead] = -1;
++ m_iLength;
m_iInsertPos = m_iHead;
return;
}
// positioning...
__int32 offset = seqno - m_piData[m_iHead];
if (offset < -m_iSeqNoTH)
offset += m_iMaxSeqNo;
else if (offset > m_iSeqNoTH)
offset -= m_iMaxSeqNo;
__int32 loc = (m_iHead + offset + m_iSize) % m_iSize;
if (offset < 0)
{
// insert at head
m_piData[loc] = seqno;
m_piErrorSize[loc] = errsize;
m_piNext[loc] = m_iHead;
m_iHead = loc;
++ m_iLength;
}
else if (offset > 0)
{
// return if it is already there
if (seqno == m_piData[loc])
return;
// locate previous node
__int32 i;
if ((-1 != m_iInsertPos) && lessthan(m_piData[m_iInsertPos], seqno))
i = m_iInsertPos;
else
i = m_iHead;
while ((-1 != m_piNext[i]) && lessthan(m_piData[m_piNext[i]], seqno))
i = m_piNext[i];
// insert the node
m_piNext[loc] = m_piNext[i];
m_piNext[i] = loc;
m_piData[loc] = seqno;
m_piErrorSize[loc] = errsize;
++ m_iLength;
}
m_iInsertPos = loc;
}
void CIrregularPktList::deleteIrregularPkt(const __int32& seqno)
{
// remove all node until the one with seq. no. larger than the parameter
__int32 i = m_iHead;
while ((-1 != i) && notgreaterthan(m_piData[i], seqno))
{
m_piData[i] = -1;
m_iLength --;
if (i == m_iInsertPos)
m_iInsertPos = -1;
i = m_piNext[i];
}
m_iHead = i;
}