blob: a96778ddaec3791442ec286c99c9b34afedfe055 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {assertTrue} from '../base/logging';
import {ColumnType} from './query_result';
// TODO(hjd): Combine with timeToCode.
export function timeToString(sec: number) {
const units = ['s', 'ms', 'us', 'ns'];
const sign = Math.sign(sec);
let n = Math.abs(sec);
let u = 0;
while (n < 1 && n !== 0 && u < units.length - 1) {
n *= 1000;
u++;
}
return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`;
}
export function tpTimeToString(time: TPTime) {
// TODO(stevegolton): Write a formatter to format bigint timestamps natively.
return timeToString(tpTimeToSeconds(time));
}
export function fromNs(ns: number) {
return ns / 1e9;
}
export function toNsFloor(seconds: number) {
return Math.floor(seconds * 1e9);
}
export function toNsCeil(seconds: number) {
return Math.ceil(seconds * 1e9);
}
export function toNs(seconds: number) {
return Math.round(seconds * 1e9);
}
// 1000000023ns -> "1.000 000 023"
export function formatTimestamp(sec: number) {
const parts = sec.toFixed(9).split('.');
parts[1] = parts[1].replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
return parts.join('.');
}
export function formatTPTime(time: TPTime) {
// TODO(stevegolton): Write a formatter to format bigint timestamps natively.
return formatTimestamp(tpTimeToSeconds(time));
}
// TODO(hjd): Rename to formatTimestampWithUnits
// 1000000023ns -> "1s 23ns"
export function timeToCode(sec: number): string {
let result = '';
let ns = Math.round(sec * 1e9);
if (ns < 1) return '0s';
const unitAndValue = [
['m', 60000000000],
['s', 1000000000],
['ms', 1000000],
['us', 1000],
['ns', 1],
];
unitAndValue.forEach((pair) => {
const unit = pair[0] as string;
const val = pair[1] as number;
if (ns >= val) {
const i = Math.floor(ns / val);
ns -= i * val;
result += i.toLocaleString() + unit + ' ';
}
});
return result.slice(0, -1);
}
export function tpTimeToCode(time: TPTime) {
// TODO(stevegolton): Write a formatter to format bigint timestamps natively.
return timeToCode(tpTimeToSeconds(time));
}
export function currentDateHourAndMinute(): string {
const date = new Date();
return `${date.toISOString().substr(0, 10)}-${date.getHours()}-${
date.getMinutes()}`;
}
// Aliased "Trace Processor" time and duration types.
// Note(stevegolton): While it might be nice to type brand these in the future,
// for now we're going to keep things simple. We do a lot of maths with these
// timestamps and type branding requires a lot of jumping through hoops to
// coerse the type back to the correct format.
export type TPTime = bigint;
export type TPDuration = bigint;
export function tpTimeFromNanos(nanos: number): TPTime {
return BigInt(Math.floor(nanos));
}
export function tpTimeFromSeconds(seconds: number): TPTime {
return BigInt(Math.floor(seconds * 1e9));
}
export function tpTimeToNanos(time: TPTime): number {
return Number(time);
}
export function tpTimeToMillis(time: TPTime): number {
return Number(time) / 1e6;
}
export function tpTimeToSeconds(time: TPTime): number {
return Number(time) / 1e9;
}
// Create a TPTime from an arbitrary SQL value.
// Throws if the value cannot be reasonably converted to a bigint.
// Assumes value is in nanoseconds.
export function tpTimeFromSql(value: ColumnType): TPTime {
if (typeof value === 'bigint') {
return value;
} else if (typeof value === 'number') {
return tpTimeFromNanos(value);
} else if (value === null) {
return 0n;
} else {
throw Error(`Refusing to create Timestamp from unrelated type ${value}`);
}
}
export function tpDurationToSeconds(dur: TPDuration): number {
return tpTimeToSeconds(dur);
}
export function tpDurationToNanos(dur: TPDuration): number {
return tpTimeToSeconds(dur);
}
export function tpDurationFromNanos(nanos: number): TPDuration {
return tpTimeFromNanos(nanos);
}
export function tpDurationFromSql(nanos: ColumnType): TPDuration {
return tpTimeFromSql(nanos);
}
export interface Span<Unit, Duration = Unit> {
get start(): Unit;
get end(): Unit;
get duration(): Duration;
get midpoint(): Unit;
contains(span: Unit|Span<Unit, Duration>): boolean;
intersects(x: Span<Unit>): boolean;
equals(span: Span<Unit, Duration>): boolean;
add(offset: Duration): Span<Unit, Duration>;
pad(padding: Duration): Span<Unit, Duration>;
}
export class TPTimeSpan implements Span<TPTime, TPDuration> {
readonly start: TPTime;
readonly end: TPTime;
constructor(start: TPTime, end: TPTime) {
assertTrue(
start <= end,
`Span start [${start}] cannot be greater than end [${end}]`);
this.start = start;
this.end = end;
}
get duration(): TPDuration {
return this.end - this.start;
}
get midpoint(): TPTime {
return (this.start + this.end) / 2n;
}
contains(x: TPTime|Span<TPTime, TPDuration>): boolean {
if (typeof x === 'bigint') {
return this.start <= x && x < this.end;
} else {
return this.start <= x.start && x.end <= this.end;
}
}
intersects(x: Span<TPTime, TPDuration>): boolean {
return !(x.end <= this.start || x.start >= this.end);
}
equals(span: Span<TPTime, TPDuration>): boolean {
return this.start === span.start && this.end === span.end;
}
add(x: TPTime): Span<TPTime, TPDuration> {
return new TPTimeSpan(this.start + x, this.end + x);
}
pad(padding: TPDuration): Span<TPTime, TPDuration> {
return new TPTimeSpan(this.start - padding, this.end + padding);
}
}