# Global Metrics

path: .metrics.loc.lloc
old: 3.0
new: 28.0
path: .metrics.loc.blank
old: 7.0
new: 21.0
path: .metrics.loc.sloc
old: 27.0
new: 235.0
path: .metrics.loc.cloc
old: 6.0
new: 118.0
path: .metrics.loc.ploc
old: 14.0
new: 96.0
path: .metrics.cognitive.average
old: 2.0
new: 1.8333333333333333
path: .metrics.cognitive.sum
old: 2.0
new: 11.0
path: .metrics.nom.functions
old: 1.0
new: 6.0
path: .metrics.nom.total
old: 1.0
new: 6.0
path: .metrics.mi.mi_visual_studio
old: 51.60071540982331
new: 22.013139579190376
path: .metrics.mi.mi_sei
old: 85.36036855463864
new: 24.948266530208237
path: .metrics.mi.mi_original
old: 88.23722335079788
new: 37.64246868041555
path: .metrics.halstead.N2
old: 19.0
new: 180.0
path: .metrics.halstead.purity_ratio
old: 2.032458178855633
new: 1.1067451937823205
path: .metrics.halstead.N1
old: 31.0
new: 221.0
path: .metrics.halstead.length
old: 50.0
new: 401.0
path: .metrics.halstead.n2
old: 12.0
new: 58.0
path: .metrics.halstead.level
old: 0.08421052631578947
new: 0.028019323671497585
path: .metrics.halstead.n1
old: 15.0
new: 23.0
path: .metrics.halstead.estimated_program_length
old: 101.62290894278166
new: 443.8048227067104
path: .metrics.halstead.difficulty
old: 11.875
new: 35.689655172413794
path: .metrics.halstead.bugs
old: 0.06658473227477106
new: 0.6730632581837971
path: .metrics.halstead.vocabulary
old: 27.0
new: 81.0
path: .metrics.halstead.volume
old: 237.7443751081734
new: 2542.2798511567344
path: .metrics.halstead.time
old: 156.84524746719774
new: 5040.727291086629
path: .metrics.halstead.effort
old: 2823.2144544095595
new: 90733.09123955932
path: .metrics.cyclomatic.average
old: 1.3333333333333333
new: 2.0
path: .metrics.cyclomatic.sum
old: 4.0
new: 18.0
path: .metrics.nexits.sum
old: 2.0
new: 5.0
path: .metrics.nexits.average
old: 2.0
new: 0.8333333333333334
path: .metrics.nargs.sum
old: 1.0
new: 11.0
path: .metrics.nargs.average
old: 1.0
new: 1.8333333333333333

# Spaces Data

## Minimal test - lines (13, 233)

path: .spaces[0].metrics.nom.total
old: 0.0
new: 6.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 6.0
path: .spaces[0].metrics.nargs.average
old: null
new: 1.8333333333333333
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.mi.mi_sei
old: null
new: 26.828141924939793
path: .spaces[0].metrics.mi.mi_original
old: null
new: 38.93623603271952
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 22.769728674104982
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 5.0
path: .spaces[0].metrics.nexits.average
old: null
new: 0.8333333333333334
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 56.0
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 5066.344835059492
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 429.2538006245371
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 398.0
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 36.348214285714285
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 1.0785271372475806
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 79.0
path: .spaces[0].metrics.halstead.level
old: null
new: 0.02751166789486613
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 91194.20703107084
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 221.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 0.6753417198709843
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 177.0
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 23.0
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 2508.904737774487
path: .spaces[0].metrics.cognitive.average
old: null
new: 1.8333333333333333
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 17.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.125
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 19.0
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 90.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 221.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 28.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 112.0

