# Global Metrics

path: .metrics.halstead.N1
old: 136.0
new: 396.0
path: .metrics.halstead.vocabulary
old: 59.0
new: 80.0
path: .metrics.halstead.level
old: 0.13717421124828533
new: 0.009623259623259625
path: .metrics.halstead.bugs
old: 0.14747506103818764
new: 1.970762883705698
path: .metrics.halstead.difficulty
old: 7.29
new: 103.91489361702128
path: .metrics.halstead.n2
old: 50.0
new: 47.0
path: .metrics.halstead.purity_ratio
old: 1.4318992373351014
new: 0.6178189045775692
path: .metrics.halstead.N2
old: 81.0
new: 296.0
path: .metrics.halstead.effort
old: 9305.929519076975
new: 454604.1999207974
path: .metrics.halstead.n1
old: 9.0
new: 33.0
path: .metrics.halstead.length
old: 217.0
new: 692.0
path: .metrics.halstead.time
old: 516.9960843931653
new: 25255.788884488746
path: .metrics.halstead.estimated_program_length
old: 310.722134501717
new: 427.5306819676779
path: .metrics.halstead.volume
old: 1276.5335417115195
new: 4374.774241662055
path: .metrics.loc.ploc
old: 57.0
new: 154.0
path: .metrics.loc.cloc
old: 31.0
new: 61.0
path: .metrics.loc.blank
old: 8.0
new: 27.0
path: .metrics.loc.sloc
old: 96.0
new: 242.0
path: .metrics.loc.lloc
old: 0.0
new: 62.0
path: .metrics.nom.functions
old: 1.0
new: 13.0
path: .metrics.nom.total
old: 1.0
new: 13.0
path: .metrics.cognitive.sum
old: 0.0
new: 22.0
path: .metrics.cognitive.average
old: 0.0
new: 1.6923076923076923
path: .metrics.nargs.sum
old: 0.0
new: 2.0
path: .metrics.nargs.average
old: 0.0
new: 0.15384615384615383
path: .metrics.mi.mi_sei
old: 48.29772582338573
new: 5.705813835441759
path: .metrics.mi.mi_visual_studio
old: 34.47231639324728
new: 17.125401063963984
path: .metrics.mi.mi_original
old: 58.94766103245286
new: 29.284435819378416
path: .metrics.cyclomatic.average
old: 1.0
new: 2.2222222222222223
path: .metrics.cyclomatic.sum
old: 4.0
new: 40.0
path: .metrics.nexits.average
old: 0.0
new: 1.1538461538461535
path: .metrics.nexits.sum
old: 0.0
new: 15.0

# Spaces Data

## Minimal test - lines (54, 54)

path: .spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: 0.6666666666666666
new: 0.5
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 8.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 8.0
new: 11.60964047443681
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 4.0
new: 5.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 12.0
new: 23.21928094887362
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 4.0
new: 5.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.0017471609294725976
new: 0.002712967490108627
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 3.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 3.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.188721875540867
new: 1.6
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 1.5
new: 2.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 0.6666666666666666
new: 1.289960052715201
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: 159.95690398326485
new: 158.02045369261705
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: 155.17000000000002
new: 152.37629276875444
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 93.54204911302038
new: 92.40962204246613

### Code

      Queue() = default;

## Minimal test - lines (52, 238)

path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 7.21875
new: 110.8953488372093
path: .spaces[0].spaces[0].metrics.halstead.length
old: 208.0
new: 678.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.4259977165749822
new: 0.5896672424646301
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 57.0
new: 76.0
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 9.0
new: 33.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.14162877362013035
new: 2.014333692733434
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.13852813852813853
new: 0.009017510747614556
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 8758.084356268359
new: 469763.2165545625
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 296.6075250475963
new: 399.7943903910192
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 131.0
new: 389.0
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 77.0
new: 289.0
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 48.0
new: 43.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 486.5602420149088
new: 26097.95647525347
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 1213.2411229462662
new: 4236.094854114751
path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 0.0
new: 22.0
path: .spaces[0].spaces[0].metrics.cognitive.average
old: 0.0
new: 1.6923076923076923
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.0
new: 0.15384615384615383
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 2.0
new: 38.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.375
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.0
new: 1.1538461538461535
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 0.0
new: 15.0
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 40.88828143745645
new: 19.934956428620534
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 69.91896125805053
new: 34.088775492941124
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: 25.374526397893277
new: 0.46540477818025394
path: .spaces[0].spaces[0].metrics.nom.functions
old: 1.0
new: 13.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 1.0
new: 13.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 51.0
new: 187.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 48.0
new: 147.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 0.0
new: 62.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 3.0
new: 22.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 0.0
new: 18.0

