// 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 }; });