### Code

    namespace mozilla {

    // Utility class that converts time values represented as an unsigned integral
    // number of milliseconds from one time source (e.g. a native event time) to
    // corresponding mozilla::TimeStamp objects.
    //
    // This class handles wrapping of integer values and skew between the time
    // source and mozilla::TimeStamp values.
    //
    // It does this by using an historical reference time recorded in both time
    // scales (i.e. both as a numerical time value and as a TimeStamp).
    //
    // For performance reasons, this class is careful to minimize calls to the
    // native "current time" function (e.g. gdk_x11_server_get_time) since this can
    // be slow.
    template
    class SystemTimeConverter {
     public:
      SystemTimeConverter()
          : mReferenceTime(Time(0)),
            mReferenceTimeStamp()  // Initializes to the null timestamp
            ,
            mLastBackwardsSkewCheck(Time(0)),
            kTimeRange(std::numeric_limits::max()),
            kTimeHalfRange(kTimeRange / 2),
            kBackwardsSkewCheckInterval(Time(2000)) {
        static_assert(!std::is_signed_v, "Expected Time to be unsigned");
      }

      template
      mozilla::TimeStamp GetTimeStampFromSystemTime(
          Time aTime, CurrentTimeGetter& aCurrentTimeGetter) {
        TimeStamp roughlyNow = TimeStampNowProvider::Now();

        // If the reference time is not set, use the current time value to fill
        // it in.
        if (mReferenceTimeStamp.IsNull()) {
          // This sometimes happens when ::GetMessageTime returns 0 for the first
          // message on Windows.
          if (!aTime) return roughlyNow;
          UpdateReferenceTime(aTime, aCurrentTimeGetter);
        }

        // Check for skew between the source of Time values and TimeStamp values.
        // We do this by comparing two durations (both in ms):
        //
        // i.  The duration from the reference time to the passed-in time.
        //     (timeDelta in the diagram below)
        // ii. The duration from the reference timestamp to the current time
        //     based on TimeStamp::Now.
        //     (timeStampDelta in the diagram below)
        //
        // Normally, we'd expect (ii) to be slightly larger than (i) to account
        // for the time taken between generating the event and processing it.
        //
        // If (ii) - (i) is negative then the source of Time values is getting
        // "ahead" of TimeStamp. We call this "forwards" skew below.
        //
        // For the reverse case, if (ii) - (i) is positive (and greater than some
        // tolerance factor), then we may have "backwards" skew. This is often
        // the case when we have a backlog of events and by the time we process
        // them, the time given by the system is comparatively "old".
        //
        // The IsNewerThanTimestamp function computes the equivalent of |aTime| in
        // the TimeStamp scale and returns that in |timeAsTimeStamp|.
        //
        // Graphically:
        //
        //                    mReferenceTime              aTime
        // Time scale:      ........+.......................*........
        //                          |--------timeDelta------|
        //
        //                  mReferenceTimeStamp             roughlyNow
        // TimeStamp scale: ........+...........................*....
        //                          |------timeStampDelta-------|
        //
        //                                                  |---|
        //                                         roughlyNow-timeAsTimeStamp
        //
        TimeStamp timeAsTimeStamp;
        bool newer = IsTimeNewerThanTimestamp(aTime, roughlyNow, &timeAsTimeStamp);

        // Tolerance when detecting clock skew.
        static const TimeDuration kTolerance = TimeDuration::FromMilliseconds(30.0);

        // Check for forwards skew
        if (newer) {
          // Make aTime correspond to roughlyNow
          UpdateReferenceTime(aTime, roughlyNow);

          // We didn't have backwards skew so don't bother checking for
          // backwards skew again for a little while.
          mLastBackwardsSkewCheck = aTime;

          return roughlyNow;
        }

        if (roughlyNow - timeAsTimeStamp <= kTolerance) {
          // If the time between event times and TimeStamp values is within
          // the tolerance then assume we don't have clock skew so we can
          // avoid checking for backwards skew for a while.
          mLastBackwardsSkewCheck = aTime;
        } else if (aTime - mLastBackwardsSkewCheck > kBackwardsSkewCheckInterval) {
          aCurrentTimeGetter.GetTimeAsyncForPossibleBackwardsSkew(roughlyNow);
          mLastBackwardsSkewCheck = aTime;
        }

        // Finally, calculate the timestamp
        return timeAsTimeStamp;
      }

      void CompensateForBackwardsSkew(Time aReferenceTime,
                                      const TimeStamp& aLowerBound) {
        // Check if we actually have backwards skew. Backwards skew looks like
        // the following:
        //
        //        mReferenceTime
        // Time:      ..+...a...b...c..........................
        //
        //     mReferenceTimeStamp
        // TimeStamp: ..+.....a.....b.....c....................
        //
        // Converted
        // time:      ......a'..b'..c'.........................
        //
        // What we need to do is bring mReferenceTime "forwards".
        //
        // Suppose when we get (c), we detect possible backwards skew and trigger
        // an async request for the current time (which is passed in here as
        // aReferenceTime).
        //
        // We end up with something like the following:
        //
        //        mReferenceTime     aReferenceTime
        // Time:      ..+...a...b...c...v......................
        //
        //     mReferenceTimeStamp
        // TimeStamp: ..+.....a.....b.....c..........x.........
        //                                ^          ^
        //                          aLowerBound  TimeStamp::Now()
        //
        // If the duration (aLowerBound - mReferenceTimeStamp) is greater than
        // (aReferenceTime - mReferenceTime) then we know we have backwards skew.
        //
        // If that's not the case, then we probably just got caught behind
        // temporarily.
        if (IsTimeNewerThanTimestamp(aReferenceTime, aLowerBound, nullptr)) {
          return;
        }

        // We have backwards skew; the equivalent TimeStamp for aReferenceTime lies
        // somewhere between aLowerBound (which was the TimeStamp when we triggered
        // the async request for the current time) and TimeStamp::Now().
        //
        // If aReferenceTime was waiting in the event queue for a long time, the
        // equivalent TimeStamp might be much closer to aLowerBound than
        // TimeStamp::Now() so for now we just set it to aLowerBound. That's
        // guaranteed to be at least somewhat of an improvement.
        UpdateReferenceTime(aReferenceTime, aLowerBound);
      }

     private:
      template
      void UpdateReferenceTime(Time aReferenceTime,
                               const CurrentTimeGetter& aCurrentTimeGetter) {
        Time currentTime = aCurrentTimeGetter.GetCurrentTime();
        TimeStamp currentTimeStamp = TimeStampNowProvider::Now();
        Time timeSinceReference = currentTime - aReferenceTime;
        TimeStamp referenceTimeStamp =
            currentTimeStamp - TimeDuration::FromMilliseconds(timeSinceReference);
        UpdateReferenceTime(aReferenceTime, referenceTimeStamp);
      }

      void UpdateReferenceTime(Time aReferenceTime,
                               const TimeStamp& aReferenceTimeStamp) {
        mReferenceTime = aReferenceTime;
        mReferenceTimeStamp = aReferenceTimeStamp;
      }

      bool IsTimeNewerThanTimestamp(Time aTime, TimeStamp aTimeStamp,
                                    TimeStamp* aTimeAsTimeStamp) {
        Time timeDelta = aTime - mReferenceTime;

        // Cast the result to signed 64-bit integer first since that should be
        // enough to hold the range of values returned by ToMilliseconds() and
        // the result of converting from double to an integer-type when the value
        // is outside the integer range is undefined.
        // Then we do an implicit cast to Time (typically an unsigned 32-bit
        // integer) which wraps times outside that range.
        TimeDuration timeStampDelta = (aTimeStamp - mReferenceTimeStamp);
        int64_t wholeMillis = static_cast(timeStampDelta.ToMilliseconds());
        Time wrappedTimeStampDelta = wholeMillis;  // truncate to unsigned

        Time timeToTimeStamp = wrappedTimeStampDelta - timeDelta;
        bool isNewer = false;
        if (timeToTimeStamp == 0) {
          // wholeMillis needs no adjustment
        } else if (timeToTimeStamp < kTimeHalfRange) {
          wholeMillis -= timeToTimeStamp;
        } else {
          isNewer = true;
          wholeMillis += (-timeToTimeStamp);
        }
        if (aTimeAsTimeStamp) {
          *aTimeAsTimeStamp =
              mReferenceTimeStamp + TimeDuration::FromMilliseconds(wholeMillis);
        }

        return isNewer;
      }

      Time mReferenceTime;
      TimeStamp mReferenceTimeStamp;
      Time mLastBackwardsSkewCheck;

      const Time kTimeRange;
      const Time kTimeHalfRange;
      const Time kBackwardsSkewCheckInterval;
    };

    }  // namespace mozilla