# Global Metrics

path: .metrics.nexits.sum
old: 0.0
new: 29.0
path: .metrics.nexits.average
old: 0.0
new: 1.16
path: .metrics.cognitive.sum
old: 0.0
new: 33.0
path: .metrics.cognitive.average
old: 0.0
new: 1.32
path: .metrics.nom.functions
old: 1.0
new: 25.0
path: .metrics.nom.total
old: 1.0
new: 25.0
path: .metrics.halstead.time
old: 0.8333333333333334
new: 21622.116968298033
path: .metrics.halstead.n2
old: 1.0
new: 159.0
path: .metrics.halstead.purity_ratio
old: 0.9509775004326936
new: 0.9998330635565752
path: .metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 1272.7874899075202
path: .metrics.halstead.N1
old: 4.0
new: 734.0
path: .metrics.halstead.n1
old: 3.0
new: 24.0
path: .metrics.halstead.vocabulary
old: 4.0
new: 183.0
path: .metrics.halstead.level
old: 0.6666666666666666
new: 0.024582560296846013
path: .metrics.halstead.volume
old: 10.0
new: 9567.485894135589
path: .metrics.halstead.effort
old: 15.0
new: 389198.1054293646
path: .metrics.halstead.difficulty
old: 1.5
new: 40.679245283018865
path: .metrics.halstead.N2
old: 1.0
new: 539.0
path: .metrics.halstead.bugs
old: 0.002027400665191133
new: 1.776884600852695
path: .metrics.halstead.length
old: 5.0
new: 1273.0
path: .metrics.cyclomatic.average
old: 1.0
new: 2.0
path: .metrics.cyclomatic.sum
old: 2.0
new: 60.0
path: .metrics.loc.sloc
old: 8.0
new: 400.0
path: .metrics.loc.lloc
old: 0.0
new: 106.0
path: .metrics.loc.cloc
old: 4.0
new: 23.0
path: .metrics.loc.ploc
old: 2.0
new: 303.0
path: .metrics.loc.blank
old: 2.0
new: 74.0
path: .metrics.mi.mi_sei
old: 149.12257611294993
new: -33.444968236058116
path: .metrics.mi.mi_visual_studio
old: 73.02900850363604
new: 7.294982733057534
path: .metrics.mi.mi_original
old: 124.87960454121762
new: 12.474420473528385
path: .metrics.nargs.average
old: 1.0
new: 2.28
path: .metrics.nargs.sum
old: 1.0
new: 57.0

# Spaces Data

## Minimal test - lines (15, 400)

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.0344827586206895
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 59.0
path: .spaces[0].metrics.mi.mi_visual_studio
old: 86.29682642769582
new: 7.812382391283802
path: .spaces[0].metrics.mi.mi_sei
old: 137.29597390658574
new: -34.00352491869492
path: .spaces[0].metrics.mi.mi_original
old: 147.56757319135986
new: 13.359173889095302
path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.32
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 33.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 106.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 18.0
path: .spaces[0].metrics.loc.sloc
old: 2.0
new: 386.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 73.0
path: .spaces[0].metrics.loc.ploc
old: 2.0
new: 295.0
path: .spaces[0].metrics.nargs.average
old: 1.0
new: 2.28
path: .spaces[0].metrics.nargs.sum
old: 1.0
new: 57.0
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 151.0
path: .spaces[0].metrics.halstead.length
old: 5.0
new: 1265.0
path: .spaces[0].metrics.halstead.N1
old: 4.0
new: 734.0
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 531.0
path: .spaces[0].metrics.halstead.volume
old: 10.0
new: 9425.782056467897
path: .spaces[0].metrics.halstead.n1
old: 3.0
new: 24.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: 4.754887502163468
new: 1203.0382156553944
path: .spaces[0].metrics.halstead.effort
old: 15.0
new: 397755.51830340025
path: .spaces[0].metrics.halstead.purity_ratio
old: 0.9509775004326936
new: 0.9510183522967544
path: .spaces[0].metrics.halstead.time
old: 0.8333333333333334
new: 22097.52879463335
path: .spaces[0].metrics.halstead.difficulty
old: 1.5
new: 42.19867549668874
path: .spaces[0].metrics.halstead.bugs
old: 0.002027400665191133
new: 1.802835997077912
path: .spaces[0].metrics.halstead.level
old: 0.6666666666666666
new: 0.02369742623979912
path: .spaces[0].metrics.halstead.vocabulary
old: 4.0
new: 175.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.16
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 29.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 25.0
path: .spaces[0].metrics.nom.total
old: 1.0
new: 25.0

