Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/files/common/io.ts
3294 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { VSBuffer } from '../../../base/common/buffer.js';
7
import { CancellationToken } from '../../../base/common/cancellation.js';
8
import { canceled } from '../../../base/common/errors.js';
9
import { IDataTransformer, IErrorTransformer, WriteableStream } from '../../../base/common/stream.js';
10
import { URI } from '../../../base/common/uri.js';
11
import { localize } from '../../../nls.js';
12
import { createFileSystemProviderError, ensureFileSystemProviderError, IFileReadStreamOptions, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability } from './files.js';
13
14
export interface ICreateReadStreamOptions extends IFileReadStreamOptions {
15
16
/**
17
* The size of the buffer to use before sending to the stream.
18
*/
19
readonly bufferSize: number;
20
21
/**
22
* Allows to massage any possibly error that happens during reading.
23
*/
24
readonly errorTransformer?: IErrorTransformer;
25
}
26
27
/**
28
* A helper to read a file from a provider with open/read/close capability into a stream.
29
*/
30
export async function readFileIntoStream<T>(
31
provider: IFileSystemProviderWithOpenReadWriteCloseCapability,
32
resource: URI,
33
target: WriteableStream<T>,
34
transformer: IDataTransformer<VSBuffer, T>,
35
options: ICreateReadStreamOptions,
36
token: CancellationToken
37
): Promise<void> {
38
let error: Error | undefined = undefined;
39
40
try {
41
await doReadFileIntoStream(provider, resource, target, transformer, options, token);
42
} catch (err) {
43
error = err;
44
} finally {
45
if (error && options.errorTransformer) {
46
error = options.errorTransformer(error);
47
}
48
49
if (typeof error !== 'undefined') {
50
target.error(error);
51
}
52
53
target.end();
54
}
55
}
56
57
async function doReadFileIntoStream<T>(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, target: WriteableStream<T>, transformer: IDataTransformer<VSBuffer, T>, options: ICreateReadStreamOptions, token: CancellationToken): Promise<void> {
58
59
// Check for cancellation
60
throwIfCancelled(token);
61
62
// open handle through provider
63
const handle = await provider.open(resource, { create: false });
64
65
try {
66
67
// Check for cancellation
68
throwIfCancelled(token);
69
70
let totalBytesRead = 0;
71
let bytesRead = 0;
72
let allowedRemainingBytes = (options && typeof options.length === 'number') ? options.length : undefined;
73
74
let buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
75
76
let posInFile = options && typeof options.position === 'number' ? options.position : 0;
77
let posInBuffer = 0;
78
do {
79
// read from source (handle) at current position (pos) into buffer (buffer) at
80
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
81
bytesRead = await provider.read(handle, posInFile, buffer.buffer, posInBuffer, buffer.byteLength - posInBuffer);
82
83
posInFile += bytesRead;
84
posInBuffer += bytesRead;
85
totalBytesRead += bytesRead;
86
87
if (typeof allowedRemainingBytes === 'number') {
88
allowedRemainingBytes -= bytesRead;
89
}
90
91
// when buffer full, create a new one and emit it through stream
92
if (posInBuffer === buffer.byteLength) {
93
await target.write(transformer(buffer));
94
95
buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
96
97
posInBuffer = 0;
98
}
99
} while (bytesRead > 0 && (typeof allowedRemainingBytes !== 'number' || allowedRemainingBytes > 0) && throwIfCancelled(token) && throwIfTooLarge(totalBytesRead, options));
100
101
// wrap up with last buffer (also respect maxBytes if provided)
102
if (posInBuffer > 0) {
103
let lastChunkLength = posInBuffer;
104
if (typeof allowedRemainingBytes === 'number') {
105
lastChunkLength = Math.min(posInBuffer, allowedRemainingBytes);
106
}
107
108
target.write(transformer(buffer.slice(0, lastChunkLength)));
109
}
110
} catch (error) {
111
throw ensureFileSystemProviderError(error);
112
} finally {
113
await provider.close(handle);
114
}
115
}
116
117
function throwIfCancelled(token: CancellationToken): boolean {
118
if (token.isCancellationRequested) {
119
throw canceled();
120
}
121
122
return true;
123
}
124
125
function throwIfTooLarge(totalBytesRead: number, options: ICreateReadStreamOptions): boolean {
126
127
// Return early if file is too large to load and we have configured limits
128
if (typeof options?.limits?.size === 'number' && totalBytesRead > options.limits.size) {
129
throw createFileSystemProviderError(localize('fileTooLargeError', "File is too large to open"), FileSystemProviderErrorCode.FileTooLarge);
130
}
131
132
return true;
133
}
134
135