mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-02 06:35:09 -05:00
Utilities for PseudoBigInt
This commit is contained in:
@@ -5761,4 +5761,10 @@ namespace ts {
|
||||
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
}
|
||||
|
||||
/** Represents a bigint literal value without requiring bigint support */
|
||||
export interface PseudoBigInt {
|
||||
negative: boolean;
|
||||
base10Value: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8427,4 +8427,88 @@ namespace ts {
|
||||
return got;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove once tslint allows tsconfig.json to include lib "esnext.bigint"
|
||||
/* @internal */
|
||||
declare const BigInt: ((value: string) => number) | undefined;
|
||||
|
||||
/**
|
||||
* Converts a bigint literal string, e.g. `0x1234n`,
|
||||
* to its decimal string representation, e.g. `4660`.
|
||||
*/
|
||||
export function parsePseudoBigInt(stringValue: string): string {
|
||||
// Use native BigInt if available
|
||||
if (typeof BigInt !== "undefined") {
|
||||
return "" + BigInt(stringValue.slice(0, -1)); // omit trailing "n"
|
||||
}
|
||||
|
||||
let log2Base: number;
|
||||
switch (stringValue.charCodeAt(1)) { // "x" in "0x123"
|
||||
case CharacterCodes.b:
|
||||
case CharacterCodes.B: // 0b or 0B
|
||||
log2Base = 1;
|
||||
break;
|
||||
case CharacterCodes.o:
|
||||
case CharacterCodes.O: // 0o or 0O
|
||||
log2Base = 3;
|
||||
break;
|
||||
case CharacterCodes.x:
|
||||
case CharacterCodes.X: // 0x or 0X
|
||||
log2Base = 4;
|
||||
break;
|
||||
default: // already in decimal; omit trailing "n"
|
||||
const nIndex = stringValue.length - 1;
|
||||
// Skip leading 0s
|
||||
let nonZeroStart = 0;
|
||||
while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) {
|
||||
nonZeroStart++;
|
||||
}
|
||||
return stringValue.slice(nonZeroStart, nIndex) || "0";
|
||||
}
|
||||
|
||||
// Omit leading "0b", "0o", or "0x", and trailing "n"
|
||||
const startIndex = 2, endIndex = stringValue.length - 1;
|
||||
const bitsNeeded = (endIndex - startIndex) * log2Base;
|
||||
// Stores the value specified by the string as a LE array of 16-bit integers
|
||||
// using Uint16 instead of Uint32 so combining steps can use bitwise operators
|
||||
const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0));
|
||||
// Add the digits, one at a time
|
||||
for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) {
|
||||
const segment = bitOffset >>> 4;
|
||||
const digitChar = stringValue.charCodeAt(i);
|
||||
// Find character range: 0-9 < A-F < a-f
|
||||
const digit = digitChar <= CharacterCodes._9
|
||||
? digitChar - CharacterCodes._0
|
||||
: 10 + digitChar -
|
||||
(digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a);
|
||||
const shiftedDigit = digit << (bitOffset & 15);
|
||||
segments[segment] |= shiftedDigit;
|
||||
const residual = shiftedDigit >>> 16;
|
||||
if (residual) segments[segment + 1] |= residual; // overflows segment
|
||||
}
|
||||
// Repeatedly divide segments by 10 and add remainder to base10Value
|
||||
let base10Value = "";
|
||||
let firstNonzeroSegment = segments.length - 1;
|
||||
let segmentsRemaining = true;
|
||||
while (segmentsRemaining) {
|
||||
let mod10 = 0;
|
||||
segmentsRemaining = false;
|
||||
for (let segment = firstNonzeroSegment; segment >= 0; segment--) {
|
||||
const newSegment = mod10 << 16 | segments[segment];
|
||||
const segmentValue = (newSegment / 10) | 0;
|
||||
segments[segment] = segmentValue;
|
||||
mod10 = newSegment - segmentValue * 10;
|
||||
if (segmentValue && !segmentsRemaining) {
|
||||
firstNonzeroSegment = segment;
|
||||
segmentsRemaining = true;
|
||||
}
|
||||
}
|
||||
base10Value = mod10 + base10Value;
|
||||
}
|
||||
return base10Value;
|
||||
}
|
||||
|
||||
export function pseudoBigIntToString({negative, base10Value}: PseudoBigInt): string {
|
||||
return (negative && base10Value !== "0" ? "-" : "") + base10Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"unittests/matchFiles.ts",
|
||||
"unittests/moduleResolution.ts",
|
||||
"unittests/organizeImports.ts",
|
||||
"unittests/parsePseudoBigInt.ts",
|
||||
"unittests/paths.ts",
|
||||
"unittests/printer.ts",
|
||||
"unittests/programMissingFiles.ts",
|
||||
|
||||
71
src/testRunner/unittests/parsePseudoBigInt.ts
Normal file
71
src/testRunner/unittests/parsePseudoBigInt.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
namespace ts {
|
||||
describe("BigInt literal base conversions", () => {
|
||||
describe("parsePseudoBigInt", () => {
|
||||
const testNumbers: number[] = [];
|
||||
for (let i = 0; i < 1e3; i++) testNumbers.push(i);
|
||||
for (let bits = 0; bits <= 52; bits++) {
|
||||
testNumbers.push(2 ** bits, 2 ** bits - 1);
|
||||
}
|
||||
it("can strip base-10 strings", () => {
|
||||
for (const testNumber of testNumbers) {
|
||||
for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) {
|
||||
assert.equal(
|
||||
parsePseudoBigInt("0".repeat(leadingZeros) + testNumber + "n"),
|
||||
String(testNumber)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
it("can parse binary literals", () => {
|
||||
for (const testNumber of testNumbers) {
|
||||
for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) {
|
||||
const binary = "0".repeat(leadingZeros) + testNumber.toString(2) + "n";
|
||||
for (const prefix of ["0b", "0B"]) {
|
||||
assert.equal(parsePseudoBigInt(prefix + binary), String(testNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
it("can parse octal literals", () => {
|
||||
for (const testNumber of testNumbers) {
|
||||
for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) {
|
||||
const octal = "0".repeat(leadingZeros) + testNumber.toString(8) + "n";
|
||||
for (const prefix of ["0o", "0O"]) {
|
||||
assert.equal(parsePseudoBigInt(prefix + octal), String(testNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
it("can parse hex literals", () => {
|
||||
for (const testNumber of testNumbers) {
|
||||
for (let leadingZeros = 0; leadingZeros < 10; leadingZeros++) {
|
||||
const hex = "0".repeat(leadingZeros) + testNumber.toString(16) + "n";
|
||||
for (const prefix of ["0x", "0X"]) {
|
||||
for (const hexCase of [hex.toLowerCase(), hex.toUpperCase()]) {
|
||||
assert.equal(parsePseudoBigInt(prefix + hexCase), String(testNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
it("can parse large literals", () => {
|
||||
assert.equal(
|
||||
parsePseudoBigInt("123456789012345678901234567890n"),
|
||||
"123456789012345678901234567890"
|
||||
);
|
||||
assert.equal(
|
||||
parsePseudoBigInt("0b1100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010n"),
|
||||
"123456789012345678901234567890"
|
||||
);
|
||||
assert.equal(
|
||||
parsePseudoBigInt("0o143564417755415637016711617605322n"),
|
||||
"123456789012345678901234567890"
|
||||
);
|
||||
assert.equal(
|
||||
parsePseudoBigInt("0x18ee90ff6c373e0ee4e3f0ad2n"),
|
||||
"123456789012345678901234567890"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -3013,6 +3013,11 @@ declare namespace ts {
|
||||
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
}
|
||||
/** Represents a bigint literal value without requiring bigint support */
|
||||
interface PseudoBigInt {
|
||||
negative: boolean;
|
||||
base10Value: string;
|
||||
}
|
||||
}
|
||||
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
|
||||
declare function clearTimeout(handle: any): void;
|
||||
|
||||
@@ -3013,6 +3013,11 @@ declare namespace ts {
|
||||
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
|
||||
readonly allowTextChangesInNewFiles?: boolean;
|
||||
}
|
||||
/** Represents a bigint literal value without requiring bigint support */
|
||||
interface PseudoBigInt {
|
||||
negative: boolean;
|
||||
base10Value: string;
|
||||
}
|
||||
}
|
||||
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
|
||||
declare function clearTimeout(handle: any): void;
|
||||
|
||||
Reference in New Issue
Block a user