#define TOKEN_BUCKET_PRIVATE
#include "lib/evloop/token_bucket.h"
#include "lib/log/util_bug.h"
#include "lib/intmath/cmp.h"
#include "lib/time/compat_time.h"
#include <string.h>
void
token_bucket_cfg_init(token_bucket_cfg_t *cfg,
uint32_t rate,
uint32_t burst)
{
tor_assert_nonfatal(rate > 0);
tor_assert_nonfatal(burst > 0);
if (burst > TOKEN_BUCKET_MAX_BURST)
burst = TOKEN_BUCKET_MAX_BURST;
cfg->rate = rate;
cfg->burst = burst;
}
void
token_bucket_raw_reset(token_bucket_raw_t *bucket,
const token_bucket_cfg_t *cfg)
{
bucket->bucket = cfg->burst;
}
void
token_bucket_raw_adjust(token_bucket_raw_t *bucket,
const token_bucket_cfg_t *cfg)
{
bucket->bucket = MIN(bucket->bucket, cfg->burst);
}
int
token_bucket_raw_refill_steps(token_bucket_raw_t *bucket,
const token_bucket_cfg_t *cfg,
const uint32_t elapsed)
{
const int was_empty = (bucket->bucket <= 0);
const size_t gap = ((size_t)cfg->burst) - ((size_t)bucket->bucket);
if (elapsed > gap / cfg->rate) {
bucket->bucket = cfg->burst;
} else {
bucket->bucket += cfg->rate * elapsed;
}
return was_empty && bucket->bucket > 0;
}
int
token_bucket_raw_dec(token_bucket_raw_t *bucket,
ssize_t n)
{
if (BUG(n < 0))
return 0;
const int becomes_empty = bucket->bucket > 0 && n >= bucket->bucket;
bucket->bucket -= n;
return becomes_empty;
}
STATIC uint32_t
rate_per_sec_to_rate_per_step(uint32_t rate)
{
uint64_t units = (uint64_t) rate * TICKS_PER_STEP;
uint32_t val = (uint32_t)
(monotime_coarse_stamp_units_to_approx_msec(units) / 1000);
return val ? val : 1;
}
void
token_bucket_rw_init(token_bucket_rw_t *bucket,
uint32_t rate,
uint32_t burst,
uint32_t now_ts)
{
memset(bucket, 0, sizeof(token_bucket_rw_t));
token_bucket_rw_adjust(bucket, rate, burst);
token_bucket_rw_reset(bucket, now_ts);
}
void
token_bucket_rw_adjust(token_bucket_rw_t *bucket,
uint32_t rate,
uint32_t burst)
{
token_bucket_cfg_init(&bucket->cfg,
rate_per_sec_to_rate_per_step(rate),
burst);
token_bucket_raw_adjust(&bucket->read_bucket, &bucket->cfg);
token_bucket_raw_adjust(&bucket->write_bucket, &bucket->cfg);
}
void
token_bucket_rw_reset(token_bucket_rw_t *bucket,
uint32_t now_ts)
{
token_bucket_raw_reset(&bucket->read_bucket, &bucket->cfg);
token_bucket_raw_reset(&bucket->write_bucket, &bucket->cfg);
bucket->last_refilled_at_timestamp = now_ts;
}
int
token_bucket_rw_refill(token_bucket_rw_t *bucket,
uint32_t now_ts)
{
const uint32_t elapsed_ticks =
(now_ts - bucket->last_refilled_at_timestamp);
if (elapsed_ticks > UINT32_MAX-(300*1000)) {
return 0;
}
const uint32_t elapsed_steps = elapsed_ticks / TICKS_PER_STEP;
if (!elapsed_steps) {
return 0;
}
int flags = 0;
if (token_bucket_raw_refill_steps(&bucket->read_bucket,
&bucket->cfg, elapsed_steps))
flags |= TB_READ;
if (token_bucket_raw_refill_steps(&bucket->write_bucket,
&bucket->cfg, elapsed_steps))
flags |= TB_WRITE;
bucket->last_refilled_at_timestamp = now_ts;
return flags;
}
int
token_bucket_rw_dec_read(token_bucket_rw_t *bucket,
ssize_t n)
{
return token_bucket_raw_dec(&bucket->read_bucket, n);
}
int
token_bucket_rw_dec_write(token_bucket_rw_t *bucket,
ssize_t n)
{
return token_bucket_raw_dec(&bucket->write_bucket, n);
}
int
token_bucket_rw_dec(token_bucket_rw_t *bucket,
ssize_t n_read, ssize_t n_written)
{
int flags = 0;
if (token_bucket_rw_dec_read(bucket, n_read))
flags |= TB_READ;
if (token_bucket_rw_dec_write(bucket, n_written))
flags |= TB_WRITE;
return flags;
}
void
token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate,
uint32_t burst, uint32_t now_ts)
{
memset(bucket, 0, sizeof(token_bucket_ctr_t));
token_bucket_ctr_adjust(bucket, rate, burst);
token_bucket_ctr_reset(bucket, now_ts);
}
void
token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate,
uint32_t burst)
{
token_bucket_cfg_init(&bucket->cfg, rate, burst);
token_bucket_raw_adjust(&bucket->counter, &bucket->cfg);
}
void
token_bucket_ctr_reset(token_bucket_ctr_t *bucket, uint32_t now_ts)
{
token_bucket_raw_reset(&bucket->counter, &bucket->cfg);
bucket->last_refilled_at_timestamp = now_ts;
}
void
token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts)
{
const uint32_t elapsed_ticks =
(now_ts - bucket->last_refilled_at_timestamp);
if (elapsed_ticks > UINT32_MAX-(300*1000)) {
return;
}
token_bucket_raw_refill_steps(&bucket->counter, &bucket->cfg,
elapsed_ticks);
bucket->last_refilled_at_timestamp = now_ts;
}