From 7173841d21533239717f96b679e70e9e5d491b24 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 21 Aug 2024 12:00:57 +0200 Subject: [PATCH] refactor FileCollectionFactory GitOrigin-RevId: d05fb6b21b5d584e652da474aaf13cc9978689e9 --- .../ChunkBuildOutputConsumerImpl.java | 7 +- .../jps/incremental/fs/BuildFSState.java | 4 +- .../util/containers/CollectionFactory.java | 16 +- .../containers/FastUtilHashingStrategies.java | 13 -- .../util/containers/FileCollectionFactory.kt | 162 +++++++----------- .../ui/PartiallyExcludedFilesStateHolder.kt | 43 ++--- .../vcs/BasePartiallyExcludedChangesTest.kt | 4 +- 7 files changed, 99 insertions(+), 150 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java index ce1392f161f8..2573ad706c7e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ChunkBuildOutputConsumerImpl.java @@ -1,7 +1,7 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.incremental; -import com.intellij.util.containers.FastUtilHashingStrategies; +import com.intellij.util.containers.FileHashStrategy; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; @@ -22,7 +22,8 @@ final class ChunkBuildOutputConsumerImpl implements ModuleLevelBuilder.OutputCon private final Map, BuildOutputConsumerImpl> myTarget2Consumer = new HashMap<>(); private final Map myClasses = new HashMap<>(); private final Map, Collection> myTargetToClassesMap = new HashMap<>(); - private final Object2ObjectMap myOutputToBuilderNameMap = Object2ObjectMaps.synchronize(new Object2ObjectOpenCustomHashMap<>(FastUtilHashingStrategies.FILE_HASH_STRATEGY)); + private final Object2ObjectMap myOutputToBuilderNameMap = + Object2ObjectMaps.synchronize(new Object2ObjectOpenCustomHashMap<>(FileHashStrategy.INSTANCE)); private volatile String myCurrentBuilderName; ChunkBuildOutputConsumerImpl(CompileContext context) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java index d0c62b3ce864..eda5ee43f5ae 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java @@ -4,7 +4,7 @@ package org.jetbrains.jps.incremental.fs; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.util.SmartList; -import com.intellij.util.containers.FastUtilHashingStrategies; +import com.intellij.util.containers.FileHashStrategy; import com.intellij.util.containers.MultiMap; import com.intellij.util.io.IOUtil; import it.unimi.dsi.fastutil.objects.Object2LongOpenCustomHashMap; @@ -40,7 +40,7 @@ public final class BuildFSState { private final boolean myAlwaysScanFS; private final Set> myInitialScanPerformed = Collections.synchronizedSet(new HashSet<>()); @SuppressWarnings("SSBasedInspection") - private final Object2LongOpenCustomHashMap myRegistrationStamps = new Object2LongOpenCustomHashMap<>(FastUtilHashingStrategies.FILE_HASH_STRATEGY); + private final Object2LongOpenCustomHashMap myRegistrationStamps = new Object2LongOpenCustomHashMap<>(FileHashStrategy.INSTANCE); private final Map, FilesDelta> myDeltas = Collections.synchronizedMap(new HashMap<>()); public BuildFSState(boolean alwaysScanFS) { diff --git a/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java b/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java index 88eb3ac3b4cd..50cc7156d8f5 100644 --- a/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java +++ b/platform/util/base/src/com/intellij/util/containers/CollectionFactory.java @@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.BiConsumer; // ContainerUtil requires trove in classpath +@SuppressWarnings("UnnecessaryFullyQualifiedName") public final class CollectionFactory { /** @@ -278,16 +279,17 @@ public final class CollectionFactory { public static @NotNull Set createFilePathLinkedSet() { return SystemInfoRt.isFileSystemCaseSensitive - ? createSmallMemoryFootprintLinkedSet() + ? new ObjectLinkedOpenHashSet<>() : new ObjectLinkedOpenCustomHashSet<>(FastUtilHashingStrategies.getCaseInsensitiveStringStrategy()); } /** - * Create linked map with key hash strategy according to file system path case sensitivity. + * Create a linked map with key hash strategy according to file system path case sensitivity. */ public static @NotNull Map createFilePathLinkedMap() { + //noinspection SSBasedInspection return SystemInfoRt.isFileSystemCaseSensitive - ? createSmallMemoryFootprintLinkedMap() + ? new Object2ObjectLinkedOpenHashMap<>() : new Object2ObjectLinkedOpenCustomHashMap<>(FastUtilHashingStrategies.getCaseInsensitiveStringStrategy()); } @@ -336,8 +338,8 @@ public final class CollectionFactory { /** * Returns a linked-keys (i.e. iteration order is the same as the insertion order) {@link Set} implementation with slightly faster access for very big collection (>100K keys) and a bit smaller memory footprint - * than {@link HashSet}. Null keys are permitted. Use sparingly only when performance considerations are utterly important; - * in all other cases please prefer {@link HashSet}. + * than {@link java.util.HashSet}. Null keys are permitted. Use sparingly only when performance considerations are utterly important; + * in all other cases please prefer {@link java.util.HashSet}. */ @Contract(value = "-> new", pure = true) public static @NotNull Set createSmallMemoryFootprintLinkedSet() { @@ -346,8 +348,8 @@ public final class CollectionFactory { /** * Returns a {@link Set} implementation with slightly faster access for very big collections (>100K keys) and a bit smaller memory footprint - * than {@link HashSet}. Null keys are permitted. Use sparingly only when performance considerations are utterly important; - * in all other cases please prefer {@link HashSet}. + * than {@link java.util.HashSet}. Null keys are permitted. Use sparingly only when performance considerations are utterly important; + * in all other cases please prefer {@link java.util.HashSet}. */ @Contract(value = "-> new", pure = true) public static @NotNull Set createSmallMemoryFootprintSet() { diff --git a/platform/util/base/src/com/intellij/util/containers/FastUtilHashingStrategies.java b/platform/util/base/src/com/intellij/util/containers/FastUtilHashingStrategies.java index d2f786f1fe67..5081ea42793d 100644 --- a/platform/util/base/src/com/intellij/util/containers/FastUtilHashingStrategies.java +++ b/platform/util/base/src/com/intellij/util/containers/FastUtilHashingStrategies.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.Serializable; import java.util.Objects; @@ -36,18 +35,6 @@ public final class FastUtilHashingStrategies { } }; - public static final Hash.Strategy FILE_HASH_STRATEGY = new SerializableHashStrategy() { - @Override - public int hashCode(@Nullable File o) { - return FileUtilRt.pathHashCode(o == null ? null : o.getPath()); - } - - @Override - public boolean equals(@Nullable File a, @Nullable File b) { - return FileUtilRt.pathsEqual(a == null ? null : a.getPath(), b == null ? null : b.getPath()); - } - }; - public static @NotNull Hash.Strategy getCharSequenceStrategy(boolean isCaseSensitive) { return isCaseSensitive ? CASE_SENSITIVE : CASE_INSENSITIVE; } diff --git a/platform/util/base/src/com/intellij/util/containers/FileCollectionFactory.kt b/platform/util/base/src/com/intellij/util/containers/FileCollectionFactory.kt index 7c542bdf9ec4..dde9941a41f4 100644 --- a/platform/util/base/src/com/intellij/util/containers/FileCollectionFactory.kt +++ b/platform/util/base/src/com/intellij/util/containers/FileCollectionFactory.kt @@ -1,123 +1,81 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.util.containers; +package com.intellij.util.containers -import com.intellij.openapi.util.io.FileUtilRt; -import it.unimi.dsi.fastutil.Hash; -import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenCustomHashSet; -import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.Serializable; -import java.nio.file.Path; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import com.intellij.openapi.util.io.FileUtilRt +import it.unimi.dsi.fastutil.Hash +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenCustomHashSet +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet +import org.jetbrains.annotations.ApiStatus.Internal +import java.io.File +import java.io.Serializable +import java.nio.file.Path /** * Creates map or set with canonicalized path hash strategy. */ -public final class FileCollectionFactory { - private interface SerializableHashingStrategy extends Hash.Strategy, Serializable {} - - private static final SerializableHashingStrategy FILE_HASH_STRATEGY = new SerializableHashingStrategy() { - @Override - public int hashCode(@Nullable File o) { - return FileUtilRt.pathHashCode(o == null ? null : o.getPath()); - } - - @Override - public boolean equals(@Nullable File a, @Nullable File b) { - return FileUtilRt.pathsEqual(a == null ? null : a.getPath(), b == null ? null : b.getPath()); - } - }; +object FileCollectionFactory { + /** + * Create a linked map with canonicalized key hash strategy. + */ + @JvmStatic + fun createCanonicalPathLinkedMap(): MutableMap = Object2ObjectLinkedOpenCustomHashMap(PathHashStrategy) /** - * Create linked map with canonicalized key hash strategy. + * Create a linked map with canonicalized key hash strategy. */ - public static @NotNull Map createCanonicalPathLinkedMap() { - return new Object2ObjectLinkedOpenCustomHashMap<>(new PathSerializableHashStrategy()); + @JvmStatic + fun createCanonicalFilePathLinkedMap(): MutableMap { + return Object2ObjectLinkedOpenCustomHashMap(object : Hash.Strategy { + override fun hashCode(value: String?): Int = FileUtilRt.pathHashCode(value) + + override fun equals(val1: String?, val2: String?): Boolean = FileUtilRt.pathsEqual(val1, val2) + }) } - /** - * Create linked map with canonicalized key hash strategy. - */ - public static @NotNull Map createCanonicalFilePathLinkedMap() { - return new Object2ObjectLinkedOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(@Nullable String value) { - return FileUtilRt.pathHashCode(value); - } + @JvmStatic + fun createCanonicalFileMap(): MutableMap = Object2ObjectOpenCustomHashMap(FileHashStrategy) - @Override - public boolean equals(@Nullable String val1, @Nullable String val2) { - return FileUtilRt.pathsEqual(val1, val2); - } - }); + @JvmStatic + fun createCanonicalPathMap(): MutableMap = Object2ObjectOpenCustomHashMap(PathHashStrategy) + + @JvmStatic + fun createCanonicalFileMap(expected: Int): MutableMap { + return Object2ObjectOpenCustomHashMap(expected, FileHashStrategy) } - public static @NotNull Map createCanonicalFileMap() { - return new Object2ObjectOpenCustomHashMap<>(FILE_HASH_STRATEGY); - } + @JvmStatic + fun createCanonicalFileMap(map: Map): MutableMap = Object2ObjectOpenCustomHashMap(map, FileHashStrategy) - public static @NotNull Map createCanonicalFileMap(int expected) { - return new Object2ObjectOpenCustomHashMap<>(expected, FILE_HASH_STRATEGY); - } + @JvmStatic + fun createCanonicalFileSet(): MutableSet = ObjectOpenCustomHashSet(FileHashStrategy) - public static @NotNull Map createCanonicalFileMap(@NotNull Map map) { - Map result = createCanonicalFileMap(map.size()); - result.putAll(map); - return result; - } + @JvmStatic + fun createCanonicalFileSet(files: Collection): MutableSet = ObjectOpenCustomHashSet(files, FileHashStrategy) - public static @NotNull Set createCanonicalFileSet() { - return new ObjectOpenCustomHashSet<>(FILE_HASH_STRATEGY); - } + @JvmStatic + fun createCanonicalPathSet(): MutableSet = ObjectOpenCustomHashSet(PathHashStrategy) - public static @NotNull Set createCanonicalFileSet(@NotNull Collection files) { - Set set = createCanonicalFileSet(); - set.addAll(files); - return set; - } + @JvmStatic + fun createCanonicalPathSet(files: Collection): MutableSet = ObjectOpenCustomHashSet(files, PathHashStrategy) - public static @NotNull Set createCanonicalPathSet() { - return new ObjectOpenCustomHashSet<>(new PathSerializableHashStrategy()); - } + @JvmStatic + fun createCanonicalFilePathSet(): MutableSet = ObjectOpenCustomHashSet(FastUtilHashingStrategies.FILE_PATH_HASH_STRATEGY) - public static @NotNull Set createCanonicalPathSet(@NotNull Collection files) { - return new ObjectOpenCustomHashSet<>(files, new PathSerializableHashStrategy()); - } - - public static @NotNull Set createCanonicalFilePathSet() { - return new ObjectOpenCustomHashSet<>(FastUtilHashingStrategies.FILE_PATH_HASH_STRATEGY); - } - - public static @NotNull Set createCanonicalFileLinkedSet() { - return new ObjectLinkedOpenCustomHashSet<>(new Hash.Strategy() { - @Override - public int hashCode(@Nullable File o) { - return FileUtilRt.pathHashCode(o == null ? null : o.getPath()); - } - - @Override - public boolean equals(@Nullable File a, @Nullable File b) { - return FileUtilRt.pathsEqual(a == null ? null : a.getPath(), b == null ? null : b.getPath()); - } - }); - } - - private static final class PathSerializableHashStrategy implements FastUtilHashingStrategies.SerializableHashStrategy { - @Override - public int hashCode(@Nullable Path o) { - return FileUtilRt.pathHashCode(o == null ? null : o.toString()); - } - - @Override - public boolean equals(@Nullable Path a, @Nullable Path b) { - return FileUtilRt.pathsEqual(a == null ? null : a.toString(), b == null ? null : b.toString()); - } - } + @JvmStatic + fun createCanonicalFileLinkedSet(): MutableSet = ObjectLinkedOpenCustomHashSet(FileHashStrategy) +} + +@Internal +object FileHashStrategy : Hash.Strategy, Serializable { + override fun hashCode(o: File?): Int = FileUtilRt.pathHashCode(o?.path) + + override fun equals(a: File?, b: File?): Boolean = FileUtilRt.pathsEqual(a?.path, b?.path) +} + +private object PathHashStrategy : Hash.Strategy, Serializable { + override fun hashCode(o: Path?): Int = FileUtilRt.pathHashCode(o?.toString()) + + override fun equals(a: Path?, b: Path?): Boolean = FileUtilRt.pathsEqual(a?.toString(), b?.toString()) } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.kt index 900f17514406..a4afddb5f0d0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.kt +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.vcs.changes.ui import com.intellij.openapi.Disposable @@ -30,12 +30,12 @@ abstract class PartiallyExcludedFilesStateHolder( return getChangeListId(element)?.let { getExcludedFromCommitState(it) } ?: ExclusionState.NO_CHANGES } - protected val myUpdateQueue = + protected val updateQueue = MergingUpdateQueue(PartiallyExcludedFilesStateHolder::class.java.name, 300, true, MergingUpdateQueue.ANY_COMPONENT, this) private val lock = ReentrantReadWriteLock() - private val myIncludedElements: MutableSet = createElementsSet() - private val myTrackerExclusionStates: MutableMap = CollectionFactory.createCustomHashingStrategyMap(hashingStrategy) + private val includedElements = createElementsSet() + private val trackerExclusionStates = CollectionFactory.createCustomHashingStrategyMap(hashingStrategy) init { MyTrackerManagerListener().install(project) @@ -45,7 +45,8 @@ abstract class PartiallyExcludedFilesStateHolder( return CollectionFactory.createCustomHashingStrategySet(hashingStrategy).also { it.addAll(elements) } } - override fun dispose() = Unit + override fun dispose() { + } protected abstract val trackableElements: Sequence protected abstract fun getChangeListId(element: T): String? @@ -57,7 +58,7 @@ abstract class PartiallyExcludedFilesStateHolder( get() = trackableElements.mapNotNull { element -> findTrackerFor(element)?.let { tracker -> element to tracker } } private fun scheduleExclusionStatesUpdate() { - myUpdateQueue.queue(DisposableUpdate.createDisposable(myUpdateQueue, "updateExcludedFromCommit") { updateExclusionStates() }) + updateQueue.queue(DisposableUpdate.createDisposable(updateQueue, "updateExcludedFromCommit") { updateExclusionStates() }) } private inner class MyTrackerListener : PartialLocalLineStatusTracker.ListenerAdapter() { @@ -82,7 +83,7 @@ abstract class PartiallyExcludedFilesStateHolder( if (tracker !is PartialLocalLineStatusTracker) return tracker.getAffectedChangeListsIds().forEach { changeListId -> - findElementFor(tracker, changeListId)?.let { element -> tracker.setExcludedFromCommit(element, element !in myIncludedElements) } + findElementFor(tracker, changeListId)?.let { element -> tracker.setExcludedFromCommit(element, element !in includedElements) } } tracker.addListener(trackerListener, disposable) } @@ -97,13 +98,13 @@ abstract class PartiallyExcludedFilesStateHolder( val exclusionState = tracker.getExcludedFromCommitState(element) lock.write { - myTrackerExclusionStates -= element + trackerExclusionStates -= element if (exclusionState != ExclusionState.NO_CHANGES) { if (exclusionState != ExclusionState.ALL_EXCLUDED) { - myIncludedElements += element + includedElements += element } else { - myIncludedElements -= element + includedElements -= element } } } @@ -115,8 +116,8 @@ abstract class PartiallyExcludedFilesStateHolder( fun getIncludedSet(): Set { lock.read { - val set: MutableSet = createElementsSet(myIncludedElements) - myTrackerExclusionStates.forEach { (element, state) -> + val set: MutableSet = createElementsSet(includedElements) + trackerExclusionStates.forEach { (element, state) -> if (state == ExclusionState.ALL_EXCLUDED) set -= element else set += element } return set @@ -125,9 +126,9 @@ abstract class PartiallyExcludedFilesStateHolder( fun getExclusionState(element: T): ExclusionState { lock.read { - val trackerState = myTrackerExclusionStates[element] + val trackerState = trackerExclusionStates[element] if (trackerState != null) return trackerState - val isIncluded = element in myIncludedElements + val isIncluded = element in includedElements return if (isIncluded) ExclusionState.ALL_INCLUDED else ExclusionState.ALL_EXCLUDED } } @@ -144,11 +145,11 @@ abstract class PartiallyExcludedFilesStateHolder( @RequiresEdt private fun rebuildTrackerExclusionStates() { assert(lock.isWriteLocked) - myTrackerExclusionStates.clear() + trackerExclusionStates.clear() trackers.forEach { (element, tracker) -> val state = tracker.getExcludedFromCommitState(element) - if (state != ExclusionState.NO_CHANGES) myTrackerExclusionStates[element] = state + if (state != ExclusionState.NO_CHANGES) trackerExclusionStates[element] = state } } @@ -160,8 +161,8 @@ abstract class PartiallyExcludedFilesStateHolder( } lock.write { - myIncludedElements.clear() - myIncludedElements += elements + includedElements.clear() + includedElements += elements rebuildTrackerExclusionStates() } @@ -173,7 +174,7 @@ abstract class PartiallyExcludedFilesStateHolder( elements.forEach { findTrackerFor(it)?.setExcludedFromCommit(it, false) } lock.write { - myIncludedElements += elements + includedElements += elements rebuildTrackerExclusionStates() } @@ -188,7 +189,7 @@ abstract class PartiallyExcludedFilesStateHolder( lock.write { for (element in elements) { - myIncludedElements.remove(element) + includedElements.remove(element) } rebuildTrackerExclusionStates() } @@ -207,7 +208,7 @@ abstract class PartiallyExcludedFilesStateHolder( } lock.write { - myIncludedElements.retainAll(toRetain) + includedElements.retainAll(toRetain) rebuildTrackerExclusionStates() } diff --git a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt index fd1dd19ced8a..ab9d02d1f23d 100644 --- a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt +++ b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.vcs import com.intellij.openapi.util.Disposer @@ -46,7 +46,7 @@ abstract class BasePartiallyExcludedChangesTest : BaseLineStatusTrackerManagerTe } fun waitExclusionStateUpdate() { - myUpdateQueue.flush() + updateQueue.flush() } }