chromium/ash/public/cpp/holding_space/holding_space_progress.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/public/cpp/holding_space/holding_space_progress.h"

#include <limits>

#include "base/check_op.h"

namespace ash {
namespace {

// Helpers ---------------------------------------------------------------------

// Returns whether or not the specified byte counts indicate completion.
bool CalculateComplete(const std::optional<int64_t>& current_bytes,
                       const std::optional<int64_t>& total_bytes) {
  return current_bytes.has_value() && current_bytes == total_bytes;
}

}  // namespace

// HoldingSpaceProgress --------------------------------------------------------

HoldingSpaceProgress::HoldingSpaceProgress()
    : HoldingSpaceProgress(/*current_bytes=*/0,
                           /*total_bytes=*/0) {}

HoldingSpaceProgress::HoldingSpaceProgress(
    const std::optional<int64_t>& current_bytes,
    const std::optional<int64_t>& total_bytes)
    : HoldingSpaceProgress(current_bytes,
                           total_bytes,
                           /*complete=*/std::nullopt) {}

HoldingSpaceProgress::HoldingSpaceProgress(
    const std::optional<int64_t>& current_bytes,
    const std::optional<int64_t>& total_bytes,
    const std::optional<bool>& complete)
    : HoldingSpaceProgress(current_bytes,
                           total_bytes,
                           complete,
                           /*hidden=*/false) {}

HoldingSpaceProgress::HoldingSpaceProgress(
    const std::optional<int64_t>& current_bytes,
    const std::optional<int64_t>& total_bytes,
    const std::optional<bool>& complete,
    bool hidden)
    : current_bytes_(current_bytes),
      total_bytes_(total_bytes),
      complete_(
          complete.value_or(CalculateComplete(current_bytes_, total_bytes_))),
      hidden_(hidden) {
  DCHECK_GE(current_bytes_.value_or(0), 0);
  DCHECK_GE(total_bytes_.value_or(0), 0);

  if (!current_bytes_.has_value() || !total_bytes_.has_value()) {
    DCHECK(!complete_);
    return;
  }

  if (complete_) {
    DCHECK_EQ(current_bytes_.value(), total_bytes_.value());
    return;
  }

  DCHECK_LE(current_bytes_.value(), total_bytes_.value());
}

HoldingSpaceProgress::HoldingSpaceProgress(const HoldingSpaceProgress&) =
    default;

HoldingSpaceProgress& HoldingSpaceProgress::operator=(
    const HoldingSpaceProgress&) = default;

HoldingSpaceProgress::~HoldingSpaceProgress() = default;

bool HoldingSpaceProgress::operator==(const HoldingSpaceProgress& rhs) const {
  return std::tie(current_bytes_, total_bytes_, complete_, hidden_) ==
         std::tie(rhs.current_bytes_, rhs.total_bytes_, rhs.complete_,
                  rhs.hidden_);
}

HoldingSpaceProgress& HoldingSpaceProgress::operator+=(
    const HoldingSpaceProgress& rhs) {
  // Hidden instances shouldn't be included in cumulative progress calculations.
  DCHECK(!hidden_);
  DCHECK(!rhs.hidden_);

  HoldingSpaceProgress temp(*this);
  temp = temp + rhs;

  current_bytes_ = temp.current_bytes_;
  total_bytes_ = temp.total_bytes_;
  complete_ = temp.complete_;

  return *this;
}

HoldingSpaceProgress HoldingSpaceProgress::operator+(
    const HoldingSpaceProgress& rhs) const {
  // Hidden instances shouldn't be included in cumulative progress calculations.
  DCHECK(!hidden_);
  DCHECK(!rhs.hidden_);

  // The number of `current_bytes` should only be present if present for both
  // the lhs and `rhs` instances. Otherwise `current_bytes` is indeterminate.
  std::optional<int64_t> current_bytes(current_bytes_);
  if (current_bytes.has_value()) {
    current_bytes = rhs.current_bytes_.has_value()
                        ? std::make_optional(current_bytes.value() +
                                             rhs.current_bytes_.value())
                        : std::nullopt;
  }

  // The number of `total_bytes` should only be present if present for both the
  // lhs and `rhs` instances. Otherwise `total_bytes` is indeterminate.
  std::optional<int64_t> total_bytes(total_bytes_);
  if (total_bytes.has_value()) {
    total_bytes =
        rhs.total_bytes_.has_value()
            ? std::make_optional(total_bytes.value() + rhs.total_bytes_.value())
            : std::nullopt;
  }

  // The result of summing lhs and `rhs` instances is `complete` if and only if
  // both the lhs and `rhs` are themselves complete.
  const bool complete = complete_ && rhs.complete_;

  return HoldingSpaceProgress(current_bytes, total_bytes, complete);
}

std::optional<float> HoldingSpaceProgress::GetValue() const {
  if (IsComplete())
    return 1.f;

  if (IsIndeterminate())
    return std::nullopt;

  // If `current_bytes_` == `total_bytes_` but progress is not complete,
  // return a value that is extremely close but not equal to `1.f`.
  if (current_bytes_.value() == total_bytes_.value())
    return 1.f - std::numeric_limits<float>::epsilon();

  return static_cast<double>(current_bytes_.value()) /
         static_cast<double>(total_bytes_.value());
}

bool HoldingSpaceProgress::IsComplete() const {
  return complete_;
}

bool HoldingSpaceProgress::IsIndeterminate() const {
  return !current_bytes_.has_value() || !total_bytes_.has_value();
}

bool HoldingSpaceProgress::IsHidden() const {
  return hidden_;
}

}  // namespace ash