### Code

    class Queue {
     public:
      Queue() = default;

      ~Queue() {
        MOZ_ASSERT(IsEmpty());

        if (mHead) {
          free(mHead);
        }
      }

      T& Push(T&& aElement) {
    #if defined(EXTRA_ASSERTS) && DEBUG
        size_t original_length = Count();
    #endif
        if (!mHead) {
          mHead = NewPage();
          MOZ_ASSERT(mHead);

          mTail = mHead;
          T& eltLocation = mTail->mEvents[0];
          eltLocation = std::move(aElement);
          mOffsetHead = 0;
          mHeadLength = 1;
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        if ((mHead == mTail && mHeadLength == ItemsPerPage) ||
            (mHead != mTail && mTailLength == ItemsPerPage)) {
          // either we have one (circular) buffer and it's full, or
          // we have multiple buffers and the last buffer is full
          Page* page = NewPage();
          MOZ_ASSERT(page);

          mTail->mNext = page;
          mTail = page;
          T& eltLocation = page->mEvents[0];
          eltLocation = std::move(aElement);
          mTailLength = 1;
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        if (mHead == mTail) {
          // we have space in the (single) head buffer
          uint16_t offset = (mOffsetHead + mHeadLength++) % ItemsPerPage;
          T& eltLocation = mTail->mEvents[offset];
          eltLocation = std::move(aElement);
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        // else we have space to insert into last buffer
        T& eltLocation = mTail->mEvents[mTailLength++];
        eltLocation = std::move(aElement);
    #ifdef EXTRA_ASSERTS
        MOZ_ASSERT(Count() == original_length + 1);
    #endif
        return eltLocation;
      }

      bool IsEmpty() const {
        return !mHead || (mHead == mTail && mHeadLength == 0);
      }

      T Pop() {
    #if defined(EXTRA_ASSERTS) && DEBUG
        size_t original_length = Count();
    #endif
        MOZ_ASSERT(!IsEmpty());

        T result = std::move(mHead->mEvents[mOffsetHead]);
        mOffsetHead = (mOffsetHead + 1) % ItemsPerPage;
        mHeadLength -= 1;

        // Check if mHead points to empty (circular) Page and we have more
        // pages
        if (mHead != mTail && mHeadLength == 0) {
          Page* dead = mHead;
          mHead = mHead->mNext;
          free(dead);
          mOffsetHead = 0;
          // if there are still >1 pages, the new head is full.
          if (mHead != mTail) {
            mHeadLength = ItemsPerPage;
          } else {
            mHeadLength = mTailLength;
            mTailLength = 0;
          }
        }

    #ifdef EXTRA_ASSERTS
        MOZ_ASSERT(Count() == original_length - 1);
    #endif
        return result;
      }

      T& FirstElement() {
        MOZ_ASSERT(!IsEmpty());
        return mHead->mEvents[mOffsetHead];
      }

      const T& FirstElement() const {
        MOZ_ASSERT(!IsEmpty());
        return mHead->mEvents[mOffsetHead];
      }

      T& LastElement() {
        MOZ_ASSERT(!IsEmpty());
        uint16_t offset =
            mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
        return mTail->mEvents[offset];
      }

      const T& LastElement() const {
        MOZ_ASSERT(!IsEmpty());
        uint16_t offset =
            mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
        return mTail->mEvents[offset];
      }

      size_t Count() const {
        // It is obvious count is 0 when the queue is empty.
        if (!mHead) {
          return 0;
        }

        // Compute full (intermediate) pages; Doesn't count first or last page
        int count = 0;
        // 1 buffer will have mHead == mTail; 2 will have mHead->mNext == mTail
        for (Page* page = mHead; page != mTail && page->mNext != mTail;
             page = page->mNext) {
          count += ItemsPerPage;
        }
        // add first and last page
        count += mHeadLength + mTailLength;
        MOZ_ASSERT(count >= 0);

        return count;
      }

      size_t ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
        size_t n = 0;
        if (mHead) {
          for (Page* page = mHead; page != mTail; page = page->mNext) {
            n += aMallocSizeOf(page);
          }
        }
        return n;
      }

      size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
        return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
      }

     private:
      static_assert(
          (RequestedItemsPerPage & (RequestedItemsPerPage - 1)) == 0,
          "RequestedItemsPerPage should be a power of two to avoid heap slop.");

      // Since a Page must also contain a "next" pointer, we use one of the items to
      // store this pointer. If sizeof(T) > sizeof(Page*), then some space will be
      // wasted. So be it.
      static const size_t ItemsPerPage = RequestedItemsPerPage - 1;

      // Page objects are linked together to form a simple deque.
      struct Page {
        struct Page* mNext;
        T mEvents[ItemsPerPage];
      };

      static Page* NewPage() {
        return static_cast(moz_xcalloc(1, sizeof(Page)));
      }

      Page* mHead = nullptr;
      Page* mTail = nullptr;

      uint16_t mOffsetHead = 0;  // Read position in head page
      uint16_t mHeadLength = 0;  // Number of items in the head page
      uint16_t mTailLength = 0;  // Number of items in the tail page
    };

## Minimal test - lines (12, 240)

path: .spaces[0].metrics.loc.ploc
old: 50.0
new: 150.0
path: .spaces[0].metrics.loc.sloc
old: 57.0
new: 229.0
path: .spaces[0].metrics.loc.cloc
old: 2.0
new: 54.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 62.0
path: .spaces[0].metrics.loc.blank
old: 5.0
new: 25.0
path: .spaces[0].metrics.nargs.average
old: 0.0
new: 0.15384615384615383
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 2.0
path: .spaces[0].metrics.mi.mi_visual_studio
old: 39.6434626581713
new: 17.813832621442984
path: .spaces[0].metrics.mi.mi_original
old: 67.79032114547293
new: 30.461653782667497
path: .spaces[0].metrics.mi.mi_sei
old: 36.71210599382182
new: 6.382592842451579
path: .spaces[0].metrics.halstead.length
old: 211.0
new: 689.0
path: .spaces[0].metrics.halstead.N1
old: 133.0
new: 396.0
path: .spaces[0].metrics.halstead.volume
old: 1236.0339899719177
new: 4330.642128796089
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.439100034950834
new: 0.6002879466961746
path: .spaces[0].metrics.halstead.level
old: 0.1396011396011396
new: 0.00930809804529941
path: .spaces[0].metrics.halstead.n2
old: 49.0
new: 45.0
path: .spaces[0].metrics.halstead.effort
old: 8854.039397553941
new: 465255.3193703265
path: .spaces[0].metrics.halstead.difficulty
old: 7.163265306122449
new: 107.43333333333334
path: .spaces[0].metrics.halstead.estimated_program_length
old: 303.650107374626
new: 413.5983952736643
path: .spaces[0].metrics.halstead.time
old: 491.8910776418856
new: 25847.517742795917
path: .spaces[0].metrics.halstead.vocabulary
old: 58.0
new: 78.0
path: .spaces[0].metrics.halstead.n1
old: 9.0
new: 33.0
path: .spaces[0].metrics.halstead.bugs
old: 0.14266136618480402
new: 2.001426489119984
path: .spaces[0].metrics.halstead.N2
old: 78.0
new: 293.0
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 22.0
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.6923076923076923
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 13.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 13.0
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 15.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.1538461538461535
path: .spaces[0].metrics.cyclomatic.sum
old: 3.0
new: 39.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.2941176470588234

### Code

    namespace mozilla {

    // define to turn on additional (DEBUG) asserts
    // #define EXTRA_ASSERTS 1

    // A queue implements a singly linked list of pages, each of which contains some
    // number of elements. Since the queue needs to store a "next" pointer, the
    // actual number of elements per page won't be quite as many as were requested.
    //
    // This class should only be used if it's valid to construct T elements from all
    // zeroes. The class also fails to call the destructor on items. However, it
    // will only destroy items after it has moved out their contents. The queue is
    // required to be empty when it is destroyed.
    //
    // Each page consists of N entries.  We use the head buffer as a circular buffer
    // if it's the only buffer; if we have more than one buffer when the head is
    // empty we release it.  This avoids occasional freeing and reallocating buffers
    // every N entries.  We'll still allocate and free every N if the normal queue
    // depth is greated than N.  A fancier solution would be to move an empty Head
    // buffer to be an empty tail buffer, freeing if we have multiple empty tails,
    // but that probably isn't worth it.
    //
    // Cases:
    //   a) single buffer, circular
    //      Push: if not full:
    //              Add to tail, bump tail and reset to 0 if at end
    //            full:
    //              Add new page, insert there and set tail to 1
    //      Pop:
    //            take entry and bump head, reset to 0 if at end
    //   b) multiple buffers:
    //      Push: if not full:
    //              Add to tail, bump tail
    //            full:
    //              Add new page, insert there and set tail to 1
    //      Pop:
    //            take entry and bump head, reset to 0 if at end
    //            if buffer is empty, free head buffer and promote next to head
    //
    template
    class Queue {
     public:
      Queue() = default;

      ~Queue() {
        MOZ_ASSERT(IsEmpty());

        if (mHead) {
          free(mHead);
        }
      }

      T& Push(T&& aElement) {
    #if defined(EXTRA_ASSERTS) && DEBUG
        size_t original_length = Count();
    #endif
        if (!mHead) {
          mHead = NewPage();
          MOZ_ASSERT(mHead);

          mTail = mHead;
          T& eltLocation = mTail->mEvents[0];
          eltLocation = std::move(aElement);
          mOffsetHead = 0;
          mHeadLength = 1;
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        if ((mHead == mTail && mHeadLength == ItemsPerPage) ||
            (mHead != mTail && mTailLength == ItemsPerPage)) {
          // either we have one (circular) buffer and it's full, or
          // we have multiple buffers and the last buffer is full
          Page* page = NewPage();
          MOZ_ASSERT(page);

          mTail->mNext = page;
          mTail = page;
          T& eltLocation = page->mEvents[0];
          eltLocation = std::move(aElement);
          mTailLength = 1;
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        if (mHead == mTail) {
          // we have space in the (single) head buffer
          uint16_t offset = (mOffsetHead + mHeadLength++) % ItemsPerPage;
          T& eltLocation = mTail->mEvents[offset];
          eltLocation = std::move(aElement);
    #ifdef EXTRA_ASSERTS
          MOZ_ASSERT(Count() == original_length + 1);
    #endif
          return eltLocation;
        }
        // else we have space to insert into last buffer
        T& eltLocation = mTail->mEvents[mTailLength++];
        eltLocation = std::move(aElement);
    #ifdef EXTRA_ASSERTS
        MOZ_ASSERT(Count() == original_length + 1);
    #endif
        return eltLocation;
      }

      bool IsEmpty() const {
        return !mHead || (mHead == mTail && mHeadLength == 0);
      }

      T Pop() {
    #if defined(EXTRA_ASSERTS) && DEBUG
        size_t original_length = Count();
    #endif
        MOZ_ASSERT(!IsEmpty());

        T result = std::move(mHead->mEvents[mOffsetHead]);
        mOffsetHead = (mOffsetHead + 1) % ItemsPerPage;
        mHeadLength -= 1;

        // Check if mHead points to empty (circular) Page and we have more
        // pages
        if (mHead != mTail && mHeadLength == 0) {
          Page* dead = mHead;
          mHead = mHead->mNext;
          free(dead);
          mOffsetHead = 0;
          // if there are still >1 pages, the new head is full.
          if (mHead != mTail) {
            mHeadLength = ItemsPerPage;
          } else {
            mHeadLength = mTailLength;
            mTailLength = 0;
          }
        }

    #ifdef EXTRA_ASSERTS
        MOZ_ASSERT(Count() == original_length - 1);
    #endif
        return result;
      }

      T& FirstElement() {
        MOZ_ASSERT(!IsEmpty());
        return mHead->mEvents[mOffsetHead];
      }

      const T& FirstElement() const {
        MOZ_ASSERT(!IsEmpty());
        return mHead->mEvents[mOffsetHead];
      }

      T& LastElement() {
        MOZ_ASSERT(!IsEmpty());
        uint16_t offset =
            mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
        return mTail->mEvents[offset];
      }

      const T& LastElement() const {
        MOZ_ASSERT(!IsEmpty());
        uint16_t offset =
            mHead == mTail ? mOffsetHead + mHeadLength - 1 : mTailLength - 1;
        return mTail->mEvents[offset];
      }

      size_t Count() const {
        // It is obvious count is 0 when the queue is empty.
        if (!mHead) {
          return 0;
        }

        // Compute full (intermediate) pages; Doesn't count first or last page
        int count = 0;
        // 1 buffer will have mHead == mTail; 2 will have mHead->mNext == mTail
        for (Page* page = mHead; page != mTail && page->mNext != mTail;
             page = page->mNext) {
          count += ItemsPerPage;
        }
        // add first and last page
        count += mHeadLength + mTailLength;
        MOZ_ASSERT(count >= 0);

        return count;
      }

      size_t ShallowSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
        size_t n = 0;
        if (mHead) {
          for (Page* page = mHead; page != mTail; page = page->mNext) {
            n += aMallocSizeOf(page);
          }
        }
        return n;
      }

      size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
        return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
      }

     private:
      static_assert(
          (RequestedItemsPerPage & (RequestedItemsPerPage - 1)) == 0,
          "RequestedItemsPerPage should be a power of two to avoid heap slop.");

      // Since a Page must also contain a "next" pointer, we use one of the items to
      // store this pointer. If sizeof(T) > sizeof(Page*), then some space will be
      // wasted. So be it.
      static const size_t ItemsPerPage = RequestedItemsPerPage - 1;

      // Page objects are linked together to form a simple deque.
      struct Page {
        struct Page* mNext;
        T mEvents[ItemsPerPage];
      };

      static Page* NewPage() {
        return static_cast(moz_xcalloc(1, sizeof(Page)));
      }

      Page* mHead = nullptr;
      Page* mTail = nullptr;

      uint16_t mOffsetHead = 0;  // Read position in head page
      uint16_t mHeadLength = 0;  // Number of items in the head page
      uint16_t mTailLength = 0;  // Number of items in the tail page
    };

    }  // namespace mozilla