# Global Metrics

path: .metrics.cognitive.average
old: 0.0
new: 6.0
path: .metrics.cognitive.sum
old: 0.0
new: 66.0
path: .metrics.cyclomatic.sum
old: 21.0
new: 58.0
path: .metrics.cyclomatic.average
old: 1.05
new: 4.461538461538462
path: .metrics.nom.total
old: 14.0
new: 11.0
path: .metrics.nom.functions
old: 14.0
new: 11.0
path: .metrics.nargs.sum
old: 8.0
new: 15.0
path: .metrics.nargs.average
old: 0.5714285714285714
new: 1.3636363636363635
path: .metrics.mi.mi_original
old: 40.613966827696814
new: 15.409574029739376
path: .metrics.mi.mi_visual_studio
old: 23.750857793974745
new: 9.011446801017176
path: .metrics.mi.mi_sei
old: 1.8563980193794336
new: -19.702814126821387
path: .metrics.nexits.average
old: 0.7857142857142857
new: 3.363636363636364
path: .metrics.nexits.sum
old: 11.0
new: 37.0
path: .metrics.loc.cloc
old: 8.0
new: 54.0
path: .metrics.loc.blank
old: 39.0
new: 63.0
path: .metrics.loc.ploc
old: 116.0
new: 254.0
path: .metrics.loc.lloc
old: 13.0
new: 131.0
path: .metrics.loc.sloc
old: 163.0
new: 371.0
path: .metrics.halstead.N2
old: 241.0
new: 432.0
path: .metrics.halstead.effort
old: 72856.53849886158
new: 686721.0192647432
path: .metrics.halstead.N1
old: 328.0
new: 674.0
path: .metrics.halstead.bugs
old: 0.5814661458837258
new: 2.5945714743056074
path: .metrics.halstead.estimated_program_length
old: 760.8457306866735
new: 656.7263790100843
path: .metrics.halstead.length
old: 569.0
new: 1106.0
path: .metrics.halstead.purity_ratio
old: 1.3371629713298303
new: 0.5937851528120112
path: .metrics.halstead.vocabulary
old: 120.0
new: 111.0
path: .metrics.halstead.n2
old: 104.0
new: 78.0
path: .metrics.halstead.volume
old: 3930.0207489012473
new: 7514.623948183217
path: .metrics.halstead.level
old: 0.05394190871369294
new: 0.010942760942760943
path: .metrics.halstead.n1
old: 16.0
new: 33.0
path: .metrics.halstead.time
old: 4047.5854721589767
new: 38151.167736930176
path: .metrics.halstead.difficulty
old: 18.53846153846154
new: 91.3846153846154

# Spaces Data

## Minimal test - lines (20, 26)

path: .spaces[0].spaces[0].metrics.nargs.average
old: 0.5714285714285714
new: 0.0
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 8.0
new: 0.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 14.0
new: 1.0
path: .spaces[0].spaces[0].metrics.nom.functions
old: 14.0
new: 1.0
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 19.0
new: 1.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.0555555555555556
new: 1.0
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 0.5852526282962249
new: 0.02006528643365642
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 19.333333333333332
new: 5.333333333333333
path: .spaces[0].spaces[0].metrics.halstead.length
old: 559.0
new: 23.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 4087.186320054659
new: 25.946418728096265
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 327.0
new: 15.0
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 73569.35376098387
new: 467.0355371057327
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 112.0
new: 14.0
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 232.0
new: 8.0
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.05172413793103449
new: 0.1875
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 16.0
new: 8.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 1.2453602863492506
new: 1.717816304535954
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 96.0
new: 6.0
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 696.156400069231
new: 39.50977500432694
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 3805.311401430201
new: 87.56916320732489
path: .spaces[0].spaces[0].metrics.mi.mi_original
old: 43.36232083335014
new: 115.9896252128054
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 25.358082358684293
new: 67.83019018292714
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: -4.748270369545855
new: 91.73862495647556
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.7857142857142857
new: 1.0
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 11.0
new: 1.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 143.0
new: 7.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 37.0
new: 1.0
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 13.0
new: 2.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 105.0
new: 6.0

### Code

    static size_t CompressedBufferLength() {
      static size_t kCompressedBufferLength =
          detail::SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize);

      MOZ_ASSERT(kCompressedBufferLength > 0);
      return kCompressedBufferLength;
    }

## Minimal test - lines (14, 371)

