You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/oscar/liboscar/rateclass.cpp

245 lines
6.3 KiB

/*
rateclass.cpp - Rate Limiting Implementation
Copyright (c) 2004 by Tom Linsky <thomas.linsky@cwru.edu>
Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
*************************************************************************
*/
#include "rateclass.h"
#include <tqtimer.h>
#include <kdebug.h>
#include "transfer.h"
using namespace Oscar;
RateClass::RateClass( TQObject* parent )
: TQObject( parent, 0 )
{
m_waitingToSend = false;
m_packetTimer.start();
}
RateClass::~ RateClass()
{
dumpQueue();
m_members.clear();
}
WORD RateClass::id() const
{
return m_rateInfo.classId;
}
void RateClass::setRateInfo( RateInfo newRateInfo )
{
m_rateInfo.classId = newRateInfo.classId;
m_rateInfo.windowSize = newRateInfo.windowSize;
m_rateInfo.clearLevel = newRateInfo.clearLevel;
m_rateInfo.alertLevel = newRateInfo.alertLevel;
m_rateInfo.limitLevel = newRateInfo.limitLevel;
m_rateInfo.disconnectLevel = newRateInfo.disconnectLevel;
m_rateInfo.currentLevel = newRateInfo.currentLevel;
m_rateInfo.initialLevel = newRateInfo.initialLevel;
m_rateInfo.maxLevel = newRateInfo.maxLevel;
m_rateInfo.lastTime = newRateInfo.lastTime;
m_rateInfo.currentState = newRateInfo.currentState;
}
void RateClass::addMember( const SNAC& s )
{
addMember( s.family, s.subtype );
}
void RateClass::addMember( WORD family, WORD subtype )
{
SnacPair snacPair;
snacPair.family = family;
snacPair.subtype = subtype;
m_members.append( snacPair );
}
bool RateClass::isMember(const SNAC &s) const
{
TQValueList<SnacPair>::const_iterator it;
TQValueList<SnacPair>::const_iterator spEnd = m_members.constEnd();
for ( it = m_members.constBegin(); it != spEnd; ++it )
{
if ( ( *it ).family == s.family && ( *it ).subtype == s.subtype )
return true;
}
return false;
}
bool RateClass::isMember( WORD family, WORD subtype ) const
{
TQValueList<SnacPair>::const_iterator it;
TQValueList<SnacPair>::const_iterator spEnd = m_members.constEnd();
for ( it = m_members.constBegin(); it != spEnd; ++it )
{
if ( ( *it ).family == family && ( *it ).subtype == subtype )
{
return true;
}
}
return false;
}
void RateClass::enqueue( Transfer* t )
{
m_packetQueue.push_back( t );
/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Send queue length is now: "
<< m_packetQueue.count() << endl;*/
setupTimer();
}
void RateClass::dequeue()
{
m_packetQueue.pop_front();
}
bool RateClass::queueIsEmpty() const
{
return m_packetQueue.isEmpty();
}
int RateClass::timeToInitialLevel()
{
DWORD newLevel = 0;
//get time elapsed since the last packet was sent
int timeDiff = m_packetTimer.elapsed();
newLevel = calcNewLevel( timeDiff );
if ( newLevel < m_rateInfo.initialLevel )
{
int waitTime = ( m_rateInfo.initialLevel * m_rateInfo.windowSize ) - ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel );
return waitTime;
}
return 0;
}
int RateClass::timeToNextSend()
{
DWORD newLevel = 0;
//get time elapsed since the last packet was sent
int timeDiff = m_packetTimer.elapsed();
DWORD windowSize = m_rateInfo.windowSize;
newLevel = calcNewLevel( timeDiff );
//The maximum level at which we can safely send a packet
DWORD maxPacket = m_rateInfo.alertLevel + RATE_SAFETY_TIME;
/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate Information:"
<< "\nWindow Size: " << windowSize
<< "\nWindow Size - 1: " << windowSize - 1
<< "\nOld Level: " << m_rateInfo.currentLevel
<< "\nAlert Level: " << m_rateInfo.alertLevel
<< "\nLimit Level: " << m_rateInfo.limitLevel
<< "\nDisconnect Level: " << m_rateInfo.disconnectLevel
<< "\nNew Level: " << newLevel
<< "\nTime elapsed: " << timeDiff
<< "\nMax level to send: " << maxPacket << endl; */
//If we are one packet or less away from being blocked, wait to send
if ( newLevel < maxPacket || newLevel < m_rateInfo.disconnectLevel )
{
int waitTime = ( windowSize * maxPacket ) - ( ( windowSize - 1 ) * m_rateInfo.currentLevel );
kdDebug(OSCAR_RAW_DEBUG) << "We're sending too fast. Will wait " << waitTime << "ms before sending" << endl;
return waitTime;
}
return 0;
}
DWORD RateClass::calcNewLevel( int timeDifference ) const
{
//kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Time since last packet: "
// << timeDifference << endl;
//Calculate new rate level
//NewLevel = ( ( Window - 1 ) * OldLevel + TimeDiff )/Window
//add 1 because we never want to round down or there will be problems
uint newLevel = ( ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ) + timeDifference ) / m_rateInfo.windowSize;
if ( newLevel > m_rateInfo.initialLevel )
newLevel = m_rateInfo.initialLevel;
return newLevel;
}
void RateClass::setupTimer()
{
if ( !m_waitingToSend )
{
m_waitingToSend = true;
int ttns = timeToNextSend();
if ( ttns <= 0 )
{
slotSend(); //send now
}
else
{
TQTimer::singleShot( ttns, this, TQT_SLOT( slotSend() ) ); //or send later
}
}
}
void RateClass::slotSend()
{
//kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
if ( m_packetQueue.isEmpty() )
return;
//send then pop off the list
emit dataReady( m_packetQueue.first() );
dequeue();
updateRateInfo();
m_waitingToSend = false;
// check if we still have packets to send
if ( !m_packetQueue.isEmpty() )
setupTimer();
}
void RateClass::updateRateInfo()
{
//Update rate info
DWORD newLevel = calcNewLevel( m_packetTimer.elapsed() );
m_rateInfo.currentLevel = newLevel;
//kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current Level = " << newLevel << endl;
//restart the timer
m_packetTimer.restart();
}
void RateClass::dumpQueue()
{
TQValueList<Transfer*>::iterator it = m_packetQueue.begin();
while ( it != m_packetQueue.end() && m_packetQueue.count() > 0 )
{
Transfer* t = ( *it );
it = m_packetQueue.remove( it );
delete t;
}
}
#include "rateclass.moc"