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. 4 5 #include "content/browser/download/rate_estimator.h" 6 7 #include "base/logging.h" 8 9 using base::TimeDelta; 10 using base::TimeTicks; 11 12 namespace content { 13 14 namespace { 15 16 static const int kDefaultBucketTimeSeconds = 1; 17 static const size_t kDefaultNumBuckets = 10; 18 19 } // namespace 20 21 RateEstimator::RateEstimator() 22 : history_(kDefaultNumBuckets), 23 bucket_time_(TimeDelta::FromSeconds(kDefaultBucketTimeSeconds)), 24 oldest_index_(0), 25 bucket_count_(1) { 26 ResetBuckets(TimeTicks::Now()); 27 } 28 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); 38 } 39 40 RateEstimator::~RateEstimator() { 41 } 42 43 void RateEstimator::Increment(uint32 count) { 44 Increment(count, TimeTicks::Now()); 45 } 46 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; 57 } 58 59 uint64 RateEstimator::GetCountPerSecond() const { 60 return GetCountPerSecond(TimeTicks::Now()); 61 } 62 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]; 73 } 74 return total_count / (bucket_count_ * bucket_time_.InSeconds()); 75 } 76 77 void RateEstimator::ClearOldBuckets(TimeTicks now) { 78 int64 seconds_since_oldest = (now - oldest_time_).InSeconds(); 79 80 int64 delta_buckets = seconds_since_oldest / bucket_time_.InSeconds(); 81 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; 87 } 88 size_t delta_index = static_cast<size_t>(delta_buckets); 89 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; 94 } 95 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; 102 } 103 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_; 110 } 111 } 112 113 void RateEstimator::ResetBuckets(TimeTicks now) { 114 for (size_t i = 0; i < history_.size(); ++i) { 115 history_[i] = 0; 116 } 117 oldest_index_ = 0; 118 bucket_count_ = 1; 119 oldest_time_ = now; 120 } 121 122 } // namespace content 123