# Global Metrics

path: .metrics.mi.mi_sei
old: 54.07937291037533
new: 13.12109826463199
path: .metrics.mi.mi_visual_studio
old: 40.53965047491376
new: 20.7905433157674
path: .metrics.mi.mi_original
old: 69.32280231210254
new: 35.55182906996225
path: .metrics.cyclomatic.sum
old: 12.0
new: 30.0
path: .metrics.cyclomatic.average
old: 2.0
new: 2.3076923076923075
path: .metrics.loc.sloc
old: 52.0
new: 199.0
path: .metrics.loc.cloc
old: 8.0
new: 48.0
path: .metrics.loc.lloc
old: 11.0
new: 49.0
path: .metrics.loc.ploc
old: 34.0
new: 132.0
path: .metrics.loc.blank
old: 10.0
new: 19.0
path: .metrics.nom.functions
old: 4.0
new: 9.0
path: .metrics.nom.total
old: 4.0
new: 9.0
path: .metrics.nexits.sum
old: 6.0
new: 9.0
path: .metrics.nexits.average
old: 1.5
new: 1.0
path: .metrics.halstead.n2
old: 36.0
new: 57.0
path: .metrics.halstead.estimated_program_length
old: 261.17595007788486
new: 460.8566933658039
path: .metrics.halstead.level
old: 0.06349206349206349
new: 0.017025089605734768
path: .metrics.halstead.n1
old: 18.0
new: 27.0
path: .metrics.halstead.purity_ratio
old: 1.8264052452998945
new: 0.785105099430671
path: .metrics.halstead.N2
old: 63.0
new: 248.0
path: .metrics.halstead.difficulty
old: 15.75
new: 58.73684210526316
path: .metrics.halstead.time
old: 720.0802987082039
new: 12244.315804453168
path: .metrics.halstead.N1
old: 80.0
new: 339.0
path: .metrics.halstead.bugs
old: 0.18392793766640725
new: 1.216232945833544
path: .metrics.halstead.effort
old: 12961.445376747672
new: 220397.68448015704
path: .metrics.halstead.vocabulary
old: 54.0
new: 84.0
path: .metrics.halstead.length
old: 143.0
new: 587.0
path: .metrics.halstead.volume
old: 822.948912809376
new: 3752.290327171133
path: .metrics.nargs.sum
old: 2.0
new: 6.0
path: .metrics.nargs.average
old: 0.5
new: 0.6666666666666666
path: .metrics.cognitive.sum
old: 9.0
new: 15.0
path: .metrics.cognitive.average
old: 2.25
new: 1.6666666666666667

# Spaces Data

## Minimal test - lines (23, 197)

path: .spaces[0].metrics.loc.lloc
old: 11.0
new: 49.0
path: .spaces[0].metrics.loc.cloc
old: 4.0
new: 39.0
path: .spaces[0].metrics.loc.sloc
old: 40.0
new: 175.0
path: .spaces[0].metrics.loc.ploc
old: 29.0
new: 122.0
path: .spaces[0].metrics.loc.blank
old: 7.0
new: 14.0
path: .spaces[0].metrics.halstead.effort
old: 12995.010783425609
new: 221982.8288140469
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.7251689711770757
new: 0.765620491942144
path: .spaces[0].metrics.halstead.n2
old: 33.0
new: 55.0
path: .spaces[0].metrics.halstead.volume
old: 794.1395478760094
new: 3706.452818692343
path: .spaces[0].metrics.halstead.N2
old: 60.0
new: 244.0
path: .spaces[0].metrics.halstead.vocabulary
old: 51.0
new: 82.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 241.52365596479055
new: 446.3567468022699
path: .spaces[0].metrics.halstead.time
old: 721.9450435236448
new: 12332.379378558158
path: .spaces[0].metrics.halstead.N1
old: 80.0
new: 339.0
path: .spaces[0].metrics.halstead.length
old: 140.0
new: 583.0
path: .spaces[0].metrics.halstead.difficulty
old: 16.363636363636363
new: 59.89090909090909
path: .spaces[0].metrics.halstead.n1
old: 18.0
new: 27.0
path: .spaces[0].metrics.halstead.level
old: 0.06111111111111111
new: 0.016697024893746207
path: .spaces[0].metrics.halstead.bugs
old: 0.1842453382001581
new: 1.2220575718771538
path: .spaces[0].metrics.nom.total
old: 4.0
new: 9.0
path: .spaces[0].metrics.nom.functions
old: 4.0
new: 9.0
path: .spaces[0].metrics.cognitive.sum
old: 9.0
new: 15.0
path: .spaces[0].metrics.cognitive.average
old: 2.25
new: 1.6666666666666667
path: .spaces[0].metrics.mi.mi_original
old: 73.98840500961064
new: 37.92774818213768
path: .spaces[0].metrics.mi.mi_visual_studio
old: 43.26807310503546
new: 22.179969697156537
path: .spaces[0].metrics.mi.mi_sei
old: 55.68866357103072
new: 15.363449845332454
path: .spaces[0].metrics.nargs.average
old: 0.5
new: 0.6666666666666666
path: .spaces[0].metrics.nargs.sum
old: 2.0
new: 6.0
path: .spaces[0].metrics.cyclomatic.average
old: 2.2
new: 2.4166666666666665
path: .spaces[0].metrics.cyclomatic.sum
old: 11.0
new: 29.0
path: .spaces[0].metrics.nexits.sum
old: 6.0
new: 9.0
path: .spaces[0].metrics.nexits.average
old: 1.5
new: 1.0

