1 // Copyright (c) 2011 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.
5 cr.define('media', function() {
8 * This class represents a collection of non-intersecting ranges. Ranges
9 * specified by (start, end) can be added and removed at will. It is used to
10 * record which sections of a media file have been cached, e.g. the first and
11 * last few kB plus several MB in the middle.
14 * someRange.add(0, 100); // Contains 0-100.
15 * someRange.add(150, 200); // Contains 0-100, 150-200.
16 * someRange.remove(25, 75); // Contains 0-24, 76-100, 150-200.
17 * someRange.add(25, 149); // Contains 0-200.
19 function DisjointRangeSet() {
23 DisjointRangeSet.prototype = {
25 * Deletes all ranges intersecting with (start ... end) and returns the
26 * extents of the cleared area.
27 * @param {int} start The start of the range to remove.
28 * @param {int} end The end of the range to remove.
29 * @param {int} sloppiness 0 removes only strictly overlapping ranges, and
30 * 1 removes adjacent ones.
31 * @return {Object} The start and end of the newly cleared range.
33 clearRange: function(start, end, sloppiness) {
34 var ranges = this.ranges_;
35 var result = {start: start, end: end};
37 for (var rangeStart in this.ranges_) {
38 rangeEnd = this.ranges_[rangeStart];
39 // A range intersects another if its start lies within the other range
41 if ((rangeStart >= start && rangeStart <= (end + sloppiness)) ||
42 (start >= rangeStart && start <= (rangeEnd + sloppiness))) {
43 delete ranges[rangeStart];
44 result.start = Math.min(result.start, rangeStart);
45 result.end = Math.max(result.end, rangeEnd);
53 * Adds a range to this DisjointRangeSet.
54 * Joins adjacent and overlapping ranges together.
55 * @param {int} start The beginning of the range to add, inclusive.
56 * @param {int} end The end of the range to add, inclusive.
58 add: function(start, end) {
62 // Remove all touching ranges.
63 result = this.clearRange(start, end, 1);
64 // Add back a single contiguous range.
65 this.ranges_[Math.min(start, result.start)] = Math.max(end, result.end);
69 * Combines a DisjointRangeSet with this one.
70 * @param {DisjointRangeSet} ranges A DisjointRangeSet to be squished into
73 merge: function(other) {
75 other.forEach(function(start, end) { ranges.add(start, end); });
79 * Removes a range from this DisjointRangeSet.
80 * Will split existing ranges if necessary.
81 * @param {int} start The beginning of the range to remove, inclusive.
82 * @param {int} end The end of the range to remove, inclusive.
84 remove: function(start, end) {
88 // Remove instersecting ranges.
89 result = this.clearRange(start, end, 0);
91 // Add back non-overlapping ranges.
92 if (result.start < start)
93 this.ranges_[result.start] = start - 1;
95 this.ranges_[end + 1] = result.end;
99 * Iterates over every contiguous range in this DisjointRangeSet, calling a
100 * function for each (start, end).
101 * @param {function(int, int)} iterator The function to call on each range.
103 forEach: function(iterator) {
104 for (var start in this.ranges_)
105 iterator(start, this.ranges_[start]);
109 * Maps this DisjointRangeSet to an array by calling a given function on the
110 * start and end of each contiguous range, sorted by start.
111 * @param {function(int, int)} mapper Maps a range to an array element.
112 * @return {Array} An array of each mapper(range).
114 map: function(mapper) {
116 for (var start in this.ranges_)
117 starts.push(parseInt(start));
118 starts.sort(function(a, b) {
122 var ranges = this.ranges_;
123 var results = starts.map(function(s) {
124 return mapper(s, ranges[s]);
131 * Finds the maximum value present in any of the contained ranges.
132 * @return {int} The maximum value contained by this DisjointRangeSet.
136 for (var start in this.ranges_)
137 max = Math.max(max, this.ranges_[start]);
143 DisjointRangeSet: DisjointRangeSet