path: .spaces[0].metrics.nom.functions
old: 14.0
new: 11.0
path: .spaces[0].metrics.nom.total
old: 14.0
new: 11.0
path: .spaces[0].metrics.mi.mi_sei
old: -2.756125139913826
new: -19.548162999982008
path: .spaces[0].metrics.mi.mi_visual_studio
old: 25.075414596450095
new: 9.518676016228598
path: .spaces[0].metrics.mi.mi_original
old: 42.87895895992966
new: 16.276935987750903
path: .spaces[0].metrics.halstead.time
old: 4084.701272458998
new: 39387.597840448434
path: .spaces[0].metrics.halstead.volume
old: 3826.12039791492
new: 7429.096619014063
path: .spaces[0].metrics.halstead.n2
old: 97.0
new: 74.0
path: .spaces[0].metrics.halstead.N2
old: 233.0
new: 428.0
path: .spaces[0].metrics.halstead.difficulty
old: 19.216494845360824
new: 95.43243243243244
path: .spaces[0].metrics.halstead.effort
old: 73524.62290426197
new: 708976.7611280718
path: .spaces[0].metrics.halstead.vocabulary
old: 113.0
new: 107.0
path: .spaces[0].metrics.halstead.level
old: 0.052038626609442064
new: 0.01047861795525347
path: .spaces[0].metrics.halstead.n1
old: 16.0
new: 33.0
path: .spaces[0].metrics.halstead.N1
old: 328.0
new: 674.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 704.1915456921514
new: 625.9645549953713
path: .spaces[0].metrics.halstead.length
old: 561.0
new: 1102.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.2552433969557066
new: 0.5680259119740211
path: .spaces[0].metrics.halstead.bugs
old: 0.5850153781943614
new: 2.650330769851925
path: .spaces[0].metrics.nargs.average
old: 0.5714285714285714
new: 1.3636363636363635
path: .spaces[0].metrics.nargs.sum
old: 8.0
new: 15.0
path: .spaces[0].metrics.nexits.average
old: 0.7857142857142857
new: 3.363636363636364
path: .spaces[0].metrics.nexits.sum
old: 11.0
new: 37.0
path: .spaces[0].metrics.loc.blank
old: 36.0
new: 61.0
path: .spaces[0].metrics.loc.cloc
old: 2.0
new: 48.0
path: .spaces[0].metrics.loc.ploc
old: 107.0
new: 249.0
path: .spaces[0].metrics.loc.sloc
old: 145.0
new: 358.0
path: .spaces[0].metrics.loc.lloc
old: 13.0
new: 131.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0526315789473684
new: 4.75
path: .spaces[0].metrics.cyclomatic.sum
old: 20.0
new: 57.0
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 6.0
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 66.0

