Chromium Code Reviews| Index: web/inc/logdog-stream-view/viewer.ts |
| diff --git a/web/inc/logdog-stream-view/viewer.ts b/web/inc/logdog-stream-view/viewer.ts |
| index a171e85c615a025e32f63824988aadb1b4916430..dbe4dab5072d129035dc3083091a600c4706d9d8 100644 |
| --- a/web/inc/logdog-stream-view/viewer.ts |
| +++ b/web/inc/logdog-stream-view/viewer.ts |
| @@ -998,6 +998,54 @@ namespace LogDog { |
| } |
| /** |
| + * LogSorter is used to extract sorted logs from a set of BufferedLogs. |
|
hinoka
2017/05/03 17:16:58
LogSorter is an interface used to....
dnj
2017/05/03 19:09:27
Done.
|
| + * |
| + * LogSorter will modify the BufferedLogs, consuming the logs that it |
|
hinoka
2017/05/03 17:16:58
A LogSorter which implements implicitNext() will m
dnj
2017/05/03 19:09:27
Done.
|
| + * returns from the buffer. |
| + */ |
| + type LogSorter = { |
| + compare: (a: LogDog.LogEntry, b: LogDog.LogEntry) => number; |
| + implicitNext?: (prev: LogDog.LogEntry, buffers: BufferedLogs[]) => |
| + LogDog.LogEntry | null; |
| + }; |
| + |
| + const prefixIndexLogSorter: LogSorter = { |
| + compare: (a: LogDog.LogEntry, b: LogDog.LogEntry) => { |
| + return (a.prefixIndex - b.prefixIndex); |
| + }, |
| + |
| + implicitNext: (prev: LogDog.LogEntry, buffers: BufferedLogs[]) => { |
| + let nextPrefixIndex = (prev.prefixIndex + 1); |
| + for (let buf of buffers) { |
| + let le = buf.peek(); |
| + if (le && le.prefixIndex === nextPrefixIndex) { |
| + return buf.next(); |
| + } |
| + } |
| + return null; |
| + }, |
| + }; |
| + |
| + const timestampLogSorter: LogSorter = { |
| + compare: (a: LogDog.LogEntry, b: LogDog.LogEntry) => { |
| + if (a.timestamp) { |
| + if (b.timestamp) { |
| + return a.timestamp.getTime() - b.timestamp.getTime(); |
| + } |
| + return 1; |
| + } |
| + if (b.timestamp) { |
| + return -1; |
| + } |
| + return 0; |
| + }, |
| + |
| + // No implicit "next" with timestamp-based logs, since the next log in |
| + // an empty buffer may actually be the next contiguous log. |
| + implicitNext: undefined, |
| + }; |
| + |
| + /** |
| * An aggregate log stream. It presents a single-stream view, but is really |
| * composed of several log streams interleaved based on their prefix indices |
| * (if they share a prefix) or timestamps (if they don't). |
| @@ -1014,7 +1062,7 @@ namespace LogDog { |
| private streams: AggregateLogStream.Entry[]; |
| private active: AggregateLogStream.Entry[]; |
| private currentNextPromise: Promise<BufferedLogs[]>|null; |
| - private compareLogs: (a: LogDog.LogEntry, b: LogDog.LogEntry) => number; |
| + private readonly logSorter: LogSorter; |
| private streamStatusCallback: StreamStatusCallback; |
| @@ -1049,20 +1097,11 @@ namespace LogDog { |
| return template.samePrefixAs(entry.ls.stream); |
| }); |
| - this.compareLogs = ((sharedPrefix) ? (a, b) => { |
| - return (a.prefixIndex - b.prefixIndex); |
| - } : (a, b) => { |
| - if (a.timestamp) { |
| - if (b.timestamp) { |
| - return a.timestamp.getTime() - b.timestamp.getTime(); |
| - } |
| - return 1; |
| - } else if (b.timestamp) { |
| - return -1; |
| - } else { |
| - return 0; |
| - } |
| - }); |
| + if (sharedPrefix) { |
| + this.logSorter = prefixIndexLogSorter; |
| + } else { |
| + this.logSorter = timestampLogSorter; |
| + } |
| } |
| split(): SplitLogProvider|null { |
| @@ -1185,7 +1224,6 @@ namespace LogDog { |
| // its entries are sorted, then that buffer is a return value. |
| return new BufferedLogs(buffers[0].getAll()); |
| default: |
| - // Nothing to do. |
| break; |
| } |
| @@ -1203,11 +1241,12 @@ namespace LogDog { |
| // Assemble our aggregate buffer array. |
| let entries: LogDog.LogEntry[] = []; |
| + let last: LogDog.LogEntry|null = null; |
|
hinoka
2017/05/03 17:16:58
how about "current"? "last" often implies "final",
dnj
2017/05/03 19:09:27
Renamed to "latestAdded".
|
| while (true) { |
| // Choose the next stream. |
| let earliest = 0; |
| for (let i = 1; i < buffers.length; i++) { |
| - if (this.compareLogs(peek[i], peek[earliest]) < 0) { |
| + if (this.logSorter.compare(peek[i], peek[earliest]) < 0) { |
| earliest = i; |
| } |
| } |
| @@ -1215,17 +1254,31 @@ namespace LogDog { |
| // Get the next log from the earliest stream. |
| let next = buffers[earliest].next(); |
| if (next) { |
| - entries.push(next); |
| + last = next; |
| + entries.push(last); |
| } |
| // Repopulate that buffer's "peek" value. If the buffer has no more |
| - // entries, then we're done. |
| + // entries, then we're done this round. |
| next = buffers[earliest].peek(); |
| if (!next) { |
| - return new BufferedLogs(entries); |
| + break; |
| } |
| peek[earliest] = next; |
| } |
| + |
| + // One or more of our buffers is exhausted. If we have the ability to load |
| + // implicit next logs, try and extract more using that. |
| + if (last && this.logSorter.implicitNext) { |
| + while (true) { |
| + last = this.logSorter.implicitNext(last, buffers); |
| + if (!last) { |
| + break; |
| + } |
| + entries.push(last); |
| + } |
| + } |
| + return new BufferedLogs(entries); |
| } |
| } |
| @@ -1287,6 +1340,14 @@ namespace LogDog { |
| } |
| /** |
| + * Returns a copy of the remaining logs in the buffer. |
| + * If there are no logs, an empty array will be returned. |
| + */ |
| + peekAll(): LogDog.LogEntry[] { |
| + return (this.logs || []).slice(0); |
| + } |
| + |
| + /** |
| * GetAll returns all logs in the buffer. Afterwards, the buffer will be |
| * empty. |
| */ |