# Global Metrics

path: .metrics.cyclomatic.average
old: 1.0
new: 3.25
path: .metrics.cyclomatic.sum
old: 6.0
new: 39.0
path: .metrics.loc.ploc
old: 175.0
new: 184.0
path: .metrics.loc.lloc
old: 0.0
new: 72.0
path: .metrics.loc.sloc
old: 254.0
new: 260.0
path: .metrics.loc.blank
old: 10.0
new: 42.0
path: .metrics.loc.cloc
old: 69.0
new: 34.0
path: .metrics.nom.functions
old: 0.0
new: 8.0
path: .metrics.nom.total
old: 0.0
new: 8.0
path: .metrics.halstead.N1
old: 276.0
new: 468.0
path: .metrics.halstead.N2
old: 269.0
new: 325.0
path: .metrics.halstead.volume
old: 4185.316654230468
new: 5356.62578921563
path: .metrics.halstead.n1
old: 12.0
new: 24.0
path: .metrics.halstead.length
old: 545.0
new: 793.0
path: .metrics.halstead.difficulty
old: 8.362694300518134
new: 46.42857142857143
path: .metrics.halstead.bugs
old: 0.356666051511256
new: 1.3182461081405512
path: .metrics.halstead.estimated_program_length
old: 1508.3637582013937
new: 646.9937635307236
path: .metrics.halstead.effort
old: 35000.523730196765
new: 248700.4830707257
path: .metrics.halstead.n2
old: 193.0
new: 84.0
path: .metrics.halstead.vocabulary
old: 205.0
new: 108.0
path: .metrics.halstead.level
old: 0.11957868649318464
new: 0.021538461538461538
path: .metrics.halstead.purity_ratio
old: 2.767639923305309
new: 0.8158811646036868
path: .metrics.halstead.time
old: 1944.473540566487
new: 13816.693503929206
path: .metrics.nexits.average
old: null
new: 2.125
path: .metrics.nexits.sum
old: 0.0
new: 17.0
path: .metrics.mi.mi_visual_studio
old: 21.3746369150271
new: 15.964498231155764
path: .metrics.mi.mi_sei
old: 13.767658759752152
new: -5.776706398536774
path: .metrics.mi.mi_original
old: 36.55062912469634
new: 27.29929197527636
path: .metrics.nargs.average
old: null
new: 1.375
path: .metrics.nargs.sum
old: 0.0
new: 11.0
path: .metrics.cognitive.sum
old: 0.0
new: 37.0
path: .metrics.cognitive.average
old: null
new: 4.625

# Spaces Data

## Minimal test - lines (16, 260)

path: .spaces[0].metrics.mi.mi_original
old: null
new: 28.66878586436843
path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 16.765371850507854
path: .spaces[0].metrics.mi.mi_sei
old: null
new: -5.4660060048984676
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 17.0
path: .spaces[0].metrics.nexits.average
old: null
new: 2.125
path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 177.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 28.0
path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 245.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 71.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 40.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 4.625
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 37.0
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 38.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 3.4545454545454546
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 11.0
path: .spaces[0].metrics.nargs.average
old: null
new: 1.375
path: .spaces[0].metrics.halstead.level
old: null
new: 0.019778481012658226
path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 75.0
path: .spaces[0].metrics.halstead.time
old: 0.0
new: 14543.099523637044
path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 5177.527520282175
path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 24.0
path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 99.0
path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 1.364054531853209
path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 465.0
path: .spaces[0].metrics.halstead.length
old: 1.0
new: 781.0
path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 577.2005018044988
path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 316.0
path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 50.56
path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 0.739053139314339
path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 261775.79142546677
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 8.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 8.0

