Skip to content

Commit ea9aed8

Browse files
Reduce buffer to string conversions. (#56961)
1 parent fbf908b commit ea9aed8

File tree

1 file changed

+40
-19
lines changed

1 file changed

+40
-19
lines changed

src/harness/vfsUtil.ts

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -656,8 +656,8 @@ export class FileSystem {
656656
if (isDirectory(node)) throw createIOError("EISDIR");
657657
if (!isFile(node)) throw createIOError("EBADF");
658658

659-
const buffer = this._getBuffer(node).slice();
660-
return encoding ? buffer.toString(encoding) : buffer;
659+
const fileBuffer = this._getBuffer(node, encoding ?? undefined);
660+
return !fileBuffer.encoding ? fileBuffer.data.slice() : fileBuffer.data;
661661
}
662662

663663
/**
@@ -681,8 +681,11 @@ export class FileSystem {
681681

682682
if (isDirectory(node)) throw createIOError("EISDIR");
683683
if (!isFile(node)) throw createIOError("EBADF");
684-
node.buffer = Buffer.isBuffer(data) ? data.slice() : ts.sys.bufferFrom!("" + data, encoding || "utf8") as Buffer;
685-
node.size = node.buffer.byteLength;
684+
node.buffer = Buffer.isBuffer(data) ?
685+
{ encoding: undefined, data: data.slice() } :
686+
{ encoding: (encoding ?? "utf8") as BufferEncoding, data };
687+
// Updated the size if it's easy to get, otherwise set to undefined. _getSize will compute the correct size
688+
node.size = !node.buffer.encoding ? node.buffer.data.byteLength : undefined;
686689
node.mtimeMs = time;
687690
node.ctimeMs = time;
688691
}
@@ -700,6 +703,7 @@ export class FileSystem {
700703
return hasDifferences ? differences : undefined;
701704
}
702705

706+
public static defaultEncoding: BufferEncoding | undefined = "utf8";
703707
/**
704708
* Generates a `FileSet` patch containing all the entries in `changed` that are not in `base`.
705709
*/
@@ -800,24 +804,28 @@ export class FileSystem {
800804
baseNode.resolver === changedNode.resolver && baseNode.source === changedNode.source
801805
) return false;
802806

803-
const changedBuffer = changed._getBuffer(changedNode);
804-
const baseBuffer = base._getBuffer(baseNode);
807+
const encoding = changedNode.buffer?.encoding ?? baseNode.buffer?.encoding ?? FileSystem.defaultEncoding;
808+
const changedBuffer = changed._getBuffer(changedNode, encoding);
809+
const baseBuffer = base._getBuffer(baseNode, encoding);
805810

806811
// no difference if both buffers are the same reference
807812
if (changedBuffer === baseBuffer) {
808813
if (!options.includeChangedFileWithSameContent || changedNode.mtimeMs === baseNode.mtimeMs) return false;
809-
container[basename] = new SameFileWithModifiedTime(changedBuffer);
814+
container[basename] = new SameFileWithModifiedTime(changedBuffer.data, { encoding: changedBuffer.encoding });
810815
return true;
811816
}
812817

813818
// no difference if both buffers are identical
814-
if (Buffer.compare(changedBuffer, baseBuffer) === 0) {
819+
if (
820+
!changedBuffer.encoding && !baseBuffer.encoding && Buffer.compare(changedBuffer.data, baseBuffer.data) === 0 // same buffer content
821+
|| changedBuffer.encoding === baseBuffer.encoding && changedBuffer.data === baseBuffer.data // same string content
822+
) {
815823
if (!options.includeChangedFileWithSameContent) return false;
816-
container[basename] = new SameFileContentFile(changedBuffer);
824+
container[basename] = new SameFileContentFile(changedBuffer.data, { encoding: changedBuffer.encoding });
817825
return true;
818826
}
819827

820-
container[basename] = new File(changedBuffer);
828+
container[basename] = new File(changedBuffer.data, { encoding: changedBuffer.encoding });
821829
return true;
822830
}
823831

@@ -838,7 +846,8 @@ export class FileSystem {
838846
container[basename] = new Symlink(node.symlink);
839847
}
840848
else {
841-
container[basename] = new File(changed._getBuffer(node));
849+
const buffer = changed._getBuffer(node, FileSystem.defaultEncoding);
850+
container[basename] = new File(buffer.data, { encoding: buffer.encoding ?? undefined });
842851
}
843852
return true;
844853
}
@@ -984,14 +993,14 @@ export class FileSystem {
984993
}
985994

