remove files

This commit is contained in:
skywind3000
2026-05-15 17:04:51 +08:00
parent c2ffc0554f
commit 8aded6f405
5 changed files with 0 additions and 2395 deletions

View File

@@ -1,172 +0,0 @@
//=====================================================================
//
// emulator.h - network emulator for testing KCP implementation
//
// Last Modified: 2026/05/14 15:50:26
//
//=====================================================================
#ifndef _EMULATOR_H_
#define _EMULATOR_H_
#include <stddef.h>
#include <stdint.h>
#include "../ikcp.h"
#include "netstats.h"
#include "netsim.h"
#include <functional>
#include <string>
//---------------------------------------------------------------------
// kcp
//---------------------------------------------------------------------
class Kcp
{
public:
inline ~Kcp();
inline Kcp(IUINT32 conv);
public:
std::function<int(const char *buf, int len)> output;
std::function<void(const char*)> writelog;
inline ikcpcb* ptr() { return _kcp; }
inline const ikcpcb* ptr() const { return _kcp; }
inline int recv(char *buffer, int len);
inline int recv(std::string &buffer);
inline int send(const char *buffer, int len);
inline int send(const std::string &buffer);
inline void update(IUINT32 current);
inline IUINT32 check(IUINT32 current) const;
inline int input(const char *data, long size);
inline void flush();
inline int peeksize() const;
inline int setmtu(int mtu);
inline int wndsize(int sndwnd, int rcvwnd);
inline int waitsnd() const;
inline int nodelay(int nodelay, int interval, int resend, int nc);
inline int setcc(const struct IKCPOPS *ops);
private:
static int udp_output(const char *buf, int len, ikcpcb *kcp, void *user);
static void kcp_writelog(const char *log, ikcpcb *kcp, void *user);
private:
ikcpcb *_kcp;
};
//---------------------------------------------------------------------
// implementation
//---------------------------------------------------------------------
inline Kcp::~Kcp() {
ikcp_release(_kcp);
}
inline Kcp::Kcp(IUINT32 conv) {
_kcp = ikcp_create(conv, this);
_kcp->output = udp_output;
}
inline int Kcp::udp_output(const char *buf, int len, ikcpcb *kcp, void *user) {
Kcp *self = (Kcp *)user;
if (self->output != nullptr) {
return self->output(buf, len);
}
return -1;
}
inline void Kcp::kcp_writelog(const char *log, ikcpcb *kcp, void *user) {
Kcp *self = (Kcp *)user;
if (self->writelog != nullptr) {
self->writelog(log);
}
}
inline int Kcp::recv(char *buffer, int len) {
return ikcp_recv(_kcp, buffer, len);
}
inline int Kcp::recv(std::string &buffer) {
int peek = ikcp_peeksize(_kcp);
if (peek < 0) {
buffer.resize(0);
return peek;
}
buffer.resize(peek);
return ikcp_recv(_kcp, &buffer[0], peek);
}
inline int Kcp::send(const char *buffer, int len) {
return ikcp_send(_kcp, buffer, len);
}
inline int Kcp::send(const std::string &buffer) {
return ikcp_send(_kcp, buffer.c_str(), (int)buffer.size());
}
inline void Kcp::update(IUINT32 current) {
ikcp_update(_kcp, current);
}
inline IUINT32 Kcp::check(IUINT32 current) const {
return ikcp_check(_kcp, current);
}
inline int Kcp::input(const char *data, long size) {
return ikcp_input(_kcp, data, size);
}
inline void Kcp::flush() {
ikcp_flush(_kcp);
}
inline int Kcp::peeksize() const {
return ikcp_peeksize(_kcp);
}
inline int Kcp::setmtu(int mtu) {
return ikcp_setmtu(_kcp, mtu);
}
inline int Kcp::wndsize(int sndwnd, int rcvwnd) {
return ikcp_wndsize(_kcp, sndwnd, rcvwnd);
}
inline int Kcp::waitsnd() const {
return ikcp_waitsnd(_kcp);
}
inline int Kcp::nodelay(int nodelay, int interval, int resend, int nc) {
return ikcp_nodelay(_kcp, nodelay, interval, resend, nc);
}
inline int Kcp::setcc(const struct IKCPOPS *ops) {
return ikcp_setcc(_kcp, ops);
}
//---------------------------------------------------------------------
// KcpPacket
//---------------------------------------------------------------------
struct KcpPacket {
int group;
int direction; // 0 for client->server, 1 for server->client
std::string data;
};
#endif

View File

