Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Unified Diff: media/blink/range_map.h

Issue 1422523007: RangeMap: A int->int mapping with fast range operations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lru
Patch Set: comments addressed Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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_

Powered by Google App Engine
This is Rietveld 408576698