### Code

    namespace mozilla {

    // Array-based LRU cache, thread-safe.
    // Optimized for cases where `FetchOrAdd` is used with the same key most
    // recently, and assuming the cost of running the value-builder function is much
    // more expensive than going through the whole list.
    // Note: No time limits on keeping items.
    // TODO: Move to more public place, if this could be used elsewhere; make sure
    // the cost/benefits are highlighted.
    template
    class SmallArrayLRUCache {
     public:
      static_assert(std::is_default_constructible_v);
      static_assert(std::is_trivially_constructible_v);
      static_assert(std::is_trivially_copyable_v);
      static_assert(std::is_default_constructible_v);
      static_assert(LRUCapacity >= 2);
      static_assert(LRUCapacity <= 1024,
                    "This seems a bit big, is this the right cache for your use?");

      // Reset all stored values to their default, and set cache size to zero.
      void Clear() {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);
        if (mSize == ShutdownSize) {
          return;
        }
        Clear(lock);
      }

      // Clear the cache, and then prevent further uses (other functions will do
      // nothing).
      void Shutdown() {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);
        if (mSize == ShutdownSize) {
          return;
        }
        Clear(lock);
        mSize = ShutdownSize;
      }

      // Add a key-value.
      template
      void Add(Key aKey, ToValue&& aValue) {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);

        if (mSize == ShutdownSize) {
          return;
        }

        // Quick add to the front, don't remove possible duplicate handles later in
        // the list, they will eventually drop off the end.
        KeyAndValue* const item0 = &mLRUArray[0];
        mSize = std::min(mSize + 1, LRUCapacity);
        if (MOZ_LIKELY(mSize != 1)) {
          // List is not empty.
          // Make a hole at the start.
          std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
        }
        item0->mKey = aKey;
        item0->mValue = std::forward(aValue);
        return;
      }

      // Look for the value associated with `aKey` in the cache.
      // If not found, run `aValueFunction()`, add it in the cache before returning.
      // After shutdown, just run `aValueFunction()`.
      template
      Value FetchOrAdd(Key aKey, ValueFunction&& aValueFunction) {
        Value value;
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);

        if (mSize == ShutdownSize) {
          value = std::forward(aValueFunction)();
          return value;
        }

        KeyAndValue* const item0 = &mLRUArray[0];
        if (MOZ_UNLIKELY(mSize == 0)) {
          // List is empty.
          value = std::forward(aValueFunction)();
          item0->mKey = aKey;
          item0->mValue = value;
          mSize = 1;
          return value;
        }

        if (MOZ_LIKELY(item0->mKey == aKey)) {
          // This is already at the beginning of the list, we're done.
    #ifdef SMALLARRAYLRUCACHE_STATS
          ++mCacheFoundAt[0];
    #endif  // SMALLARRAYLRUCACHE_STATS
          value = item0->mValue;
          return value;
        }

        for (KeyAndValue* item = item0 + 1; item != item0 + mSize; ++item) {
          if (item->mKey == aKey) {
            // Found handle in the middle.
    #ifdef SMALLARRAYLRUCACHE_STATS
            ++mCacheFoundAt[unsigned(item - item0)];
    #endif  // SMALLARRAYLRUCACHE_STATS
            value = item->mValue;
            // Move this item to the start of the list.
            std::rotate(item0, item, item + 1);
            return value;
          }
        }

        // Handle was not in the list.
    #ifdef SMALLARRAYLRUCACHE_STATS
        ++mCacheFoundAt[LRUCapacity];
    #endif  // SMALLARRAYLRUCACHE_STATS
        {
          // Don't lock while doing the potentially-expensive ValueFunction().
          // This means that the list could change when we lock again, but
          // it's okay because we'll want to add the new entry at the beginning
          // anyway, whatever else is in the list then.
          // In the worst case, it could be the same handle as another `FetchOrAdd`
          // in parallel, it just means it will be duplicated, so it's a little bit
          // less efficient (using the extra space), but not wrong (the next
          // `FetchOrAdd` will find the first one).
          mozilla::OffTheBooksMutexAutoUnlock unlock(mMutex);
          value = std::forward(aValueFunction)();
        }
        // Make a hole at the start, and put the value there.
        mSize = std::min(mSize + 1, LRUCapacity);
        std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
        item0->mKey = aKey;
        item0->mValue = value;
        return value;
      }

    #ifdef SMALLARRAYLRUCACHE_STATS
      ~SmallArrayLRUCache() {
        if (mSize != 0 && mSize != ShutdownSize) {
          fprintf(stderr,
                  "***** SmallArrayLRUCache stats: (position -> hit count)\n");
          for (unsigned i = 0; i < mSize; ++i) {
            fprintf(stderr, "***** %3u -> %6u\n", i, mCacheFoundAt[i]);
          }
          fprintf(stderr, "***** not found -> %6u\n", mCacheFoundAt[LRUCapacity]);
        }
      }
    #endif  // SMALLARRAYLRUCACHE_STATS

     private:
      void Clear(const mozilla::OffTheBooksMutexAutoLock&) {
        for (KeyAndValue* item = &mLRUArray[0]; item != &mLRUArray[mSize]; ++item) {
          item->mValue = Value{};
        }
        mSize = 0;
      }

      struct KeyAndValue {
        Key mKey;
        Value mValue;

        KeyAndValue() = default;
        KeyAndValue(KeyAndValue&&) = default;
        KeyAndValue& operator=(KeyAndValue&&) = default;
      };

      // Special size value that indicates the cache is in shutdown mode.
      constexpr static unsigned ShutdownSize = unsigned(-1);

      mozilla::OffTheBooksMutex mMutex{"LRU cache"};
      unsigned mSize = 0;
      KeyAndValue mLRUArray[LRUCapacity];
    #ifdef SMALLARRAYLRUCACHE_STATS
      // Hit count for each position in the case. +1 for counting not-found cases.
      unsigned mCacheFoundAt[LRUCapacity + 1] = {0u};
    #endif  // SMALLARRAYLRUCACHE_STATS
    };

    }  // namespace mozilla

