/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: SocketStatAverageBps.hpp Date: 2021-10-31 Author: Reece ***/ #pragma once namespace Aurora::IO::Net { struct SocketStatAverageBps { inline AuUInt32 GetBytesPerMS(AuUInt32 timeNow) { if (!frameLastEnd) return bytesTransferred; return (double(bytesTransferred) / (double(timeNow - frameLastEnd) + std::numeric_limits::epsilon())); } inline AuUInt32 GetBytesPerSecondNormalized(AuUInt32 timeNow) { AU_LOCK_GUARD(lock); // If timeNow isn't at least that of one socket frame, then we just have to return some old data if (frameZero) return GetLastBytesPerSecond(); // Cool we can extrapolate usage using a time weight at least that of one server frame // Some useful variables... auto frameAtPoint = GetBytesPerMS(timeNow); auto timeElapsed = timeNow - frameLastEnd; // time elapsed since last packet dispatch finish. makes sense to use this is the packet delta of a high bandwidth stream // Edge case: we aren't receiving much data. if we have more than 1 second of data, we should use the stream average if (timeElapsed > 1000) return frameAtPoint * 1000; // from ms // else assume constant usage will continue to trend for at least another second // the actual extrapolation auto weight = double(1000) / double(timeElapsed); return double(frameAtPoint) * weight; } inline AuUInt32 GetLastBytesPerSecond() { AU_LOCK_GUARD(lock); return double(lastBytesTransferred) / (double(frameLastEnd - frameLastStart) + std::numeric_limits::epsilon()) * 1000.f; } inline void Reset() { bytesTransferred = 0; frameStart = 0; frameLastEnd = 0; frameLastStart = 0; frameZero = true; lastBytesTransferred = 0; } inline void Add(AuUInt32 timeNow, AuUInt32 bytes) { AU_LOCK_GUARD(lock); auto nextTick = frameLastEnd + 1000; if (nextTick < timeNow) { frameLastEnd = timeNow; frameLastStart = std::exchange(frameStart, timeNow); lastBytesTransferred = std::exchange(bytesTransferred, bytes); frameZero = true; } else { frameZero = false; bytesTransferred += bytes; } } AuThreadPrimitives::SpinLock lock; AuUInt32 frameStart {}; AuUInt32 bytesTransferred {}; AuUInt32 frameLastStart {}; AuUInt32 frameLastEnd {}; AuUInt32 lastBytesTransferred {}; bool frameZero {true}; }; }