#ifndef INC_SRT_COMMON_H
#define INC_SRT_COMMON_H
#define _CRT_SECURE_NO_WARNINGS 1
#include <memory>
#include <cstdlib>
#include <cstdio>
#ifndef _WIN32
#include <sys/time.h>
#include <sys/uio.h>
#else
#endif
#include "srt.h"
#include "utilities.h"
#include "sync.h"
#include "netinet_any.h"
#include "packetfilter_api.h"
#ifndef _WIN32
#define NET_ERROR errno
#else
#define NET_ERROR WSAGetLastError()
#endif
#ifdef _DEBUG
#include <assert.h>
#define SRT_ASSERT(cond) assert(cond)
#else
#define SRT_ASSERT(cond)
#endif
#include <exception>
class SRT_API CUDTException: public std::exception
{
public:
CUDTException(CodeMajor major = MJ_SUCCESS, CodeMinor minor = MN_NONE, int err = -1);
virtual ~CUDTException() ATR_NOTHROW {}
const char* getErrorMessage() const ATR_NOTHROW;
virtual const char* what() const ATR_NOTHROW ATR_OVERRIDE
{
return getErrorMessage();
}
const std::string& getErrorString() const;
int getErrorCode() const;
int getErrno() const;
void clear();
private:
CodeMajor m_iMajor; CodeMinor m_iMinor; int m_iErrno; mutable std::string m_strMsg;
std::string m_strAPI; std::string m_strDebug;
public:
static const int EUNKNOWN = SRT_EUNKNOWN;
static const int SUCCESS = SRT_SUCCESS;
static const int ECONNSETUP = SRT_ECONNSETUP;
static const int ENOSERVER = SRT_ENOSERVER;
static const int ECONNREJ = SRT_ECONNREJ;
static const int ESOCKFAIL = SRT_ESOCKFAIL;
static const int ESECFAIL = SRT_ESECFAIL;
static const int ECONNFAIL = SRT_ECONNFAIL;
static const int ECONNLOST = SRT_ECONNLOST;
static const int ENOCONN = SRT_ENOCONN;
static const int ERESOURCE = SRT_ERESOURCE;
static const int ETHREAD = SRT_ETHREAD;
static const int ENOBUF = SRT_ENOBUF;
static const int EFILE = SRT_EFILE;
static const int EINVRDOFF = SRT_EINVRDOFF;
static const int ERDPERM = SRT_ERDPERM;
static const int EINVWROFF = SRT_EINVWROFF;
static const int EWRPERM = SRT_EWRPERM;
static const int EINVOP = SRT_EINVOP;
static const int EBOUNDSOCK = SRT_EBOUNDSOCK;
static const int ECONNSOCK = SRT_ECONNSOCK;
static const int EINVPARAM = SRT_EINVPARAM;
static const int EINVSOCK = SRT_EINVSOCK;
static const int EUNBOUNDSOCK = SRT_EUNBOUNDSOCK;
static const int ESTREAMILL = SRT_EINVALMSGAPI;
static const int EDGRAMILL = SRT_EINVALBUFFERAPI;
static const int ENOLISTEN = SRT_ENOLISTEN;
static const int ERDVNOSERV = SRT_ERDVNOSERV;
static const int ERDVUNBOUND = SRT_ERDVUNBOUND;
static const int EINVALMSGAPI = SRT_EINVALMSGAPI;
static const int EINVALBUFFERAPI = SRT_EINVALBUFFERAPI;
static const int EDUPLISTEN = SRT_EDUPLISTEN;
static const int ELARGEMSG = SRT_ELARGEMSG;
static const int EINVPOLLID = SRT_EINVPOLLID;
static const int EASYNCFAIL = SRT_EASYNCFAIL;
static const int EASYNCSND = SRT_EASYNCSND;
static const int EASYNCRCV = SRT_EASYNCRCV;
static const int ETIMEOUT = SRT_ETIMEOUT;
static const int ECONGEST = SRT_ECONGEST;
static const int EPEERERR = SRT_EPEERERR;
};
enum UDTSockType
{
UDT_UNDEFINED = 0, UDT_STREAM = 1,
UDT_DGRAM
};
enum UDTMessageType
{
UMSG_HANDSHAKE = 0, UMSG_KEEPALIVE = 1, UMSG_ACK = 2, UMSG_LOSSREPORT = 3, UMSG_CGWARNING = 4, UMSG_SHUTDOWN = 5, UMSG_ACKACK = 6, UMSG_DROPREQ = 7, UMSG_PEERERROR = 8, UMSG_END_OF_TYPES,
UMSG_EXT = 0x7FFF };
enum HandshakeSide
{
HSD_DRAW,
HSD_INITIATOR, HSD_RESPONDER };
std::string MessageTypeStr(UDTMessageType mt, uint32_t extt = 0);
enum EReadStatus
{
RST_OK = 0, RST_AGAIN, RST_ERROR = -1 };
enum EConnectStatus
{
CONN_ACCEPT = 0, CONN_REJECT = -1, CONN_CONTINUE = 1, CONN_RENDEZVOUS = 2, CONN_CONFUSED = 3, CONN_RUNNING = 10, CONN_AGAIN = -2 };
enum EConnectMethod
{
COM_ASYNCHRO,
COM_SYNCHRO
};
std::string ConnectStatusStr(EConnectStatus est);
const int64_t BW_INFINITE = 1000000000/8;
enum ETransmissionEvent
{
TEV_INIT, TEV_ACK, TEV_ACKACK, TEV_LOSSREPORT, TEV_CHECKTIMER, TEV_SEND, TEV_RECEIVE, TEV_CUSTOM,
TEV_E_SIZE
};
std::string TransmissionEventStr(ETransmissionEvent ev);
enum ECheckTimerStage
{
TEV_CHT_INIT, TEV_CHT_FASTREXMIT, TEV_CHT_REXMIT };
enum EInitEvent
{
TEV_INIT_RESET = 0,
TEV_INIT_INPUTBW,
TEV_INIT_OHEADBW
};
class CPacket;
struct EventVariant
{
enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type;
union U
{
CPacket* packet;
int32_t ack;
struct
{
const int32_t* ptr;
size_t len;
} array;
ECheckTimerStage stage;
EInitEvent init;
} u;
EventVariant()
{
type = UNDEFINED;
memset(&u, 0, sizeof u);
}
template<Type t>
struct VariantFor;
template <Type tp, typename Arg>
void Assign(Arg arg)
{
type = tp;
(u.*(VariantFor<tp>::field())) = arg;
}
void operator=(CPacket* arg) { Assign<PACKET>(arg); };
void operator=(int32_t arg) { Assign<ACK>(arg); };
void operator=(ECheckTimerStage arg) { Assign<STAGE>(arg); };
void operator=(EInitEvent arg) { Assign<INIT>(arg); };
template <class T>
EventVariant(const T arg)
{
*this = arg;
}
const int32_t* get_ptr() const
{
return u.array.ptr;
}
size_t get_len() const
{
return u.array.len;
}
void set(const int32_t* ptr, size_t len)
{
type = ARRAY;
u.array.ptr = ptr;
u.array.len = len;
}
EventVariant(const int32_t* ptr, size_t len)
{
set(ptr, len);
}
template<Type T>
typename VariantFor<T>::type get() const
{
return u.*(VariantFor<T>::field());
}
};
template<> struct EventVariant::VariantFor<EventVariant::PACKET>
{
typedef CPacket* type;
static type U::*field() {return &U::packet;}
};
template<> struct EventVariant::VariantFor<EventVariant::ACK>
{
typedef int32_t type;
static type U::*field() { return &U::ack; }
};
template<> struct EventVariant::VariantFor<EventVariant::STAGE>
{
typedef ECheckTimerStage type;
static type U::*field() { return &U::stage; }
};
template<> struct EventVariant::VariantFor<EventVariant::INIT>
{
typedef EInitEvent type;
static type U::*field() { return &U::init; }
};
class EventSlotBase
{
public:
virtual void emit(ETransmissionEvent tev, EventVariant var) = 0;
typedef void dispatcher_t(void* opaque, ETransmissionEvent tev, EventVariant var);
virtual ~EventSlotBase() {}
};
class SimpleEventSlot: public EventSlotBase
{
public:
void* opaque;
dispatcher_t* dispatcher;
SimpleEventSlot(void* op, dispatcher_t* disp): opaque(op), dispatcher(disp) {}
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
{
(*dispatcher)(opaque, tev, var);
}
};
template <class Class>
class ObjectEventSlot: public EventSlotBase
{
public:
typedef void (Class::*method_ptr_t)(ETransmissionEvent tev, EventVariant var);
method_ptr_t pm;
Class* po;
ObjectEventSlot(Class* o, method_ptr_t m): pm(m), po(o) {}
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
{
(po->*pm)(tev, var);
}
};
struct EventSlot
{
mutable EventSlotBase* slot;
EventSlot(): slot(0) {}
EventSlot(const EventSlot& victim)
{
slot = victim.slot; victim.slot = 0;
}
EventSlot(void* op, EventSlotBase::dispatcher_t* disp)
{
slot = new SimpleEventSlot(op, disp);
}
template <class ObjectClass>
EventSlot(ObjectClass* obj, typename ObjectEventSlot<ObjectClass>::method_ptr_t method)
{
slot = new ObjectEventSlot<ObjectClass>(obj, method);
}
void emit(ETransmissionEvent tev, EventVariant var)
{
if (!slot)
return;
slot->emit(tev, var);
}
~EventSlot()
{
delete slot;
}
};
class CSeqNo
{
int32_t value;
public:
explicit CSeqNo(int32_t v): value(v) {}
bool operator == (const CSeqNo& other) const { return other.value == value; }
bool operator < (const CSeqNo& other) const
{
return seqcmp(value, other.value) < 0;
}
bool operator != (const CSeqNo& other) const { return other.value != value; }
bool operator > (const CSeqNo& other) const { return other < *this; }
bool operator >= (const CSeqNo& other) const
{
return seqcmp(value, other.value) >= 0;
}
bool operator <=(const CSeqNo& other) const
{
return seqcmp(value, other.value) <= 0;
}
friend int operator-(const CSeqNo& c1, const CSeqNo& c2)
{
return seqoff(c2.value, c1.value);
}
friend CSeqNo operator-(const CSeqNo& c1, int off)
{
return CSeqNo(decseq(c1.value, off));
}
friend CSeqNo operator+(const CSeqNo& c1, int off)
{
return CSeqNo(incseq(c1.value, off));
}
friend CSeqNo operator+(int off, const CSeqNo& c1)
{
return CSeqNo(incseq(c1.value, off));
}
CSeqNo& operator++()
{
value = incseq(value);
return *this;
}
inline static int seqcmp(int32_t seq1, int32_t seq2)
{return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);}
inline static int seqlen(int32_t seq1, int32_t seq2)
{return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);}
inline static int seqoff(int32_t seq1, int32_t seq2)
{
if (abs(seq1 - seq2) < m_iSeqNoTH)
return seq2 - seq1;
if (seq1 < seq2)
return seq2 - seq1 - m_iMaxSeqNo - 1;
return seq2 - seq1 + m_iMaxSeqNo + 1;
}
inline static int32_t incseq(int32_t seq)
{return (seq == m_iMaxSeqNo) ? 0 : seq + 1;}
inline static int32_t decseq(int32_t seq)
{return (seq == 0) ? m_iMaxSeqNo : seq - 1;}
inline static int32_t incseq(int32_t seq, int32_t inc)
{return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;}
inline static int32_t decseq(int32_t seq, int32_t dec)
{
if ( seq < dec )
{
int32_t left = dec - seq; return m_iMaxSeqNo - left + 1;
}
return seq - dec;
}
static int32_t maxseq(int32_t seq1, int32_t seq2)
{
if (seqcmp(seq1, seq2) < 0)
return seq2;
return seq1;
}
public:
static const int32_t m_iSeqNoTH = 0x3FFFFFFF; static const int32_t m_iMaxSeqNo = 0x7FFFFFFF; };
class CAckNo
{
public:
inline static int32_t incack(int32_t ackno)
{return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;}
public:
static const int32_t m_iMaxAckSeqNo = 0x7FFFFFFF; };
template <size_t BITS, uint32_t MIN = 0>
class RollNumber
{
typedef RollNumber<BITS, MIN> this_t;
typedef Bits<BITS, 0> number_t;
uint32_t number;
public:
static const size_t OVER = number_t::mask+1;
static const size_t HALF = (OVER-MIN)/2;
private:
static int Diff(uint32_t left, uint32_t right)
{
if ( left < right )
{
int32_t diff = right - left;
if ( diff >= int32_t(HALF) ) {
left += OVER - MIN; }
}
else
{
int32_t diff = left - right;
if ( diff >= int32_t(HALF) )
{
right += OVER - MIN;
}
}
return left - right;
}
public:
explicit RollNumber(uint32_t val): number(val)
{
}
bool operator<(const this_t& right) const
{
int32_t ndiff = number - right.number;
if (ndiff < -int32_t(HALF))
{
return false;
}
if (ndiff > int32_t(HALF))
{
return true;
}
return ndiff < 0;
}
bool operator>(const this_t& right) const
{
return right < *this;
}
bool operator=(const this_t& right) const
{
return number == right.number;
}
bool operator<=(const this_t& right) const
{
return !(*this > right);
}
bool operator>=(const this_t& right) const
{
return !(*this < right);
}
void operator++(int)
{
++number;
if (number > number_t::mask)
number = MIN;
}
this_t& operator++() { (*this)++; return *this; }
void operator--(int)
{
if (number == MIN)
number = number_t::mask;
else
--number;
}
this_t& operator--() { (*this)--; return *this; }
int32_t operator-(this_t right)
{
return Diff(this->number, right.number);
}
void operator+=(int32_t delta)
{
if (-delta > int64_t(number))
{
number = OVER - MIN + number + delta; }
else
{
number += delta;
if (number >= OVER)
number -= OVER - MIN;
}
}
operator uint32_t() const { return number; }
};
struct CIPAddress
{
static bool ipcmp(const struct sockaddr* addr1, const struct sockaddr* addr2, int ver = AF_INET);
static void ntop(const struct sockaddr_any& addr, uint32_t ip[4]);
static void pton(sockaddr_any& addr, const uint32_t ip[4], int sa_family, const sockaddr_any& peer);
static std::string show(const struct sockaddr* adr);
};
struct CMD5
{
static void compute(const char* input, unsigned char result[16]);
};
template <size_t SIZE>
class StatsLossRecords
{
int32_t initseq;
std::bitset<SIZE> array;
public:
StatsLossRecords(): initseq(SRT_SEQNO_NONE) {}
bool exists(int32_t seq)
{
return initseq != SRT_SEQNO_NONE && CSeqNo::seqcmp(seq, initseq) >= 0;
}
int32_t base() { return initseq; }
void clear()
{
initseq = SRT_SEQNO_NONE;
array.reset();
}
void add(int32_t lo, int32_t hi)
{
int32_t end = CSeqNo::incseq(hi);
for (int32_t i = lo; i != end; i = CSeqNo::incseq(i))
add(i);
}
void add(int32_t seq)
{
if ( array.none() )
{
initseq = seq;
array[0] = true;
return;
}
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff > int(SIZE) )
{
size_t toremove = seqdiff - SIZE;
while ( !array[toremove] && toremove <= SIZE )
++toremove;
if ( toremove == SIZE )
{
initseq = seq;
array[0] = true;
return;
}
initseq += toremove;
seqdiff -= toremove;
array >>= toremove;
}
array[seqdiff] = true;
}
StatsLossRecords& operator << (int32_t seq)
{
add(seq);
return *this;
}
void remove(int32_t seq)
{
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff < 0 )
return; if ( seqdiff > SIZE )
return;
array[seqdiff] = true;
}
bool find(int32_t seq) const
{
int seqdiff = CSeqNo::seqoff(initseq, seq);
if ( seqdiff < 0 )
return false; if ( size_t(seqdiff) > SIZE )
return false;
return array[seqdiff];
}
#if HAVE_CXX11
std::string to_string() const
{
std::string out;
for (size_t i = 0; i < SIZE; ++i)
{
if ( array[i] )
out += std::to_string(initseq+i) + " ";
}
return out;
}
#endif
};
template <class Value>
class CircularBuffer
{
#ifdef SRT_TEST_CIRCULAR_BUFFER
public:
#endif
int m_iSize;
Value* m_aStorage;
int m_xBegin;
int m_xEnd;
static void destr(Value& v)
{
v.~Value();
}
static void constr(Value& v)
{
new ((void*)&v) Value();
}
template <class V>
static void constr(Value& v, const V& source)
{
new ((void*)&v) Value(source);
}
CircularBuffer(const CircularBuffer&);
public:
typedef Value value_type;
CircularBuffer(int size)
:m_iSize(size+1),
m_xBegin(0),
m_xEnd(0)
{
if (size == 0)
m_aStorage = 0;
else
m_aStorage = (Value*)::operator new (sizeof(Value) * m_iSize);
}
void set_capacity(int size)
{
reset();
if (size != m_iSize)
{
if (m_aStorage)
::operator delete (m_aStorage);
m_iSize = size+1;
m_aStorage = (Value*)::operator new (sizeof(Value) * m_iSize);
}
}
void reset()
{
if (m_xEnd < m_xBegin)
{
for (int i = m_xBegin; i < m_iSize; ++i)
destr(m_aStorage[i]);
for (int i = 0; i < m_xEnd; ++i)
destr(m_aStorage[i]);
}
else
{
for (int i = m_xBegin; i < m_xEnd; ++i)
destr(m_aStorage[i]);
}
m_xBegin = 0;
m_xEnd = 0;
}
~CircularBuffer()
{
reset();
::operator delete (m_aStorage);
}
int shift(int basepos, int shift) const
{
return (basepos + shift) % m_iSize;
}
int shift_forward(int basepos) const
{
if (++basepos == m_iSize)
return 0;
return basepos;
}
int shift_backward(int basepos) const
{
if (basepos == 0)
return m_iSize-1;
return --basepos;
}
int size() const
{
if (m_xEnd < m_xBegin)
{
return m_iSize - (m_xBegin - m_xEnd);
}
return m_xEnd - m_xBegin;
}
bool empty() const { return m_xEnd == m_xBegin; }
size_t capacity() const { return m_iSize-1; }
int spaceleft() const
{
if (m_xEnd < m_xBegin)
{
return m_xBegin - m_xEnd;
}
return m_iSize - (m_xEnd - m_xBegin);
}
template <class V>
int push(const V& v)
{
int nend = shift_forward(m_xEnd);
if ( nend == m_xBegin)
return -1;
constr(m_aStorage[m_xEnd], v);
m_xEnd = nend;
return size() - 1;
}
Value* push()
{
int nend = shift_forward(m_xEnd);
if ( nend == m_xBegin)
return NULL;
Value* pos = &m_aStorage[m_xEnd];
constr(*pos);
m_xEnd = nend;
return pos;
}
bool access(int position, Value*& w_v)
{
int ipos, vend;
if (!INT_checkAccess(position, ipos, vend))
return false;
if (ipos >= vend) return false;
INT_access(ipos, false, (w_v)); return true;
}
bool access(int position, Value*& w_v, bool& w_isnew)
{
int ipos, vend;
if (!INT_checkAccess(position, ipos, vend))
return false;
bool exceeds = (ipos >= vend);
w_isnew = exceeds;
INT_access(ipos, exceeds, (w_v));
return true;
}
private:
bool INT_checkAccess(int position, int& ipos, int& vend)
{
if (position >= (m_iSize-1) || position < 0)
return false;
ipos = m_xBegin + position;
vend = m_xEnd;
if (m_xEnd < m_xBegin)
vend += m_iSize;
return true;
}
void INT_access(int ipos, bool exceeds, Value*& w_v)
{
if (ipos >= m_iSize)
ipos -= m_iSize;
if (exceeds)
{
int nend = ipos+1;
if (m_xEnd > nend)
{
for (int i = m_xEnd; i < m_iSize; ++i)
constr(m_aStorage[i]);
for (int i = 0; i < nend; ++i)
constr(m_aStorage[i]);
}
else
{
for (int i = m_xEnd; i < nend; ++i)
constr(m_aStorage[i]);
}
if (nend == m_iSize)
nend = 0;
m_xEnd = nend;
}
w_v = &m_aStorage[ipos];
}
public:
bool set(int position, const Value& newval, bool overwrite = true)
{
Value* pval = 0;
bool isnew = false;
if (!access(position, (pval), (isnew)))
return false;
if (isnew || overwrite)
*pval = newval;
return true;
}
template<class Updater>
bool update(int position, Updater updater)
{
Value* pval = 0;
bool isnew = false;
if (!access(position, (pval), (isnew)))
return false;
updater(*pval, isnew);
return true;
}
int getIndexFor(int position) const
{
int ipos = m_xBegin + position;
int vend = m_xEnd;
if (vend < m_xBegin)
vend += m_iSize;
if (ipos >= vend)
return -1;
if (ipos >= m_iSize)
ipos -= m_iSize;
return ipos;
}
bool get(int position, Value& w_out) const
{
if (position > m_iSize || position < 0)
return false;
int ipos = getIndexFor(position);
if (ipos == -1)
return false;
w_out = m_aStorage[ipos];
return true;
}
bool drop(int position)
{
if (position > m_iSize || position < 1)
return false;
int ipos = m_xBegin + position;
int vend = m_xEnd;
if (vend < m_xBegin)
vend += m_iSize;
if (ipos >= vend)
{
reset();
return true;
}
int nbegin = ipos;
if (nbegin >= m_iSize)
{
nbegin -= m_iSize;
for (int i = m_xBegin; i < m_iSize; ++i)
destr(m_aStorage[i]);
for (int i = 0; i < nbegin; ++i)
destr(m_aStorage[i]);
}
else
{
for (int i = m_xBegin; i < nbegin; ++i)
destr(m_aStorage[i]);
}
m_xBegin = nbegin;
return true;
}
template <class Predicate>
int find_if(Predicate pred)
{
if (m_xEnd < m_xBegin)
{
for (int i = m_xBegin; i < m_iSize; ++i)
if (pred(m_aStorage[i]))
return i - m_xBegin;
for (int i = 0; i < m_xEnd; ++i)
if (pred(m_aStorage[i]))
return i + m_iSize - m_xBegin;
}
else
{
for (int i = m_xBegin; i < m_xEnd; ++i)
if (pred(m_aStorage[i]))
return i - m_xBegin;
}
return -1;
}
};
namespace srt_logging
{
std::string SockStatusStr(SRT_SOCKSTATUS s);
#if ENABLE_EXPERIMENTAL_BONDING
std::string MemberStatusStr(SRT_MEMBERSTATUS s);
#endif
}
inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch)
{
return patch + minor*0x100 + major*0x10000;
}
inline int32_t SrtParseVersion(const char* v)
{
int major, minor, patch;
int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch);
if (result != 3)
{
return 0;
}
return SrtVersion(major, minor, patch);
}
inline std::string SrtVersionString(int version)
{
int patch = version % 0x100;
int minor = (version/0x100)%0x100;
int major = version/0x10000;
char buf[20];
sprintf(buf, "%d.%d.%d", major, minor, patch);
return buf;
}
bool SrtParseConfig(std::string s, SrtConfig& w_config);
struct PacketMetric
{
uint32_t pkts;
uint64_t bytes;
void update(uint64_t size)
{
++pkts;
bytes += size;
}
void update(size_t mult, uint64_t value)
{
pkts += mult;
bytes += mult * value;
}
uint64_t fullBytes();
};
template <class METRIC_TYPE>
struct MetricOp;
template <class METRIC_TYPE>
struct MetricUsage
{
METRIC_TYPE local;
METRIC_TYPE total;
void Clear()
{
MetricOp<METRIC_TYPE>::Clear(local);
}
void Init()
{
MetricOp<METRIC_TYPE>::Clear(total);
Clear();
}
void Update(uint64_t value)
{
local += value;
total += value;
}
void UpdateTimes(size_t mult, uint64_t value)
{
local += mult * value;
total += mult * value;
}
};
template <>
inline void MetricUsage<PacketMetric>::Update(uint64_t value)
{
local.update(value);
total.update(value);
}
template <>
inline void MetricUsage<PacketMetric>::UpdateTimes(size_t mult, uint64_t value)
{
local.update(mult, value);
total.update(mult, value);
}
template <class METRIC_TYPE>
struct MetricOp
{
static void Clear(METRIC_TYPE& m)
{
m = 0;
}
};
template <>
struct MetricOp<PacketMetric>
{
static void Clear(PacketMetric& p)
{
p.pkts = 0;
p.bytes = 0;
}
};
#endif