Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/vs/base/common/buffer.ts
13405 views
1
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'
2
3
/*---------------------------------------------------------------------------------------------
4
* Copyright (c) Microsoft Corporation. All rights reserved.
5
* Licensed under the MIT License. See License.txt in the project root for license information.
6
*--------------------------------------------------------------------------------------------*/
7
8
import { Lazy } from './lazy';
9
import * as streams from './stream';
10
11
interface NodeBuffer {
12
allocUnsafe(size: number): Uint8Array;
13
isBuffer(obj: unknown): obj is NodeBuffer;
14
from(arrayBuffer: ArrayBufferLike, byteOffset?: number, length?: number): Uint8Array;
15
from(data: string): Uint8Array;
16
}
17
18
declare const Buffer: NodeBuffer;
19
20
const hasBuffer = (typeof Buffer !== 'undefined');
21
const indexOfTable = new Lazy(() => new Uint8Array(256));
22
23
let textEncoder: { encode: (input: string) => Uint8Array } | null;
24
let textDecoder: { decode: (input: Uint8Array) => string } | null;
25
26
export class VSBuffer {
27
28
/**
29
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
30
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
31
*/
32
static alloc(byteLength: number): VSBuffer {
33
if (hasBuffer) {
34
return new VSBuffer(Buffer.allocUnsafe(byteLength));
35
} else {
36
return new VSBuffer(new Uint8Array(byteLength));
37
}
38
}
39
40
/**
41
* When running in a nodejs context, if `actual` is not a nodejs Buffer, the backing store for
42
* the returned `VSBuffer` instance might use a nodejs Buffer allocated from node's Buffer pool,
43
* which is not transferrable.
44
*/
45
static wrap(actual: Uint8Array): VSBuffer {
46
if (hasBuffer && !(Buffer.isBuffer(actual))) {
47
// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
48
// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array
49
actual = Buffer.from(actual.buffer, actual.byteOffset, actual.byteLength);
50
}
51
return new VSBuffer(actual);
52
}
53
54
/**
55
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
56
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
57
*/
58
static fromString(source: string, options?: { dontUseNodeBuffer?: boolean }): VSBuffer {
59
const dontUseNodeBuffer = options?.dontUseNodeBuffer || false;
60
if (!dontUseNodeBuffer && hasBuffer) {
61
return new VSBuffer(Buffer.from(source));
62
} else {
63
if (!textEncoder) {
64
textEncoder = new TextEncoder();
65
}
66
return new VSBuffer(textEncoder.encode(source));
67
}
68
}
69
70
/**
71
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
72
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
73
*/
74
static fromByteArray(source: number[]): VSBuffer {
75
const result = VSBuffer.alloc(source.length);
76
for (let i = 0, len = source.length; i < len; i++) {
77
result.buffer[i] = source[i];
78
}
79
return result;
80
}
81
82
/**
83
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
84
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
85
*/
86
static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
87
if (typeof totalLength === 'undefined') {
88
totalLength = 0;
89
for (let i = 0, len = buffers.length; i < len; i++) {
90
totalLength += buffers[i].byteLength;
91
}
92
}
93
94
const ret = VSBuffer.alloc(totalLength);
95
let offset = 0;
96
for (let i = 0, len = buffers.length; i < len; i++) {
97
const element = buffers[i];
98
ret.set(element, offset);
99
offset += element.byteLength;
100
}
101
102
return ret;
103
}
104
105
static isNativeBuffer(buffer: unknown): boolean {
106
return hasBuffer && Buffer.isBuffer(buffer);
107
}
108
109
readonly buffer: Uint8Array;
110
readonly byteLength: number;
111
112
private constructor(buffer: Uint8Array) {
113
this.buffer = buffer;
114
this.byteLength = this.buffer.byteLength;
115
}
116
117
/**
118
* When running in a nodejs context, the backing store for the returned `VSBuffer` instance
119
* might use a nodejs Buffer allocated from node's Buffer pool, which is not transferrable.
120
*/
121
clone(): VSBuffer {
122
const result = VSBuffer.alloc(this.byteLength);
123
result.set(this);
124
return result;
125
}
126
127
toString(): string {
128
if (hasBuffer) {
129
return this.buffer.toString();
130
} else {
131
if (!textDecoder) {
132
textDecoder = new TextDecoder(undefined, { ignoreBOM: true });
133
}
134
return textDecoder.decode(this.buffer);
135
}
136
}
137
138
slice(start?: number, end?: number): VSBuffer {
139
// IMPORTANT: use subarray instead of slice because TypedArray#slice
140
// creates shallow copy and NodeBuffer#slice doesn't. The use of subarray
141
// ensures the same, performance, behaviour.
142
return new VSBuffer(this.buffer.subarray(start, end));
143
}
144
145
set(array: VSBuffer, offset?: number): void;
146
set(array: Uint8Array, offset?: number): void;
147
set(array: ArrayBuffer, offset?: number): void;
148
set(array: ArrayBufferView, offset?: number): void;
149
set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void;
150
set(array: VSBuffer | Uint8Array | ArrayBuffer | ArrayBufferView, offset?: number): void {
151
if (array instanceof VSBuffer) {
152
this.buffer.set(array.buffer, offset);
153
} else if (array instanceof Uint8Array) {
154
this.buffer.set(array, offset);
155
} else if (array instanceof ArrayBuffer) {
156
this.buffer.set(new Uint8Array(array), offset);
157
} else if (ArrayBuffer.isView(array)) {
158
this.buffer.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), offset);
159
} else {
160
throw new Error(`Unknown argument 'array'`);
161
}
162
}
163
164
readUInt32BE(offset: number): number {
165
return readUInt32BE(this.buffer, offset);
166
}
167
168
writeUInt32BE(value: number, offset: number): void {
169
writeUInt32BE(this.buffer, value, offset);
170
}
171
172
readUInt32LE(offset: number): number {
173
return readUInt32LE(this.buffer, offset);
174
}
175
176
writeUInt32LE(value: number, offset: number): void {
177
writeUInt32LE(this.buffer, value, offset);
178
}
179
180
readUInt8(offset: number): number {
181
return readUInt8(this.buffer, offset);
182
}
183
184
writeUInt8(value: number, offset: number): void {
185
writeUInt8(this.buffer, value, offset);
186
}
187
188
indexOf(subarray: VSBuffer | Uint8Array, offset = 0) {
189
return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset);
190
}
191
192
equals(other: VSBuffer): boolean {
193
if (this === other) {
194
return true;
195
}
196
197
if (this.byteLength !== other.byteLength) {
198
return false;
199
}
200
201
return this.buffer.every((value, index) => value === other.buffer[index]);
202
}
203
}
204
205
/**
206
* Like String.indexOf, but works on Uint8Arrays.
207
* Uses the boyer-moore-horspool algorithm to be reasonably speedy.
208
*/
209
export function binaryIndexOf(haystack: Uint8Array, needle: Uint8Array, offset = 0): number {
210
const needleLen = needle.byteLength;
211
const haystackLen = haystack.byteLength;
212
213
if (needleLen === 0) {
214
return 0;
215
}
216
217
if (needleLen === 1) {
218
return haystack.indexOf(needle[0], offset);
219
}
220
221
if (needleLen > haystackLen - offset) {
222
return -1;
223
}
224
225
// find index of the subarray using boyer-moore-horspool algorithm
226
const table = indexOfTable.value;
227
table.fill(needle.length);
228
for (let i = 0; i < needle.length; i++) {
229
table[needle[i]] = needle.length - i - 1;
230
}
231
232
let i = offset + needle.length - 1;
233
let j = i;
234
let result = -1;
235
while (i < haystackLen) {
236
if (haystack[i] === needle[j]) {
237
if (j === 0) {
238
result = i;
239
break;
240
}
241
242
i--;
243
j--;
244
} else {
245
i += Math.max(needle.length - j, table[haystack[i]]);
246
j = needle.length - 1;
247
}
248
}
249
250
return result;
251
}
252
253
export function readUInt16LE(source: Uint8Array, offset: number): number {
254
return (
255
((source[offset + 0] << 0) >>> 0) |
256
((source[offset + 1] << 8) >>> 0)
257
);
258
}
259
260
export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {
261
destination[offset + 0] = (value & 0b11111111);
262
value = value >>> 8;
263
destination[offset + 1] = (value & 0b11111111);
264
}
265
266
export function readUInt32BE(source: Uint8Array, offset: number): number {
267
return (
268
source[offset] * 2 ** 24
269
+ source[offset + 1] * 2 ** 16
270
+ source[offset + 2] * 2 ** 8
271
+ source[offset + 3]
272
);
273
}
274
275
export function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void {
276
destination[offset + 3] = value;
277
value = value >>> 8;
278
destination[offset + 2] = value;
279
value = value >>> 8;
280
destination[offset + 1] = value;
281
value = value >>> 8;
282
destination[offset] = value;
283
}
284
285
export function readUInt32LE(source: Uint8Array, offset: number): number {
286
return (
287
((source[offset + 0] << 0) >>> 0) |
288
((source[offset + 1] << 8) >>> 0) |
289
((source[offset + 2] << 16) >>> 0) |
290
((source[offset + 3] << 24) >>> 0)
291
);
292
}
293
294
export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void {
295
destination[offset + 0] = (value & 0b11111111);
296
value = value >>> 8;
297
destination[offset + 1] = (value & 0b11111111);
298
value = value >>> 8;
299
destination[offset + 2] = (value & 0b11111111);
300
value = value >>> 8;
301
destination[offset + 3] = (value & 0b11111111);
302
}
303
304
export function readUInt8(source: Uint8Array, offset: number): number {
305
return source[offset];
306
}
307
308
export function writeUInt8(destination: Uint8Array, value: number, offset: number): void {
309
destination[offset] = value;
310
}
311
312
export interface VSBufferReadable extends streams.Readable<VSBuffer> { }
313
314
export interface VSBufferReadableStream extends streams.ReadableStream<VSBuffer> { }
315
316
export interface VSBufferWriteableStream extends streams.WriteableStream<VSBuffer> { }
317
318
export interface VSBufferReadableBufferedStream extends streams.ReadableBufferedStream<VSBuffer> { }
319
320
export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
321
return streams.consumeReadable<VSBuffer>(readable, chunks => VSBuffer.concat(chunks));
322
}
323
324
export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
325
return streams.toReadable<VSBuffer>(buffer);
326
}
327
328
export function streamToBuffer(stream: streams.ReadableStream<VSBuffer>): Promise<VSBuffer> {
329
return streams.consumeStream<VSBuffer>(stream, chunks => VSBuffer.concat(chunks));
330
}
331
332
export async function bufferedStreamToBuffer(bufferedStream: streams.ReadableBufferedStream<VSBuffer>): Promise<VSBuffer> {
333
if (bufferedStream.ended) {
334
return VSBuffer.concat(bufferedStream.buffer);
335
}
336
337
return VSBuffer.concat([
338
339
// Include already read chunks...
340
...bufferedStream.buffer,
341
342
// ...and all additional chunks
343
await streamToBuffer(bufferedStream.stream)
344
]);
345
}
346
347
export function bufferToStream(buffer: VSBuffer): streams.ReadableStream<VSBuffer> {
348
return streams.toStream<VSBuffer>(buffer, chunks => VSBuffer.concat(chunks));
349
}
350
351
export function streamToBufferReadableStream(stream: streams.ReadableStreamEvents<Uint8Array | string>): streams.ReadableStream<VSBuffer> {
352
return streams.transform<Uint8Array | string, VSBuffer>(stream, { data: data => typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data) }, chunks => VSBuffer.concat(chunks));
353
}
354
355
export function newWriteableBufferStream(options?: streams.WriteableStreamOptions): streams.WriteableStream<VSBuffer> {
356
return streams.newWriteableStream<VSBuffer>(chunks => VSBuffer.concat(chunks), options);
357
}
358
359
export function prefixedBufferReadable(prefix: VSBuffer, readable: VSBufferReadable): VSBufferReadable {
360
return streams.prefixedReadable(prefix, readable, chunks => VSBuffer.concat(chunks));
361
}
362
363
export function prefixedBufferStream(prefix: VSBuffer, stream: VSBufferReadableStream): VSBufferReadableStream {
364
return streams.prefixedStream(prefix, stream, chunks => VSBuffer.concat(chunks));
365
}
366
367
/** Decodes base64 to a uint8 array. URL-encoded and unpadded base64 is allowed. */
368
export function decodeBase64(encoded: string) {
369
let building = 0;
370
let remainder = 0;
371
let bufi = 0;
372
373
// The simpler way to do this is `Uint8Array.from(atob(str), c => c.charCodeAt(0))`,
374
// but that's about 10-20x slower than this function in current Chromium versions.
375
376
const buffer = new Uint8Array(Math.floor(encoded.length / 4 * 3));
377
const append = (value: number) => {
378
switch (remainder) {
379
case 3:
380
buffer[bufi++] = building | value;
381
remainder = 0;
382
break;
383
case 2:
384
buffer[bufi++] = building | (value >>> 2);
385
building = value << 6;
386
remainder = 3;
387
break;
388
case 1:
389
buffer[bufi++] = building | (value >>> 4);
390
building = value << 4;
391
remainder = 2;
392
break;
393
default:
394
building = value << 2;
395
remainder = 1;
396
}
397
};
398
399
for (let i = 0; i < encoded.length; i++) {
400
const code = encoded.charCodeAt(i);
401
// See https://datatracker.ietf.org/doc/html/rfc4648#section-4
402
// This branchy code is about 3x faster than an indexOf on a base64 char string.
403
if (code >= 65 && code <= 90) {
404
append(code - 65); // A-Z starts ranges from char code 65 to 90
405
} else if (code >= 97 && code <= 122) {
406
append(code - 97 + 26); // a-z starts ranges from char code 97 to 122, starting at byte 26
407
} else if (code >= 48 && code <= 57) {
408
append(code - 48 + 52); // 0-9 starts ranges from char code 48 to 58, starting at byte 52
409
} else if (code === 43 || code === 45) {
410
append(62); // "+" or "-" for URLS
411
} else if (code === 47 || code === 95) {
412
append(63); // "/" or "_" for URLS
413
} else if (code === 61) {
414
break; // "="
415
} else {
416
throw new SyntaxError(`Unexpected base64 character ${encoded[i]}`);
417
}
418
}
419
420
const unpadded = bufi;
421
while (remainder > 0) {
422
append(0);
423
}
424
425
// slice is needed to account for overestimation due to padding
426
return VSBuffer.wrap(buffer).slice(0, unpadded);
427
}
428
429
const base64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
430
const base64UrlSafeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
431
432
/** Encodes a buffer to a base64 string. */
433
export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = false) {
434
const dictionary = urlSafe ? base64UrlSafeAlphabet : base64Alphabet;
435
let output = '';
436
437
const remainder = buffer.byteLength % 3;
438
439
let i = 0;
440
for (; i < buffer.byteLength - remainder; i += 3) {
441
const a = buffer[i + 0];
442
const b = buffer[i + 1];
443
const c = buffer[i + 2];
444
445
output += dictionary[a >>> 2];
446
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
447
output += dictionary[(b << 2 | c >>> 6) & 0b111111];
448
output += dictionary[c & 0b111111];
449
}
450
451
if (remainder === 1) {
452
const a = buffer[i + 0];
453
output += dictionary[a >>> 2];
454
output += dictionary[(a << 4) & 0b111111];
455
if (padded) { output += '=='; }
456
} else if (remainder === 2) {
457
const a = buffer[i + 0];
458
const b = buffer[i + 1];
459
output += dictionary[a >>> 2];
460
output += dictionary[(a << 4 | b >>> 4) & 0b111111];
461
output += dictionary[(b << 2) & 0b111111];
462
if (padded) { output += '='; }
463
}
464
465
return output;
466
}
467
468
const hexChars = '0123456789abcdef';
469
export function encodeHex({ buffer }: VSBuffer): string {
470
let result = '';
471
for (let i = 0; i < buffer.length; i++) {
472
const byte = buffer[i];
473
result += hexChars[byte >>> 4];
474
result += hexChars[byte & 0x0f];
475
}
476
return result;
477
}
478
479
export function decodeHex(hex: string): VSBuffer {
480
if (hex.length % 2 !== 0) {
481
throw new SyntaxError('Hex string must have an even length');
482
}
483
const out = new Uint8Array(hex.length >> 1);
484
for (let i = 0; i < hex.length;) {
485
out[i >> 1] = (decodeHexChar(hex, i++) << 4) | decodeHexChar(hex, i++);
486
}
487
return VSBuffer.wrap(out);
488
}
489
490
function decodeHexChar(str: string, position: number) {
491
const s = str.charCodeAt(position);
492
if (s >= 48 && s <= 57) { // '0'-'9'
493
return s - 48;
494
} else if (s >= 97 && s <= 102) { // 'a'-'f'
495
return s - 87;
496
} else if (s >= 65 && s <= 70) { // 'A'-'F'
497
return s - 55;
498
} else {
499
throw new SyntaxError(`Invalid hex character at position ${position}`);
500
}
501
}
502
503