blob: dee618299f7d5a40ed446c63ccd04ac5555f6043 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2014 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.
-->
<link rel="import" href="/tracing/base/base.html">
<link rel="import" href="/tracing/base/iteration_helpers.html">
<script>
'use strict';
/**
* @fileoverview Provides event merging functionality for grouping/analysis.
*/
tr.exportTo('tr.b.math', function() {
function convertEventsToRanges(events) {
return events.map(function(event) {
return tr.b.math.Range.fromExplicitRange(event.start, event.end);
});
}
function mergeRanges(inRanges, mergeThreshold, mergeFunction) {
var remainingEvents = inRanges.slice();
remainingEvents.sort(function(x, y) {
return x.min - y.min;
});
if (remainingEvents.length <= 1) {
var merged = [];
if (remainingEvents.length === 1) {
merged.push(mergeFunction(remainingEvents));
}
return merged;
}
var mergedEvents = [];
var currentMergeBuffer = [];
var rightEdge;
function beginMerging() {
currentMergeBuffer.push(remainingEvents[0]);
remainingEvents.splice(0, 1);
rightEdge = currentMergeBuffer[0].max;
}
function flushCurrentMergeBuffer() {
if (currentMergeBuffer.length === 0)
return;
mergedEvents.push(mergeFunction(currentMergeBuffer));
currentMergeBuffer = [];
// Refill merge buffer if needed.
if (remainingEvents.length !== 0)
beginMerging();
}
beginMerging();
while (remainingEvents.length) {
var currentEvent = remainingEvents[0];
var distanceFromRightEdge = currentEvent.min - rightEdge;
if (distanceFromRightEdge < mergeThreshold) {
rightEdge = Math.max(rightEdge, currentEvent.max);
remainingEvents.splice(0, 1);
currentMergeBuffer.push(currentEvent);
continue;
}
// Too big a gap.
flushCurrentMergeBuffer();
}
flushCurrentMergeBuffer();
return mergedEvents;
}
// Pass in |opt_totalRange| in order to find empty ranges before the first of
// |inRanges| and after the last of |inRanges|.
function findEmptyRangesBetweenRanges(inRanges, opt_totalRange) {
if (opt_totalRange && opt_totalRange.isEmpty)
opt_totalRange = undefined;
var emptyRanges = [];
if (!inRanges.length) {
if (opt_totalRange)
emptyRanges.push(opt_totalRange);
return emptyRanges;
}
inRanges = inRanges.slice();
inRanges.sort(function(x, y) {
return x.min - y.min;
});
if (opt_totalRange &&
(opt_totalRange.min < inRanges[0].min)) {
emptyRanges.push(tr.b.math.Range.fromExplicitRange(
opt_totalRange.min, inRanges[0].min));
}
inRanges.forEach(function(range, index) {
for (var otherIndex = 0; otherIndex < inRanges.length; ++otherIndex) {
if (index === otherIndex)
continue;
var other = inRanges[otherIndex];
if (other.min > range.max) {
// |inRanges| is sorted, so |other| is the first range after |range|,
// and there is an empty range between them.
emptyRanges.push(tr.b.math.Range.fromExplicitRange(
range.max, other.min));
return;
}
// Otherwise, |other| starts before |range| ends, so |other| might
// possibly contain the end of |range|.
if (other.max > range.max) {
// |other| does contain the end of |range|, so no empty range starts
// at the end of this |range|.
return;
}
}
if (opt_totalRange && (range.max < opt_totalRange.max)) {
emptyRanges.push(tr.b.math.Range.fromExplicitRange(
range.max, opt_totalRange.max));
}
});
return emptyRanges;
}
return {
convertEventsToRanges,
findEmptyRangesBetweenRanges,
mergeRanges,
};
});
</script>