dcsctp: Use integer math in RTO calculations

Following Congestion avoidance and control by V. Jacobson at
https://dl.acm.org/doi/10.1145/52324.52356, use integer math instead
of floating point. Not that it matters, but it results in some code size
savings, and is more efficient. Due to not using floating point math,
some golden values in test cases were rounded a bit differently.

Bug: webrtc:12614
Change-Id: I0b7d54b8fd9ce7156e6b2582437ef5720f8838ea
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231229
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34956}
diff --git a/net/dcsctp/tx/retransmission_timeout.cc b/net/dcsctp/tx/retransmission_timeout.cc
index 7d545a0..2cb59f1 100644
--- a/net/dcsctp/tx/retransmission_timeout.cc
+++ b/net/dcsctp/tx/retransmission_timeout.cc
@@ -9,17 +9,12 @@
  */
 #include "net/dcsctp/tx/retransmission_timeout.h"
 
-#include <cmath>
+#include <algorithm>
 #include <cstdint>
 
 #include "net/dcsctp/public/dcsctp_options.h"
 
 namespace dcsctp {
-namespace {
-// https://tools.ietf.org/html/rfc4960#section-15
-constexpr double kRtoAlpha = 0.125;
-constexpr double kRtoBeta = 0.25;
-}  // namespace
 
 RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options)
     : min_rto_(*options.rto_min),
@@ -28,42 +23,39 @@
       rto_(*options.rto_initial) {}
 
 void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) {
-  double rtt = *measured_rtt;
+  int32_t rtt = *measured_rtt;
 
   // Unrealistic values will be skipped. If a wrongly measured (or otherwise
   // corrupt) value was processed, it could change the state in a way that would
   // take a very long time to recover.
-  if (rtt < 0.0 || rtt > max_rtt_) {
+  if (rtt < 0 || rtt > max_rtt_) {
     return;
   }
 
+  // From https://tools.ietf.org/html/rfc4960#section-6.3.1, but avoiding
+  // floating point math by implementing algorithm from "V. Jacobson: Congestion
+  // avoidance and control", but adapted for SCTP.
   if (first_measurement_) {
-    // https://tools.ietf.org/html/rfc4960#section-6.3.1
-    // "When the first RTT measurement R is made, set
-    //        SRTT <- R,
-    //        RTTVAR <- R/2, and
-    //        RTO <- SRTT + 4 * RTTVAR."
-    srtt_ = rtt;
-    rttvar_ = rtt * 0.5;
-    rto_ = srtt_ + 4 * rttvar_;
+    scaled_srtt_ = rtt << kRttShift;
+    scaled_rtt_var_ = (rtt / 2) << kRttVarShift;
     first_measurement_ = false;
   } else {
-    // https://tools.ietf.org/html/rfc4960#section-6.3.1
-    // "When a new RTT measurement R' is made, set
-    //        RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
-    //        SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
-    //        RTO <- SRTT + 4 * RTTVAR."
-    rttvar_ = (1 - kRtoBeta) * rttvar_ + kRtoBeta * std::abs(srtt_ - rtt);
-    srtt_ = (1 - kRtoAlpha) * srtt_ + kRtoAlpha * rtt;
-    rto_ = srtt_ + 4 * rttvar_;
+    rtt -= (scaled_srtt_ >> kRttShift);
+    scaled_srtt_ += rtt;
+    if (rtt < 0) {
+      rtt = -rtt;
+    }
+    rtt -= (scaled_rtt_var_ >> kRttVarShift);
+    scaled_rtt_var_ += rtt;
   }
+  rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_;
 
   // If the RTO becomes smaller or equal to RTT, expiration timers will be
   // scheduled at the same time as packets are expected. Only happens in
   // extremely stable RTTs, i.e. in simulations.
-  rto_ = std::fmax(rto_, rtt + 1);
+  rto_ = std::max(rto_, rtt + 1);
 
   // Clamp RTO between min and max.
-  rto_ = std::fmin(std::fmax(rto_, min_rto_), max_rto_);
+  rto_ = std::min(std::max(rto_, min_rto_), max_rto_);
 }
 }  // namespace dcsctp
