#1 - quicr module

This commit is contained in:
Martin Slachta
2026-07-22 17:34:44 +02:00
parent a04f0dc262
commit f4174eb0c7
177 changed files with 5309 additions and 2265 deletions
@@ -1,157 +0,0 @@
#pragma once
#include <chrono>
#include <cstdint>
#include <stdexcept>
#include <vector>
#include <print>
namespace tw::net {
template<typename T>
struct AverageOp {
uint32_t count;
T sum;
AverageOp() :
count(0),
sum{} {
}
void add(const T value) {
sum += value;
count++;
}
T result() const {
return count == 0 ? 0 : sum / count;
}
};
template<typename T>
struct SumOp {
T sum;
void add(const T value) {
sum += value;
}
T result() const {
return sum;
}
};
template<typename T, typename Interval,
typename Operation = SumOp<T>,
typename Clock = std::chrono::steady_clock>
class BucketMetric {
T m_min, m_max;
std::vector<T> m_metric;
std::vector<uint32_t> m_bucket_idx;
Operation m_op;
uint32_t m_offset;
uint32_t m_right, m_left;
std::string m_format;
const uint32_t get_bucket(Clock::time_point time_point) const {
return std::chrono::floor<Interval>(time_point).time_since_epoch().count() - m_offset;
}
public:
BucketMetric(std::string format, uint32_t size) :
m_metric(size),
m_bucket_idx(size),
m_right(0), m_left(0),
m_offset(0),
m_format(format)
{
m_offset = get_bucket(Clock::now());
}
const T max() const {
return m_max;
}
const T min() const {
return m_min;
}
const std::string& format() const {
return m_format;
}
size_t max_size() const {
return m_metric.size();
}
void push(T value) {
auto time = Clock::now();
size_t bucket = get_bucket(time) % m_metric.size();
size_t idx = get_bucket(time);
// set result to correct bucket
if(m_right != bucket) {
m_metric[m_right] = m_op.result();
m_min = std::min(m_min, m_op.result());
m_max = std::max(m_max, m_op.result());
m_bucket_idx[m_right] = idx++;
m_right++;
m_op = {};
}
// reset all buckets until the required one
for(; m_right != bucket; m_right = (m_right + 1) % m_metric.size()) {
m_metric[m_right] = {};
m_bucket_idx[m_right] = idx++;
if(m_right == m_left) {
m_left = (m_left + 1) % m_metric.size();
}
}
m_op.add(value);
}
const size_t get_size() const {
return m_right - m_left + (m_left > m_right ? m_metric.size() : 0);
}
const T get(uint32_t idx) const {
if(idx > get_size()) {
throw std::invalid_argument("`idx` cannot be higher than buffer size");
}
return m_metric[m_left + idx].result();
}
std::span<T> get_head() {
return std::span(m_metric).subspan(m_left, (m_right > m_left ? m_right : m_metric.size()));
}
std::span<uint32_t> get_head_timeline() {
return std::span(m_bucket_idx).subspan(m_left, (m_right > m_left ? m_right : m_bucket_idx.size()));
}
std::span<T> get_tail() {
if(m_right > m_left) {
return std::span<T>();
}
return std::span(m_metric).subspan(0, m_right);
}
std::span<uint32_t> get_tail_timeline() {
if(m_right > m_left) {
return std::span<T>();
}
return std::span(m_bucket_idx).subspan(0, m_right);
}
};
}
@@ -42,7 +42,7 @@ public:
}
std::optional<const TValue*> get(TKey key) const {
for(size_t i = m_tail; i != m_head; (i++) % max_size()) {
for(size_t i = m_tail; i != m_head; i = (i + 1) % max_size()) {
if(m_buffer[i].first > key) {
return {};
}
@@ -56,24 +56,25 @@ public:
}
bool set(TKey key, const TValue& value) {
if(key < m_buffer.at(m_tail).first) {
// If buffer has entries and key is older than the oldest, reject it
if(m_tail != m_head && key < m_buffer.at(m_tail).first) {
return false;
}
int i = m_tail + 1;
size_t i = (m_tail + 1) % max_size();
if(m_tail != m_head) {
for(i = m_tail + 1; i != m_head; i++) {
for(i = (m_tail + 1) % max_size(); i != m_head; i = (i + 1) % max_size()) {
if(m_buffer.at(i).first > key) {
m_buffer[(i - 1) % max_size()] = std::make_pair(key, value);
m_head++;
m_buffer[(i - 1 + max_size()) % max_size()] = std::make_pair(key, value);
m_head = (m_head + 1) % max_size();
return true;
} else {
m_buffer[(i - 1) % max_size()] = m_buffer[i];
m_buffer[(i - 1 + max_size()) % max_size()] = m_buffer[i];
}
}
}
m_buffer[(i - 1) % max_size()] = std::make_pair(key, value);
m_buffer[(i - 1 + max_size()) % max_size()] = std::make_pair(key, value);
m_head = (m_head + 1) % max_size();
return true;
}
@@ -1,134 +0,0 @@
#pragma once
#include <cstdint>
#include <chrono>
#include <filesystem>
#include <cassert>
#include "Address.hpp"
#include "BucketMetric.hpp"
#include "packets/Packet.hpp"
#include "MessageRegistry.hpp"
namespace tw::net {
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
struct NetworkSendInfo {
PacketType message_type;
bool is_sent_by_us;
Address target;
TimePoint timepoint;
std::span<uint8_t> buffer;
NetworkSendInfo(
PacketType message_type,
bool is_sent_by_us,
const Address& target,
const std::span<uint8_t> buffer
) :
message_type(message_type),
is_sent_by_us(is_sent_by_us),
target(target),
timepoint(std::chrono::steady_clock::now()),
buffer(buffer)
{
}
};
class NetworkStatsLogger {
private:
std::vector<NetworkSendInfo> m_backlog;
std::vector<uint8_t> m_buffer;
size_t m_left, m_right;
std::optional<std::ostream> m_output;
using Interval = std::chrono::seconds;
BucketMetric<uint32_t, Interval, AverageOp<uint32_t>> m_ping_metric;
BucketMetric<uint32_t, Interval, SumOp<uint32_t>> m_outgoing;
BucketMetric<uint32_t, Interval, SumOp<uint32_t>> m_incoming;
public:
NetworkStatsLogger() :
m_backlog(10000, {MESSAGE_PACKET, false, Address({}, 0), {}}),
m_buffer(1000000),
m_left(0), m_right(0),
m_ping_metric("ms", 1000),
m_outgoing("b/s", 1000),
m_incoming("b/s", 1000)
{ }
void set_file_output(std::filesystem::path path);
size_t get_size() {
return m_right - m_left + (m_right < m_left ? m_backlog.size() : 0);
}
NetworkSendInfo& get_item(uint32_t idx) {
return m_backlog[(m_left + idx) % m_backlog.size()];
}
std::span<uint8_t> allocate_memory_for_buffer(size_t size) {
uint32_t start = m_right;
if(m_buffer.size() - m_right < size) {
// throw away packets from the start to make space
for(; m_backlog[m_left].buffer.data() < m_buffer.data() + start + size &&
m_left != m_right; m_left = (m_left + 1) % m_buffer.size()) { }
start = 0;
}
uint32_t end = start + size;
return std::span<uint8_t>(m_buffer.begin() + start, m_buffer.begin() + end);
}
// constexpr void log(PacketType message_type, bool is_sent, const Address& target, const ByteBuffer& content) {
// std::span<uint8_t> span = allocate_memory_for_buffer(content.size());
// memcpy(span.data(), content.data().data(), content.size());
// m_right = (m_right + 1) % m_backlog.size();
// m_backlog[m_right] = NetworkSendInfo(message_type, is_sent, target, span);
// }
// constexpr void log_receive(
// PacketType message_type,
// const Address& from
// ) {
// log(message_type, false, from, content);
// m_incoming.push(content.size());
// }
// constexpr void log_send(
// PacketType message_type,
// const Address& to
// ) {
// log(message_type, true, to, content);
// m_outgoing.push(content.size());
// }
void log_ping(uint32_t ping) {
m_ping_metric.push(ping);
}
BucketMetric<uint32_t, Interval, AverageOp<uint32_t>>& ping(){
return m_ping_metric;
}
BucketMetric<uint32_t, Interval>& outgoing(){
return m_outgoing;
}
BucketMetric<uint32_t, Interval>& incoming(){
return m_incoming;
}
};
}