blob: bd504bb9a37a32651e65a8428f693701ae63f666 [file] [log] [blame]
// Copyright (c) 2011 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.
cr.define('media', function() {
/**
* This class represents a collection of non-intersecting ranges. Ranges
* specified by (start, end) can be added and removed at will. It is used to
* record which sections of a media file have been cached, e.g. the first and
* last few kB plus several MB in the middle.
*
* Example usage:
* someRange.add(0, 100); // Contains 0-100.
* someRange.add(150, 200); // Contains 0-100, 150-200.
* someRange.remove(25, 75); // Contains 0-24, 76-100, 150-200.
* someRange.add(25, 149); // Contains 0-200.
*/
function DisjointRangeSet() {
this.ranges_ = {};
}
DisjointRangeSet.prototype = {
/**
* Deletes all ranges intersecting with (start ... end) and returns the
* extents of the cleared area.
* @param {int} start The start of the range to remove.
* @param {int} end The end of the range to remove.
* @param {int} sloppiness 0 removes only strictly overlapping ranges, and
* 1 removes adjacent ones.
* @return {Object} The start and end of the newly cleared range.
*/
clearRange: function(start, end, sloppiness) {
var ranges = this.ranges_;
var result = {start: start, end: end};
for (var rangeStart in this.ranges_) {
rangeEnd = this.ranges_[rangeStart];
// A range intersects another if its start lies within the other range
// or vice versa.
if ((rangeStart >= start && rangeStart <= (end + sloppiness)) ||
(start >= rangeStart && start <= (rangeEnd + sloppiness))) {
delete ranges[rangeStart];
result.start = Math.min(result.start, rangeStart);
result.end = Math.max(result.end, rangeEnd);
}
}
return result;
},
/**
* Adds a range to this DisjointRangeSet.
* Joins adjacent and overlapping ranges together.
* @param {int} start The beginning of the range to add, inclusive.
* @param {int} end The end of the range to add, inclusive.
*/
add: function(start, end) {
if (end < start)
return;
// Remove all touching ranges.
result = this.clearRange(start, end, 1);
// Add back a single contiguous range.
this.ranges_[Math.min(start, result.start)] = Math.max(end, result.end);
},
/**
* Combines a DisjointRangeSet with this one.
* @param {DisjointRangeSet} ranges A DisjointRangeSet to be squished into
* this one.
*/
merge: function(other) {
var ranges = this;
other.forEach(function(start, end) { ranges.add(start, end); });
},
/**
* Removes a range from this DisjointRangeSet.
* Will split existing ranges if necessary.
* @param {int} start The beginning of the range to remove, inclusive.
* @param {int} end The end of the range to remove, inclusive.
*/
remove: function(start, end) {
if (end < start)
return;
// Remove instersecting ranges.
result = this.clearRange(start, end, 0);
// Add back non-overlapping ranges.
if (result.start < start)
this.ranges_[result.start] = start - 1;
if (result.end > end)
this.ranges_[end + 1] = result.end;
},
/**
* Iterates over every contiguous range in this DisjointRangeSet, calling a
* function for each (start, end).
* @param {function(int, int)} iterator The function to call on each range.
*/
forEach: function(iterator) {
for (var start in this.ranges_)
iterator(start, this.ranges_[start]);
},
/**
* Maps this DisjointRangeSet to an array by calling a given function on the
* start and end of each contiguous range, sorted by start.
* @param {function(int, int)} mapper Maps a range to an array element.
* @return {Array} An array of each mapper(range).
*/
map: function(mapper) {
var starts = [];
for (var start in this.ranges_)
starts.push(parseInt(start));
starts.sort(function(a, b) {
return a - b;
});
var ranges = this.ranges_;
var results = starts.map(function(s) {
return mapper(s, ranges[s]);
});
return results;
},
/**
* Finds the maximum value present in any of the contained ranges.
* @return {int} The maximum value contained by this DisjointRangeSet.
*/
max: function() {
var max = -Infinity;
for (var start in this.ranges_)
max = Math.max(max, this.ranges_[start]);
return max;
},
};
return {
DisjointRangeSet: DisjointRangeSet
};
});