Blob Blame Raw

#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)
		return false;
	
	if (!e && !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() {
	int sent = 0;
	while(sendUdpSingle()) {
		++sent;
		if (sent == 100) {
			if (timeLess(nextSendTime, connection.tunnel.time)) {
				DebugMsg( "%u, time correction by %llu after sent %d packets",
						  connection.id.id, connection.tunnel.time - nextSendTime, sent );
				nextSendTime = connection.tunnel.time;
			}
			break;
		}
	}
	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) {
		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;
	}
}