### Code

    namespace mozilla {

    NS_IMPL_ISUPPORTS(SnappyUncompressInputStream, nsIInputStream);

    // Putting kCompressedBufferLength inside a function avoids a static
    // constructor.
    static size_t CompressedBufferLength() {
      static size_t kCompressedBufferLength =
          detail::SnappyFrameUtils::MaxCompressedBufferLength(snappy::kBlockSize);

      MOZ_ASSERT(kCompressedBufferLength > 0);
      return kCompressedBufferLength;
    }

    SnappyUncompressInputStream::SnappyUncompressInputStream(
        nsIInputStream* aBaseStream)
        : mBaseStream(aBaseStream),
          mUncompressedBytes(0),
          mNextByte(0),
          mNextChunkType(Unknown),
          mNextChunkDataLength(0),
          mNeedFirstStreamIdentifier(true) {
      // This implementation only supports sync base streams.  Verify this in debug
      // builds.  Note, this is a bit complicated because the streams we support
      // advertise different capabilities:
      //  - nsFileInputStream - blocking and sync
      //  - nsStringInputStream - non-blocking and sync
      //  - nsPipeInputStream - can be blocking, but provides async interface
    #ifdef DEBUG
      bool baseNonBlocking;
      nsresult rv = mBaseStream->IsNonBlocking(&baseNonBlocking);
      MOZ_ASSERT(NS_SUCCEEDED(rv));
      if (baseNonBlocking) {
        nsCOMPtr async = do_QueryInterface(mBaseStream);
        MOZ_ASSERT(!async);
      }
    #endif
    }

    NS_IMETHODIMP
    SnappyUncompressInputStream::Close() {
      if (!mBaseStream) {
        return NS_OK;
      }

      mBaseStream->Close();
      mBaseStream = nullptr;

      mUncompressedBuffer = nullptr;
      mCompressedBuffer = nullptr;

      return NS_OK;
    }

    NS_IMETHODIMP
    SnappyUncompressInputStream::Available(uint64_t* aLengthOut) {
      if (!mBaseStream) {
        return NS_BASE_STREAM_CLOSED;
      }

      // If we have uncompressed bytes, then we are done.
      *aLengthOut = UncompressedLength();
      if (*aLengthOut > 0) {
        return NS_OK;
      }

      // Otherwise, attempt to uncompress bytes until we get something or the
      // underlying stream is drained.  We loop here because some chunks can
      // be StreamIdentifiers, padding, etc with no data.
      uint32_t bytesRead;
      do {
        nsresult rv = ParseNextChunk(&bytesRead);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }
        *aLengthOut = UncompressedLength();
      } while (*aLengthOut == 0 && bytesRead);

      return NS_OK;
    }

    NS_IMETHODIMP
    SnappyUncompressInputStream::Read(char* aBuf, uint32_t aCount,
                                      uint32_t* aBytesReadOut) {
      return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aBytesReadOut);
    }

    NS_IMETHODIMP
    SnappyUncompressInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                              void* aClosure, uint32_t aCount,
                                              uint32_t* aBytesReadOut) {
      *aBytesReadOut = 0;

      if (!mBaseStream) {
        return NS_BASE_STREAM_CLOSED;
      }

      nsresult rv;

      // Do not try to use the base stream's ReadSegements here.  Its very
      // unlikely we will get a single buffer that contains all of the compressed
      // data and therefore would have to copy into our own buffer anyways.
      // Instead, focus on making efficient use of the Read() interface.

      while (aCount > 0) {
        // We have some decompressed data in our buffer.  Provide it to the
        // callers writer function.
        if (mUncompressedBytes > 0) {
          MOZ_ASSERT(mUncompressedBuffer);
          uint32_t remaining = UncompressedLength();
          uint32_t numToWrite = std::min(aCount, remaining);
          uint32_t numWritten;
          rv = aWriter(this, aClosure, &mUncompressedBuffer[mNextByte],
                       *aBytesReadOut, numToWrite, &numWritten);

          // As defined in nsIInputputStream.idl, do not pass writer func errors.
          if (NS_FAILED(rv)) {
            return NS_OK;
          }

          // End-of-file
          if (numWritten == 0) {
            return NS_OK;
          }

          *aBytesReadOut += numWritten;
          mNextByte += numWritten;
          MOZ_ASSERT(mNextByte <= mUncompressedBytes);

          if (mNextByte == mUncompressedBytes) {
            mNextByte = 0;
            mUncompressedBytes = 0;
          }

          aCount -= numWritten;

          continue;
        }

        // Otherwise uncompress the next chunk and loop.  Any resulting data
        // will set mUncompressedBytes which we check at the top of the loop.
        uint32_t bytesRead;
        rv = ParseNextChunk(&bytesRead);
        if (NS_FAILED(rv)) {
          return rv;
        }

        // If we couldn't read anything and there is no more data to provide
        // to the caller, then this is eof.
        if (bytesRead == 0 && mUncompressedBytes == 0) {
          return NS_OK;
        }
      }

      return NS_OK;
    }

    NS_IMETHODIMP
    SnappyUncompressInputStream::IsNonBlocking(bool* aNonBlockingOut) {
      *aNonBlockingOut = false;
      return NS_OK;
    }

    SnappyUncompressInputStream::~SnappyUncompressInputStream() { Close(); }

    nsresult SnappyUncompressInputStream::ParseNextChunk(uint32_t* aBytesReadOut) {
      // There must not be any uncompressed data already in mUncompressedBuffer.
      MOZ_ASSERT(mUncompressedBytes == 0);
      MOZ_ASSERT(mNextByte == 0);

      nsresult rv;
      *aBytesReadOut = 0;

      // Lazily create our two buffers so we can report OOM during stream
      // operation.  These allocations only happens once.  The buffers are reused
      // until the stream is closed.
      if (!mUncompressedBuffer) {
        mUncompressedBuffer.reset(new (fallible) char[snappy::kBlockSize]);
        if (NS_WARN_IF(!mUncompressedBuffer)) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
      }

      if (!mCompressedBuffer) {
        mCompressedBuffer.reset(new (fallible) char[CompressedBufferLength()]);
        if (NS_WARN_IF(!mCompressedBuffer)) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
      }

      // We have no decompressed data and we also have not seen the start of stream
      // yet. Read and validate the StreamIdentifier chunk.  Also read the next
      // header to determine the size of the first real data chunk.
      if (mNeedFirstStreamIdentifier) {
        const uint32_t firstReadLength =
            kHeaderLength + kStreamIdentifierDataLength + kHeaderLength;
        MOZ_ASSERT(firstReadLength <= CompressedBufferLength());

        rv = ReadAll(mCompressedBuffer.get(), firstReadLength, firstReadLength,
                     aBytesReadOut);
        if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) {
          return rv;
        }

        rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength, &mNextChunkType,
                         &mNextChunkDataLength);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }
        if (NS_WARN_IF(mNextChunkType != StreamIdentifier ||
                       mNextChunkDataLength != kStreamIdentifierDataLength)) {
          return NS_ERROR_CORRUPTED_CONTENT;
        }
        size_t offset = kHeaderLength;

        mNeedFirstStreamIdentifier = false;

        size_t numRead;
        size_t numWritten;
        rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize,
                       mNextChunkType, &mCompressedBuffer[offset],
                       mNextChunkDataLength, &numWritten, &numRead);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }
        MOZ_ASSERT(numWritten == 0);
        MOZ_ASSERT(numRead == mNextChunkDataLength);
        offset += numRead;

        rv = ParseHeader(&mCompressedBuffer[offset], *aBytesReadOut - offset,
                         &mNextChunkType, &mNextChunkDataLength);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }

        return NS_OK;
      }

      // We have no compressed data and we don't know how big the next chunk is.
      // This happens when we get an EOF pause in the middle of a stream and also
      // at the end of the stream.  Simply read the next header and return.  The
      // chunk body will be read on the next entry into this method.
      if (mNextChunkType == Unknown) {
        rv = ReadAll(mCompressedBuffer.get(), kHeaderLength, kHeaderLength,
                     aBytesReadOut);
        if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) {
          return rv;
        }

        rv = ParseHeader(mCompressedBuffer.get(), kHeaderLength, &mNextChunkType,
                         &mNextChunkDataLength);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }

        return NS_OK;
      }

      // We have no decompressed data, but we do know the size of the next chunk.
      // Read at least that much from the base stream.
      uint32_t readLength = mNextChunkDataLength;
      MOZ_ASSERT(readLength <= CompressedBufferLength());

      // However, if there is enough data in the base stream, also read the next
      // chunk header.  This helps optimize the stream by avoiding many small reads.
      uint64_t avail;
      rv = mBaseStream->Available(&avail);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      if (avail >= (readLength + kHeaderLength)) {
        readLength += kHeaderLength;
        MOZ_ASSERT(readLength <= CompressedBufferLength());
      }

      rv = ReadAll(mCompressedBuffer.get(), readLength, mNextChunkDataLength,
                   aBytesReadOut);
      if (NS_WARN_IF(NS_FAILED(rv)) || *aBytesReadOut == 0) {
        return rv;
      }

      size_t numRead;
      size_t numWritten;
      rv = ParseData(mUncompressedBuffer.get(), snappy::kBlockSize, mNextChunkType,
                     mCompressedBuffer.get(), mNextChunkDataLength, &numWritten,
                     &numRead);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      MOZ_ASSERT(numRead == mNextChunkDataLength);

      mUncompressedBytes = numWritten;

      // If we were unable to directly read the next chunk header, then clear
      // our internal state.  We will have to perform a small read to get the
      // header the next time we enter this method.
      if (*aBytesReadOut <= mNextChunkDataLength) {
        mNextChunkType = Unknown;
        mNextChunkDataLength = 0;
        return NS_OK;
      }

      // We got the next chunk header.  Parse it so that we are ready to for the
      // next call into this method.
      rv = ParseHeader(&mCompressedBuffer[numRead], *aBytesReadOut - numRead,
                       &mNextChunkType, &mNextChunkDataLength);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      return NS_OK;
    }

    nsresult SnappyUncompressInputStream::ReadAll(char* aBuf, uint32_t aCount,
                                                  uint32_t aMinValidCount,
                                                  uint32_t* aBytesReadOut) {
      MOZ_ASSERT(aCount >= aMinValidCount);

      *aBytesReadOut = 0;

      if (!mBaseStream) {
        return NS_BASE_STREAM_CLOSED;
      }

      uint32_t offset = 0;
      while (aCount > 0) {
        uint32_t bytesRead = 0;
        nsresult rv = mBaseStream->Read(aBuf + offset, aCount, &bytesRead);
        if (NS_WARN_IF(NS_FAILED(rv))) {
          return rv;
        }

        // EOF, but don't immediately return.  We need to validate min read bytes
        // below.
        if (bytesRead == 0) {
          break;
        }

        *aBytesReadOut += bytesRead;
        offset += bytesRead;
        aCount -= bytesRead;
      }

      // Reading zero bytes is not an error.  Its the expected EOF condition.
      // Only compare to the minimum valid count if we read at least one byte.
      if (*aBytesReadOut != 0 && *aBytesReadOut < aMinValidCount) {
        return NS_ERROR_CORRUPTED_CONTENT;
      }

      return NS_OK;
    }

    size_t SnappyUncompressInputStream::UncompressedLength() const {
      MOZ_ASSERT(mNextByte <= mUncompressedBytes);
      return mUncompressedBytes - mNextByte;
    }

    }  // namespace mozilla