Chromium Code Reviews| Index: media/blink/range_map.h |
| diff --git a/media/blink/range_map.h b/media/blink/range_map.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..099ef2885c8c23f1f09f9e550a882d8b5b2e63ff |
| --- /dev/null |
| +++ b/media/blink/range_map.h |
| @@ -0,0 +1,319 @@ |
| +// Copyright 2015 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. |
| + |
| +#ifndef MEDIA_BLINK_RANGE_MAP_H_ |
| +#define MEDIA_BLINK_RANGE_MAP_H_ |
| + |
| +#include <limits> |
| +#include <map> |
| + |
| +#include "base/logging.h" |
| + |
| +namespace media { |
| + |
| +// A RangeMap<KeyType, ValueType> is similar to a std::map<KeyType, ValueType>, |
| +// where incrementing, decrementing and setting ranges of values has been |
| +// optimized. The default state for a rangemap is to map all values in |
| +// KeyType to ValueType(). |
| +// |
| +// Set/Increment operations should generally take |
| +// O(log(N)) + O(M) time where N is the number of ranges in the map and |
| +// M is the number of modified ranges. |
| +// |
| +// Internally, RangeMap<> uses an std::map, where the beginning of each range |
| +// is stored along with the value for that range. Adjacent ranges which have the |
| +// same value are automatically merged. For instance, if you did: |
| +// |
| +// RangeMap<int, int> tmp; |
| +// tmp.IncrementRange(2, 5, 2); |
| +// tmp.IncrementRange(4, 6, 1); |
| +// |
| +// Then: |
| +// tmp[0] = 0 |
| +// tmp[1] = 0 |
| +// tmp[2] = 2 |
| +// tmp[3] = 2 |
| +// tmp[4] = 3 |
| +// tmp[5] = 1 |
| +// tmp[6] = 0 |
| +// |
| +// If you iterate over tmp, you get the following ranges: |
| +// -maxint .. 2 => 0 |
| +// 2 .. 4 => 2 |
| +// 4 .. 5 => 3 |
| +// 5 .. 6 => 1 |
| +// 6 .. maxint => 0 |
| +// |
| +// Internally, this would be stored in a map as: |
| +// 2:2, 4:3, 5:1, 6:0 |
| +// |
| +// TODO(hubbe): Consider consolidating with media::Ranges. |
| + |
| +// Simple range class. |
| +// Range ends are always non-inclusive. |
| +// Please note that end <= begin is a valid (but empty) range. |
| +template <typename T> |
| +struct Range { |
| + public: |
| + Range(const T& begin_, const T& end_) : begin(begin_), end(end_) {} |
| + |
| + // May return empty ranges (begin >= end). |
| + Range Intersect(const Range& other) const { |
| + return Range(std::max(begin, other.begin), std::min(end, other.end)); |
| + } |
| + |
| + bool Empty() const { return begin >= end; } |
| + |
| + T begin; |
| + T end; |
| +}; |
| + |
| +template <typename KeyType, |
| + typename ValueType, |
| + class Compare = std::less<KeyType>, |
| + class NumericLimits = std::numeric_limits<KeyType>> |
| +class RangeMapConstIterator { |
| + public: |
| + typedef std::map<KeyType, ValueType, Compare> MapType; |
| + RangeMapConstIterator() {} |
| + RangeMapConstIterator(const MapType* map, |
| + typename MapType::const_iterator first, |
| + typename MapType::const_iterator second) |
| + : map_(map), first_(first), second_(second) {} |
| + |
| + bool operator==(const RangeMapConstIterator& other) const { |
| + return first_ == other.first_ && second_ == other.second_; |
| + } |
| + |
| + bool operator!=(const RangeMapConstIterator& other) const { |
| + return first_ != other.first_ || second_ != other.second_; |
| + } |
| + |
| + // Returns the beginning of the current range. |
| + KeyType range_begin() const { |
| + if (first_ == map_->end()) { |
| + return NumericLimits::min(); |
| + } else { |
| + return first_->first; |
| + } |
| + } |
| + |
| + // Returns the end of the current range, non-inclusive. |
| + KeyType range_end() const { |
| + if (second_ == map_->end()) { |
| + return NumericLimits::max(); |
| + } else { |
| + return second_->first; |
| + } |
| + } |
| + |
| + // Returns the current range. |
| + Range<KeyType> range() const { |
| + return Range<KeyType>(range_begin(), range_end()); |
| + } |
| + |
| + // Returns the value associated with the current range. |
| + ValueType value() const { |
| + if (first_ == map_->end()) |
| + return 0; |
| + return first_->second; |
| + } |
| + |
| + // Needed to make the following construct work: |
| + // for (const auto& range_value_pair : rangemap) |
| + // Note however that this will skip the "end" range, which |
| + // is usually ok since it generally has the default value. |
| + std::pair<Range<KeyType>, ValueType> operator*() const { |
| + return std::make_pair(range(), value()); |
| + } |
| + |
| + // Go to the next range. |
| + // The beginning of the next range always matches the end of the current |
| + // range. (But should always have a different value.) |
| + // Not allowed if we're already at map_->end(). |
| + void operator++() { |
| + DCHECK(second_ != map_->end()); |
| + first_ = second_; |
| + second_++; |
| + } |
| + |
| + // Go to the previous range. |
| + // The end of the previous range always matches the beginning of the current |
| + // range. (But should always have a different value.) |
| + // Not allowed if we're already at map_->begin(). |
| + void operator--() { |
| + DCHECK(first_ != map_->end()); |
| + second_ = first_; |
| + if (first_ == map_->begin()) { |
| + first_ = map_->end(); |
| + } else { |
| + first_--; |
| + } |
| + } |
| + |
| + private: |
| + const MapType* map_; |
| + |
| + // Pointer to the entry in the RangeMap that specifies the |
| + // beginning of the current range. (Or map_->end() if we are |
| + // currently at the first range.) |
| + typename MapType::const_iterator first_; |
| + |
| + // Pointer to the entry in the RangeMap that specifies the |
| + // end of the current range (non-inclusive) Or, it will |
| + // point to map_->end() if we are currently at the last range. |
| + // Note that if there are no entries in map_, both first_ and |
| + // second_ will point to map_->end(). |
| + typename MapType::const_iterator second_; |
| +}; |
| + |
| +template <typename KeyType, |
| + typename ValueType, |
| + class Compare = std::less<KeyType>, |
| + class NumericLimits = std::numeric_limits<KeyType>> |
| +class RangeMap { |
| + public: |
| + typedef std::map<KeyType, ValueType, Compare> MapType; |
| + typedef RangeMapConstIterator<KeyType, ValueType, Compare, NumericLimits> |
| + const_iterator; |
|
xhwang
2015/11/11 07:46:22
nit: How about typedef MapType::iterator and MapTy
hubbe
2015/11/12 06:57:44
It seems like it would hurt readability proportion
|
| + |
| + // Returns the value at a particular point. |
| + // Defaults to ValueType(). |
| + ValueType operator[](const KeyType& k) const { |
| + typename MapType::const_iterator i = map_.upper_bound(k); |
| + if (i == map_.begin()) { |
| + return 0; |
| + } else { |
| + --i; |
| + return i->second; |
| + } |
| + } |
| + |
| + // Increase [from..to) by |how_much|. |
| + void IncrementRange(KeyType from, KeyType to, ValueType how_much) { |
| + DCHECK_GT(to, from); |
| + if (how_much == 0) |
| + return; |
| + typename MapType::iterator a = MakeEntry(from); |
| + typename MapType::iterator b = MakeEntry(to); |
| + for (typename MapType::iterator i = a; i != b; ++i) { |
| + i->second += how_much; |
| + } |
| + RemoveDuplicates(a); |
| + // b may be invalid |
| + RemoveDuplicates(map_.lower_bound(to)); |
| + } |
| + |
| + // Set [from..to) to |how_much|. |
| + void SetRange(KeyType from, KeyType to, ValueType how_much) { |
| + DCHECK_GT(to, from); |
| + typename MapType::iterator a = MakeEntry(from); |
| + typename MapType::iterator b = MakeEntry(to); |
| + a->second = how_much; |
| + while (true) { |
| + typename MapType::iterator c = a; |
| + ++c; |
| + if (c == b) { |
| + break; |
| + } else { |
| + map_.erase(c); |
| + } |
| + } |
| + RemoveDuplicates(a); |
| + // b may be invalid |
| + RemoveDuplicates(map_.lower_bound(to)); |
| + } |
| + |
| + // Returns an iterator to the first range. |
| + // Note, there is always at least one range. |
| + const_iterator begin() const { |
| + return const_iterator(&map(), map_.end(), map_.begin()); |
| + } |
| + |
| + // Returns an iterator to the last range. |
| + // Note that this works slightly different than most containers |
| + // as end() returns a valid, but mostly uninteresting iterator. |
|
xhwang
2015/11/11 07:46:22
This seems a bit counter-intuitive. I don't know h
hubbe
2015/11/12 06:57:44
Should be more intuitive now.
Adding an explicit e
|
| + // For example, if you have you do: |
| + // RangeMap<int, int> tmp; |
| + // tmp.IncrementRange(-5, 5, 1); |
| + // Then end will return the range [6, maxint) with a value of 0. |
| + const_iterator end() const { |
| + if (map_.empty()) { |
| + return const_iterator(&map(), map_.end(), map_.end()); |
| + } |
| + typename MapType::const_iterator i = map_.end(); |
| + --i; |
| + return const_iterator(&map(), i, map_.end()); |
| + } |
| + |
| + // Returns an iterator to the range containing |k|. |
| + // Always returns a valid iterator. |
| + const_iterator find(KeyType k) const { |
| + if (map_.empty()) { |
| + return const_iterator(&map(), map_.end(), map_.end()); |
| + } |
| + typename MapType::const_iterator i = map_.upper_bound(k); |
| + typename MapType::const_iterator j = i; |
|
xhwang
2015/11/11 07:46:22
nit: replace i, j with second, first to match what
hubbe
2015/11/12 06:57:43
Good idea, done.
|
| + if (j == map_.begin()) { |
| + j = map_.end(); |
| + } else { |
| + --j; |
| + } |
| + return const_iterator(&map(), j, i); |
| + } |
| + |
| + private: |
| + const MapType& map() const { return map_; } |
| + |
| + // Make an entry in map_ with the key |k| and return it's iterator. |
| + // If such an entry already exists, just re-use it. |
| + // If a new entry is created, it's value will be set to the same |
| + // as the preceeding entry, or ValueType() if no preceeding entry exists. |
| + // After calling this function, we'll need to call RemoveDuplicates() |
| + // to clean up any duplicates that we made. |
| + typename MapType::iterator MakeEntry(KeyType k) { |
| + typename MapType::value_type tmp(k, 0); |
| + std::pair<typename MapType::iterator, bool> insert_result; |
| + insert_result = map_.insert(tmp); |
| + if (insert_result.second) { |
| + if (insert_result.first != map_.begin()) { |
| + typename MapType::iterator i = insert_result.first; |
| + --i; |
| + insert_result.first->second = i->second; |
| + } |
| + } |
| + return insert_result.first; |
| + } |
| + |
| + // Remove duplicates before and after |i|. |
| + void RemoveDuplicates(typename MapType::iterator i) { |
| + if (i == map_.end()) |
| + return; |
| + if (i == map_.begin() && i->second == 0) { |
| + typename MapType::iterator j = i; |
| + ++i; |
| + map_.erase(j); |
| + if (i == map_.end()) |
| + return; |
| + } |
| + if (i != map_.begin()) { |
| + typename MapType::iterator j = i; |
| + --i; |
| + if (i->second == j->second) { |
| + map_.erase(j); |
| + } |
| + } |
| + typename MapType::iterator j = i; |
| + ++j; |
| + if (j != map_.end() && i->second == j->second) { |
| + map_.erase(j); |
| + } |
| + } |
| + |
| + MapType map_; |
| +}; |
| + |
| +} // namespace media |
| + |
| +#endif // MEDIA_BLINK_RANGE_MAP_H_ |