@@ -1,674 +0,0 @@
//=====================================================================
//
// NetSimulator.cpp - Pure State-Machine Weak Network Simulator
//
// Reimplementation of inetsim.c in standalone C++ (STL only).
// Deterministic, no system calls, portable across projects.
//
//=====================================================================
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <algorithm>
#include <climits>
#include "netsim.h"
//---------------------------------------------------------------------
// namespace System: general-purpose utilities and components
//---------------------------------------------------------------------
NAMESPACE_BEGIN(System);
//---------------------------------------------------------------------
// internal constants
//---------------------------------------------------------------------
static const int FLAG_CORRUPT = 1;
static const int64_t TIME_JUMP_US = 600000000LL;
static const double LOSS_RANDOM_INIT = 5000.0;
static const int MAX_OPTION = 9;
//---------------------------------------------------------------------
// PRNG: xoshiro128++
//---------------------------------------------------------------------
static inline uint32_t Rotl32(uint32_t x, int k)
{
return (x << k) | (x >> (32 - k));
}
static inline uint32_t Xoshiro128PP(uint32_t* s)
{
uint32_t result = Rotl32(s[0] + s[3], 7) + s[0];
uint32_t t = s[1] << 9;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] ^= Rotl32(s[3], 11);
return result;
}
//---------------------------------------------------------------------
// PRNG: SplitMix64 for seeding
//---------------------------------------------------------------------
static inline uint64_t SplitMix64Next(uint64_t* state)
{
uint64_t z = (*state += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
//---------------------------------------------------------------------
// PRNG output mapping
//---------------------------------------------------------------------
static inline double PrngToPermyriad(uint32_t r)
{
return (double)r * 10000.0 / 4294967296.0;
}
static inline double PrngToJitterTable(uint32_t r)
{
return (double)(int32_t)r / 2147483648.0;
}
//---------------------------------------------------------------------
// safe multiply-divide (overflow protection)
//---------------------------------------------------------------------
static int64_t SafeMulDiv(int64_t a, int64_t b, int64_t c)
{
if (a == 0 || b == 0 || c == 0) return 0;
if (a <= INT64_MAX / b) {
return (a * b) / c;
}
int64_t q = a / c;
int64_t r = a % c;
if (q <= INT64_MAX / b) {
return q * b + (r * b) / c;
}
return (int64_t)((double)a * (double)b / (double)c);
}
//---------------------------------------------------------------------
// clamp permyriad value to [0, 10000]
//---------------------------------------------------------------------
static inline int64_t ClampPermyriad(int64_t v)
{
if (v < 0) return 0;
if (v > 10000) return 10000;
return v;
}
//---------------------------------------------------------------------
// config key mapping
//---------------------------------------------------------------------
struct KeyMap {
const char* key;
Option what;
};
static const KeyMap keymap[] = {
{ "delay", Option::Delay },
{ "jitter", Option::Jitter },
{ "delay_corr", Option::DelayCorr },
{ "loss", Option::Loss },
{ "loss_corr", Option::LossCorr },
{ "corrupt", Option::Corrupt },
{ "reorder", Option::Reorder },
{ "rate", Option::Rate },
{ "burst", Option::Burst },
{ "queue", Option::QueueLimit },
};
static const int KEYMAP_SIZE = 10;
//---------------------------------------------------------------------
// config suffix mapping (longest first within each group)
//---------------------------------------------------------------------
struct SuffixMap {
const char* suffix;
int64_t factor;
};
static const SuffixMap suffixes[] = {
/* rate suffixes */
{ "Gbps", 1000000000 },
{ "Mbps", 1000000 },
{ "kbps", 1000 },
{ "bps", 1 },
/* size suffixes */
{ "MB", 1048576 },
{ "KB", 1024 },
{ "B", 1 },
/* time suffixes */
{ "ms", 1000 },
{ "us", 1 },
{ "s", 1000000 },
/* probability suffix */
{ "%", 100 },
};
static const int SUFFIX_SIZE = 11;
//---------------------------------------------------------------------
// config helpers
//---------------------------------------------------------------------
static int FindKey(const char* key, size_t keylen)
{
for (int i = 0; i < KEYMAP_SIZE; i++) {
size_t kl = strlen(keymap[i].key);
if (kl == keylen && strncmp(key, keymap[i].key, keylen) == 0)
return (int)keymap[i].what;
}
return -1;
}
static int ParseValue(const char* val, size_t vallen, int64_t* out)
{
/* try suffixes from longest to shortest */
for (int i = 0; i < SUFFIX_SIZE; i++) {
size_t slen = strlen(suffixes[i].suffix);
if (slen <= vallen) {
size_t numlen = vallen - slen;
if (strncmp(val + numlen, suffixes[i].suffix, slen) == 0 && numlen > 0) {
std::string numstr(val, numlen);
char* endptr;
int64_t num = strtoll(numstr.c_str(), &endptr, 10);
if (endptr != numstr.c_str() + numlen) return -1;
*out = num * suffixes[i].factor;
return 0;
}
}
}
/* no suffix: parse as raw integer */
std::string numstr(val, vallen);
char* endptr;
int64_t num = strtoll(numstr.c_str(), &endptr, 10);
if (endptr != numstr.c_str() + vallen) return -1;
*out = num;
return 0;
}
//---------------------------------------------------------------------
// NetSimulator: constructor
//---------------------------------------------------------------------
NetSimulator::NetSimulator(uint64_t seed)
{
PrngSeed(seed);
_delay = 0;
_jitter = 0;
_delay_corr = 0;
_loss = 0;
_loss_corr = 0;
_corrupt = 0;
_reorder = 0;
_rate = 0;
_burst = -1;
_queue_limit = -1;
_jitter_table_prev = 0.0;
_loss_random_prev = LOSS_RANDOM_INIT;
_next_depart_time = 0;
_current_time = 0;
_next_push_seq = 0;
_pending_bytes = 0;
_stats = {};
}
//---------------------------------------------------------------------
// NetSimulator: destructor
//---------------------------------------------------------------------
NetSimulator::~NetSimulator()
{
assert(_pending.empty() && _immediate.empty());
}
//---------------------------------------------------------------------
// NetSimulator: Push
//---------------------------------------------------------------------
int NetSimulator::Push(void* pkt, size_t size, int64_t time_us)
{
_stats.packets_enqueued++;
_stats.bytes_enqueued += (int64_t)size;
//----- queue_limit check (before pipeline)
if (_queue_limit >= 0) {
size_t current_bytes = _pending_bytes;
if (current_bytes + size > (size_t)_queue_limit) {
_stats.packets_dropped_queue++;
_stats.packets_dropped++;
NetSimEvent evt;
evt.pkt = pkt;
evt.type = EventType::Drop;
evt.time_us = time_us;
_immediate.push_back(evt);
return 0;
}
}
//----- consume 4 random numbers
uint32_t r0 = PrngNext();
uint32_t r1 = PrngNext();
uint32_t r2 = PrngNext();
uint32_t r3 = PrngNext();
//----- loss determination (using r0)
double loss_random_new = PrngToPermyriad(r0);
double loss_random = (_loss_corr / 10000.0) * _loss_random_prev
+ (1.0 - _loss_corr / 10000.0) * loss_random_new;
_loss_random_prev = loss_random;
if (loss_random < (double)_loss) {
_stats.packets_dropped_loss++;
_stats.packets_dropped++;
NetSimEvent evt;
evt.pkt = pkt;
evt.type = EventType::Drop;
evt.time_us = time_us;
_immediate.push_back(evt);
return 0;
}
//----- corrupt determination (using r1)
int flags = 0;
double corrupt_random = PrngToPermyriad(r1);
if (corrupt_random < (double)_corrupt) {
flags |= FLAG_CORRUPT;
_stats.packets_corrupted++;
}
//----- delay/reorder calculation (using r2 and r3)
int64_t time_to_send;
double reorder_random = PrngToPermyriad(r2);
if (reorder_random < (double)_reorder && _delay > 0) {
time_to_send = time_us;
}
else {
double jitter_table_new = PrngToJitterTable(r3);
double jitter_table = (_delay_corr / 10000.0) * _jitter_table_prev
+ (1.0 - _delay_corr / 10000.0) * jitter_table_new;
_jitter_table_prev = jitter_table;
int64_t actual_delay = _delay + (int64_t)(_jitter * jitter_table);
if (actual_delay < 0) actual_delay = 0;
time_to_send = time_us + actual_delay;
}
//----- burst drop check (before entering TBF queue)
if (_rate > 0 && size > (size_t)EffectiveBurst()) {
_stats.packets_dropped_burst++;
_stats.packets_dropped++;
NetSimEvent evt;
evt.pkt = pkt;
evt.type = EventType::Drop;
evt.time_us = time_us;
_immediate.push_back(evt);
return 0;
}
//----- create and insert node
Node node;
node.pkt = pkt;
node.size = size;
node.push_time = time_us;
node.time_to_send = time_to_send;
node.push_seq = _next_push_seq++;
node.flags = flags;
auto it = std::lower_bound(_pending.begin(), _pending.end(), node,
[](const Node& a, const Node& b) {
return a.time_to_send < b.time_to_send ||
(a.time_to_send == b.time_to_send && a.push_seq < b.push_seq);
});
_pending.insert(it, node);
_pending_bytes += size;
return 0;
}
//---------------------------------------------------------------------
// NetSimulator: Update
//---------------------------------------------------------------------
bool NetSimulator::Update(int64_t current_time)
{
if (current_time < _current_time) return false;
int64_t delta = current_time - _current_time;
if (delta > TIME_JUMP_US) {
//----- time jump reset: flush all pending nodes
for (size_t i = 0; i < _pending.size(); i++) {
Node& n = _pending[i];
/* consume 4 PRNG per node (keep sequence consistent) */
PrngNext(); PrngNext(); PrngNext(); PrngNext();
NetSimEvent evt;
evt.pkt = n.pkt;
evt.time_us = current_time;
evt.type = (n.flags & FLAG_CORRUPT)
? EventType::Corrupt : EventType::Sent;
_immediate.push_back(evt);
}
_pending.clear();
_pending_bytes = 0;
//----- reset FIFO departure and correlation state
_next_depart_time = 0;
_jitter_table_prev = 0.0;
_loss_random_prev = LOSS_RANDOM_INIT;
_current_time = current_time;
return true;
}
_current_time = current_time;
return true;
}
//---------------------------------------------------------------------
// NetSimulator: Poll
//---------------------------------------------------------------------
bool NetSimulator::Poll(NetSimEvent& evt)
{
int64_t imm_time = INT64_MAX;
int64_t pend_time = INT64_MAX;
if (!_immediate.empty())
imm_time = _immediate[0].time_us;
if (!_pending.empty()) {
Node& first = _pending[0];
int64_t actual = first.time_to_send;
if (_rate > 0 && actual < _next_depart_time)
actual = _next_depart_time;
if (actual <= _current_time)
pend_time = actual;
}
if (imm_time == INT64_MAX && pend_time == INT64_MAX)
return false;
//----- same time: DROP (immediate) takes priority
if (imm_time <= pend_time) {
evt = _immediate[0];
_immediate.erase(_immediate.begin());
return true;
}
//----- pending node is ready and has earlier time
Node& node = _pending[0];
int64_t actual = node.time_to_send;
if (_rate > 0 && actual < _next_depart_time)
actual = _next_depart_time;
if (_rate > 0) {
int64_t tx_time = SafeMulDiv((int64_t)node.size * 8, 1000000, _rate);
_next_depart_time = actual + tx_time;
}
_pending_bytes -= node.size;
_stats.packets_sent++;
_stats.bytes_sent += (int64_t)node.size;
evt.pkt = node.pkt;
evt.time_us = actual;
evt.type = (node.flags & FLAG_CORRUPT)
? EventType::Corrupt : EventType::Sent;
_pending.erase(_pending.begin());
return true;
}
//---------------------------------------------------------------------
// NetSimulator: Drain
//---------------------------------------------------------------------
bool NetSimulator::Drain(NetSimEvent& evt)
{
//----- check immediate first
if (!_immediate.empty()) {
evt = _immediate[0];
_immediate.erase(_immediate.begin());
return true;
}
//----- then pending
if (!_pending.empty()) {
Node& node = _pending[0];
int64_t actual = node.time_to_send;
if (_rate > 0 && actual < _next_depart_time)
actual = _next_depart_time;
if (_rate > 0) {
int64_t tx_time = SafeMulDiv((int64_t)node.size * 8, 1000000, _rate);
_next_depart_time = actual + tx_time;
}
_pending_bytes -= node.size;
_stats.packets_sent++;
_stats.bytes_sent += (int64_t)node.size;
evt.pkt = node.pkt;
evt.time_us = actual;
evt.type = (node.flags & FLAG_CORRUPT)
? EventType::Corrupt : EventType::Sent;
_pending.erase(_pending.begin());
return true;
}
return false;
}
//---------------------------------------------------------------------
// NetSimulator: NextTime
//---------------------------------------------------------------------
int64_t NetSimulator::NextTime() const
{
if (!_immediate.empty())
return _immediate[0].time_us;
if (!_pending.empty()) {
int64_t ts = _pending[0].time_to_send;
if (_rate > 0 && ts < _next_depart_time)
ts = _next_depart_time;
return ts;
}
return INT64_MAX;
}
//---------------------------------------------------------------------
// NetSimulator: SetOption
//---------------------------------------------------------------------
int NetSimulator::SetOption(Option what, int64_t value)
{
int w = (int)what;
if (w < 0 || w > MAX_OPTION) return -1;
switch (what) {
case Option::Delay: _delay = value; break;
case Option::Jitter: _jitter = value; break;
case Option::DelayCorr: _delay_corr = ClampPermyriad(value); break;
case Option::Loss: _loss = ClampPermyriad(value); break;
case Option::LossCorr: _loss_corr = ClampPermyriad(value); break;
case Option::Corrupt: _corrupt = ClampPermyriad(value); break;
case Option::Reorder: _reorder = ClampPermyriad(value); break;
case Option::Rate: _rate = value; break;
case Option::Burst: _burst = value; break;
case Option::QueueLimit: _queue_limit = value; break;
default: return -1;
}
return 0;
}
//---------------------------------------------------------------------
// NetSimulator: Config
//---------------------------------------------------------------------
int NetSimulator::Config(const char* str)
{
//----- cache old values for rollback
int64_t old[(int)Option::QueueLimit + 1];
old[(int)Option::Delay] = _delay;
old[(int)Option::Jitter] = _jitter;
old[(int)Option::DelayCorr] = _delay_corr;
old[(int)Option::Loss] = _loss;
old[(int)Option::LossCorr] = _loss_corr;
old[(int)Option::Corrupt] = _corrupt;
old[(int)Option::Reorder] = _reorder;
old[(int)Option::Rate] = _rate;
old[(int)Option::Burst] = _burst;
old[(int)Option::QueueLimit] = _queue_limit;
const char* p = str;
while (*p) {
// skip whitespace
while (*p == ' ' || *p == '\t') p++;
if (*p == '\0') break;
// find '=' separator
const char* eq = strchr(p, '=');
if (!eq) goto rollback;
size_t keylen = (size_t)(eq - p);
const char* val_start = eq + 1;
// find end of value (next whitespace or end)
const char* val_end = val_start;
while (*val_end && *val_end != ' ' && *val_end != '\t')
val_end++;
size_t vallen = (size_t)(val_end - val_start);
if (keylen == 0 || vallen == 0) goto rollback;
// find key
int what = FindKey(p, keylen);
if (what < 0) goto rollback;
// parse value
int64_t value;
if (ParseValue(val_start, vallen, &value) < 0)
goto rollback;
// apply option
if (SetOption((Option)what, value) < 0) goto rollback;
p = val_end;
}
return 0;
rollback:
_delay = old[(int)Option::Delay];
_jitter = old[(int)Option::Jitter];
_delay_corr = old[(int)Option::DelayCorr];
_loss = old[(int)Option::Loss];
_loss_corr = old[(int)Option::LossCorr];
_corrupt = old[(int)Option::Corrupt];
_reorder = old[(int)Option::Reorder];
_rate = old[(int)Option::Rate];
_burst = old[(int)Option::Burst];
_queue_limit = old[(int)Option::QueueLimit];
return -1;
}
int NetSimulator::Config(const std::string& str)
{
return Config(str.c_str());
}
//---------------------------------------------------------------------
// NetSimulator: QueuedBytes
//---------------------------------------------------------------------
size_t NetSimulator::QueuedBytes() const
{
return _pending_bytes;
}
//---------------------------------------------------------------------
// NetSimulator: QueuedCount
//---------------------------------------------------------------------
size_t NetSimulator::QueuedCount() const
{
return _pending.size() + _immediate.size();
}
//---------------------------------------------------------------------
// NetSimulator: GetStats
//---------------------------------------------------------------------
NetSimStats NetSimulator::GetStats() const
{
NetSimStats stats = _stats;
stats.packets_dropped = stats.packets_dropped_loss
+ stats.packets_dropped_burst
+ stats.packets_dropped_queue;
return stats;
}
//---------------------------------------------------------------------
// NetSimulator: EffectiveBurst
//---------------------------------------------------------------------
int64_t NetSimulator::EffectiveBurst() const
{
if (_burst < 0) {
int64_t auto_burst = _rate / 8000;
return (auto_burst < 1600) ? 1600 : auto_burst;
}
return _burst;
}
//---------------------------------------------------------------------
// NetSimulator: PrngNext
//---------------------------------------------------------------------
uint32_t NetSimulator::PrngNext()
{
return Xoshiro128PP(_prng);
}
//---------------------------------------------------------------------
// NetSimulator: PrngSeed
//---------------------------------------------------------------------
void NetSimulator::PrngSeed(uint64_t seed)
{
uint64_t sm_state = seed;
uint64_t v0 = SplitMix64Next(&sm_state);
_prng[0] = (uint32_t)(v0);
_prng[1] = (uint32_t)(v0 >> 32);
uint64_t v1 = SplitMix64Next(&sm_state);
_prng[2] = (uint32_t)(v1);
_prng[3] = (uint32_t)(v1 >> 32);
}
//---------------------------------------------------------------------
// namespace end
//---------------------------------------------------------------------
NAMESPACE_END(System);

View File

@@ -1,272 +0,0 @@
//=====================================================================
//
// NetSimulator.h - Pure State-Machine Weak Network Simulator
//
// Reimplementation of inetsim.c in standalone C++ (STL only).
// Deterministic, no system calls, portable across projects.
//
// Rate limiting uses a FIFO serialization model: packets depart
// in order, each waiting for the previous one's transmission time.
// This means evt.time_us = max(propagation_delay_ready, wire_free_time),
// naturally producing queuing delay that increases with queue depth —
// the signal BBR and WebRTC GCC rely on for congestion detection.
//
// Packet lifetime management:
//
// The simulator NEVER copies, frees, or owns the packet data.
// Push() accepts an opaque void* pointer; the same pointer is
// returned verbatim in every output event (Sent, Drop, Corrupt).
// It is the caller's responsibility to:
// 1. Keep the pointed-to object alive until the event is polled.
// 2. Free or recycle the object AFTER Poll/Drain returns it.
// A typical pattern is to allocate before Push and deallocate
// inside the Poll loop — since every pushed packet is guaranteed
// to produce exactly one output event (never silently swallowed).
//
// Usage example:
//
// #include "NetSimulator.h"
// using namespace System;
//
// // Create simulator with seed (same seed = deterministic replay)
// NetSimulator sim(12345);
//
// // Configure network impairments
// sim.Config("delay=50ms jitter=10ms loss=5% reorder=2% rate=1Mbps");
//
// // Push packets into the simulator at given timestamps
// sim.Push((void*)"pkt1", 100, 0); // 100-byte packet at t=0
// sim.Push((void*)"pkt2", 200, 1000); // 200-byte packet at t=1ms
//
// // Advance clock and poll for output events
// sim.Update(50000); // advance to t=50ms
// NetSimEvent evt;
// while (sim.Poll(evt)) {
// switch (evt.type) {
// case EventType::Sent: /* packet delivered */ break;
// case EventType::Drop: /* packet lost */ break;
// case EventType::Corrupt: /* packet delivered but bad */ break;
// }
// }
//
// // Drain remaining packets before destruction
// while (sim.Drain(evt)) { /* handle final events */ }
//
//=====================================================================
#ifndef _NET_SIMULATOR_H_
#define _NET_SIMULATOR_H_
#ifndef __cplusplus
#error This file can only be compiled in C++ mode !!
#endif
#include <cstdint>
#include <cstddef>
#include <string>
#include <vector>
#ifndef NAMESPACE_BEGIN
#define NAMESPACE_BEGIN(x) namespace x {
#endif
#ifndef NAMESPACE_END
#define NAMESPACE_END(x) }
#endif
//---------------------------------------------------------------------
// namespace System: general-purpose utilities and components
//---------------------------------------------------------------------
NAMESPACE_BEGIN(System);
//---------------------------------------------------------------------
// EventType: event types emitted by NetSimulator
//---------------------------------------------------------------------
enum class EventType {
Sent = 1, // Packet passed through the simulator normally
Drop = 2, // Packet was dropped (loss, burst, or queue overflow)
Corrupt = 3, // Packet passed but marked as corrupted
};
//---------------------------------------------------------------------
// Option: configuration parameters
// Permyriad values range 0-10000 (e.g. 1000 = 10%, 10000 = 100%).
// Negative values for Burst/QueueLimit mean "auto" / "unlimited".
//---------------------------------------------------------------------
enum class Option {
Delay = 0, // Base delay in microseconds, default 0
Jitter = 1, // Jitter amplitude in microseconds, default 0
DelayCorr = 2, // Delay correlation in permyriad, default 0
Loss = 3, // Loss probability in permyriad, default 0
LossCorr = 4, // Loss correlation in permyriad, default 0
Corrupt = 5, // Corrupt probability in permyriad, default 0
Reorder = 6, // Reorder probability in permyriad, default 0
Rate = 7, // Bandwidth limit in bits/s, default 0 (no limit)
Burst = 8, // Max burst size in bytes (drop packets exceeding this), default -1 (auto)
QueueLimit = 9, // Max queued bytes, default -1 (unlimited)
};
//---------------------------------------------------------------------
// NetSimEvent: output event structure
//---------------------------------------------------------------------
struct NetSimEvent {
void* pkt; // Caller-owned packet pointer (from Push)
EventType type; // Sent, Drop, or Corrupt
int64_t time_us; // Event time in microseconds
};
//---------------------------------------------------------------------
// NetSimStats: statistics counters
//---------------------------------------------------------------------
struct NetSimStats {
int64_t packets_enqueued; // Total packets pushed into simulator
int64_t packets_sent; // Total packets successfully sent
int64_t packets_dropped_loss; // Packets dropped by random loss
int64_t packets_dropped_burst; // Packets dropped by burst overflow
int64_t packets_dropped_queue; // Packets dropped by queue_limit overflow
int64_t packets_dropped; // Total dropped (= loss + burst + queue)
int64_t packets_corrupted; // Packets marked as corrupted
int64_t bytes_enqueued; // Total bytes pushed into simulator
int64_t bytes_sent; // Total bytes successfully sent
};
//---------------------------------------------------------------------
// NetSimulator: pure state-machine weak network simulator
//---------------------------------------------------------------------
class NetSimulator
{
public:
// Construct simulator with given PRNG seed.
// Same seed + same input produces identical output (deterministic).
NetSimulator(uint64_t seed);
// Destructor. Asserts that both queues are empty in debug mode.
// Call Drain() in a loop before destruction to avoid the assert.
~NetSimulator();
//----- core operations -----
// Enqueue a packet into the simulator.
// pkt: caller-owned pointer, returned verbatim in output events.
// size: packet size in bytes (used for rate serialization and queue_limit).
// time_us: enqueue time in microseconds.
// Returns 0 on success. Packet may result in Sent, Drop, or Corrupt event.
int Push(void* pkt, size_t size, int64_t time_us);
// Advance the simulator clock to current_time.
// Returns true if time advanced normally.
// Returns false if current_time < previous clock (time backward).
// If delta > 10 minutes, triggers time-jump reset (flushes queues).
bool Update(int64_t current_time);
// Retrieve the next due event (by time priority).
// Immediate (DROP) events take priority over pending events at same time.
// For pending events under rate limiting, actual_send_time = max(time_to_send, wire_free_time).
// Returns true and fills evt if an event is available, false otherwise.
bool Poll(NetSimEvent& evt);
// Retrieve all remaining events regardless of time or token constraints.
// Used before destruction to empty the queues.
// Returns true and fills evt if an event remains, false if both queues empty.
bool Drain(NetSimEvent& evt);
// Return the time_us of the next upcoming event.
// Checks immediate queue first, then pending queue.
// Returns INT64_MAX if both queues are empty.
int64_t NextTime() const;
//----- configuration -----
// Set a single simulation parameter.
// Permyraid-type options (Loss, Corrupt, etc.) are clamped to [0, 10000].
// Returns 0 on success, -1 for invalid Option value.
int SetOption(Option what, int64_t value);
// Batch configure from a key=value string, e.g. "delay=50ms loss=10%".
// Supported units: us, ms, s, bps, kbps, Mbps, Gbps, B, KB, MB, %.
// All-or-nothing: if any key=value fails, all parameters roll back.
// Returns 0 on success, -1 on parse failure (parameters unchanged).
int Config(const char* str);
// std::string overload for Config.
int Config(const std::string& str);
//----- query -----
// Total bytes currently held in the pending queue.
// Does not include immediate queue (DROP events are not "queued data").
size_t QueuedBytes() const;
// Total number of events in both pending and immediate queues.
size_t QueuedCount() const;
// Return a copy of the accumulated statistics.
// packets_dropped is computed as the sum of loss + burst + queue drops.
NetSimStats GetStats() const;
private:
struct Node {
void* pkt;
size_t size;
int64_t push_time;
int64_t time_to_send;
uint32_t push_seq;
int flags;
};
int64_t EffectiveBurst() const;
uint32_t PrngNext();
void PrngSeed(uint64_t seed);
// PRNG state
uint32_t _prng[4];
// configuration
int64_t _delay;
int64_t _jitter;
int64_t _delay_corr;
int64_t _loss;
int64_t _loss_corr;
int64_t _corrupt;
int64_t _reorder;
int64_t _rate;
int64_t _burst;
int64_t _queue_limit;
// correlation state
double _jitter_table_prev;
double _loss_random_prev;
// FIFO departure: time when last bit of previous packet left the wire
int64_t _next_depart_time;
// clock
int64_t _current_time;
// queues
std::vector<Node> _pending;
std::vector<NetSimEvent> _immediate;
// push sequence counter
uint32_t _next_push_seq;
// running total of pending bytes
size_t _pending_bytes;
// statistics
NetSimStats _stats;
};
//---------------------------------------------------------------------
// namespace end
//---------------------------------------------------------------------
NAMESPACE_END(System);
#endif // _NET_SIMULATOR_H_

View File

@@ -1,926 +0,0 @@
//=====================================================================
//
// AbstractStats.cpp -
//
// Last Modified: 2020/04/03 17:47:58
//
//=====================================================================
#include <assert.h>
#include <limits>
#include "netstats.h"
//---------------------------------------------------------------------
// Namespace begin
//---------------------------------------------------------------------
NAMESPACE_BEGIN(System);
//=====================================================================
// unsined integer unwrapper
//=====================================================================
UnsignedWrap::UnsignedWrap()
{
size = 16;
last_value = -1;
}
UnsignedWrap::UnsignedWrap(int size)
{
this->size = 16;
last_value = -1;
reset(size);
}
UnsignedWrap::UnsignedWrap(const UnsignedWrap& src)
{
size = src.size;
last_value = src.last_value;
}
UnsignedWrap& UnsignedWrap::operator = (const UnsignedWrap& src)
{
size = src.size;
last_value = src.last_value;
return *this;
}
void UnsignedWrap::reset(int size)
{
if (size == 16 || size == 2) {
this->size = 16;
}
else if (size == 32 || size == 4) {
this->size = 32;
}
else {
this->size = size;
}
assert(size == 16 || size == 2 || size == 32 || size == 4);
last_value = -1;
}
void UnsignedWrap::set_last(int64_t last)
{
last_value = last;
}
bool UnsignedWrap::u16_is_newer(uint16_t val, uint16_t prev_val)
{
const uint16_t half = ((uint16_t)0x8000);
if (val - prev_val == half)
return (val > prev_val)? true : false;
if (val != prev_val && ((uint16_t)(val - prev_val)) < half)
return true;
return false;
}
bool UnsignedWrap::u32_is_newer(uint32_t val, uint32_t prev_val)
{
const uint32_t half = ((uint32_t)0x80000000);
if (val - prev_val == half)
return (val > prev_val)? true : false;
if (val != prev_val && ((uint32_t)(val - prev_val)) < half)
return true;
return false;
}
int64_t UnsignedWrap::u16_update(uint16_t val)
{
const int64_t max_plus = ((int64_t)0xffff) + 1;
uint16_t cropped_last = (uint16_t)(last_value & 0xffff);
int64_t delta = ((int64_t)val) - ((int64_t)cropped_last);
if (u16_is_newer(val, cropped_last)) {
if (delta < 0) {
delta += max_plus;
}
}
else if (delta > 0 && last_value + delta - max_plus >= 0) {
delta -= max_plus;
}
return last_value + delta;
}
int64_t UnsignedWrap::u32_update(uint32_t val)
{
const int64_t max_plus = ((int64_t)0xffffffff) + 1;
uint32_t cropped_last = (uint32_t)(last_value & 0xffffffff);
int64_t delta = ((int64_t)val) - ((int64_t)cropped_last);
if (u32_is_newer(val, cropped_last)) {
if (delta < 0) {
delta += max_plus;
}
}
else if (delta > 0 && last_value + delta - max_plus >= 0) {
delta -= max_plus;
}
return last_value + delta;
}
int64_t UnsignedWrap::wrap_uint16(uint16_t val)
{
assert(size == 16);
if (last_value < 0) {
last_value = val;
} else {
last_value = u16_update(val);
}
return last_value;
}
int64_t UnsignedWrap::wrap_uint32(uint32_t val)
{
assert(size == 32);
if (last_value < 0) {
last_value = val;
} else {
last_value = u32_update(val);
}
return last_value;
}
//=====================================================================
// 丢包统计
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats()
{
init(4000, 200, 100);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
AbstractLossStats::~AbstractLossStats()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats(const AbstractLossStats& src):
_loss_window(src._loss_window),
_wrapper(src._wrapper),
_stat_ts(src._stat_ts),
_max_id(src._max_id),
_k_loss_stats_window_ms(src._k_loss_stats_window_ms),
_k_max_stats_window_num(src._k_max_stats_window_num),
_k_calculation_limit(src._k_calculation_limit)
{
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
AbstractLossStats::AbstractLossStats(AbstractLossStats&& src):
_loss_window(std::move(src._loss_window)),
_wrapper(src._wrapper),
_stat_ts(src._stat_ts),
_max_id(src._max_id),
_k_loss_stats_window_ms(src._k_loss_stats_window_ms),
_k_max_stats_window_num(src._k_max_stats_window_num),
_k_calculation_limit(src._k_calculation_limit)
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
AbstractLossStats& AbstractLossStats::operator = (const AbstractLossStats& src)
{
_loss_window.clear();
for (auto it: src._loss_window) {
_loss_window[it.first] = it.second;
}
_wrapper = src._wrapper;
_stat_ts = src._stat_ts;
_max_id = src._max_id;
_k_loss_stats_window_ms = src._k_loss_stats_window_ms;
_k_max_stats_window_num = src._k_max_stats_window_num;
_k_calculation_limit = src._k_calculation_limit;
return *this;
}
//---------------------------------------------------------------------
// 初始化统计
//---------------------------------------------------------------------
void AbstractLossStats::init(int window_ms, int window_num, int limit)
{
if (limit < 10) limit = 10;
if (window_num <= limit) window_num = limit + 1;
_k_loss_stats_window_ms = window_ms;
_k_max_stats_window_num = window_num;
_k_calculation_limit = limit;
_stat_ts = -1;
_max_id = -1;
_loss_window.clear();
_wrapper.reset(16);
}
//---------------------------------------------------------------------
// 复位统计
//---------------------------------------------------------------------
void AbstractLossStats::reset()
{
_stat_ts = -1;
_max_id = -1;
_loss_window.clear();
_wrapper.reset(16);
}
//---------------------------------------------------------------------
// 淘汰超过窗口的太老的数据
//---------------------------------------------------------------------
void AbstractLossStats::evict_oldest(int64_t now_ts)
{
while (_loss_window.size() > 0) {
LossWindow::iterator it = _loss_window.begin();
bool drop = false;
if ((int)_loss_window.size() > _k_max_stats_window_num) {
drop = true;
}
else if (it->second + _k_loss_stats_window_ms < now_ts) {
drop = true;
}
if (drop == false) {
break;
}
else {
_loss_window.erase(it);
}
}
}
//---------------------------------------------------------------------
// 收到一个包时调用
//---------------------------------------------------------------------
void AbstractLossStats::update(uint16_t seq, int64_t now_ts)
{
int64_t id = _wrapper.wrap_uint16(seq);
if (_max_id < id) {
_max_id = id;
}
_loss_window[id] = now_ts;
evict_oldest(now_ts);
}
//---------------------------------------------------------------------
// 计算丢包率fraction_loss 是返回的小数丢包率255 代表 100%
//---------------------------------------------------------------------
int AbstractLossStats::calculate(int64_t now_ts, uint8_t *fraction_loss, int *num)
{
*fraction_loss = 0;
*num = 0;
evict_oldest(now_ts);
if (_max_id < 0) {
return -1;
}
if ((int)_loss_window.size() < _k_calculation_limit) {
// printf("not enough: size=%d limit=%d\n", (int)_loss_window.size(), _k_calculation_limit);
return -2;
}
int count = (int)_loss_window.size();
if (count <= 0) {
return -3;
}
auto it = _loss_window.begin();
int64_t oldest = it->first;
int distance = (int32_t)(_max_id - oldest + 1);
if (distance <= 0)
return -4;
if (distance <= count) {
*fraction_loss = 0;
}
else {
*fraction_loss = (distance - count) * 255 / distance;
}
*num = distance;
_stat_ts = now_ts;
return 0;
}
//=====================================================================
// 速率统计
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats()
{
_wnd_size = 2000;
_scale = 8000;
init(2000, 8000);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
AbstractRateStats::~AbstractRateStats()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats(const AbstractRateStats& src):
_buckets(src._buckets),
_oldest_ts(src._oldest_ts),
_oldest_index(src._oldest_index),
_wnd_size(src._wnd_size),
_scale(src._scale),
_accumulated_count(src._accumulated_count),
_sample_num(src._sample_num)
{
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
AbstractRateStats::AbstractRateStats(AbstractRateStats&& src):
_buckets(std::move(src._buckets)),
_oldest_ts(src._oldest_ts),
_oldest_index(src._oldest_index),
_wnd_size(src._wnd_size),
_scale(src._scale),
_accumulated_count(src._accumulated_count),
_sample_num(src._sample_num)
{
}
//---------------------------------------------------------------------
// copy assign
//---------------------------------------------------------------------
AbstractRateStats& AbstractRateStats::operator = (const AbstractRateStats& src)
{
_buckets = src._buckets;
_oldest_ts = src._oldest_ts;
_oldest_index = src._oldest_index;
_wnd_size = src._wnd_size;
_scale = src._scale;
_accumulated_count = src._accumulated_count;
_sample_num = src._sample_num;
return *this;
}
//---------------------------------------------------------------------
// initialize
//---------------------------------------------------------------------
void AbstractRateStats::init(int wnd_size, float scale)
{
if (wnd_size < 0) wnd_size = _wnd_size;
if (scale < 0) scale = _scale;
_wnd_size = wnd_size;
_scale = scale;
_buckets.resize(wnd_size);
reset();
}
//---------------------------------------------------------------------
// reset
//---------------------------------------------------------------------
void AbstractRateStats::reset()
{
_buckets.resize(_wnd_size);
_accumulated_count = 0;
_sample_num = 0;
_oldest_index = 0;
_oldest_ts = -1;
for (int i = 0; i < _wnd_size; i++) {
_buckets[i].sum = 0;
_buckets[i].sample = 0;
}
}
//---------------------------------------------------------------------
// 删除过期数据
//---------------------------------------------------------------------
void AbstractRateStats::evict_oldest(int64_t now_ts)
{
if (_oldest_ts < 0)
return;
int64_t new_oldest_ts = now_ts - _wnd_size + 1;
if (new_oldest_ts < _oldest_ts)
return;
while (_sample_num > 0 && _oldest_ts < new_oldest_ts) {
RateBucket& bucket = _buckets[_oldest_index];
_sample_num -= bucket.sample;
_accumulated_count -= bucket.sum;
bucket.sum = 0;
bucket.sample = 0;
if (++_oldest_index >= _wnd_size) {
_oldest_index = 0;
}
_oldest_ts++;
}
_oldest_ts = new_oldest_ts;
}
//---------------------------------------------------------------------
// 收到一个包时调用
//---------------------------------------------------------------------
void AbstractRateStats::update(size_t count, int64_t now_ts)
{
if (_oldest_ts > now_ts)
return;
evict_oldest(now_ts);
if (_oldest_ts < 0) {
_oldest_ts = now_ts;
}
int offset = (int)(now_ts - _oldest_ts);
int index = (_oldest_index + offset) % _wnd_size;
_sample_num++;
_buckets[index].sum += (int)count;
_buckets[index].sample++;
_accumulated_count += count;
}
//---------------------------------------------------------------------
// 统计码率,数据不够返回 -1成功返回速率
//---------------------------------------------------------------------
int AbstractRateStats::calculate(int64_t now_ts)
{
evict_oldest(now_ts);
int active_wnd_size = (int)((int64_t)(now_ts - _oldest_ts + 1));
if (_sample_num <= 0 || active_wnd_size <= 1 || active_wnd_size < _wnd_size)
return -1;
double rate = ((((double)_accumulated_count) * _scale) / _wnd_size) + 0.5;
return (int)rate;
}
//---------------------------------------------------------------------
// Minimum Sliding Window
//---------------------------------------------------------------------
MinHistory::MinHistory()
{
init(4000, false);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MinHistory::~MinHistory()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
MinHistory::MinHistory(const MinHistory& src)
{
this->operator=(src);
}
//---------------------------------------------------------------------
// moving ctor
//---------------------------------------------------------------------
MinHistory::MinHistory(MinHistory&& src):
_history(std::move(src._history)),
_wnd_size(src._wnd_size),
_reverse(src._reverse)
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
MinHistory& MinHistory::operator = (const MinHistory& src)
{
_history.clear();
for (auto &x : src._history) {
_history.push_back(x);
}
_wnd_size = src._wnd_size;
_reverse = src._reverse;
return *this;
}
//---------------------------------------------------------------------
// mode: 0 for minimal, 1 for maximal, win size in millisecs
//---------------------------------------------------------------------
void MinHistory::init(int wnd_size, bool reverse)
{
_wnd_size = wnd_size;
_reverse = reverse;
_history.clear();
}
//---------------------------------------------------------------------
// clear history
//---------------------------------------------------------------------
void MinHistory::clear()
{
_history.clear();
}
//---------------------------------------------------------------------
// update value
//---------------------------------------------------------------------
void MinHistory::update(int value, int64_t now_ts)
{
while (!_history.empty()) {
if (now_ts - _history.front().first + 1 <= _wnd_size) break;
_history.pop_front();
}
while (!_history.empty()) {
if (_reverse == false) {
if (_history.back().second < value) break;
}
else {
if (_history.back().second > value) break;
}
_history.pop_back();
}
_history.push_back(std::make_pair(now_ts, value));
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory()
{
_min_history.init(4000, true);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MaxHistory::~MaxHistory()
{
}
//---------------------------------------------------------------------
// copy ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory(const MaxHistory& src)
{
_min_history = src._min_history;
}
//---------------------------------------------------------------------
// move ctor
//---------------------------------------------------------------------
MaxHistory::MaxHistory(MaxHistory&& src):
_min_history(std::move(src._min_history))
{
}
//---------------------------------------------------------------------
// copy assignment
//---------------------------------------------------------------------
MaxHistory& MaxHistory::operator=(const MaxHistory &src)
{
_min_history = src._min_history;
return *this;
}
//---------------------------------------------------------------------
// init
//---------------------------------------------------------------------
void MaxHistory::init(int wnd_size)
{
_min_history.init(wnd_size, true);
}
//---------------------------------------------------------------------
// clear
//---------------------------------------------------------------------
void MaxHistory::clear()
{
_min_history.clear();
}
//---------------------------------------------------------------------
// update
//---------------------------------------------------------------------
void MaxHistory::update(int value, int64_t now_ts)
{
_min_history.update(value, now_ts);
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
MovingAverage::MovingAverage()
{
_wnd_size = 5000;
_sum = 0;
_average = 0;
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
MovingAverage::~MovingAverage()
{
}
//---------------------------------------------------------------------
// reset
//---------------------------------------------------------------------
void MovingAverage::init(int wnd_size)
{
_wnd_size = wnd_size;
}
//---------------------------------------------------------------------
// clear
//---------------------------------------------------------------------
void MovingAverage::clear()
{
_history.clear();
_sum = 0;
_average = 0;
}
//---------------------------------------------------------------------
// update value
//---------------------------------------------------------------------
void MovingAverage::update(int value, int64_t now_ts)
{
while (!_history.empty()) {
if (now_ts - _history.front().first + 1 <= _wnd_size) break;
_sum -= _history.front().second;
_history.pop_front();
}
_history.push_back(std::make_pair(now_ts, value));
_sum += value;
_average = (int)(_sum / _history.size());
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
RttHistory::RttHistory()
{
init(200);
}
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
RttHistory::~RttHistory()
{
}
//---------------------------------------------------------------------
// initialize, wnd_size is the number of samples to keep
//---------------------------------------------------------------------
void RttHistory::init(int wnd_size)
{
_wnd_size = wnd_size;
_sum = 0;
_avg = 0;
_srtt = -1;
_rttval = -1;
_rto = 0;
_deviation = 0;
_min = 0;
_max = 0;
_jitter = 0;
_samples.clear();
}
//---------------------------------------------------------------------
// clear history
//---------------------------------------------------------------------
void RttHistory::clear()
{
_samples.clear();
}
//---------------------------------------------------------------------
// push a new rtt sample
//---------------------------------------------------------------------
void RttHistory::push(double rtt)
{
_samples.push_back(rtt);
_sum += rtt;
if ((int)_samples.size() > _wnd_size) {
_sum -= _samples.front();
_samples.pop_front();
}
if (_samples.size() > 0) {
_avg = _sum / _samples.size();
double sum = 0;
double minimum = 0;
double maximum = 0;
bool first = true;
for (double rtt : _samples) {
double diff = rtt - _avg;
sum += diff * diff;
if (first) {
minimum = rtt;
maximum = rtt;
first = false;
}
if (rtt < minimum) minimum = rtt;
if (rtt > maximum) maximum = rtt;
}
_deviation = sum / _samples.size();
_min = minimum;
_max = maximum;
_jitter = (maximum - minimum) / 2;
}
if (_srtt < 0) {
_srtt = rtt;
_rttval = rtt / 2;
} else {
double delta = rtt - _srtt;
if (delta < 0) delta = -delta;
_rttval = (3 * _rttval + delta) / 4;
_srtt = (7 * _srtt + rtt) / 8;
if (_srtt < 0) _srtt = 0;
}
_rto = _srtt + (_rttval < 0? 0 : (4 * _rttval));
}
//=====================================================================
// 带宽预算Token Bucket
//=====================================================================
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
PacingBudget::PacingBudget()
{
init(0);
}
//---------------------------------------------------------------------
// 初始化bandwidth 为带宽bytes/secburst 默认等于 bandwidth
//---------------------------------------------------------------------
void PacingBudget::init(int64_t bandwidth_bps)
{
_bandwidth = bandwidth_bps;
_burst = bandwidth_bps;
_budget = 0;
_last_time = -1;
_remainder = 0;
}
//---------------------------------------------------------------------
// 重置状态
//---------------------------------------------------------------------
void PacingBudget::reset()
{
_budget = 0;
_last_time = -1;
_remainder = 0;
}
//---------------------------------------------------------------------
// 查询当前可发送字节数(内部自动更新时间,累加预算)
//---------------------------------------------------------------------
int64_t PacingBudget::available(int64_t now_ms)
{
if (_last_time < 0) {
_last_time = now_ms;
return _budget;
}
int64_t elapsed = now_ms - _last_time;
if (elapsed <= 0) {
return (_budget > 0) ? _budget : 0;
}
_last_time = now_ms;
// 累加预算bandwidth * elapsed / 1000用 remainder 保留余数
int64_t total = _bandwidth * elapsed + _remainder;
int64_t increment = total / 1000;
_remainder = total % 1000;
_budget += increment;
// 限制预算不超过 burst
if (_budget > _burst) {
_budget = _burst;
_remainder = 0;
}
return (_budget > 0) ? _budget : 0;
}
//---------------------------------------------------------------------
// 登记实际发送的字节数,扣减预算
//---------------------------------------------------------------------
void PacingBudget::consume(int64_t bytes_sent)
{
_budget -= bytes_sent;
}
//---------------------------------------------------------------------
// 计算发送 bytes_needed 还需等多少毫秒(已够返回 0
//---------------------------------------------------------------------
int64_t PacingBudget::wait_time(int64_t bytes_needed) const
{
int64_t deficit = bytes_needed - _budget;
if (deficit <= 0) {
return 0;
}
if (_bandwidth <= 0) {
return -1;
}
return (deficit * 1000 + _bandwidth - 1) / _bandwidth;
}
//---------------------------------------------------------------------
// namespace end
//---------------------------------------------------------------------
NAMESPACE_END(System);

View File

@@ -1,351 +0,0 @@
//=====================================================================
//
// AbstractStats.h -
//
// Last Modified: 2020/04/03 17:47:56
//
//=====================================================================
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include <map>
#include <deque>
#ifndef NAMESPACE_BEGIN
#define NAMESPACE_BEGIN(x) namespace x {
#endif
#ifndef NAMESPACE_END
#define NAMESPACE_END(x) }
#endif
NAMESPACE_BEGIN(System);
//---------------------------------------------------------------------
// unsined integer unwrapper
//---------------------------------------------------------------------
class UnsignedWrap
{
public:
UnsignedWrap();
UnsignedWrap(int size);
UnsignedWrap(const UnsignedWrap& src);
UnsignedWrap& operator = (const UnsignedWrap& src);
public:
int64_t wrap_uint16(uint16_t val);
int64_t wrap_uint32(uint32_t val);
void reset(int size);
void set_last(int64_t last);
protected:
static bool u16_is_newer(uint16_t val, uint16_t prev_val);
static bool u32_is_newer(uint32_t val, uint32_t prev_val);
int64_t u16_update(uint16_t val);
int64_t u32_update(uint32_t val);
protected:
int size;
int64_t last_value;
};
//---------------------------------------------------------------------
// 带时间窗口的丢包统计
//---------------------------------------------------------------------
class AbstractLossStats
{
public:
virtual ~AbstractLossStats();
AbstractLossStats();
AbstractLossStats(const AbstractLossStats& src);
AbstractLossStats(AbstractLossStats&& src);
AbstractLossStats& operator = (const AbstractLossStats& src);
public:
// 初始化,默认统计 4 秒窗口,最大包数为 200至少 100 个包开始统计
void init(int window_ms = 4000, int window_num = 200, int limit = 100);
// 复位
void reset();
// 收到一个包时调用
void update(uint16_t seq, int64_t now_ts);
// 计算丢包率fraction_loss 是返回的小数丢包率255 代表 100%
// 成功返回 0包不够统计就返回 -1
int calculate(int64_t now_ts, uint8_t *fraction_loss, int *num);
protected:
// 淘汰超过窗口的太老的数据
void evict_oldest(int64_t now_ts);
protected:
// 使用有序字典保存收包信息key=id, value=ts, 越小的 id 越在前面
typedef std::map<int64_t, int64_t, std::less<int64_t>> LossWindow;
LossWindow _loss_window; // 保存一段时间窗口的数据
UnsignedWrap _wrapper; // 整数展开16 -> 32
int64_t _stat_ts; // 时间戳
int64_t _max_id; // 最大序号
int64_t _k_loss_stats_window_ms; // 最大时间窗口
int64_t _k_max_stats_window_num; // 最大丢包序号
int _k_calculation_limit; // 最少多少个包开始统计
};
//---------------------------------------------------------------------
// 收包速率统计
//---------------------------------------------------------------------
class AbstractRateStats
{
public:
virtual ~AbstractRateStats();
AbstractRateStats();
AbstractRateStats(const AbstractRateStats& src);
AbstractRateStats(AbstractRateStats&& src);
AbstractRateStats& operator = (const AbstractRateStats& src);
public:
// 初始化wnd_size 是窗口大小scale 是速率的单位,比如 8000 代表速率单位是 kbps
// wnd_size 和 scale 传负数表示不变,继续使用之前的值
// (2000, 8000) 是比较合理的默认值,表示统计最近 2 秒的速率,单位是 kbps
// (2000, 1000) 也是比较合理的默认值,表示统计最近 2 秒的速率,单位是 Bytes/s
void init(int wnd_size, float scale);
// 复位
void reset();
// 收到一个包时调用
void update(size_t count, int64_t now_ts);
// 统计码率,数据不够返回 -1成功返回速率
int calculate(int64_t now_ts);
// 取得积累值
int64_t accumulated() const { return _accumulated_count; }
// 取得样本数
int samples() const { return _sample_num; }
protected:
void evict_oldest(int64_t now_ts);
protected:
struct RateBucket { int sum, sample; };
std::vector<RateBucket> _buckets;
protected:
int64_t _oldest_ts;
int _oldest_index;
int _wnd_size;
float _scale;
int64_t _accumulated_count;
int _sample_num;
};
//---------------------------------------------------------------------
// Minimum Sliding Window
//---------------------------------------------------------------------
class MinHistory
{
public:
virtual ~MinHistory();
MinHistory();
MinHistory(const MinHistory& src);
MinHistory(MinHistory&& src);
MinHistory& operator = (const MinHistory& src);
public:
// reverse 为假时求最小值,否则求最大值
void init(int wnd_size, bool reverse = false);
// clear history
void clear();
// update value
void update(int value, int64_t now_ts);
// check if empty
inline bool empty() const { return _history.empty(); }
// get value
inline int value() const { return empty()? 0 : _history.front().second; }
protected:
std::deque<std::pair<int64_t, int>> _history;
int _wnd_size = 2000;
bool _reverse = false;
};
//---------------------------------------------------------------------
// 求时间窗口内的最大值
//---------------------------------------------------------------------
class MaxHistory
{
public:
virtual ~MaxHistory();
MaxHistory();
MaxHistory(const MaxHistory& src);
MaxHistory(MaxHistory&& src);
MaxHistory& operator = (const MaxHistory& src);
public:
void init(int wnd_size);
void clear();
void update(int value, int64_t now_ts);
inline bool empty() const { return _min_history.empty(); }
inline int value() const { return _min_history.value(); }
protected:
MinHistory _min_history;
};
//---------------------------------------------------------------------
// 平均值
//---------------------------------------------------------------------
class MovingAverage
{
public:
virtual ~MovingAverage();
MovingAverage();
public:
void init(int wnd_size);
void clear();
void update(int value, int64_t now_ts);
inline bool empty() const { return _history.empty(); }
inline int value() const { return _average; }
inline int count() const { return (int)_history.size(); }
protected:
std::deque<std::pair<int64_t, int>> _history;
int64_t _sum;
int _average;
int _wnd_size;
};
//---------------------------------------------------------------------
// RttHistory
//---------------------------------------------------------------------
class RttHistory
{
public:
virtual ~RttHistory();
RttHistory();
public:
// initialize, wnd_size is the number of samples to keep
void init(int wnd_size);
// clear history
void clear();
// push a new rtt sample
void push(double rtt);
inline int count() const { return (int)_samples.size(); }
inline bool empty() const { return count() == 0; }
inline double rto() const { return _rto; }
inline double avg() const { return _avg; }
inline double deviation() const { return _deviation; }
inline double minimum() const { return _min; }
inline double maximum() const { return _max; }
inline double jitter() const { return _jitter; }
private:
std::deque<double> _samples;
int _wnd_size;
double _sum;
double _avg;
double _rto;
double _srtt;
double _rttval;
double _deviation;
double _min;
double _max;
double _jitter;
};
//---------------------------------------------------------------------
// 带宽预算Token Bucket
//---------------------------------------------------------------------
class PacingBudget
{
public:
PacingBudget();
public:
// 初始化bandwidth 为带宽bytes/secburst 默认等于 bandwidth
void init(int64_t bandwidth_bps);
// 重置状态
void reset();
// 查询当前可发送字节数(内部自动更新时间,累加预算)
int64_t available(int64_t now_ms);
// 登记实际发送的字节数,扣减预算
void consume(int64_t bytes_sent);
// 计算发送 bytes_needed 还需等多少毫秒(已够返回 0
int64_t wait_time(int64_t bytes_needed) const;
// 取得当前预算
inline int64_t budget() const { return _budget; }
// 取得带宽设置
inline int64_t bandwidth() const { return _bandwidth; }
protected:
int64_t _bandwidth; // bytes per second
int64_t _burst; // 最大累积预算(字节)
int64_t _budget; // 当前可用预算(字节),可为负数
int64_t _last_time; // 上次更新的时间戳(毫秒)
int64_t _remainder; // 累积余数(微秒级精度补偿)
};
//---------------------------------------------------------------------
// namespace end
//---------------------------------------------------------------------
NAMESPACE_END(System);