mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 15:14:02 -05:00
Stacked on #7850 This PR implements this aspect of the cutter layout algorithm, using exact subtree shapes instead of the full bounding box  Example 1:   Example 2:  
164 lines
6.3 KiB
TypeScript
164 lines
6.3 KiB
TypeScript
// Copyright (c) 2021, Compiler Explorer Authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import bigInt from 'big-integer';
|
|
import {addDigitSeparator} from '../shared/common-utils.js';
|
|
|
|
/**
|
|
* Get an image relative to `/public` in the source root.
|
|
*/
|
|
export function getStaticImage(filename: string, parent?: string) {
|
|
const root = window.staticRoot;
|
|
if (parent) {
|
|
return `${root}${parent}/${filename}`;
|
|
}
|
|
return `${root}${filename}`;
|
|
}
|
|
|
|
export function updateAndCalcTopBarHeight(domRoot: JQuery, topBar: JQuery, hideable: JQuery): number {
|
|
let topBarHeight = 0;
|
|
if (!topBar.hasClass('d-none')) {
|
|
hideable.show();
|
|
const topBarHeightMax = topBar.outerHeight(true) ?? 0;
|
|
hideable.hide();
|
|
const topBarHeightMin = topBar.outerHeight(true) ?? 0;
|
|
topBarHeight = topBarHeightMin;
|
|
if (topBarHeightMin === topBarHeightMax) {
|
|
hideable.show();
|
|
}
|
|
}
|
|
return topBarHeight;
|
|
}
|
|
|
|
export function formatDateTimeWithSpaces(d: Date) {
|
|
const t = (x: string) => x.slice(-2);
|
|
// Hopefully some day we can use the temporal api to make this less of a pain
|
|
return (
|
|
`${d.getFullYear()} ${t('0' + (d.getMonth() + 1))} ${t('0' + d.getDate())}` +
|
|
`${t('0' + d.getHours())} ${t('0' + d.getMinutes())} ${t('0' + d.getSeconds())}`
|
|
);
|
|
}
|
|
|
|
export function formatISODate(dt: Date, full = false) {
|
|
const month = '' + (dt.getUTCMonth() + 1);
|
|
const day = '' + dt.getUTCDate();
|
|
const hrs = '' + dt.getUTCHours();
|
|
const min = '' + dt.getUTCMinutes();
|
|
const today = new Date(Date.now());
|
|
if (full || dt.toDateString() === today.toDateString()) {
|
|
return (
|
|
dt.getUTCFullYear() +
|
|
'-' +
|
|
month.padStart(2, '0') +
|
|
'-' +
|
|
day.padStart(2, '0') +
|
|
' ' +
|
|
hrs.padStart(2, '0') +
|
|
':' +
|
|
min.padStart(2, '0')
|
|
);
|
|
}
|
|
return dt.getUTCFullYear() + '-' + month.padStart(2, '0') + '-' + day.padStart(2, '0');
|
|
}
|
|
|
|
const hexLike = /^(#?[$]|0x)([0-9a-fA-F]+)$/;
|
|
const hexLike2 = /^(#?)([0-9a-fA-F]+)H$/;
|
|
const decimalLike = /^(#?)(-?[0-9]+)$/;
|
|
const ptxFloat32 = /^0[fF]([0-9a-fA-F]{8})$/;
|
|
const ptxFloat64 = /^0[dD]([0-9a-fA-F]{16})$/;
|
|
|
|
function parseNumericValue(value: string): bigInt.BigInteger | null {
|
|
const hexMatch = hexLike.exec(value) || hexLike2.exec(value);
|
|
if (hexMatch) return bigInt(hexMatch[2], 16);
|
|
|
|
const hexMatchPTX = ptxFloat32.exec(value) ?? ptxFloat64.exec(value);
|
|
if (hexMatchPTX) return bigInt(hexMatchPTX[1], 16);
|
|
|
|
const decMatch = decimalLike.exec(value);
|
|
if (decMatch) return bigInt(decMatch[2]);
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getNumericToolTip(value: string, digitSeparator?: string): string | null {
|
|
const formatNumber = (num: bigInt.BigInteger, base: number, chunkSize: number) => {
|
|
const numberString = num.toString(base).toUpperCase();
|
|
if (digitSeparator !== undefined) {
|
|
return addDigitSeparator(numberString, digitSeparator, chunkSize);
|
|
}
|
|
return numberString;
|
|
};
|
|
const numericValue = parseNumericValue(value);
|
|
if (numericValue === null) return null;
|
|
|
|
// PTX floats
|
|
const view = new DataView(new ArrayBuffer(8));
|
|
view.setBigUint64(0, BigInt(numericValue.toString()), true);
|
|
if (ptxFloat32.test(value)) return view.getFloat32(0, true).toPrecision(9) + 'f';
|
|
if (ptxFloat64.test(value)) return view.getFloat64(0, true).toPrecision(17);
|
|
|
|
// Decimal representation.
|
|
let result = formatNumber(numericValue, 10, 3);
|
|
|
|
// Hexadecimal representation.
|
|
if (numericValue.isNegative()) {
|
|
const masked = bigInt('ffffffffffffffff', 16).and(numericValue);
|
|
result += ' = 0x' + formatNumber(masked, 16, 4);
|
|
} else {
|
|
result += ' = 0x' + formatNumber(numericValue, 16, 4);
|
|
}
|
|
|
|
// Float32/64 representation.
|
|
view.setBigUint64(0, BigInt(numericValue.toString()), true);
|
|
if (numericValue.bitLength().lesserOrEquals(32)) result += ' = ' + view.getFloat32(0, true).toPrecision(9) + 'f';
|
|
// only subnormal doubles and zero may have upper 32 bits all 0, assume unlikely to be double
|
|
else result += ' = ' + view.getFloat64(0, true).toPrecision(17);
|
|
|
|
// Printable UTF-8 characters.
|
|
const bytes = numericValue.isNegative()
|
|
? // bytes of negative number without sign extension
|
|
numericValue
|
|
.add(1)
|
|
.toArray(256)
|
|
.value.map(byte => byte ^ 0xff)
|
|
: numericValue.toArray(256).value;
|
|
// This assumes that `numericValue` is encoded as little-endian.
|
|
bytes.reverse();
|
|
const decoder = new TextDecoder('utf-8', {fatal: true});
|
|
try {
|
|
result += ' = ' + JSON.stringify(decoder.decode(Uint8Array.from(bytes)));
|
|
} catch {
|
|
// ignore `TypeError` when the number is not valid UTF-8
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// zip two arrays up until min(a.length, b.length)
|
|
export function* zip<T>(a: T[], b: T[]) {
|
|
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
yield [a[i], b[i]] as [T, T];
|
|
}
|
|
}
|