Files
rippled/include/xrpl/basics/DecayingSample.h
Pratik Mankawde 3547112540 fix: Fix ubsan flagged issues (#6151)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-04-27 20:34:16 +00:00

135 lines
3.0 KiB
C++

#pragma once
#include <chrono>
#include <cmath>
namespace xrpl {
/** Sampling function using exponential decay to provide a continuous value.
@tparam The number of seconds in the decay window.
*/
template <int Window, typename Clock>
class DecayingSample
{
public:
using value_type = typename Clock::duration::rep;
using time_point = typename Clock::time_point;
DecayingSample() = delete;
/**
@param now Start time of DecayingSample.
*/
explicit DecayingSample(time_point now) : m_value(value_type()), m_when(now)
{
}
/** Add a new sample.
The value is first aged according to the specified time.
*/
value_type
add(value_type value, time_point now)
{
decay(now);
m_value += value;
return m_value / Window;
}
/** Retrieve the current value in normalized units.
The samples are first aged according to the specified time.
*/
value_type
value(time_point now)
{
decay(now);
return m_value / Window;
}
private:
// Apply exponential decay based on the specified time.
void
decay(time_point now)
{
if (now == m_when)
return;
if (m_value != value_type())
{
std::size_t elapsed =
std::chrono::duration_cast<std::chrono::seconds>(now - m_when).count();
// A span larger than four times the window decays the
// value to an insignificant amount so just reset it.
//
if (elapsed > 4 * Window)
{
m_value = value_type();
}
else
{
for (; elapsed > 0; --elapsed)
{
m_value -= (m_value + Window - 1) / Window;
}
}
}
m_when = now;
}
// Current value in exponential units
value_type m_value;
// Last time the aging function was applied
time_point m_when;
};
//------------------------------------------------------------------------------
/** Sampling function using exponential decay to provide a continuous value.
@tparam HalfLife The half life of a sample, in seconds.
*/
template <int HalfLife, class Clock>
class DecayWindow
{
public:
using time_point = typename Clock::time_point;
explicit DecayWindow(time_point now) : when_(now)
{
}
void
add(double value, time_point now)
{
decay(now);
value_ += value;
}
double
value(time_point now)
{
decay(now);
return value_ / HalfLife;
}
private:
static_assert(HalfLife > 0, "half life must be positive");
void
decay(time_point now)
{
if (now <= when_)
return;
using namespace std::chrono;
auto const elapsed = duration<double>(now - when_).count();
value_ *= std::pow(2.0, -elapsed / HalfLife);
when_ = now;
}
double value_{0};
time_point when_;
};
} // namespace xrpl