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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 #ifndef MEDIA_BLINK_RANGE_MAP_H_
6 #define MEDIA_BLINK_RANGE_MAP_H_
7
8 #include <limits>
9 #include <map>
10
11 #include "base/logging.h"
12
13 namespace media {
14
15 // A RangeMap<KeyType, ValueType> is similar to a std::map<KeyType, ValueType>,
16 // where incrementing, decrementing and setting ranges of values has been
17 // optimized. The default state for a rangemap is to map all values in
18 // KeyType to ValueType().
19 //
20 // Set/Increment operations should generally take
21 // O(log(N)) + O(M) time where N is the number of ranges in the map and
22 // M is the number of modified ranges.
23 //
24 // Internally, RangeMap<> uses an std::map, where the beginning of each range
25 // is stored along with the value for that range. Adjacent ranges which have the
26 // same value are automatically merged. For instance, if you did:
27 //
28 // RangeMap<int, int> tmp;
29 // tmp.IncrementRange(2, 5, 2);
30 // tmp.IncrementRange(4, 6, 1);
31 //
32 // Then:
33 // tmp[0] = 0
34 // tmp[1] = 0
35 // tmp[2] = 2
36 // tmp[3] = 2
37 // tmp[4] = 3
38 // tmp[5] = 1
39 // tmp[6] = 0
40 //
41 // If you iterate over tmp, you get the following ranges:
42 // -maxint .. 2 => 0
43 // 2 .. 4 => 2
44 // 4 .. 5 => 3
45 // 5 .. 6 => 1
46 // 6 .. maxint => 0
47 //
48 // Internally, this would be stored in a map as:
49 // 2:2, 4:3, 5:1, 6:0
50 //
51 // TODO(hubbe): Consider consolidating with media::Ranges.
52
53 // Simple range class.
54 // Range ends are always non-inclusive.
55 // Please note that end <= begin is a valid (but empty) range.
56 template <typename T>
57 struct Range {
58 public:
59 Range(const T& begin_, const T& end_) : begin(begin_), end(end_) {}
60
61 // May return empty ranges (begin >= end).
62 Range Intersect(const Range& other) const {
63 return Range(std::max(begin, other.begin), std::min(end, other.end));
64 }
65
66 bool Empty() const { return begin >= end; }
67
68 T begin;
69 T end;
70 };
71
72 template <typename KeyType,
73 typename ValueType,
74 class Compare = std::less<KeyType>,
75 class NumericLimits = std::numeric_limits<KeyType>>
76 class RangeMapConstIterator {
77 public:
78 typedef std::map<KeyType, ValueType, Compare> MapType;
79 RangeMapConstIterator() {}
80 RangeMapConstIterator(const MapType* map,
81 typename MapType::const_iterator first,
82 typename MapType::const_iterator second)
83 : map_(map), first_(first), second_(second) {}
84
85 bool operator==(const RangeMapConstIterator& other) const {
86 return first_ == other.first_ && second_ == other.second_;
87 }
88
89 bool operator!=(const RangeMapConstIterator& other) const {
90 return first_ != other.first_ || second_ != other.second_;
91 }
92
93 // Returns the beginning of the current range.
94 KeyType range_begin() const {
95 if (first_ == map_->end()) {
96 return NumericLimits::min();
97 } else {
98 return first_->first;
99 }
100 }
101
102 // Returns the end of the current range, non-inclusive.
103 KeyType range_end() const {
104 if (second_ == map_->end()) {
105 return NumericLimits::max();
106 } else {
107 return second_->first;
108 }
109 }
110
111 // Returns the current range.
112 Range<KeyType> range() const {
113 return Range<KeyType>(range_begin(), range_end());
114 }
115
116 // Returns the value associated with the current range.
117 ValueType value() const {
118 if (first_ == map_->end())
119 return 0;
120 return first_->second;
121 }
122
123 // Needed to make the following construct work:
124 // for (const auto& range_value_pair : rangemap)
125 // Note however that this will skip the "end" range, which
126 // is usually ok since it generally has the default value.
127 std::pair<Range<KeyType>, ValueType> operator*() const {
128 return std::make_pair(range(), value());
129 }
130
131 // Go to the next range.
132 // The beginning of the next range always matches the end of the current
133 // range. (But should always have a different value.)
134 // Not allowed if we're already at map_->end().
135 void operator++() {
136 DCHECK(second_ != map_->end());
137 first_ = second_;
138 second_++;
139 }
140
141 // Go to the previous range.
142 // The end of the previous range always matches the beginning of the current
143 // range. (But should always have a different value.)
144 // Not allowed if we're already at map_->begin().
145 void operator--() {
146 DCHECK(first_ != map_->end());
147 second_ = first_;
148 if (first_ == map_->begin()) {
149 first_ = map_->end();
150 } else {
151 first_--;
152 }
153 }
154
155 private:
156 const MapType* map_;
157
158 // Pointer to the entry in the RangeMap that specifies the
159 // beginning of the current range. (Or map_->end() if we are
160 // currently at the first range.)
161 typename MapType::const_iterator first_;
162
163 // Pointer to the entry in the RangeMap that specifies the
164 // end of the current range (non-inclusive) Or, it will
165 // point to map_->end() if we are currently at the last range.
166 // Note that if there are no entries in map_, both first_ and
167 // second_ will point to map_->end().
168 typename MapType::const_iterator second_;
169 };
170
171 template <typename KeyType,
172 typename ValueType,
173 class Compare = std::less<KeyType>,
174 class NumericLimits = std::numeric_limits<KeyType>>
175 class RangeMap {
176 public:
177 typedef std::map<KeyType, ValueType, Compare> MapType;
178 typedef RangeMapConstIterator<KeyType, ValueType, Compare, NumericLimits>
179 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
180
181 // Returns the value at a particular point.
182 // Defaults to ValueType().
183 ValueType operator[](const KeyType& k) const {
184 typename MapType::const_iterator i = map_.upper_bound(k);
185 if (i == map_.begin()) {
186 return 0;
187 } else {
188 --i;
189 return i->second;
190 }
191 }
192
193 // Increase [from..to) by |how_much|.
194 void IncrementRange(KeyType from, KeyType to, ValueType how_much) {
195 DCHECK_GT(to, from);
196 if (how_much == 0)
197 return;
198 typename MapType::iterator a = MakeEntry(from);
199 typename MapType::iterator b = MakeEntry(to);
200 for (typename MapType::iterator i = a; i != b; ++i) {
201 i->second += how_much;
202 }
203 RemoveDuplicates(a);
204 // b may be invalid
205 RemoveDuplicates(map_.lower_bound(to));
206 }
207
208 // Set [from..to) to |how_much|.
209 void SetRange(KeyType from, KeyType to, ValueType how_much) {
210 DCHECK_GT(to, from);
211 typename MapType::iterator a = MakeEntry(from);
212 typename MapType::iterator b = MakeEntry(to);
213 a->second = how_much;
214 while (true) {
215 typename MapType::iterator c = a;
216 ++c;
217 if (c == b) {
218 break;
219 } else {
220 map_.erase(c);
221 }
222 }
223 RemoveDuplicates(a);
224 // b may be invalid
225 RemoveDuplicates(map_.lower_bound(to));
226 }
227
228 // Returns an iterator to the first range.
229 // Note, there is always at least one range.
230 const_iterator begin() const {
231 return const_iterator(&map(), map_.end(), map_.begin());
232 }
233
234 // Returns an iterator to the last range.
235 // Note that this works slightly different than most containers
236 // 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
237 // For example, if you have you do:
238 // RangeMap<int, int> tmp;
239 // tmp.IncrementRange(-5, 5, 1);
240 // Then end will return the range [6, maxint) with a value of 0.
241 const_iterator end() const {
242 if (map_.empty()) {
243 return const_iterator(&map(), map_.end(), map_.end());
244 }
245 typename MapType::const_iterator i = map_.end();
246 --i;
247 return const_iterator(&map(), i, map_.end());
248 }
249
250 // Returns an iterator to the range containing |k|.
251 // Always returns a valid iterator.
252 const_iterator find(KeyType k) const {
253 if (map_.empty()) {
254 return const_iterator(&map(), map_.end(), map_.end());
255 }
256 typename MapType::const_iterator i = map_.upper_bound(k);
257 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.
258 if (j == map_.begin()) {
259 j = map_.end();
260 } else {
261 --j;
262 }
263 return const_iterator(&map(), j, i);
264 }
265
266 private:
267 const MapType& map() const { return map_; }
268
269 // Make an entry in map_ with the key |k| and return it's iterator.
270 // If such an entry already exists, just re-use it.
271 // If a new entry is created, it's value will be set to the same
272 // as the preceeding entry, or ValueType() if no preceeding entry exists.
273 // After calling this function, we'll need to call RemoveDuplicates()
274 // to clean up any duplicates that we made.
275 typename MapType::iterator MakeEntry(KeyType k) {
276 typename MapType::value_type tmp(k, 0);
277 std::pair<typename MapType::iterator, bool> insert_result;
278 insert_result = map_.insert(tmp);
279 if (insert_result.second) {
280 if (insert_result.first != map_.begin()) {
281 typename MapType::iterator i = insert_result.first;
282 --i;
283 insert_result.first->second = i->second;
284 }
285 }
286 return insert_result.first;
287 }
288
289 // Remove duplicates before and after |i|.
290 void RemoveDuplicates(typename MapType::iterator i) {
291 if (i == map_.end())
292 return;
293 if (i == map_.begin() && i->second == 0) {
294 typename MapType::iterator j = i;
295 ++i;
296 map_.erase(j);
297 if (i == map_.end())
298 return;
299 }
300 if (i != map_.begin()) {
301 typename MapType::iterator j = i;
302 --i;
303 if (i->second == j->second) {
304 map_.erase(j);
305 }
306 }
307 typename MapType::iterator j = i;
308 ++j;
309 if (j != map_.end() && i->second == j->second) {
310 map_.erase(j);
311 }
312 }
313
314 MapType map_;
315 };
316
317 } // namespace media
318
319 #endif // MEDIA_BLINK_RANGE_MAP_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698