diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java index 423a6201f6ed..f493e4e17da9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/temp/TempFileSystem.java @@ -30,10 +30,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author max @@ -65,14 +62,23 @@ public class TempFileSystem extends LocalFileSystemBase { return parentItem.findChild(file.getName()); } + @NotNull + private FSDir convertDirectory(@NotNull VirtualFile parent) { + final FSItem fsItem = convert(parent); + if (fsItem == null) { + FSRecords.invalidateCaches(); + throw new IllegalStateException("cannot find parent directory: " + parent.getPath()); + } + assert fsItem.isDirectory() : "parent is not a directory: " + parent.getPath(); + + return (FSDir)fsItem; + } + @Override @NotNull public VirtualFile createChildDirectory(Object requestor, @NotNull VirtualFile parent, @NotNull String dir) throws IOException { - final FSItem fsItem = convert(parent); - assert fsItem != null && fsItem.isDirectory(); - - final FSDir fsDir = (FSDir)fsItem; - final FSItem existingDir = fsDir.findChild(dir); + FSDir fsDir = convertDirectory(parent); + FSItem existingDir = fsDir.findChild(dir); if (existingDir == null) { fsDir.addChild(new FSDir(fsDir, dir)); } @@ -85,15 +91,8 @@ public class TempFileSystem extends LocalFileSystemBase { @NotNull @Override - public VirtualFile createChildFile(Object requestor, @NotNull VirtualFile parent, @NotNull String file) throws IOException { - final FSItem fsItem = convert(parent); - if (fsItem == null) { - FSRecords.invalidateCaches(); - throw new IllegalStateException("cannot find parent directory: " + parent.getPath()); - } - assert fsItem.isDirectory() : "parent is not a directory: " + parent.getPath(); - - final FSDir fsDir = (FSDir)fsItem; + public VirtualFile createChildFile(Object requestor, @NotNull VirtualFile parent, @NotNull String file) { + FSDir fsDir = convertDirectory(parent); assert fsDir.findChild(file) == null : "File " + file + " already exists in " + parent.getPath(); fsDir.addChild(new FSFile(fsDir, file)); @@ -101,6 +100,11 @@ public class TempFileSystem extends LocalFileSystemBase { return new FakeVirtualFile(parent, file); } + @Nullable public VirtualFile findModelChild(@NotNull VirtualFile parent, @NotNull String name) { + FSItem child = convertDirectory(parent).findChild(name); + return child == null ? null : new FakeVirtualFile(parent, name); + } + @NotNull @Override public VirtualFile copyFile(Object requestor, @@ -111,7 +115,7 @@ public class TempFileSystem extends LocalFileSystemBase { } @Override - public void deleteFile(final Object requestor, @NotNull final VirtualFile file) throws IOException { + public void deleteFile(final Object requestor, @NotNull final VirtualFile file) { final FSItem fsItem = convert(file); if (fsItem == null) { FSRecords.invalidateCaches(); @@ -137,7 +141,7 @@ public class TempFileSystem extends LocalFileSystemBase { } @Override - public void renameFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final String newName) throws IOException { + public void renameFile(final Object requestor, @NotNull final VirtualFile file, @NotNull final String newName) { final FSItem fsItem = convert(file); assert fsItem != null; fsItem.setName(newName); @@ -195,7 +199,7 @@ public class TempFileSystem extends LocalFileSystemBase { } @Override - public void setWritable(@NotNull final VirtualFile file, final boolean writableFlag) throws IOException { + public void setWritable(@NotNull final VirtualFile file, final boolean writableFlag) { final FSItem fsItem = convert(file); assert fsItem != null; fsItem.myWritable = writableFlag; @@ -266,7 +270,9 @@ public class TempFileSystem extends LocalFileSystemBase { } void setName(@NotNull String name) { + myParent.myChildren.remove(myName); myName = name; + myParent.myChildren.put(name, this); } public FSDir getParent() { @@ -285,7 +291,7 @@ public class TempFileSystem extends LocalFileSystemBase { } private static class FSDir extends FSItem { - private final List myChildren = new ArrayList<>(); + private final Map myChildren = new LinkedHashMap<>(); FSDir(@Nullable FSDir parent, @NotNull String name) { super(parent, name); @@ -294,13 +300,7 @@ public class TempFileSystem extends LocalFileSystemBase { @Override @Nullable public FSItem findChild(final String name) { - for (FSItem child : myChildren) { - if (name.equals(child.myName)) { - return child; - } - } - - return null; + return myChildren.get(name); } @Override @@ -309,24 +309,20 @@ public class TempFileSystem extends LocalFileSystemBase { } void addChild(@NotNull FSItem item) { - myChildren.add(item); + myChildren.put(item.myName, item); } void removeChild(@NotNull FSItem fsItem) { if (fsItem.myName.equals("src") && getParent() == null) { throw new RuntimeException("removing src directory"); } - myChildren.remove(fsItem); + myChildren.remove(fsItem.myName); } @NotNull @Override public String[] list() { - String[] names = ArrayUtil.newStringArray(myChildren.size()); - for (int i = 0; i < names.length; i++) { - names[i] = myChildren.get(i).myName; - } - return names; + return ArrayUtil.toStringArray(myChildren.keySet()); } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java index cd3b5a4536d9..cd76ed134b98 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/VfsUtilPerformanceTest.java @@ -17,6 +17,8 @@ package com.intellij.openapi.vfs; import com.intellij.concurrency.JobLauncher; import com.intellij.concurrency.JobSchedulerImpl; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Disposer; @@ -24,6 +26,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.IoTestUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.ex.temp.TempFileSystem; import com.intellij.openapi.vfs.newvfs.ManagingFS; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent; @@ -43,6 +46,7 @@ import com.intellij.util.ThrowableRunnable; import com.intellij.util.TimeoutUtil; import com.intellij.util.ui.UIUtil; import gnu.trove.TIntHashSet; +import org.jetbrains.annotations.NotNull; import org.junit.Rule; import org.junit.Test; @@ -279,10 +283,11 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { } @Test - public void PersistentFS_performance_ofManyFilesCreateDelete() throws Exception { + public void PersistentFS_performance_ofManyFilesCreateDelete() { int N = 100_000; List events = new ArrayList<>(N); - VirtualDirectoryImpl temp = (VirtualDirectoryImpl)PlatformTestUtil.notNull(LocalFileSystem.getInstance().findFileByIoFile(myTempDir.newFolder())); + VirtualDirectoryImpl temp = createTempFsDirectory(); + UIUtil.invokeAndWaitIfNeeded((Runnable)() -> { PlatformTestUtil.startPerformanceTest("many files creations", 10000, () -> { assertEquals(N, events.size()); @@ -296,7 +301,7 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { processEvents(events); } eventsForCreating(events, N, temp); - assertEquals(N, new File(temp.getPath()).listFiles().length); // do not call getChildren which caches everything + assertEquals(N, TempFileSystem.getInstance().list(temp).length); // do not call getChildren which caches everything }) .assertTiming(); @@ -315,22 +320,45 @@ public class VfsUtilPerformanceTest extends BareTestFixtureTestCase { processEvents(events); } eventsForDeleting(events, temp); - assertEquals(N, new File(temp.getPath()).listFiles().length); // do not call getChildren which caches everything + assertEquals(N, TempFileSystem.getInstance().list(temp).length); // do not call getChildren which caches everything }) .assertTiming(); } ); } + private VirtualDirectoryImpl createTempFsDirectory() { + VirtualDirectoryImpl temp = new WriteAction(){ + @Override + protected void run(@NotNull Result result) throws Throwable { + result.setResult((VirtualDirectoryImpl)TempFileSystem.getInstance().findFileByPath("/").createChildDirectory(this, "temp")); + } + }.execute().getResultObject(); + Disposer.register(getTestRootDisposable(), () -> { + try { + WriteAction.run(() -> temp.delete(this)); + } + catch (IOException e) { + throw new RuntimeException(); + } + }); + return temp; + } + private static void processEvents(List events) { WriteCommandAction.runWriteCommandAction(null, () -> PersistentFS.getInstance().processEvents(events)); } private void eventsForCreating(List events, int N, VirtualDirectoryImpl temp) { events.clear(); + TempFileSystem fs = TempFileSystem.getInstance(); IntStream.range(0, N) .mapToObj(i -> new VFileCreateEvent(this, temp, i + ".txt", false, false)) - .peek(event -> FileUtil.createIfDoesntExist(new File(event.getPath()))) + .peek(event -> { + if (fs.findModelChild(temp, event.getChildName()) == null) { + fs.createChildFile(this, temp, event.getChildName()); + } + }) .forEach(events::add); List names = events.stream().map(e -> ((VFileCreateEvent)e).getChildName()).collect(Collectors.toList()); temp.removeChildren(new TIntHashSet(), names);