Files
2025-08-19 12:45:50 +02:00

477 lines
14 KiB
C#

using System;
using System.Collections.Generic;
namespace UdtSharp
{
public class SndBuffer
{
object m_BufLock = new object(); // used to synchronize buffer operation
class Block
{
internal byte[] m_pcData; // pointer to the data block
internal int m_iLength; // length of the block
internal uint m_iMsgNo; // message number
internal ulong m_OriginTime; // original request time
internal int m_iTTL; // time to live (milliseconds)
}
List<Block> mBlockList = new List<Block>();
int m_iLastBlock = 0;
int m_iCurrentBlock = 0;
int m_iFirstBlock = 0;
uint m_iNextMsgNo; // next message number
int m_iSize; // buffer size (number of packets)
int m_iMSS; // maximum seqment/packet size
int m_iCount; // number of used blocks
public SndBuffer(int size, int mss)
{
m_iSize = size;
m_iMSS = mss;
// circular linked list for out bound packets
for (int i = 0; i < m_iSize; ++i)
{
Block block = new Block();
block.m_iMsgNo = 0;
block.m_pcData = new byte[m_iMSS];
mBlockList.Add(block);
}
}
// Functionality:
// Insert a user buffer into the sending list.
// Parameters:
// 0) [in] data: pointer to the user data block.
// 1) [in] len: size of the block.
// 2) [in] ttl: time to live in milliseconds
// 3) [in] order: if the block should be delivered in order, for DGRAM only
// Returned value:
// None.
public void addBuffer(byte[] data, int offset, int len, int ttl = -1, bool order = false)
{
int size = len / m_iMSS;
if ((len % m_iMSS) != 0)
size++;
// dynamically increase sender buffer
while (size + m_iCount >= m_iSize)
increase();
ulong time = Timer.getTime();
uint inorder = Convert.ToUInt32(order);
inorder <<= 29;
for (int i = 0; i < size; ++i)
{
Block s = mBlockList[m_iLastBlock];
IncrementBlockIndex(ref m_iLastBlock);
int pktlen = len - i * m_iMSS;
if (pktlen > m_iMSS)
pktlen = m_iMSS;
Array.Copy(data, i * m_iMSS + offset, s.m_pcData, 0, pktlen);
s.m_iLength = pktlen;
s.m_iMsgNo = m_iNextMsgNo | inorder;
if (i == 0)
s.m_iMsgNo |= 0x80000000;
if (i == size - 1)
s.m_iMsgNo |= 0x40000000;
s.m_OriginTime = time;
s.m_iTTL = ttl;
}
lock (m_BufLock)
{
m_iCount += size;
}
m_iNextMsgNo++;
if (m_iNextMsgNo == MessageNumber.m_iMaxMsgNo)
m_iNextMsgNo = 1;
}
public int readData(ref byte[] data, ref uint msgno)
{
// No data to read
if (m_iCurrentBlock == m_iLastBlock)
return 0;
data = mBlockList[m_iCurrentBlock].m_pcData;
int readlen = mBlockList[m_iCurrentBlock].m_iLength;
msgno = mBlockList[m_iCurrentBlock].m_iMsgNo;
IncrementBlockIndex(ref m_iCurrentBlock);
return readlen;
}
public int readData(ref byte[] data, int offset, ref uint msgno, out int msglen)
{
msglen = 0;
lock (m_BufLock)
{
int blockIndex = m_iFirstBlock;
IncrementBlockIndex(ref blockIndex, offset);
Block p = mBlockList[blockIndex];
if ((p.m_iTTL >= 0) && ((Timer.getTime() - p.m_OriginTime) / 1000 > (ulong)p.m_iTTL))
{
msgno = p.m_iMsgNo & 0x1FFFFFFF;
msglen = 1;
IncrementBlockIndex(ref blockIndex);
p = mBlockList[blockIndex];
bool move = false;
while (msgno == (p.m_iMsgNo & 0x1FFFFFFF))
{
if (blockIndex == m_iCurrentBlock)
move = true;
IncrementBlockIndex(ref blockIndex);
p = mBlockList[blockIndex];
if (move)
m_iCurrentBlock = blockIndex;
msglen++;
}
return -1;
}
data = p.m_pcData;
int readlen = p.m_iLength;
msgno = p.m_iMsgNo;
return readlen;
}
}
void IncrementBlockIndex(ref int blockIndex, int offset = 1)
{
blockIndex = (blockIndex + offset) % mBlockList.Count;
}
public void ackData(int offset)
{
lock (m_BufLock)
{
IncrementBlockIndex(ref m_iFirstBlock, offset);
m_iCount -= offset;
Timer.triggerEvent();
}
}
public int getCurrBufSize()
{
return m_iCount;
}
void increase()
{
int unitsize = m_iSize;
for (int i = 0; i < unitsize; ++i)
{
Block block = new Block();
block.m_iMsgNo = 0;
block.m_pcData = new byte[m_iMSS];
mBlockList.Add(block);
}
m_iSize += unitsize;
}
}
public class RcvBuffer
{
Unit[] m_pUnit; // pointer to the protocol buffer
int m_iSize; // size of the protocol buffer
int m_iStartPos; // the head position for I/O (inclusive)
int m_iLastAckPos; // the last ACKed position (exclusive)
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // the furthest data position
int m_iNotch; // the starting read point of the first unit
public RcvBuffer(int bufsize)
{
m_iSize = bufsize;
m_iStartPos = 0;
m_iLastAckPos = 0;
m_iMaxPos = 0;
m_iNotch = 0;
m_pUnit = new Unit[m_iSize];
for (int i = 0; i < m_iSize; ++i)
m_pUnit[i] = null;
}
~RcvBuffer()
{
for (int i = 0; i < m_iSize; ++i)
{
if (null != m_pUnit[i])
{
m_pUnit[i].m_iFlag = 0;
}
}
}
public int addData(Unit unit, int offset)
{
int pos = (m_iLastAckPos + offset) % m_iSize;
if (offset > m_iMaxPos)
m_iMaxPos = offset;
if (null != m_pUnit[pos])
return -1;
m_pUnit[pos] = unit;
unit.m_iFlag = 1;
return 0;
}
public int readBuffer(byte[] data, int offset, int len)
{
int p = m_iStartPos;
int lastack = m_iLastAckPos;
int rs = len;
while ((p != lastack) && (rs > 0))
{
int unitsize = m_pUnit[p].m_Packet.getLength() - m_iNotch;
if (unitsize > rs)
unitsize = rs;
unitsize = m_pUnit[p].m_Packet.GetDataBytes(m_iNotch, data, offset, unitsize);
offset += unitsize;
if ((rs > unitsize) || (rs == m_pUnit[p].m_Packet.getLength() - m_iNotch))
{
Unit tmp = m_pUnit[p];
m_pUnit[p] = null;
tmp.m_iFlag = 0;
if (++p == m_iSize)
p = 0;
m_iNotch = 0;
}
else
m_iNotch += rs;
rs -= unitsize;
}
m_iStartPos = p;
return len - rs;
}
public void ackData(int len)
{
m_iLastAckPos = (m_iLastAckPos + len) % m_iSize;
m_iMaxPos -= len;
if (m_iMaxPos < 0)
m_iMaxPos = 0;
Timer.triggerEvent();
}
public int getAvailBufSize()
{
// One slot must be empty in order to tell the difference between "empty buffer" and "full buffer"
return m_iSize - getRcvDataSize() - 1;
}
public int getRcvDataSize()
{
if (m_iLastAckPos >= m_iStartPos)
return m_iLastAckPos - m_iStartPos;
return m_iSize + m_iLastAckPos - m_iStartPos;
}
public void dropMsg(int msgno)
{
for (int i = m_iStartPos, n = (m_iLastAckPos + m_iMaxPos) % m_iSize; i != n; i = (i + 1) % m_iSize)
if ((null != m_pUnit[i]) && (msgno == m_pUnit[i].m_Packet.GetMessageNumber()))
m_pUnit[i].m_iFlag = 3;
}
public int readMsg(byte[] data, int len)
{
int p = 0;
int q = 0;
bool passack = false;
if (!scanMsg(ref p, ref q, ref passack))
return 0;
int rs = len;
int dataOffset = 0;
while (p != (q + 1) % m_iSize)
{
byte[] allData = m_pUnit[p].m_Packet.GetDataBytes();
int unitsize = allData.Length;
if ((rs >= 0) && (unitsize > rs))
unitsize = rs;
if (unitsize > 0)
{
Array.Copy(allData, 0, data, dataOffset, unitsize);
dataOffset += unitsize;
rs -= unitsize;
}
if (!passack)
{
Unit tmp = m_pUnit[p];
m_pUnit[p] = null;
tmp.m_iFlag = 0;
}
else
m_pUnit[p].m_iFlag = 2;
if (++p == m_iSize)
p = 0;
}
if (!passack)
m_iStartPos = (q + 1) % m_iSize;
return len - rs;
}
int getRcvMsgNum()
{
int p = 0;
int q = 0;
bool passack = false;
return scanMsg(ref p, ref q, ref passack) ? 1 : 0;
}
bool scanMsg(ref int p, ref int q, ref bool passack)
{
// empty buffer
if ((m_iStartPos == m_iLastAckPos) && (m_iMaxPos <= 0))
return false;
//skip all bad msgs at the beginning
while (m_iStartPos != m_iLastAckPos)
{
if (null == m_pUnit[m_iStartPos])
{
if (++m_iStartPos == m_iSize)
m_iStartPos = 0;
continue;
}
if ((1 == m_pUnit[m_iStartPos].m_iFlag) && (m_pUnit[m_iStartPos].m_Packet.getMsgBoundary() > 1))
{
bool good = true;
// look ahead for the whole message
for (int i = m_iStartPos; i != m_iLastAckPos;)
{
if ((null == m_pUnit[i]) || (1 != m_pUnit[i].m_iFlag))
{
good = false;
break;
}
if ((m_pUnit[i].m_Packet.getMsgBoundary() == 1) || (m_pUnit[i].m_Packet.getMsgBoundary() == 3))
break;
if (++i == m_iSize)
i = 0;
}
if (good)
break;
}
Unit tmp = m_pUnit[m_iStartPos];
m_pUnit[m_iStartPos] = null;
tmp.m_iFlag = 0;
if (++m_iStartPos == m_iSize)
m_iStartPos = 0;
}
p = -1; // message head
q = m_iStartPos; // message tail
passack = m_iStartPos == m_iLastAckPos;
bool found = false;
// looking for the first message
for (int i = 0, n = m_iMaxPos + getRcvDataSize(); i <= n; ++i)
{
if ((null != m_pUnit[q]) && (1 == m_pUnit[q].m_iFlag))
{
switch (m_pUnit[q].m_Packet.getMsgBoundary())
{
case 3: // 11
p = q;
found = true;
break;
case 2: // 10
p = q;
break;
case 1: // 01
if (p != -1)
found = true;
break;
}
}
else
{
// a hole in this message, not valid, restart search
p = -1;
}
if (found)
{
// the msg has to be ack'ed or it is allowed to read out of order, and was not read before
if (!passack || !m_pUnit[q].m_Packet.getMsgOrderFlag())
break;
found = false;
}
if (++q == m_iSize)
q = 0;
if (q == m_iLastAckPos)
passack = true;
}
// no msg found
if (!found)
{
// if the message is larger than the receiver buffer, return part of the message
if ((p != -1) && ((q + 1) % m_iSize == p))
found = true;
}
return found;
}
}
}