From d3c4b4b4a0683af01748942a004871ae99ef69a5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 8 Jul 2020 13:15:30 +0200 Subject: [PATCH] Encodings: do not load iconv-lite if file is UTF-8 (fix #101916) --- .../textfile/browser/textFileService.ts | 6 ++- .../services/textfile/common/encoding.ts | 43 +++++++++++++++++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index b7c5414eba4e0..5dc5d59009149 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -619,8 +619,10 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { fileEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding'); // and last we check for settings } - if (!fileEncoding || !(await encodingExists(fileEncoding))) { - fileEncoding = UTF8; // the default is UTF 8 + if (fileEncoding !== UTF8) { + if (!fileEncoding || !(await encodingExists(fileEncoding))) { + fileEncoding = UTF8; // the default is UTF-8 + } } return fileEncoding; diff --git a/src/vs/workbench/services/textfile/common/encoding.ts b/src/vs/workbench/services/textfile/common/encoding.ts index 2d29aafed52b2..bdcc42528e387 100644 --- a/src/vs/workbench/services/textfile/common/encoding.ts +++ b/src/vs/workbench/services/textfile/common/encoding.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DecoderStream } from 'iconv-lite-umd'; import { Readable, ReadableStream, newWriteableStream } from 'vs/base/common/stream'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; @@ -39,6 +38,43 @@ export interface IDecodeStreamResult { detected: IDetectedEncodingResult; } +export interface IDecoderStream { + write(buffer: Uint8Array): string; + end(): string | undefined; +} + +class DecoderStream implements IDecoderStream { + + /** + * This stream will only load iconv-lite lazily if the encoding + * is not UTF-8. This ensures that for most common cases we do + * not pay the price of loading the module from disk. + */ + static async create(encoding: string): Promise { + let decoder: IDecoderStream | undefined = undefined; + if (encoding !== UTF8) { + const iconv = await import('iconv-lite-umd'); + decoder = iconv.getDecoder(toNodeEncoding(encoding)); + } + + return new DecoderStream(decoder); + } + + private constructor(private iconvLiteDecoder: IDecoderStream | undefined) { } + + write(buffer: Uint8Array): string { + if (this.iconvLiteDecoder) { + return this.iconvLiteDecoder.write(buffer); + } + + return VSBuffer.wrap(buffer).toString(); + } + + end(): string | undefined { + return this.iconvLiteDecoder?.end(); + } +} + export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeStreamOptions): Promise { const minBytesRequiredForDetection = options.minBytesRequiredForDetection ?? options.guessEncoding ? AUTO_ENCODING_GUESS_MIN_BYTES : NO_ENCODING_GUESS_MIN_BYTES; @@ -48,7 +84,7 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS const bufferedChunks: VSBuffer[] = []; let bytesBuffered = 0; - let decoder: DecoderStream | undefined = undefined; + let decoder: IDecoderStream | undefined = undefined; const createDecoder = async () => { try { @@ -63,8 +99,7 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS detected.encoding = await options.overwriteEncoding(detected.encoding); // decode and write buffered content - const iconv = await import('iconv-lite-umd'); - decoder = iconv.getDecoder(toNodeEncoding(detected.encoding)); + decoder = await DecoderStream.create(detected.encoding); const decoded = decoder.write(VSBuffer.concat(bufferedChunks).buffer); target.write(decoded);