### Code

    namespace mozilla {

    namespace {

    class AvailableEvent final : public Runnable {
     public:
      AvailableEvent(nsIInputStream* stream,
                     const std::function& aCallback)
          : Runnable("mozilla::AvailableEvent"),
            mStream(stream),
            mCallback(aCallback),
            mSize(-1) {
        mCallbackTarget = GetCurrentSerialEventTarget();
        MOZ_ASSERT(NS_IsMainThread());
      }

      NS_IMETHOD
      Run() override {
        // ping
        if (!NS_IsMainThread()) {
          uint64_t size = 0;
          if (NS_WARN_IF(NS_FAILED(mStream->Available(&size)))) {
            mSize = -1;
          } else {
            mSize = (int64_t)size;
          }

          mStream = nullptr;

          nsCOMPtr self(this);  // overly cute
          mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
          mCallbackTarget = nullptr;
          return NS_OK;
        }

        // pong
        std::function callback;
        callback.swap(mCallback);
        callback(mSize);
        return NS_OK;
      }

     private:
      nsCOMPtr mStream;
      std::function mCallback;
      nsCOMPtr mCallbackTarget;

      int64_t mSize;
    };

    }  // namespace

    /* static */
    bool InputStreamLengthHelper::GetSyncLength(nsIInputStream* aStream,
                                                int64_t* aLength) {
      MOZ_ASSERT(aStream);
      MOZ_ASSERT(aLength);

      *aLength = -1;

      // Sync length access.
      nsCOMPtr streamLength = do_QueryInterface(aStream);
      if (streamLength) {
        int64_t length = -1;
        nsresult rv = streamLength->Length(&length);

        // All good!
        if (NS_SUCCEEDED(rv)) {
          *aLength = length;
          return true;
        }

        // Already closed stream or an error occurred.
        if (rv == NS_BASE_STREAM_CLOSED ||
            NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
            NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
          return true;
        }
      }

      nsCOMPtr asyncStreamLength =
          do_QueryInterface(aStream);
      if (asyncStreamLength) {
        // GetAsyncLength should be used.
        return false;
      }

      // We cannot calculate the length of an async stream.
      nsCOMPtr asyncStream = do_QueryInterface(aStream);
      if (asyncStream) {
        return false;
      }

      // For main-thread only, we want to avoid calling ::Available() for blocking
      // streams.
      if (NS_IsMainThread()) {
        bool nonBlocking = false;
        if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) {
          // Let's return -1. There is nothing else we can do here.
          return true;
        }

        if (!nonBlocking) {
          return false;
        }
      }

      // Fallback using available().
      uint64_t available = 0;
      nsresult rv = aStream->Available(&available);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        // Let's return -1. There is nothing else we can do here.
        return true;
      }

      *aLength = (int64_t)available;
      return true;
    }

    /* static */
    void InputStreamLengthHelper::GetAsyncLength(
        nsIInputStream* aStream,
        const std::function& aCallback) {
      MOZ_ASSERT(aStream);
      MOZ_ASSERT(aCallback);

      // We don't want to allow this class to be used on workers because we are not
      // using the correct Runnable types.
      MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() ||
                            !dom::IsCurrentThreadRunningWorker());

      RefPtr helper =
          new InputStreamLengthHelper(aStream, aCallback);

      // Let's be sure that we don't call ::Available() on main-thread.
      if (NS_IsMainThread()) {
        nsCOMPtr streamLength = do_QueryInterface(aStream);
        nsCOMPtr asyncStreamLength =
            do_QueryInterface(aStream);
        if (!streamLength && !asyncStreamLength) {
          // We cannot calculate the length of an async stream. We must fix the
          // caller if this happens.
    #ifdef DEBUG
          nsCOMPtr asyncStream = do_QueryInterface(aStream);
          MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
    #endif

          bool nonBlocking = false;
          if (NS_SUCCEEDED(aStream->IsNonBlocking(&nonBlocking)) && !nonBlocking) {
            nsCOMPtr target =
                do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
            MOZ_ASSERT(target);

            RefPtr event = new AvailableEvent(aStream, aCallback);
            target->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
            return;
          }
        }
      }

      // Let's go async in order to have similar behaviors for sync and async
      // nsIInputStreamLength implementations.
      GetCurrentSerialEventTarget()->Dispatch(helper, NS_DISPATCH_NORMAL);
    }

    InputStreamLengthHelper::InputStreamLengthHelper(
        nsIInputStream* aStream,
        const std::function& aCallback)
        : Runnable("InputStreamLengthHelper"),
          mStream(aStream),
          mCallback(aCallback) {
      MOZ_ASSERT(aStream);
      MOZ_ASSERT(aCallback);
    }

    InputStreamLengthHelper::~InputStreamLengthHelper() = default;

    NS_IMETHODIMP
    InputStreamLengthHelper::Run() {
      // Sync length access.
      nsCOMPtr streamLength = do_QueryInterface(mStream);
      if (streamLength) {
        int64_t length = -1;
        nsresult rv = streamLength->Length(&length);

        // All good!
        if (NS_SUCCEEDED(rv)) {
          ExecCallback(length);
          return NS_OK;
        }

        // Already closed stream or an error occurred.
        if (rv == NS_BASE_STREAM_CLOSED ||
            NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
            NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
          ExecCallback(-1);
          return NS_OK;
        }
      }

      // Async length access.
      nsCOMPtr asyncStreamLength =
          do_QueryInterface(mStream);
      if (asyncStreamLength) {
        nsresult rv =
            asyncStreamLength->AsyncLengthWait(this, GetCurrentSerialEventTarget());
        if (NS_WARN_IF(NS_FAILED(rv))) {
          ExecCallback(-1);
        }

        return NS_OK;
      }

      // Fallback using available().
      uint64_t available = 0;
      nsresult rv = mStream->Available(&available);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        ExecCallback(-1);
        return NS_OK;
      }

      ExecCallback((int64_t)available);
      return NS_OK;
    }

    NS_IMETHODIMP
    InputStreamLengthHelper::OnInputStreamLengthReady(
        nsIAsyncInputStreamLength* aStream, int64_t aLength) {
      ExecCallback(aLength);
      return NS_OK;
    }

    void InputStreamLengthHelper::ExecCallback(int64_t aLength) {
      MOZ_ASSERT(mCallback);

      std::function callback;
      callback.swap(mCallback);

      callback(aLength);
    }

    NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper, Runnable,
                                nsIInputStreamLengthCallback)

    }  // namespace mozilla