84 lines
2.2 KiB
Zig
84 lines
2.2 KiB
Zig
// zig fmt: off
|
|
const std = @import("std");
|
|
|
|
const RateLimit = @This();
|
|
|
|
const assert = std.debug.assert;
|
|
|
|
pub const Category = enum { account_creation, token, data, actions };
|
|
|
|
pub const Timespan = struct {
|
|
counter: u32 = 0,
|
|
limit: u32,
|
|
timer_ms: u64 = 0,
|
|
};
|
|
|
|
pub const CategoryArray = std.EnumArray(Category, RateLimit);
|
|
|
|
seconds: ?Timespan = null,
|
|
minutes: ?Timespan = null,
|
|
hours: ?Timespan = null,
|
|
|
|
last_update_at_ms: i64 = 0,
|
|
|
|
pub fn init(now_ms: i64, limit_per_hour: ?u32, limit_per_minute: ?u32, limit_per_second: ?u32) RateLimit {
|
|
var seconds: ?Timespan = null;
|
|
if (limit_per_second) |limit| {
|
|
seconds = Timespan{ .limit = limit };
|
|
}
|
|
|
|
var minutes: ?Timespan = null;
|
|
if (limit_per_minute) |limit| {
|
|
minutes = Timespan{ .limit = limit };
|
|
}
|
|
|
|
var hours: ?Timespan = null;
|
|
if (limit_per_hour) |limit| {
|
|
hours = Timespan{ .limit = limit };
|
|
}
|
|
|
|
return RateLimit{
|
|
.seconds = seconds,
|
|
.minutes = minutes,
|
|
.hours = hours,
|
|
.last_update_at_ms = now_ms
|
|
};
|
|
}
|
|
|
|
pub fn update_timers(self: *RateLimit, now_ms: i64) void {
|
|
const time_passed_ms = now_ms - self.last_update_at_ms;
|
|
assert(time_passed_ms >= 0);
|
|
|
|
inline for (.{
|
|
.{ &self.seconds, std.time.ms_per_s },
|
|
.{ &self.minutes, std.time.ms_per_min },
|
|
.{ &self.hours, std.time.ms_per_hour },
|
|
}) |tuple| {
|
|
const maybe_timespan = tuple[0];
|
|
const timespan_size = tuple[1];
|
|
|
|
if (maybe_timespan.*) |*timespan| {
|
|
timespan.timer_ms += @intCast(time_passed_ms);
|
|
|
|
const ms_per_request = @divFloor(timespan_size, timespan.limit);
|
|
const requests_passed: u32 = @intCast(@divFloor(timespan.timer_ms, ms_per_request));
|
|
timespan.counter -= @min(timespan.counter, requests_passed);
|
|
timespan.timer_ms = @mod(timespan.timer_ms, ms_per_request);
|
|
}
|
|
}
|
|
|
|
self.last_update_at_ms = now_ms;
|
|
}
|
|
|
|
pub fn increment_counters(self: *RateLimit) void {
|
|
inline for (.{
|
|
&self.hours,
|
|
&self.minutes,
|
|
&self.seconds,
|
|
}) |maybe_timespan| {
|
|
if (maybe_timespan.*) |*timespan| {
|
|
timespan.counter += 1;
|
|
}
|
|
}
|
|
}
|