Path: blob/main/extensions/copilot/src/util/vs/base/common/buffer.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import { Lazy } from './lazy';8import * as streams from './stream';910interface NodeBuffer {11allocUnsafe(size: number): Uint8Array;12isBuffer(obj: unknown): obj is NodeBuffer;13from(arrayBuffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;14from(data: string): Uint8Array;15}1617declare const Buffer: NodeBuffer;1819const hasBuffer = (typeof Buffer !== 'undefined');20const indexOfTable = new Lazy(() => new Uint8Array(256));2122let textEncoder: { encode: (input: string) => Uint8Array } | null;23let textDecoder: { decode: (input: Uint8Array) => string } | null;2425export class VSBuffer {2627/**28* When running in a nodejs context, the backing store for the returned `VSBuffer` instance29* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.30*/31static alloc(byteLength: number): VSBuffer {32if (hasBuffer) {33return new VSBuffer(Buffer.allocUnsafe(byteLength));34} else {35return new VSBuffer(new Uint8Array(byteLength));36}37}3839/**40* When running in a nodejs context, if `actual` is not a nodejs Buffer, the backing store for41* the returned `VSBuffer` instance might use a nodejs Buffer allocated from node's Buffer pool,42* which is not transferrable.43*/44static wrap(actual: Uint8Array): VSBuffer {45if (hasBuffer && !(Buffer.isBuffer(actual))) {46// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length47// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array48actual = Buffer.from(actual.buffer, actual.byteOffset, actual.byteLength);49}50return new VSBuffer(actual);51}5253/**54* When running in a nodejs context, the backing store for the returned `VSBuffer` instance55* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.56*/57static fromString(source: string, options?: { dontUseNodeBuffer?: boolean }): VSBuffer {58const dontUseNodeBuffer = options?.dontUseNodeBuffer || false;59if (!dontUseNodeBuffer && hasBuffer) {60return new VSBuffer(Buffer.from(source));61} else {62if (!textEncoder) {63textEncoder = new TextEncoder();64}65return new VSBuffer(textEncoder.encode(source));66}67}6869/**70* When running in a nodejs context, the backing store for the returned `VSBuffer` instance71* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.72*/73static fromByteArray(source: number[]): VSBuffer {74const result = VSBuffer.alloc(source.length);75for (let i = 0, len = source.length; i < len; i++) {76result.buffer[i] = source[i];77}78return result;79}8081/**82* When running in a nodejs context, the backing store for the returned `VSBuffer` instance83* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.84*/85static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {86if (typeof totalLength === 'undefined') {87totalLength = 0;88for (let i = 0, len = buffers.length; i < len; i++) {89totalLength += buffers[i].byteLength;90}91}9293const ret = VSBuffer.alloc(totalLength);94let offset = 0;95for (let i = 0, len = buffers.length; i < len; i++) {96const element = buffers[i];97ret.set(element, offset);98offset += element.byteLength;99}100101return ret;102}103104static isNativeBuffer(buffer: unknown): boolean {105return hasBuffer && Buffer.isBuffer(buffer);106}107108readonly buffer: Uint8Array;109readonly byteLength: number;110111private constructor(buffer: Uint8Array) {112this.buffer = buffer;113this.byteLength = this.buffer.byteLength;114}115116/**117* When running in a nodejs context, the backing store for the returned `VSBuffer` instance118* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.119*/120clone(): VSBuffer {121const result = VSBuffer.alloc(this.byteLength);122result.set(this);123return result;124}125126toString(): string {127if (hasBuffer) {128return this.buffer.toString();129} else {130if (!textDecoder) {131textDecoder = new TextDecoder(undefined, { ignoreBOM: true });132}133return textDecoder.decode(this.buffer);134}135}136137slice(start?: number, end?: number): VSBuffer {138// IMPORTANT: use subarray instead of slice because TypedArray#slice139// creates shallow copy and NodeBuffer#slice doesn't. The use of subarray140// ensures the same, performance, behaviour.141return new VSBuffer(this.buffer.subarray(start, end));142}143144set(array: VSBuffer, offset?: number): void;145set(array: Uint8Array, offset?: number): void;146set(array: ArrayBuffer, offset?: number): void;147set(array: ArrayBufferView, offset?: number): void;148set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void;149set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void {150if (array instanceof VSBuffer) {151this.buffer.set(array.buffer, offset);152} else if (array instanceof Uint8Array) {153this.buffer.set(array, offset);154} else if (array instanceof ArrayBuffer) {155this.buffer.set(new Uint8Array(array), offset);156} else if (ArrayBuffer.isView(array)) {157this.buffer.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), offset);158} else {159throw new Error(`Unknown argument 'array'`);160}161}162163readUInt32BE(offset: number): number {164return readUInt32BE(this.buffer, offset);165}166167writeUInt32BE(value: number, offset: number): void {168writeUInt32BE(this.buffer, value, offset);169}170171readUInt32LE(offset: number): number {172return readUInt32LE(this.buffer, offset);173}174175writeUInt32LE(value: number, offset: number): void {176writeUInt32LE(this.buffer, value, offset);177}178179readUInt8(offset: number): number {180return readUInt8(this.buffer, offset);181}182183writeUInt8(value: number, offset: number): void {184writeUInt8(this.buffer, value, offset);185}186187indexOf(subarray: VSBuffer | Uint8Array, offset = 0) {188return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset);189}190191equals(other: VSBuffer): boolean {192if (this === other) {193return true;194}195196if (this.byteLength !== other.byteLength) {197return false;198}199200return this.buffer.every((value, index) => value === other.buffer[index]);201}202}203204/**205* Like String.indexOf, but works on Uint8Arrays.206* Uses the boyer-moore-horspool algorithm to be reasonably speedy.207*/208export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset = 0): number {209const needleLen = needle.byteLength;210const haystackLen = haystack.byteLength;211212if (needleLen === 0) {213return 0;214}215216if (needleLen === 1) {217return haystack.indexOf(needle[0], offset);218}219220if (needleLen > haystackLen - offset) {221return -1;222}223224// find index of the subarray using boyer-moore-horspool algorithm225const table = indexOfTable.value;226table.fill(needle.length);227for (let i = 0; i < needle.length; i++) {228table[needle[i]] = needle.length - i - 1;229}230231let i = offset + needle.length - 1;232let j = i;233let result = -1;234while (i < haystackLen) {235if (haystack[i] === needle[j]) {236if (j === 0) {237result = i;238break;239}240241i--;242j--;243} else {244i += Math.max(needle.length - j, table[haystack[i]]);245j = needle.length - 1;246}247}248249return result;250}251252export function readUInt16LE(source: Uint8Array, offset: number): number {253return (254((source[offset + 0] << 0) >>> 0) |255((source[offset + 1] << 8) >>> 0)256);257}258259export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {260destination[offset + 0] = (value & 0b11111111);261value = value >>> 8;262destination[offset + 1] = (value & 0b11111111);263}264265export function readUInt32BE(source: Uint8Array, offset: number): number {266return (267source[offset] * 2 ** 24268+ source[offset + 1] * 2 ** 16269+ source[offset + 2] * 2 ** 8270+ source[offset + 3]271);272}273274export function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void {275destination[offset + 3] = value;276value = value >>> 8;277destination[offset + 2] = value;278value = value >>> 8;279destination[offset + 1] = value;280value = value >>> 8;281destination[offset] = value;282}283284export function readUInt32LE(source: Uint8Array, offset: number): number {285return (286((source[offset + 0] << 0) >>> 0) |287((source[offset + 1] << 8) >>> 0) |288((source[offset + 2] << 16) >>> 0) |289((source[offset + 3] << 24) >>> 0)290);291}292293export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void {294destination[offset + 0] = (value & 0b11111111);295value = value >>> 8;296destination[offset + 1] = (value & 0b11111111);297value = value >>> 8;298destination[offset + 2] = (value & 0b11111111);299value = value >>> 8;300destination[offset + 3] = (value & 0b11111111);301}302303export function readUInt8(source: Uint8Array, offset: number): number {304return source[offset];305}306307export function writeUInt8(destination: Uint8Array, value: number, offset: number): void {308destination[offset] = value;309}310311export interface VSBufferReadable extends streams.Readable<VSBuffer> { }312313export interface VSBufferReadableStream extends streams.ReadableStream<VSBuffer> { }314315export interface VSBufferWriteableStream extends streams.WriteableStream<VSBuffer> { }316317export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream<VSBuffer> { }318319export function readableToBuffer(readable: VSBufferReadable): VSBuffer {320return streams.consumeReadable<VSBuffer>(readable, chunks => VSBuffer.concat(chunks));321}322323export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {324return streams.toReadable<VSBuffer>(buffer);325}326327export function streamToBuffer(stream: streams.ReadableStream<VSBuffer>): Promise<VSBuffer> {328return streams.consumeStream<VSBuffer>(stream, chunks => VSBuffer.concat(chunks));329}330331export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream<VSBuffer>): Promise<VSBuffer> {332if (bufferedStream.ended) {333return VSBuffer.concat(bufferedStream.buffer);334}335336return VSBuffer.concat([337338// Include already read chunks...339...bufferedStream.buffer,340341// ...and all additional chunks342await streamToBuffer(bufferedStream.stream)343]);344}345346export function bufferToStream(buffer: VSBuffer): streams.ReadableStream<VSBuffer> {347return streams.toStream<VSBuffer>(buffer, chunks => VSBuffer.concat(chunks));348}349350export function streamToBufferReadableStream(stream: streams.ReadableStreamEvents<Uint8Array | string>): streams.ReadableStream<VSBuffer> {351return streams.transform<Uint8Array | string, VSBuffer>(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks));352}353354export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {355return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);356}357358export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReadable): VSBufferReadable {359return streams.prefixedReadable(prefix, readable, chunks => VSBuffer.concat(chunks));360}361362export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {363return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));364}365366/** Decodes base64 to a uint8 array. URL-encoded and unpadded base64 is allowed. */367export function decodeBase64(encoded: string) {368let building = 0;369let remainder = 0;370let bufi = 0;371372// The simpler way to do this is `Uint8Array.from(atob(str), c => c.charCodeAt(0))`,373// but that's about 10-20x slower than this function in current Chromium versions.374375const buffer = new Uint8Array(Math.floor(encoded.length / 4 * 3));376const append = (value: number) => {377switch (remainder) {378case 3:379buffer[bufi++] = building | value;380remainder = 0;381break;382case 2:383buffer[bufi++] = building | (value >>> 2);384building = value << 6;385remainder = 3;386break;387case 1:388buffer[bufi++] = building | (value >>> 4);389building = value << 4;390remainder = 2;391break;392default:393building = value << 2;394remainder = 1;395}396};397398for (let i = 0; i < encoded.length; i++) {399const code = encoded.charCodeAt(i);400// See https://datatracker.ietf.org/doc/html/rfc4648#section-4401// This branchy code is about 3x faster than an indexOf on a base64 char string.402if (code >= 65 && code <= 90) {403append(code - 65); // A-Z starts ranges from char code 65 to 90404} else if (code >= 97 && code <= 122) {405append(code - 97 + 26); // a-z starts ranges from char code 97 to 122, starting at byte 26406} else if (code >= 48 && code <= 57) {407append(code - 48 + 52); // 0-9 starts ranges from char code 48 to 58, starting at byte 52408} else if (code === 43 || code === 45) {409append(62); // "+" or "-" for URLS410} else if (code === 47 || code === 95) {411append(63); // "/" or "_" for URLS412} else if (code === 61) {413break; // "="414} else {415throw new SyntaxError(`Unexpected base64 character ${encoded[i]}`);416}417}418419const unpadded = bufi;420while (remainder > 0) {421append(0);422}423424// slice is needed to account for overestimation due to padding425return VSBuffer.wrap(buffer).slice(0, unpadded);426}427428const base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';429const base64UrlSafeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';430431/** Encodes a buffer to a base64 string. */432export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = false) {433const dictionary = urlSafe ? base64UrlSafeAlphabet : base64Alphabet;434let output = '';435436const remainder = buffer.byteLength % 3;437438let i = 0;439for (; i < buffer.byteLength - remainder; i += 3) {440const a = buffer[i + 0];441const b = buffer[i + 1];442const c = buffer[i + 2];443444output += dictionary[a >>> 2];445output += dictionary[(a << 4 | b >>> 4) & 0b111111];446output += dictionary[(b << 2 | c >>> 6) & 0b111111];447output += dictionary[c & 0b111111];448}449450if (remainder === 1) {451const a = buffer[i + 0];452output += dictionary[a >>> 2];453output += dictionary[(a << 4) & 0b111111];454if (padded) { output += '=='; }455} else if (remainder === 2) {456const a = buffer[i + 0];457const b = buffer[i + 1];458output += dictionary[a >>> 2];459output += dictionary[(a << 4 | b >>> 4) & 0b111111];460output += dictionary[(b << 2) & 0b111111];461if (padded) { output += '='; }462}463464return output;465}466467const hexChars = '0123456789abcdef';468export function encodeHex({ buffer }: VSBuffer): string {469let result = '';470for (let i = 0; i < buffer.length; i++) {471const byte = buffer[i];472result += hexChars[byte >>> 4];473result += hexChars[byte & 0x0f];474}475return result;476}477478export function decodeHex(hex: string): VSBuffer {479if (hex.length % 2 !== 0) {480throw new SyntaxError('Hex string must have an even length');481}482const out = new Uint8Array(hex.length >> 1);483for (let i = 0; i < hex.length;) {484out[i >> 1] = (decodeHexChar(hex, i++) << 4) | decodeHexChar(hex, i++);485}486return VSBuffer.wrap(out);487}488489function decodeHexChar(str: string, position: number) {490const s = str.charCodeAt(position);491if (s >= 48 && s <= 57) { // '0'-'9'492return s - 48;493} else if (s >= 97 && s <= 102) { // 'a'-'f'494return s - 87;495} else if (s >= 65 && s <= 70) { // 'A'-'F'496return s - 55;497} else {498throw new SyntaxError(`Invalid hex character at position ${position}`);499}500}501502503