chrome-devtools-frontend
Version:
Chrome DevTools UI
100 lines (84 loc) • 2.51 kB
text/typescript
// Copyright 2025 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.
/**
* @fileoverview
*
* VLQ implementation taken mostly verbatim from Chrome DevTools itself.
*/
const BASE64_CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const BASE64_CODES = new Uint8Array(123);
for (let index = 0; index < BASE64_CHARS.length; ++index) {
BASE64_CODES[BASE64_CHARS.charCodeAt(index)] = index;
}
const VLQ_BASE_SHIFT = 5;
const VLQ_BASE_MASK = (1 << 5) - 1;
const VLQ_CONTINUATION_MASK = 1 << 5;
export function encodeSigned(n: number): string {
// Set the sign bit as the least significant bit.
n = n >= 0 ? 2 * n : 1 - 2 * n;
return encodeUnsigned(n);
}
export function encodeUnsigned(n: number): string {
// Encode into a base64 run.
let result = "";
while (true) {
// Extract the lowest 5 bits and remove them from the number.
const digit = n & 0x1f;
n >>>= 5;
// Is there anything more left to encode?
if (n === 0) {
// We are done encoding, finish the run.
result += BASE64_CHARS[digit];
break;
} else {
// There is still more encode, so add the digit and the continuation bit.
result += BASE64_CHARS[0x20 + digit];
}
}
return result;
}
export class TokenIterator {
readonly #string: string;
#position: number;
constructor(string: string) {
this.#string = string;
this.#position = 0;
}
nextChar(): string {
return this.#string.charAt(this.#position++);
}
/** Returns the unicode value of the next character and advances the iterator */
nextCharCode(): number {
return this.#string.charCodeAt(this.#position++);
}
peek(): string {
return this.#string.charAt(this.#position);
}
hasNext(): boolean {
return this.#position < this.#string.length;
}
nextSignedVLQ(): number {
let result = this.nextUnsignedVLQ();
// Fix the sign.
const negative = result & 1;
result >>>= 1;
return negative ? -result : result;
}
nextUnsignedVLQ(): number {
let result = 0;
let shift = 0;
let digit = 0;
do {
const charCode = this.nextCharCode();
digit = BASE64_CODES[charCode];
result += (digit & VLQ_BASE_MASK) << shift;
shift += VLQ_BASE_SHIFT;
} while (digit & VLQ_CONTINUATION_MASK);
return result;
}
currentChar(): string {
return this.#string.charAt(this.#position - 1);
}
}