// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/download/rate_estimator.h" #include "base/logging.h" using base::TimeDelta; using base::TimeTicks; namespace content { namespace { static const int kDefaultBucketTimeSeconds = 1; static const size_t kDefaultNumBuckets = 10; } // namespace RateEstimator::RateEstimator() : history_(kDefaultNumBuckets), bucket_time_(TimeDelta::FromSeconds(kDefaultBucketTimeSeconds)), oldest_index_(0), bucket_count_(1) { ResetBuckets(TimeTicks::Now()); } RateEstimator::RateEstimator(TimeDelta bucket_time, size_t num_buckets, TimeTicks now) : history_(num_buckets), bucket_time_(bucket_time), oldest_index_(0), bucket_count_(1) { DCHECK(bucket_time_.InSeconds() > 0); ResetBuckets(now); } RateEstimator::~RateEstimator() { } void RateEstimator::Increment(uint32 count) { Increment(count, TimeTicks::Now()); } void RateEstimator::Increment(uint32 count, TimeTicks now) { ClearOldBuckets(now); int64 seconds_since_oldest = (now - oldest_time_).InSeconds(); DCHECK(seconds_since_oldest >= 0); int64 delta_buckets = seconds_since_oldest / bucket_time_.InSeconds(); DCHECK(delta_buckets >= 0); size_t index_offset = static_cast<size_t>(delta_buckets); DCHECK(index_offset <= history_.size()); int current_index = (oldest_index_ + delta_buckets) % history_.size(); history_[current_index] += count; } uint64 RateEstimator::GetCountPerSecond() const { return GetCountPerSecond(TimeTicks::Now()); } uint64 RateEstimator::GetCountPerSecond(TimeTicks now) const { const_cast<RateEstimator*>(this)->ClearOldBuckets(now); // TODO(cbentzel): Support fractional seconds for active bucket? // We explicitly don't check for overflow here. If it happens, unsigned // arithmetic at least guarantees behavior by wrapping around. The estimate // will be off, but the code will still be valid. uint64 total_count = 0; for (size_t i = 0; i < bucket_count_; ++i) { size_t index = (oldest_index_ + i) % history_.size(); total_count += history_[index]; } return total_count / (bucket_count_ * bucket_time_.InSeconds()); } void RateEstimator::ClearOldBuckets(TimeTicks now) { int64 seconds_since_oldest = (now - oldest_time_).InSeconds(); int64 delta_buckets = seconds_since_oldest / bucket_time_.InSeconds(); // It's possible (although unlikely) for there to be rollover with TimeTicks. // If that's the case, just reset the history. if (delta_buckets < 0) { ResetBuckets(now); return; } size_t delta_index = static_cast<size_t>(delta_buckets); // If we are within the current window, keep the existing data. if (delta_index < history_.size()) { bucket_count_ = delta_index + 1; return; } // If it's been long enough that all history data is too stale, just // clear all the buckets. size_t extra_buckets = delta_index - history_.size() + 1; if (extra_buckets > history_.size()) { ResetBuckets(now); return; } // Clear out stale buckets in the history. bucket_count_ = history_.size(); for (size_t i = 0; i < extra_buckets; ++i) { history_[oldest_index_] = 0; oldest_index_ = (oldest_index_ + 1) % history_.size(); oldest_time_ = oldest_time_ + bucket_time_; } } void RateEstimator::ResetBuckets(TimeTicks now) { for (size_t i = 0; i < history_.size(); ++i) { history_[i] = 0; } oldest_index_ = 0; bucket_count_ = 1; oldest_time_ = now; } } // namespace content