Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / download / rate_estimator.cc
blobdcdd71af41d0d645f7060b633dcd2d27a2d67417
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/download/rate_estimator.h"
7 #include "base/logging.h"
9 using base::TimeDelta;
10 using base::TimeTicks;
12 namespace content {
14 namespace {
16 static const int kDefaultBucketTimeSeconds = 1;
17 static const size_t kDefaultNumBuckets = 10;
19 } // namespace
21 RateEstimator::RateEstimator()
22 : history_(kDefaultNumBuckets),
23 bucket_time_(TimeDelta::FromSeconds(kDefaultBucketTimeSeconds)),
24 oldest_index_(0),
25 bucket_count_(1) {
26 ResetBuckets(TimeTicks::Now());
29 RateEstimator::RateEstimator(TimeDelta bucket_time,
30 size_t num_buckets,
31 TimeTicks now)
32 : history_(num_buckets),
33 bucket_time_(bucket_time),
34 oldest_index_(0),
35 bucket_count_(1) {
36 DCHECK(bucket_time_.InSeconds() > 0);
37 ResetBuckets(now);
40 RateEstimator::~RateEstimator() {
43 void RateEstimator::Increment(uint32 count) {
44 Increment(count, TimeTicks::Now());
47 void RateEstimator::Increment(uint32 count, TimeTicks now) {
48 ClearOldBuckets(now);
49 int64 seconds_since_oldest = (now - oldest_time_).InSeconds();
50 DCHECK(seconds_since_oldest >= 0);
51 int64 delta_buckets = seconds_since_oldest / bucket_time_.InSeconds();
52 DCHECK(delta_buckets >= 0);
53 size_t index_offset = static_cast<size_t>(delta_buckets);
54 DCHECK(index_offset <= history_.size());
55 int current_index = (oldest_index_ + delta_buckets) % history_.size();
56 history_[current_index] += count;
59 uint64 RateEstimator::GetCountPerSecond() const {
60 return GetCountPerSecond(TimeTicks::Now());
63 uint64 RateEstimator::GetCountPerSecond(TimeTicks now) const {
64 const_cast<RateEstimator*>(this)->ClearOldBuckets(now);
65 // TODO(cbentzel): Support fractional seconds for active bucket?
66 // We explicitly don't check for overflow here. If it happens, unsigned
67 // arithmetic at least guarantees behavior by wrapping around. The estimate
68 // will be off, but the code will still be valid.
69 uint64 total_count = 0;
70 for (size_t i = 0; i < bucket_count_; ++i) {
71 size_t index = (oldest_index_ + i) % history_.size();
72 total_count += history_[index];
74 return total_count / (bucket_count_ * bucket_time_.InSeconds());
77 void RateEstimator::ClearOldBuckets(TimeTicks now) {
78 int64 seconds_since_oldest = (now - oldest_time_).InSeconds();
80 int64 delta_buckets = seconds_since_oldest / bucket_time_.InSeconds();
82 // It's possible (although unlikely) for there to be rollover with TimeTicks.
83 // If that's the case, just reset the history.
84 if (delta_buckets < 0) {
85 ResetBuckets(now);
86 return;
88 size_t delta_index = static_cast<size_t>(delta_buckets);
90 // If we are within the current window, keep the existing data.
91 if (delta_index < history_.size()) {
92 bucket_count_ = delta_index + 1;
93 return;
96 // If it's been long enough that all history data is too stale, just
97 // clear all the buckets.
98 size_t extra_buckets = delta_index - history_.size() + 1;
99 if (extra_buckets > history_.size()) {
100 ResetBuckets(now);
101 return;
104 // Clear out stale buckets in the history.
105 bucket_count_ = history_.size();
106 for (size_t i = 0; i < extra_buckets; ++i) {
107 history_[oldest_index_] = 0;
108 oldest_index_ = (oldest_index_ + 1) % history_.size();
109 oldest_time_ = oldest_time_ + bucket_time_;
113 void RateEstimator::ResetBuckets(TimeTicks now) {
114 for (size_t i = 0; i < history_.size(); ++i) {
115 history_[i] = 0;
117 oldest_index_ = 0;
118 bucket_count_ = 1;
119 oldest_time_ = now;
122 } // namespace content