986995
private _getSize(node: FileInode): number {
987-
if (node.buffer) return node.buffer.byteLength;
996+
if (node.buffer) return Buffer.byteLength(node.buffer.data);
988997
if (node.size !== undefined) return node.size;
989998
if (node.source && node.resolver) return node.size = node.resolver.statSync(node.source).size;
990999
if (this._shadowRoot && node.shadowRoot) return node.size = this._shadowRoot._getSize(node.shadowRoot);
9911000
return 0;
9921001
}
9931002

994-
private _getBuffer(node: FileInode): Buffer {
1003+
private _getBuffer(node: FileInode, encoding: BufferEncoding | undefined): FileDataBuffer {
9951004
if (!node.buffer) {
9961005
const { source, resolver } = node;
9971006
if (source && resolver) {
@@ -1001,12 +1010,13 @@ export class FileSystem {
10011010
node.buffer = resolver.readFileSync(source);
10021011
}
10031012
else if (this._shadowRoot && node.shadowRoot) {
1004-
node.buffer = this._shadowRoot._getBuffer(node.shadowRoot);
1013+
node.buffer = this._shadowRoot._getBuffer(node.shadowRoot, encoding);
10051014
}
10061015
else {
1007-
node.buffer = Buffer.allocUnsafe(0);
1016+
node.buffer = { encoding: undefined, data: Buffer.allocUnsafe(0) };
10081017
}
10091018
}
1019+
ensureBufferEncoding(node.buffer, encoding);
10101020
return node.buffer;
10111021
}
10121022

@@ -1189,7 +1199,7 @@ export interface Traversal {
11891199
export interface FileSystemResolver {
11901200
statSync(path: string): { mode: number; size: number; };
11911201
readdirSync(path: string): string[];
1192-
readFileSync(path: string): Buffer;
1202+
readFileSync(path: string): FileDataBuffer;
11931203
}
11941204

11951205
export interface FileSystemResolverHost {
@@ -1219,8 +1229,8 @@ export function createResolver(host: FileSystemResolverHost): FileSystemResolver
12191229
throw new Error("ENOENT: path does not exist");
12201230
}
12211231
},
1222-
readFileSync(path: string): Buffer {
1223-
return ts.sys.bufferFrom!(host.readFile(path)!, "utf8") as Buffer; // TODO: GH#18217
1232+
readFileSync(path: string): FileDataBuffer {
1233+
return { encoding: "utf8", data: host.readFile(path)! };
12241234
},
12251235
};
12261236
}
@@ -1444,6 +1454,17 @@ export class Mount {
14441454
// a generic POSIX inode
14451455
type Inode = FileInode | DirectoryInode | SymlinkInode;
14461456

1457+
type FileDataBuffer = { encoding?: undefined; data: Buffer; } | { encoding: BufferEncoding; data: string; };
1458+
1459+
function ensureBufferEncoding(fileBuffer: FileDataBuffer, encoding: BufferEncoding | undefined) {
1460+
if (fileBuffer.encoding === encoding) return;
1461+
1462+
const buffer = !fileBuffer.encoding ? fileBuffer.data : ts.sys.bufferFrom!(fileBuffer.data, fileBuffer.encoding);
1463+
1464+
fileBuffer.encoding = encoding;
1465+
fileBuffer.data = !encoding ? buffer as Buffer : buffer.toString(encoding);
1466+
}
1467+
14471468
interface FileInode {
14481469
dev: number; // device id
14491470
ino: number; // inode id
@@ -1454,7 +1475,7 @@ interface FileInode {
14541475
birthtimeMs: number; // creation time
14551476
nlink: number; // number of hard links
14561477
size?: number;
1457-
buffer?: Buffer;
1478+
buffer?: FileDataBuffer;
14581479
source?: string;
14591480
resolver?: FileSystemResolver;
14601481
shadowRoot?: FileInode;

0 commit comments

Comments
 (0)