## Minimal test - lines (33, 195)

path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.5454545454545454
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 28.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 9.0
path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 9.0
path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 1.6666666666666667
path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 15.0
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 120.36819247706724
new: 15.699447339817125
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 79.4351632867568
new: 23.0485347669935
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 135.83412922035413
new: 39.41299445155889
path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 0.6666666666666666
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 6.0
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 1.0
new: 9.0
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 67.5
new: 217964.04309917337
path: .spaces[0].spaces[0].metrics.halstead.length
old: 9.0
new: 573.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.005526047247960579
new: 1.2072632541666437
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 5.0
new: 27.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 3.0
new: 54.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.81828088628892
new: 0.7663977097299144
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.4
new: 0.016666666666666666
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 3.0
new: 240.0
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 2.5
new: 60.0
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 8.0
new: 81.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 3.75
new: 12109.113505509633
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 6.0
new: 333.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 16.36452797660028
new: 439.14588767524094
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 27.0
new: 3632.73405165289
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 3.0
new: 163.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 1.0
new: 49.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 0.0
new: 12.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 0.0
new: 32.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 3.0
new: 119.0

### Code

    class SmallArrayLRUCache {
     public:
      static_assert(std::is_default_constructible_v);
      static_assert(std::is_trivially_constructible_v);
      static_assert(std::is_trivially_copyable_v);
      static_assert(std::is_default_constructible_v);
      static_assert(LRUCapacity >= 2);
      static_assert(LRUCapacity <= 1024,
                    "This seems a bit big, is this the right cache for your use?");

      // Reset all stored values to their default, and set cache size to zero.
      void Clear() {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);
        if (mSize == ShutdownSize) {
          return;
        }
        Clear(lock);
      }

      // Clear the cache, and then prevent further uses (other functions will do
      // nothing).
      void Shutdown() {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);
        if (mSize == ShutdownSize) {
          return;
        }
        Clear(lock);
        mSize = ShutdownSize;
      }

      // Add a key-value.
      template
      void Add(Key aKey, ToValue&& aValue) {
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);

        if (mSize == ShutdownSize) {
          return;
        }

        // Quick add to the front, don't remove possible duplicate handles later in
        // the list, they will eventually drop off the end.
        KeyAndValue* const item0 = &mLRUArray[0];
        mSize = std::min(mSize + 1, LRUCapacity);
        if (MOZ_LIKELY(mSize != 1)) {
          // List is not empty.
          // Make a hole at the start.
          std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
        }
        item0->mKey = aKey;
        item0->mValue = std::forward(aValue);
        return;
      }

      // Look for the value associated with `aKey` in the cache.
      // If not found, run `aValueFunction()`, add it in the cache before returning.
      // After shutdown, just run `aValueFunction()`.
      template
      Value FetchOrAdd(Key aKey, ValueFunction&& aValueFunction) {
        Value value;
        mozilla::OffTheBooksMutexAutoLock lock(mMutex);

        if (mSize == ShutdownSize) {
          value = std::forward(aValueFunction)();
          return value;
        }

        KeyAndValue* const item0 = &mLRUArray[0];
        if (MOZ_UNLIKELY(mSize == 0)) {
          // List is empty.
          value = std::forward(aValueFunction)();
          item0->mKey = aKey;
          item0->mValue = value;
          mSize = 1;
          return value;
        }

        if (MOZ_LIKELY(item0->mKey == aKey)) {
          // This is already at the beginning of the list, we're done.
    #ifdef SMALLARRAYLRUCACHE_STATS
          ++mCacheFoundAt[0];
    #endif  // SMALLARRAYLRUCACHE_STATS
          value = item0->mValue;
          return value;
        }

        for (KeyAndValue* item = item0 + 1; item != item0 + mSize; ++item) {
          if (item->mKey == aKey) {
            // Found handle in the middle.
    #ifdef SMALLARRAYLRUCACHE_STATS
            ++mCacheFoundAt[unsigned(item - item0)];
    #endif  // SMALLARRAYLRUCACHE_STATS
            value = item->mValue;
            // Move this item to the start of the list.
            std::rotate(item0, item, item + 1);
            return value;
          }
        }

        // Handle was not in the list.
    #ifdef SMALLARRAYLRUCACHE_STATS
        ++mCacheFoundAt[LRUCapacity];
    #endif  // SMALLARRAYLRUCACHE_STATS
        {
          // Don't lock while doing the potentially-expensive ValueFunction().
          // This means that the list could change when we lock again, but
          // it's okay because we'll want to add the new entry at the beginning
          // anyway, whatever else is in the list then.
          // In the worst case, it could be the same handle as another `FetchOrAdd`
          // in parallel, it just means it will be duplicated, so it's a little bit
          // less efficient (using the extra space), but not wrong (the next
          // `FetchOrAdd` will find the first one).
          mozilla::OffTheBooksMutexAutoUnlock unlock(mMutex);
          value = std::forward(aValueFunction)();
        }
        // Make a hole at the start, and put the value there.
        mSize = std::min(mSize + 1, LRUCapacity);
        std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
        item0->mKey = aKey;
        item0->mValue = value;
        return value;
      }

    #ifdef SMALLARRAYLRUCACHE_STATS
      ~SmallArrayLRUCache() {
        if (mSize != 0 && mSize != ShutdownSize) {
          fprintf(stderr,
                  "***** SmallArrayLRUCache stats: (position -> hit count)\n");
          for (unsigned i = 0; i < mSize; ++i) {
            fprintf(stderr, "***** %3u -> %6u\n", i, mCacheFoundAt[i]);
          }
          fprintf(stderr, "***** not found -> %6u\n", mCacheFoundAt[LRUCapacity]);
        }
      }
    #endif  // SMALLARRAYLRUCACHE_STATS

     private:
      void Clear(const mozilla::OffTheBooksMutexAutoLock&) {
        for (KeyAndValue* item = &mLRUArray[0]; item != &mLRUArray[mSize]; ++item) {
          item->mValue = Value{};
        }
        mSize = 0;
      }

      struct KeyAndValue {
        Key mKey;
        Value mValue;

        KeyAndValue() = default;
        KeyAndValue(KeyAndValue&&) = default;
        KeyAndValue& operator=(KeyAndValue&&) = default;
      };

      // Special size value that indicates the cache is in shutdown mode.
      constexpr static unsigned ShutdownSize = unsigned(-1);

      mozilla::OffTheBooksMutex mMutex{"LRU cache"};
      unsigned mSize = 0;
      KeyAndValue mLRUArray[LRUCapacity];
    #ifdef SMALLARRAYLRUCACHE_STATS
      // Hit count for each position in the case. +1 for counting not-found cases.
      unsigned mCacheFoundAt[LRUCapacity + 1] = {0u};
    #endif  // SMALLARRAYLRUCACHE_STATS
    };