avoid O(N^2) for processing create or delete events (N number of such additions / removals)

This commit is contained in:
Maxim.Mossienko
2013-10-23 18:04:55 +02:00
parent 824868b1f8
commit cd68157d45
@@ -33,6 +33,7 @@ import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.StripedLockIntObjectConcurrentHashMap;
@@ -762,12 +763,112 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
BulkFileListener publisher = myEventsBus.syncPublisher(VirtualFileManager.VFS_CHANGES);
publisher.before(validated);
THashMap<VirtualFile, List<VFileEvent>> parentToChildrenEventsChanges = null;
for (VFileEvent event : validated) {
applyEvent(event);
VirtualFile changedParent = null;
if (event instanceof VFileCreateEvent) {
changedParent = ((VFileCreateEvent)event).getParent();
} else if (event instanceof VFileDeleteEvent) {
changedParent = ((VFileDeleteEvent)event).getFile().getParent();
}
if (changedParent != null) {
if (parentToChildrenEventsChanges == null) parentToChildrenEventsChanges = new THashMap<VirtualFile, List<VFileEvent>>();
List<VFileEvent> parentChildrenChanges = parentToChildrenEventsChanges.get(changedParent);
if (parentChildrenChanges == null) {
parentToChildrenEventsChanges.put(changedParent, parentChildrenChanges = new SmartList<VFileEvent>());
}
parentChildrenChanges.add(event);
} else {
applyEvent(event);
}
}
if (parentToChildrenEventsChanges != null) {
parentToChildrenEventsChanges.forEachEntry(new TObjectObjectProcedure<VirtualFile, List<VFileEvent>>() {
@Override
public boolean execute(VirtualFile parent, List<VFileEvent> childrenEvents) {
applyChildrenChangeEvents(parent, childrenEvents);
return true;
}
});
parentToChildrenEventsChanges.clear();
}
publisher.after(validated);
}
private void applyChildrenChangeEvents(VirtualFile parent, List<VFileEvent> events) {
final NewVirtualFileSystem delegate = getDelegate(parent);
List<VirtualFile> childrenToBeAdded = null;
TIntArrayList childrenIdsToBeRemoved = null;
List<VirtualFile> childrenToBeRemoved = null;
assert parent != null && parent != mySuperRoot;
final int parentId = getFileId(parent);
assert parentId != 0;
TIntHashSet childrenIds = new TIntHashSet(FSRecords.list(parentId));
for(VFileEvent event:events) {
if (event instanceof VFileCreateEvent) {
String name = ((VFileCreateEvent)event).getChildName();
final VirtualFile fake = new FakeVirtualFile(parent, name);
final FileAttributes attributes = delegate.getAttributes(fake);
if (attributes != null) {
final int childId = createAndFillRecord(delegate, fake, parentId, attributes);
if (childrenToBeAdded == null) {
childrenToBeAdded = new SmartList<VirtualFile>();
}
assert parent instanceof VirtualDirectoryImpl : parent;
final VirtualDirectoryImpl dir = (VirtualDirectoryImpl)parent;
VirtualFileSystemEntry child = dir.createChild(name, childId, dir.getFileSystem());
childrenToBeAdded.add(child);
childrenIds.add(childId);
}
} else if (event instanceof VFileDeleteEvent) {
VirtualFile file = ((VFileDeleteEvent)event).getFile();
if (!file.exists()) {
LOG.error("Deleting a file, which does not exist: " + file.getPath());
continue;
}
int id = getFileId(file);
if(childrenIdsToBeRemoved == null) {
childrenIdsToBeRemoved = new TIntArrayList();
childrenToBeRemoved = new SmartList<VirtualFile>();
}
childrenIdsToBeRemoved.add(id);
childrenToBeRemoved.add(file);
childrenIds.remove(id);
}
}
FSRecords.updateList(parentId, childrenIds.toArray());
if (childrenToBeAdded != null) {
for(VirtualFile addedFile:childrenToBeAdded) {
((VirtualDirectoryImpl)parent).addChild((VirtualFileSystemEntry)addedFile);
}
}
if (childrenToBeRemoved != null) {
clearIdCache();
VirtualDirectoryImpl virtualDirectory = (VirtualDirectoryImpl)parent;
for(int i = 0, len = childrenIdsToBeRemoved.size(); i < len; ++i) {
FSRecords.deleteRecordRecursively(childrenIdsToBeRemoved.get(i));
VirtualFile removedFile = childrenToBeRemoved.get(i);
virtualDirectory.removeChild(removedFile);
invalidateSubtree(removedFile);
}
}
}
@Override
@Nullable
public VirtualFileSystemEntry findRoot(@NotNull String basePath, @NotNull NewVirtualFileSystem fs) {