diff --git a/net/dcsctp/tx/retransmission_timeout.h b/net/dcsctp/tx/retransmission_timeout.h
index 0fac33e..f3a9553 100644
--- a/net/dcsctp/tx/retransmission_timeout.h
+++ b/net/dcsctp/tx/retransmission_timeout.h
@@ -27,6 +27,8 @@
 // a lot, which is an indicator of a bad connection.
 class RetransmissionTimeout {
  public:
+  static constexpr int kRttShift = 3;
+  static constexpr int kRttVarShift = 2;
   explicit RetransmissionTimeout(const DcSctpOptions& options);
 
   // To be called when a RTT has been measured, to update the RTO value.
@@ -36,22 +38,20 @@
   DurationMs rto() const { return DurationMs(rto_); }
 
   // Returns the smoothed RTT value, in milliseconds.
-  DurationMs srtt() const { return DurationMs(srtt_); }
+  DurationMs srtt() const { return DurationMs(scaled_srtt_ >> kRttShift); }
 
  private:
-  // Note that all intermediate state calculation is done in the floating point
-  // domain, to maintain precision.
-  const double min_rto_;
-  const double max_rto_;
-  const double max_rtt_;
+  const int32_t min_rto_;
+  const int32_t max_rto_;
+  const int32_t max_rtt_;
   // If this is the first measurement
   bool first_measurement_ = true;
-  // Smoothed Round-Trip Time
-  double srtt_ = 0.0;
-  // Round-Trip Time Variation
-  double rttvar_ = 0.0;
+  // Smoothed Round-Trip Time, shifted by kRttShift
+  int32_t scaled_srtt_ = 0;
+  // Round-Trip Time Variation, shifted by kRttVarShift
+  int32_t scaled_rtt_var_ = 0;
   // Retransmission Timeout
-  double rto_;
+  int32_t rto_;
 };
 }  // namespace dcsctp
 
diff --git a/net/dcsctp/tx/retransmission_timeout_test.cc b/net/dcsctp/tx/retransmission_timeout_test.cc
index 3b2e339..d2d0719 100644
--- a/net/dcsctp/tx/retransmission_timeout_test.cc
+++ b/net/dcsctp/tx/retransmission_timeout_test.cc
@@ -88,7 +88,7 @@
   rto_.ObserveRTT(DurationMs(125));
   EXPECT_EQ(*rto_.rto(), 233);
   rto_.ObserveRTT(DurationMs(127));
-  EXPECT_EQ(*rto_.rto(), 208);
+  EXPECT_EQ(*rto_.rto(), 209);
 }
 
 TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) {
@@ -116,21 +116,21 @@
   rto_.ObserveRTT(DurationMs(124));
   EXPECT_EQ(*rto_.rto(), 800);
   rto_.ObserveRTT(DurationMs(122));
-  EXPECT_EQ(*rto_.rto(), 709);
+  EXPECT_EQ(*rto_.rto(), 710);
   rto_.ObserveRTT(DurationMs(123));
-  EXPECT_EQ(*rto_.rto(), 630);
+  EXPECT_EQ(*rto_.rto(), 631);
   rto_.ObserveRTT(DurationMs(124));
-  EXPECT_EQ(*rto_.rto(), 561);
+  EXPECT_EQ(*rto_.rto(), 562);
   rto_.ObserveRTT(DurationMs(122));
-  EXPECT_EQ(*rto_.rto(), 504);
+  EXPECT_EQ(*rto_.rto(), 505);
   rto_.ObserveRTT(DurationMs(124));
-  EXPECT_EQ(*rto_.rto(), 453);
+  EXPECT_EQ(*rto_.rto(), 454);
   rto_.ObserveRTT(DurationMs(124));
-  EXPECT_EQ(*rto_.rto(), 409);
+  EXPECT_EQ(*rto_.rto(), 410);
   rto_.ObserveRTT(DurationMs(124));
   EXPECT_EQ(*rto_.rto(), 372);
   rto_.ObserveRTT(DurationMs(124));
-  EXPECT_EQ(*rto_.rto(), 339);
+  EXPECT_EQ(*rto_.rto(), 340);
 }
 
 TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {