#include <cstring>
#include "tunnel.h"
#include "connection.h"
#include "udpsendqueue.h"
//#define DebugMsg HideDebugMSG
#define DebugMsg ShowDebugMSG
UdpSendQueue::UdpSendQueue(Connection &connection):
connection(connection),
nextSendTime(connection.tunnel.time + connection.tunnel.udpSendDuration),
nextPartialSendTime(connection.tunnel.time + connection.tunnel.udpPartialSendDuration),
finalEntryAdded(),
nextIndex(),
freeFirst(entries),
busyFirst(),
busyLast(),
entries()
{
Entry *e = entries;
Entry *end = e + sizeof(entries)/sizeof(*entries) - 1;
while(e < end) { e->next = e + 1; ++e; }
}
UdpSendQueue::~UdpSendQueue()
{ }
UdpSendQueue::Entry* UdpSendQueue::allocEntry() {
Entry *e = freeFirst;
if (!e) return nullptr;
freeFirst = e->next;
e->prev = nullptr;
e->next = busyFirst;
(e->next ? e->next->prev : busyLast) = e;
busyFirst = e;
return e;
}
void UdpSendQueue::moveToEnd(Entry *e) {
if (!e->next) return;
(e->prev ? e->prev->next : busyFirst) = e->next;
(e->next ? e->next->prev : busyLast ) = e->prev;
e->next = nullptr;
e->prev = busyLast;
(e->prev ? e->prev->next : busyFirst) = e;
busyLast = e;
}
void UdpSendQueue::freeEntry(Entry *e) {
(e->prev ? e->prev->next : busyFirst) = e->next;
(e->next ? e->next->prev : busyLast ) = e->prev;
memset(e, 0, sizeof(*e));
e->next = freeFirst;
freeFirst = e;
}
void UdpSendQueue::confirm(unsigned int index, const unsigned char *bits, unsigned int bitsCount) {
DebugMsg("%u, %u, %s", connection.id.id, index, bitsToString(bits, bitsCount ? (bitsCount-1)/8 + 1 : 0));
Entry *e = busyFirst;
while(e) {
if (cycleLequal(index, e->index)) {
unsigned int bit = e->index - index;
if (bit >= bitsCount)
{ e = e->next; continue; }
unsigned int byte = bit / 8;
bit %= 8;
if ( !(bits[byte] & (1 << bit)) )
{ e = e->next; continue; }
}
DebugMsg("%u, %u - confirmed", connection.id.id, e->index);
Entry *ee = e;
e = e->next;
freeEntry(ee);
}
}
bool UdpSendQueue::sendUdpSingle() {
Time time = connection.tunnel.time;
if (timeLess(time, nextSendTime))
return false;
Entry *e = busyFirst;
if (e && timeLess(time, e->resendTime))
e = nullptr;
if (!e && freeFirst && !finalEntryAdded) {
int size = connection.tcpRecvQueue.busySize();
if (size > PACKET_SIZE) size = PACKET_SIZE;
if (size < 0) size = 0;
bool load = true;
if (size == 0) {
if (connection.tcpSockId >= 0) {
nextPartialSendTime = time + connection.tunnel.udpPartialSendDuration;
load = false;
}
} else {
if (size < PACKET_SIZE && timeLess(time, nextPartialSendTime))
load = false;
}
if (load && (e = allocEntry())) {
nextPartialSendTime = time + connection.tunnel.udpPartialSendDuration;
e->index = nextIndex++;
e->size = size;
if (e->size) connection.tcpRecvQueue.pop(e->data, e->size); else finalEntryAdded = true;
DebugMsg("%u, %u, %d - created packet to send by UDP", connection.id.id, e->index, e->size);
}
}
if (!e) e = busyFirst;
if (!e) return false;
Packet packet;
packet.init(connection.id.id, e->index, CMD_DATA, e->size);
if (e->size) memcpy(packet.payload, e->data, e->size);
DebugMsg("%u, %u, %hu", connection.id.id, e->index, e->size);
connection.tunnel.sendUdpPacket(connection.id.address, packet);
e->resendTime = time + connection.tunnel.udpResendDuration;
nextSendTime += connection.tunnel.udpSendDuration;
moveToEnd(e);
return true;
}
int UdpSendQueue::sendUdp() {
Time time = connection.tunnel.time;
int sent = 0;
while(sendUdpSingle()) {
++sent;
if (sent == 100) {
if (timeLess(nextSendTime, time)) {
DebugMsg( "%u, time correction by %llu after sent %d packets",
connection.id.id, time + 1 - nextSendTime, sent );
nextSendTime = time + 1;
}
break;
}
}
/*
while(timeLequal(nextSendTime, time) && connection.udpRecvQueue.isConfirmRequired()) {
if (sent == 100) {
DebugMsg( "%u, time correction by %llu after sent %d packets (extra confirmations)",
connection.id.id, time + 1 - nextSendTime, sent );
nextSendTime = time + 1;
break;
}
DebugMsg("%u - nothing to send, send confirmation", connection.id.id);
connection.udpRecvQueue.sendUdpConfirm(true);
nextSendTime += connection.tunnel.udpSendDuration;
++sent;
}
*/
return sent;
}
bool UdpSendQueue::canSentToUdp() const
{ return connection.udpActive && (busyFirst || connection.tcpRecvQueue.busySize()); }
void UdpSendQueue::whenWriteToUdp(Time &t) const {
int size = connection.tcpRecvQueue.busySize();
if (busyFirst || size >= PACKET_SIZE /*|| connection.udpRecvQueue.isConfirmRequired()*/) {
if (timeLess(nextSendTime, t)) t = nextSendTime;
} else
if (size > 0) {
Time tt = nextPartialSendTime;
if (timeLess(nextPartialSendTime, nextSendTime)) tt = nextSendTime;
if (timeLess(tt, t)) t = tt;
}
}