// Copyright (c) 2012 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 "base/metrics/sample_vector.h"
#include "base/logging.h"
#include "base/metrics/bucket_ranges.h"
using std::vector;
namespace base {
typedef HistogramBase::Count Count;
typedef HistogramBase::Sample Sample;
SampleVector::SampleVector(const BucketRanges* bucket_ranges)
: counts_(bucket_ranges->bucket_count()),
bucket_ranges_(bucket_ranges) {
CHECK_GE(bucket_ranges_->bucket_count(), 1u);
}
SampleVector::~SampleVector() {}
void SampleVector::Accumulate(Sample value, Count count) {
size_t bucket_index = GetBucketIndex(value);
subtle::NoBarrier_Store(&counts_[bucket_index],
subtle::NoBarrier_Load(&counts_[bucket_index]) + count);
IncreaseSum(count * value);
IncreaseRedundantCount(count);
}
Count SampleVector::GetCount(Sample value) const {
size_t bucket_index = GetBucketIndex(value);
return counts_[bucket_index];
}
Count SampleVector::TotalCount() const {
Count count = 0;
for (size_t i = 0; i < counts_.size(); i++) {
count += counts_[i];
}
return count;
}
Count SampleVector::GetCountAtIndex(size_t bucket_index) const {
DCHECK(bucket_index < counts_.size());
return counts_[bucket_index];
}
scoped_ptr<SampleCountIterator> SampleVector::Iterator() const {
return scoped_ptr<SampleCountIterator>(
new SampleVectorIterator(&counts_, bucket_ranges_));
}
bool SampleVector::AddSubtractImpl(SampleCountIterator* iter,
HistogramSamples::Operator op) {
HistogramBase::Sample min;
HistogramBase::Sample max;
HistogramBase::Count count;
// Go through the iterator and add the counts into correct bucket.
size_t index = 0;
while (index < counts_.size() && !iter->Done()) {
iter->Get(&min, &max, &count);
if (min == bucket_ranges_->range(index) &&
max == bucket_ranges_->range(index + 1)) {
// Sample matches this bucket!
counts_[index] += (op == HistogramSamples::ADD) ? count : -count;
iter->Next();
} else if (min > bucket_ranges_->range(index)) {
// Sample is larger than current bucket range. Try next.
index++;
} else {
// Sample is smaller than current bucket range. We scan buckets from
// smallest to largest, so the sample value must be invalid.
return false;
}
}
return iter->Done();
}
// Use simple binary search. This is very general, but there are better
// approaches if we knew that the buckets were linearly distributed.
size_t SampleVector::GetBucketIndex(Sample value) const {
size_t bucket_count = bucket_ranges_->bucket_count();
CHECK_GE(bucket_count, 1u);
CHECK_GE(value, bucket_ranges_->range(0));
CHECK_LT(value, bucket_ranges_->range(bucket_count));
size_t under = 0;
size_t over = bucket_count;
size_t mid;
do {
DCHECK_GE(over, under);
mid = under + (over - under)/2;
if (mid == under)
break;
if (bucket_ranges_->range(mid) <= value)
under = mid;
else
over = mid;
} while (true);
DCHECK_LE(bucket_ranges_->range(mid), value);
CHECK_GT(bucket_ranges_->range(mid + 1), value);
return mid;
}
SampleVectorIterator::SampleVectorIterator(const vector<Count>* counts,
const BucketRanges* bucket_ranges)
: counts_(counts),
bucket_ranges_(bucket_ranges),
index_(0) {
CHECK_GE(bucket_ranges_->bucket_count(), counts_->size());
SkipEmptyBuckets();
}
SampleVectorIterator::~SampleVectorIterator() {}
bool SampleVectorIterator::Done() const {
return index_ >= counts_->size();
}
void SampleVectorIterator::Next() {
DCHECK(!Done());
index_++;
SkipEmptyBuckets();
}
void SampleVectorIterator::Get(HistogramBase::Sample* min,
HistogramBase::Sample* max,
HistogramBase::Count* count) const {
DCHECK(!Done());
if (min != NULL)
*min = bucket_ranges_->range(index_);
if (max != NULL)
*max = bucket_ranges_->range(index_ + 1);
if (count != NULL)
*count = (*counts_)[index_];
}
bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
DCHECK(!Done());
if (index != NULL)
*index = index_;
return true;
}
void SampleVectorIterator::SkipEmptyBuckets() {
if (Done())
return;
while (index_ < counts_->size()) {
if ((*counts_)[index_] != 0)
return;
index_++;
}
}
} // namespace base