Skip to content

Commit 3152631

Browse files
authored
Do not compute sha1 for large files (microsoft#194404)
Fixes microsoft#194334: Do not compute sha1 for large files
1 parent afb67e3 commit 3152631

File tree

1 file changed

+39
-15
lines changed

1 file changed

+39
-15
lines changed

src/vs/editor/common/services/modelService.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,6 @@ function MODEL_ID(resource: URI): string {
2929
return resource.toString();
3030
}
3131

32-
function computeModelSha1(model: ITextModel): string {
33-
// compute the sha1
34-
const shaComputer = new StringSHA1();
35-
const snapshot = model.createSnapshot();
36-
let text: string | null;
37-
while ((text = snapshot.read())) {
38-
shaComputer.update(text);
39-
}
40-
return shaComputer.digest();
41-
}
42-
4332
class ModelData implements IDisposable {
4433

4534
private readonly _modelEventListeners = new DisposableStore();
@@ -337,7 +326,12 @@ export class ModelService extends Disposable implements IModelService {
337326
if (resource && this._disposedModels.has(MODEL_ID(resource))) {
338327
const disposedModelData = this._removeDisposedModel(resource)!;
339328
const elements = this._undoRedoService.getElements(resource);
340-
const sha1IsEqual = (computeModelSha1(model) === disposedModelData.sha1);
329+
const sha1Computer = this._getSHA1Computer();
330+
const sha1IsEqual = (
331+
sha1Computer.canComputeSHA1(model)
332+
? sha1Computer.computeSHA1(model) === disposedModelData.sha1
333+
: false
334+
);
341335
if (sha1IsEqual || disposedModelData.sharesUndoRedoStack) {
342336
for (const element of elements.past) {
343337
if (isEditStackElement(element) && element.matchesResource(resource)) {
@@ -535,15 +529,16 @@ export class ModelService extends Disposable implements IModelService {
535529
}
536530

537531
const maxMemory = ModelService.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
532+
const sha1Computer = this._getSHA1Computer();
538533
if (!maintainUndoRedoStack) {
539534
if (!sharesUndoRedoStack) {
540535
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
541536
if (initialUndoRedoSnapshot !== null) {
542537
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
543538
}
544539
}
545-
} else if (!sharesUndoRedoStack && heapSize > maxMemory) {
546-
// the undo stack for this file would never fit in the configured memory, so don't bother with it.
540+
} else if (!sharesUndoRedoStack && (heapSize > maxMemory || !sha1Computer.canComputeSHA1(model))) {
541+
// the undo stack for this file would never fit in the configured memory or the file is very large, so don't bother with it.
547542
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
548543
if (initialUndoRedoSnapshot !== null) {
549544
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
@@ -552,7 +547,7 @@ export class ModelService extends Disposable implements IModelService {
552547
this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
553548
// We only invalidate the elements, but they remain in the undo-redo service.
554549
this._undoRedoService.setElementsValidFlag(model.uri, false, (element) => (isEditStackElement(element) && element.matchesResource(model.uri)));
555-
this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
550+
this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, sha1Computer.computeSHA1(model), model.getVersionId(), model.getAlternativeVersionId()));
556551
}
557552

558553
delete this._models[modelId];
@@ -572,4 +567,33 @@ export class ModelService extends Disposable implements IModelService {
572567
ModelService._setModelOptionsForModel(model, newOptions, oldOptions);
573568
this._onModelModeChanged.fire({ model, oldLanguageId: oldLanguageId });
574569
}
570+
571+
protected _getSHA1Computer(): ITextModelSHA1Computer {
572+
return new DefaultModelSHA1Computer();
573+
}
574+
}
575+
576+
export interface ITextModelSHA1Computer {
577+
canComputeSHA1(model: ITextModel): boolean;
578+
computeSHA1(model: ITextModel): string;
579+
}
580+
581+
export class DefaultModelSHA1Computer implements ITextModelSHA1Computer {
582+
583+
public static MAX_MODEL_SIZE = 10 * 1024 * 1024; // takes 200ms to compute a sha1 on a 10MB model on a new machine
584+
585+
canComputeSHA1(model: ITextModel): boolean {
586+
return (model.getValueLength() <= DefaultModelSHA1Computer.MAX_MODEL_SIZE);
587+
}
588+
589+
computeSHA1(model: ITextModel): string {
590+
// compute the sha1
591+
const shaComputer = new StringSHA1();
592+
const snapshot = model.createSnapshot();
593+
let text: string | null;
594+
while ((text = snapshot.read())) {
595+
shaComputer.update(text);
596+
}
597+
return shaComputer.digest();
598+
}
575599
}

0 commit comments

Comments
 (0)