mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-19 14:49:48 +01:00
Refactor stream.ts
This commit is contained in:
@@ -91,14 +91,5 @@ export function detectEncodingByBOMFromBuffer(buffer: NodeBuffer, bytesRead: num
|
||||
* If no BOM is detected, null will be passed to callback.
|
||||
*/
|
||||
export function detectEncodingByBOM(file: string): TPromise<string> {
|
||||
return new TPromise((complete, error) => {
|
||||
stream.readExactlyByFile(file, 3, (err: Error, buffer: NodeBuffer, bytesRead: number) => {
|
||||
if (err) {
|
||||
error(err);
|
||||
} else {
|
||||
complete(detectEncodingByBOMFromBuffer(buffer, bytesRead));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return stream.readExactlyByFile(file, 3).then(({buffer, bytesRead}) => detectEncodingByBOMFromBuffer(buffer, bytesRead));
|
||||
}
|
||||
@@ -59,37 +59,14 @@ export interface IMimeAndEncoding {
|
||||
}
|
||||
|
||||
function doDetectMimesFromStream(instream: streams.Readable): TPromise<IMimeAndEncoding> {
|
||||
return new TPromise((complete, error) =>
|
||||
stream.readExactlyByStream(instream, BUFFER_READ_MAX_LEN, (err, buffer, bytesRead) => {
|
||||
if (err) {
|
||||
error(error);
|
||||
} else {
|
||||
complete(detectMimeAndEncodingFromBuffer({ buffer, bytesRead }));
|
||||
}
|
||||
})
|
||||
);
|
||||
return stream.readExactlyByStream(instream, BUFFER_READ_MAX_LEN).then(detectMimeAndEncodingFromBuffer);
|
||||
}
|
||||
|
||||
function doDetectMimesFromFile(absolutePath: string): TPromise<IMimeAndEncoding> {
|
||||
return new TPromise((complete, error) =>
|
||||
stream.readExactlyByFile(absolutePath, BUFFER_READ_MAX_LEN, (err, buffer, bytesRead) => {
|
||||
if (err) {
|
||||
error(error);
|
||||
} else {
|
||||
complete(detectMimeAndEncodingFromBuffer({ buffer, bytesRead }));
|
||||
}
|
||||
})
|
||||
);
|
||||
return stream.readExactlyByFile(absolutePath, BUFFER_READ_MAX_LEN).then(detectMimeAndEncodingFromBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ReadResult {
|
||||
buffer: NodeBuffer;
|
||||
bytesRead: number;
|
||||
}
|
||||
|
||||
export function detectMimeAndEncodingFromBuffer({buffer, bytesRead}: ReadResult): IMimeAndEncoding {
|
||||
export function detectMimeAndEncodingFromBuffer({buffer, bytesRead}: stream.ReadResult): IMimeAndEncoding {
|
||||
let enc = encoding.detectEncodingByBOMFromBuffer(buffer, bytesRead);
|
||||
|
||||
// Detect 0 bytes to see if file is binary (ignore for UTF 16 though)
|
||||
|
||||
+122
-109
@@ -8,94 +8,105 @@
|
||||
import fs = require('fs');
|
||||
import stream = require('stream');
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
export interface ReadResult {
|
||||
buffer: NodeBuffer;
|
||||
bytesRead: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to total bytes from the provided stream.
|
||||
*/
|
||||
export function readExactlyByStream(stream: stream.Readable, totalBytes: number, callback: (err: Error, buffer: NodeBuffer, bytesRead: number) => void): void {
|
||||
let done = false;
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
export function readExactlyByStream(stream: stream.Readable, totalBytes: number): TPromise<ReadResult> {
|
||||
return new TPromise((complete, error) => {
|
||||
let done = false;
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
|
||||
stream.on('data', (data: NodeBuffer) => {
|
||||
let bytesToRead = Math.min(totalBytes - bytesRead, data.length);
|
||||
data.copy(buffer, bytesRead, 0, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
stream.on('data', (data: NodeBuffer) => {
|
||||
let bytesToRead = Math.min(totalBytes - bytesRead, data.length);
|
||||
data.copy(buffer, bytesRead, 0, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
|
||||
if (bytesRead === totalBytes) {
|
||||
stream.destroy(); // Will trigger the close event eventually
|
||||
}
|
||||
});
|
||||
if (bytesRead === totalBytes) {
|
||||
stream.destroy(); // Will trigger the close event eventually
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (e: Error) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
callback(e, null, null);
|
||||
}
|
||||
});
|
||||
stream.on('error', (e: Error) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
|
||||
let onSuccess = () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
callback(null, buffer, bytesRead);
|
||||
}
|
||||
};
|
||||
let onSuccess = () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
complete({ buffer, bytesRead });
|
||||
}
|
||||
};
|
||||
|
||||
stream.on('close', onSuccess);
|
||||
stream.on('close', onSuccess);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads totalBytes from the provided file.
|
||||
*/
|
||||
export function readExactlyByFile(file: string, totalBytes: number, callback: (error: Error, buffer: NodeBuffer, bytesRead: number) => void): void {
|
||||
fs.open(file, 'r', null, (err, fd) => {
|
||||
if (err) {
|
||||
return callback(err, null, 0);
|
||||
}
|
||||
export function readExactlyByFile(file: string, totalBytes: number): TPromise<ReadResult> {
|
||||
return new TPromise((complete, error) => {
|
||||
fs.open(file, 'r', null, (err, fd) => {
|
||||
if (err) {
|
||||
return error(err);
|
||||
}
|
||||
|
||||
function end(err: Error, resultBuffer: NodeBuffer, bytesRead: number): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
if (closeError) {
|
||||
return callback(closeError, null, bytesRead);
|
||||
}
|
||||
function end(err: Error, resultBuffer: NodeBuffer, bytesRead: number): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
if (closeError) {
|
||||
return error(closeError);
|
||||
}
|
||||
|
||||
if (err && (<any>err).code === 'EISDIR') {
|
||||
return callback(err, null, bytesRead); // we want to bubble this error up (file is actually a folder)
|
||||
}
|
||||
if (err && (<any>err).code === 'EISDIR') {
|
||||
return error(err); // we want to bubble this error up (file is actually a folder)
|
||||
}
|
||||
|
||||
return callback(null, resultBuffer, bytesRead);
|
||||
});
|
||||
}
|
||||
return complete({ buffer:resultBuffer, bytesRead });
|
||||
});
|
||||
}
|
||||
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, totalBytes - bytesRead, null, (err, moreBytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null, 0);
|
||||
}
|
||||
let buffer = new Buffer(totalBytes);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, totalBytes - bytesRead, null, (err, moreBytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null, 0);
|
||||
}
|
||||
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, buffer, bytesRead);
|
||||
}
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, buffer, bytesRead);
|
||||
}
|
||||
|
||||
return loop();
|
||||
}
|
||||
return loop();
|
||||
}
|
||||
|
||||
bytesRead += moreBytesRead;
|
||||
bytesRead += moreBytesRead;
|
||||
|
||||
if (bytesRead === totalBytes) {
|
||||
return end(null, buffer, bytesRead);
|
||||
}
|
||||
if (bytesRead === totalBytes) {
|
||||
return end(null, buffer, bytesRead);
|
||||
}
|
||||
|
||||
return loop();
|
||||
});
|
||||
}
|
||||
return loop();
|
||||
});
|
||||
}
|
||||
|
||||
loop();
|
||||
});
|
||||
loop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,59 +118,61 @@ export function readExactlyByFile(file: string, totalBytes: number, callback: (e
|
||||
* @param maximumBytesToRead The maximum number of bytes to read before giving up.
|
||||
* @param callback The finished callback.
|
||||
*/
|
||||
export function readToMatchingString(file: string, matchingString: string, chunkBytes: number, maximumBytesToRead: number, callback: (error: Error, result: string) => void): void {
|
||||
fs.open(file, 'r', null, (err, fd) => {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
export function readToMatchingString(file: string, matchingString: string, chunkBytes: number, maximumBytesToRead: number): TPromise<string> {
|
||||
return new TPromise((complete, error) =>
|
||||
fs.open(file, 'r', null, (err, fd) => {
|
||||
if (err) {
|
||||
return error(err);
|
||||
}
|
||||
|
||||
function end(err: Error, result: string): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
if (closeError) {
|
||||
return callback(closeError, null);
|
||||
}
|
||||
function end(err: Error, result: string): void {
|
||||
fs.close(fd, (closeError: Error) => {
|
||||
if (closeError) {
|
||||
return error(closeError);
|
||||
}
|
||||
|
||||
if (err && (<any>err).code === 'EISDIR') {
|
||||
return callback(err, null); // we want to bubble this error up (file is actually a folder)
|
||||
}
|
||||
if (err && (<any>err).code === 'EISDIR') {
|
||||
return error(err); // we want to bubble this error up (file is actually a folder)
|
||||
}
|
||||
|
||||
return callback(null, result);
|
||||
});
|
||||
}
|
||||
return complete(result);
|
||||
});
|
||||
}
|
||||
|
||||
let buffer = new Buffer(maximumBytesToRead);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, chunkBytes, null, (err, moreBytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null);
|
||||
}
|
||||
let buffer = new Buffer(maximumBytesToRead);
|
||||
let bytesRead = 0;
|
||||
let zeroAttempts = 0;
|
||||
function loop(): void {
|
||||
fs.read(fd, buffer, bytesRead, chunkBytes, null, (err, moreBytesRead) => {
|
||||
if (err) {
|
||||
return end(err, null);
|
||||
}
|
||||
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, null);
|
||||
}
|
||||
// Retry up to N times in case 0 bytes where read
|
||||
if (moreBytesRead === 0) {
|
||||
if (++zeroAttempts === 10) {
|
||||
return end(null, null);
|
||||
}
|
||||
|
||||
return loop();
|
||||
}
|
||||
return loop();
|
||||
}
|
||||
|
||||
bytesRead += moreBytesRead;
|
||||
bytesRead += moreBytesRead;
|
||||
|
||||
const newLineIndex = buffer.indexOf(matchingString);
|
||||
if (newLineIndex >= 0) {
|
||||
return end(null, buffer.toString('utf8').substr(0, newLineIndex));
|
||||
}
|
||||
const newLineIndex = buffer.indexOf(matchingString);
|
||||
if (newLineIndex >= 0) {
|
||||
return end(null, buffer.toString('utf8').substr(0, newLineIndex));
|
||||
}
|
||||
|
||||
if (bytesRead >= maximumBytesToRead) {
|
||||
return end(new Error(`Could not find ${matchingString} in first ${maximumBytesToRead} bytes of ${file}`), null);
|
||||
}
|
||||
if (bytesRead >= maximumBytesToRead) {
|
||||
return end(new Error(`Could not find ${matchingString} in first ${maximumBytesToRead} bytes of ${file}`), null);
|
||||
}
|
||||
|
||||
return loop();
|
||||
});
|
||||
}
|
||||
return loop();
|
||||
});
|
||||
}
|
||||
|
||||
loop();
|
||||
});
|
||||
loop();
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -11,72 +11,61 @@ import fs = require('fs');
|
||||
import stream = require('vs/base/node/stream');
|
||||
|
||||
suite('Stream', () => {
|
||||
test('readExactlyByFile - ANSI', function (done: () => void) {
|
||||
test('readExactlyByFile - ANSI', function (done: (err?) => void) {
|
||||
const file = require.toUrl('./fixtures/file.css');
|
||||
|
||||
stream.readExactlyByFile(file, 10, (error: Error, buffer: NodeBuffer, count: number) => {
|
||||
assert.equal(error, null);
|
||||
assert.equal(count, 10);
|
||||
stream.readExactlyByFile(file, 10).then(({buffer, bytesRead}) => {
|
||||
assert.equal(bytesRead, 10);
|
||||
assert.equal(buffer.toString(), '/*--------');
|
||||
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('readExactlyByFile - empty', function (done: () => void) {
|
||||
test('readExactlyByFile - empty', function (done: (err?: any) => void) {
|
||||
const file = require.toUrl('./fixtures/empty.txt');
|
||||
|
||||
stream.readExactlyByFile(file, 10, (error: Error, buffer: NodeBuffer, count: number) => {
|
||||
assert.equal(error, null);
|
||||
assert.equal(count, 0);
|
||||
|
||||
stream.readExactlyByFile(file, 10).then(({bytesRead}) => {
|
||||
assert.equal(bytesRead, 0);
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('readExactlyByStream - ANSI', function (done: () => void) {
|
||||
test('readExactlyByStream - ANSI', function (done: (err?: any) => void) {
|
||||
const file = require.toUrl('./fixtures/file.css');
|
||||
|
||||
stream.readExactlyByStream(fs.createReadStream(file), 10, (error: Error, buffer: NodeBuffer, count: number) => {
|
||||
assert.equal(error, null);
|
||||
assert.equal(count, 10);
|
||||
stream.readExactlyByStream(fs.createReadStream(file), 10).then(({buffer, bytesRead}) => {
|
||||
assert.equal(bytesRead, 10);
|
||||
assert.equal(buffer.toString(), '/*--------');
|
||||
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('readExactlyByStream - empty', function (done: () => void) {
|
||||
test('readExactlyByStream - empty', function (done: (err?: any) => void) {
|
||||
const file = require.toUrl('./fixtures/empty.txt');
|
||||
|
||||
stream.readExactlyByStream(fs.createReadStream(file), 10, (error: Error, buffer: NodeBuffer, count: number) => {
|
||||
assert.equal(error, null);
|
||||
assert.equal(count, 0);
|
||||
|
||||
stream.readExactlyByStream(fs.createReadStream(file), 10).then(({bytesRead}) => {
|
||||
assert.equal(bytesRead, 0);
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('readToMatchingString - ANSI', function (done: () => void) {
|
||||
test('readToMatchingString - ANSI', function (done: (err?: any) => void) {
|
||||
const file = require.toUrl('./fixtures/file.css');
|
||||
|
||||
stream.readToMatchingString(file, '\n', 10, 100, (error: Error, result: string) => {
|
||||
assert.equal(error, null);
|
||||
stream.readToMatchingString(file, '\n', 10, 100).then ((result: string) => {
|
||||
// \r may be present on Windows
|
||||
assert.equal(result.replace('\r', ''), '/*---------------------------------------------------------------------------------------------');
|
||||
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
test('readToMatchingString - empty', function (done: () => void) {
|
||||
test('readToMatchingString - empty', function (done: (err?: any) => void) {
|
||||
const file = require.toUrl('./fixtures/empty.txt');
|
||||
|
||||
stream.readToMatchingString(file, '\n', 10, 100, (error: Error, result: string) => {
|
||||
assert.equal(error, null);
|
||||
stream.readToMatchingString(file, '\n', 10, 100).then((result: string) => {
|
||||
assert.equal(result, null);
|
||||
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
@@ -229,15 +229,10 @@ export class BackupFileService implements IBackupFileService {
|
||||
const readPromises: TPromise<Uri>[] = [];
|
||||
|
||||
model.get().forEach(fileBackup => {
|
||||
readPromises.push(new TPromise<Uri>((c, e) => {
|
||||
readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000, (error, result) => {
|
||||
if (result === null) {
|
||||
e(error);
|
||||
}
|
||||
|
||||
c(Uri.parse(result));
|
||||
});
|
||||
}));
|
||||
readPromises.push(
|
||||
readToMatchingString(fileBackup.fsPath, BackupFileService.META_MARKER, 2000, 10000)
|
||||
.then(Uri.parse)
|
||||
);
|
||||
});
|
||||
|
||||
return TPromise.join(readPromises);
|
||||
|
||||
Reference in New Issue
Block a user