### Code

    namespace mozilla {

    using namespace ipc;

    class NonBlockingAsyncInputStream::AsyncWaitRunnable final
        : public CancelableRunnable {
      RefPtr mStream;
      nsCOMPtr mCallback;

     public:
      AsyncWaitRunnable(NonBlockingAsyncInputStream* aStream,
                        nsIInputStreamCallback* aCallback)
          : CancelableRunnable("AsyncWaitRunnable"),
            mStream(aStream),
            mCallback(aCallback) {}

      NS_IMETHOD
      Run() override {
        mStream->RunAsyncWaitCallback(this, mCallback.forget());
        return NS_OK;
      }

      nsresult Cancel() override {
        mStream = nullptr;
        return NS_OK;
      }
    };

    NS_IMPL_ADDREF(NonBlockingAsyncInputStream);
    NS_IMPL_RELEASE(NonBlockingAsyncInputStream);

    NonBlockingAsyncInputStream::WaitClosureOnly::WaitClosureOnly(
        AsyncWaitRunnable* aRunnable, nsIEventTarget* aEventTarget)
        : mRunnable(aRunnable), mEventTarget(aEventTarget) {}

    NS_INTERFACE_MAP_BEGIN(NonBlockingAsyncInputStream)
      NS_INTERFACE_MAP_ENTRY(nsIInputStream)
      NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
      NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                         mWeakCloneableInputStream)
      NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                         mWeakIPCSerializableInputStream)
      NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
                                         mWeakSeekableInputStream)
      NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
                                         mWeakTellableInputStream)
      NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
    NS_INTERFACE_MAP_END

    /* static */
    nsresult NonBlockingAsyncInputStream::Create(
        already_AddRefed aInputStream,
        nsIAsyncInputStream** aResult) {
      MOZ_DIAGNOSTIC_ASSERT(aResult);

      nsCOMPtr inputStream = std::move(aInputStream);

      bool nonBlocking = false;
      nsresult rv = inputStream->IsNonBlocking(&nonBlocking);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      MOZ_DIAGNOSTIC_ASSERT(nonBlocking);

    #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
      nsCOMPtr asyncInputStream =
          do_QueryInterface(inputStream);
      MOZ_DIAGNOSTIC_ASSERT(!asyncInputStream);
    #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED

      RefPtr stream =
          new NonBlockingAsyncInputStream(inputStream.forget());

      stream.forget(aResult);
      return NS_OK;
    }

    NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(
        already_AddRefed aInputStream)
        : mInputStream(std::move(aInputStream)),
          mWeakCloneableInputStream(nullptr),
          mWeakIPCSerializableInputStream(nullptr),
          mWeakSeekableInputStream(nullptr),
          mWeakTellableInputStream(nullptr),
          mLock("NonBlockingAsyncInputStream::mLock"),
          mClosed(false) {
      MOZ_ASSERT(mInputStream);

      nsCOMPtr cloneableStream =
          do_QueryInterface(mInputStream);
      if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
        mWeakCloneableInputStream = cloneableStream;
      }

      nsCOMPtr serializableStream =
          do_QueryInterface(mInputStream);
      if (serializableStream && SameCOMIdentity(mInputStream, serializableStream)) {
        mWeakIPCSerializableInputStream = serializableStream;
      }

      nsCOMPtr seekableStream = do_QueryInterface(mInputStream);
      if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
        mWeakSeekableInputStream = seekableStream;
      }

      nsCOMPtr tellableStream = do_QueryInterface(mInputStream);
      if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
        mWeakTellableInputStream = tellableStream;
      }
    }

    NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream() = default;

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Close() {
      RefPtr waitClosureOnlyRunnable;
      nsCOMPtr waitClosureOnlyEventTarget;

      {
        MutexAutoLock lock(mLock);

        if (mClosed) {
          // Here we could return NS_BASE_STREAM_CLOSED as well, but just to avoid
          // warning messages, let's make everybody happy with a NS_OK.
          return NS_OK;
        }

        mClosed = true;

        NS_ENSURE_STATE(mInputStream);
        nsresult rv = mInputStream->Close();
        if (NS_WARN_IF(NS_FAILED(rv))) {
          mWaitClosureOnly.reset();
          return rv;
        }

        // If we have a WaitClosureOnly runnable, it's time to use it.
        if (mWaitClosureOnly.isSome()) {
          waitClosureOnlyRunnable = std::move(mWaitClosureOnly->mRunnable);
          waitClosureOnlyEventTarget = std::move(mWaitClosureOnly->mEventTarget);

          mWaitClosureOnly.reset();

          // Now we want to dispatch the asyncWaitCallback.
          mAsyncWaitCallback = waitClosureOnlyRunnable;
        }
      }

      if (waitClosureOnlyRunnable) {
        if (waitClosureOnlyEventTarget) {
          waitClosureOnlyEventTarget->Dispatch(waitClosureOnlyRunnable,
                                               NS_DISPATCH_NORMAL);
        } else {
          waitClosureOnlyRunnable->Run();
        }
      }

      return NS_OK;
    }

    // nsIInputStream interface

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Available(uint64_t* aLength) {
      nsresult rv = mInputStream->Available(aLength);
      // Don't issue warnings for legal condition NS_BASE_STREAM_CLOSED.
      if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      // Nothing more to read. Let's close the stream now.
      if (*aLength == 0) {
        mInputStream->Close();
        mClosed = true;
        return NS_BASE_STREAM_CLOSED;
      }

      return NS_OK;
    }

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Read(char* aBuffer, uint32_t aCount,
                                      uint32_t* aReadCount) {
      return mInputStream->Read(aBuffer, aCount, aReadCount);
    }

    namespace {

    class MOZ_RAII ReadSegmentsData {
     public:
      ReadSegmentsData(NonBlockingAsyncInputStream* aStream,
                       nsWriteSegmentFun aFunc, void* aClosure)
          : mStream(aStream), mFunc(aFunc), mClosure(aClosure) {}

      NonBlockingAsyncInputStream* mStream;
      nsWriteSegmentFun mFunc;
      void* mClosure;
    };

    nsresult ReadSegmentsWriter(nsIInputStream* aInStream, void* aClosure,
                                const char* aFromSegment, uint32_t aToOffset,
                                uint32_t aCount, uint32_t* aWriteCount) {
      ReadSegmentsData* data = static_cast(aClosure);
      return data->mFunc(data->mStream, data->mClosure, aFromSegment, aToOffset,
                         aCount, aWriteCount);
    }

    }  // namespace

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                              void* aClosure, uint32_t aCount,
                                              uint32_t* aResult) {
      ReadSegmentsData data(this, aWriter, aClosure);
      return mInputStream->ReadSegments(ReadSegmentsWriter, &data, aCount, aResult);
    }

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::IsNonBlocking(bool* aNonBlocking) {
      *aNonBlocking = true;
      return NS_OK;
    }

    // nsICloneableInputStream interface

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::GetCloneable(bool* aCloneable) {
      NS_ENSURE_STATE(mWeakCloneableInputStream);
      return mWeakCloneableInputStream->GetCloneable(aCloneable);
    }

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Clone(nsIInputStream** aResult) {
      NS_ENSURE_STATE(mWeakCloneableInputStream);

      nsCOMPtr clonedStream;
      nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      nsCOMPtr asyncStream;
      rv = Create(clonedStream.forget(), getter_AddRefs(asyncStream));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      asyncStream.forget(aResult);
      return NS_OK;
    }

    // nsIAsyncInputStream interface

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::CloseWithStatus(nsresult aStatus) {
      return Close();
    }

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                                           uint32_t aFlags,
                                           uint32_t aRequestedCount,
                                           nsIEventTarget* aEventTarget) {
      RefPtr runnable;
      {
        MutexAutoLock lock(mLock);

        if (aCallback && (mWaitClosureOnly.isSome() || mAsyncWaitCallback)) {
          return NS_ERROR_FAILURE;
        }

        if (!aCallback) {
          // Canceling previous callbacks.
          mWaitClosureOnly.reset();
          mAsyncWaitCallback = nullptr;
          return NS_OK;
        }

        // Maybe the stream is already closed.
        if (!mClosed) {
          uint64_t length;
          nsresult rv = mInputStream->Available(&length);
          if (NS_SUCCEEDED(rv) && length == 0) {
            mInputStream->Close();
            mClosed = true;
          }
        }

        runnable = new AsyncWaitRunnable(this, aCallback);
        if ((aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) && !mClosed) {
          mWaitClosureOnly.emplace(runnable, aEventTarget);
          return NS_OK;
        }

        mAsyncWaitCallback = runnable;
      }

      MOZ_ASSERT(runnable);

      if (aEventTarget) {
        return aEventTarget->Dispatch(runnable.forget());
      }

      return runnable->Run();
    }

    // nsIIPCSerializableInputStream

    void NonBlockingAsyncInputStream::Serialize(
        mozilla::ipc::InputStreamParams& aParams,
        FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
        uint32_t aMaxSize, uint32_t* aSizeUsed,
        mozilla::ipc::ParentToChildStreamActorManager* aManager) {
      SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                        aSizeUsed, aManager);
    }

    void NonBlockingAsyncInputStream::Serialize(
        mozilla::ipc::InputStreamParams& aParams,
        FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
        uint32_t aMaxSize, uint32_t* aSizeUsed,
        mozilla::ipc::ChildToParentStreamActorManager* aManager) {
      SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
                        aSizeUsed, aManager);
    }

    template
    void NonBlockingAsyncInputStream::SerializeInternal(
        mozilla::ipc::InputStreamParams& aParams,
        FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
        uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
      MOZ_ASSERT(mWeakIPCSerializableInputStream);
      InputStreamHelper::SerializeInputStream(mInputStream, aParams,
                                              aFileDescriptors, aDelayedStart,
                                              aMaxSize, aSizeUsed, aManager);
    }

    bool NonBlockingAsyncInputStream::Deserialize(
        const mozilla::ipc::InputStreamParams& aParams,
        const FileDescriptorArray& aFileDescriptors) {
      MOZ_CRASH("NonBlockingAsyncInputStream cannot be deserialized!");
      return true;
    }

    // nsISeekableStream

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset) {
      NS_ENSURE_STATE(mWeakSeekableInputStream);
      return mWeakSeekableInputStream->Seek(aWhence, aOffset);
    }

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::SetEOF() {
      NS_ENSURE_STATE(mWeakSeekableInputStream);
      return NS_ERROR_NOT_IMPLEMENTED;
    }

    // nsITellableStream

    NS_IMETHODIMP
    NonBlockingAsyncInputStream::Tell(int64_t* aResult) {
      NS_ENSURE_STATE(mWeakTellableInputStream);
      return mWeakTellableInputStream->Tell(aResult);
    }

    void NonBlockingAsyncInputStream::RunAsyncWaitCallback(
        NonBlockingAsyncInputStream::AsyncWaitRunnable* aRunnable,
        already_AddRefed aCallback) {
      nsCOMPtr callback = std::move(aCallback);

      {
        MutexAutoLock lock(mLock);
        if (mAsyncWaitCallback != aRunnable) {
          // The callback has been canceled in the meantime.
          return;
        }

        mAsyncWaitCallback = nullptr;
      }

      callback->OnInputStreamReady(this);
    }

    }  // namespace mozilla