diff --git a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt b/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt index 162146a50b9f..9b38a14262d6 100644 --- a/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt +++ b/platform/workspaceModel-ide-tests/testSrc/com/intellij/workspace/jps/VirtualFileNameStoreTest.kt @@ -29,7 +29,7 @@ class VirtualFileNameStoreTest { Assert.assertEquals(3, store.generateIdForName("c")) store.removeName("b") store.removeName("a") - Assert.assertEquals(2, store.generateIdForName("e")) - Assert.assertEquals(1, store.generateIdForName("f")) + Assert.assertEquals(4, store.generateIdForName("e")) + Assert.assertEquals(5, store.generateIdForName("f")) } } \ No newline at end of file diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/IdGenerator.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/IdGenerator.kt deleted file mode 100644 index 699d1d4f7beb..000000000000 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/IdGenerator.kt +++ /dev/null @@ -1,18 +0,0 @@ -// 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. -package com.intellij.workspace.ide - -import org.jetbrains.annotations.TestOnly -import java.util.* - -internal class IdGenerator { - private val freeIdsQueue: Queue = LinkedList() - private var generator: Int = 0 - fun generateId() = freeIdsQueue.poll() ?: ++generator - fun releaseId(id: Int) = freeIdsQueue.add(id) - - @TestOnly - fun clear() { - generator = 0 - freeIdsQueue.clear() - } -} \ No newline at end of file diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt index 973d5b5c58d2..70a0382eafa8 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileNameStore.kt @@ -1,47 +1,45 @@ // 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. package com.intellij.workspace.ide -import com.intellij.util.containers.BidirectionalMap +import gnu.trove.THashMap import org.jetbrains.annotations.TestOnly internal class VirtualFileNameStore { private val generator = IdGenerator() - private val nameStore = BidirectionalMap() + private val name2IdStore = THashMap() + private val id2NameStore = THashMap() fun generateIdForName(name: String): Int { - val idPerCount = nameStore[name] + val idPerCount = name2IdStore[name] if (idPerCount != null) { idPerCount.usageCount++ return idPerCount.id } else { val id = generator.generateId() - nameStore[name] = IdPerCount(id, 1) + name2IdStore[name] = IdPerCount(id, 1) + id2NameStore[id] = name return id } } fun removeName(name: String) { - val idPerCount = nameStore[name] ?: return + val idPerCount = name2IdStore[name] ?: return if (idPerCount.usageCount == 1L) { - nameStore.remove(name) - generator.releaseId(idPerCount.id) + name2IdStore.remove(name) + id2NameStore.remove(idPerCount.id) } else { idPerCount.usageCount-- } } - fun getNameForId(id: Int): String? { - val list = nameStore.getKeysByValue(IdPerCount(id, 1)) ?: return null - if (list.isEmpty()) return null - assert(list.size == 1) - return list[0] - } + fun getNameForId(id: Int): String? = id2NameStore[id] - fun getIdForName(name: String) = nameStore[name]?.id + fun getIdForName(name: String) = name2IdStore[name]?.id @TestOnly fun clear() { - nameStore.clear() + name2IdStore.clear() + id2NameStore.clear() generator.clear() } } @@ -58,4 +56,14 @@ private data class IdPerCount(val id: Int, var usageCount: Long) { } override fun hashCode() = 31 * id.hashCode() +} + +internal class IdGenerator { + private var generator: Int = 0 + fun generateId() = ++generator + + @TestOnly + fun clear() { + generator = 0 + } } \ No newline at end of file diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt index b65450a86048..1bd5d900434d 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/ide/VirtualFileUrlManagerImpl.kt @@ -4,16 +4,18 @@ package com.intellij.workspace.ide import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.SmartList import com.intellij.workspace.api.VirtualFileUrl import com.intellij.workspace.api.VirtualFileUrlManager +import gnu.trove.THashMap import kotlin.collections.set class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { private val idGenerator= IdGenerator() private val EMPTY_URL = VirtualFileUrl(0, this) private val fileNameStore = VirtualFileNameStore() - private val id2NodeMapping = HashMap() - private var segmentId2RootNodeMapping = mutableMapOf() + private val id2NodeMapping = THashMap() + private var segmentId2RootNodeMapping = THashMap() companion object { fun getInstance(project: Project): VirtualFileUrlManager = project.service() @@ -82,12 +84,12 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { } } - val node = latestNode.children.find { it.contentId == nameId } + val node = latestNode.findChild(nameId) if (node == null) { val nodeId = idGenerator.generateId() val newNode = FilePathNode(nodeId, nameId, latestNode) id2NodeMapping[nodeId] = newNode - latestNode.children.add(newNode) + latestNode.addChild(newNode) latestNode = newNode // If it's the latest name of folder or files, save entity Id as node value if (index == latestElement) return VirtualFileUrl(nodeId, this) @@ -106,27 +108,25 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { println("File not found") return } - if (node.children.isNotEmpty()) return + if (!node.isEmpty()) return var currentNode: FilePathNode = node do { val parent = currentNode.parent if (parent == null) { - if (currentNode === findRootNode(currentNode.contentId) && currentNode.children.isEmpty()) { + if (currentNode === findRootNode(currentNode.contentId) && currentNode.isEmpty()) { removeNameUsage(currentNode.contentId) - idGenerator.releaseId(currentNode.nodeId) id2NodeMapping.remove(currentNode.nodeId) segmentId2RootNodeMapping.remove(currentNode.contentId) } return } - parent.children.remove(currentNode) + parent.removeChild(currentNode) removeNameUsage(currentNode.contentId) - idGenerator.releaseId(currentNode.nodeId) id2NodeMapping.remove(currentNode.nodeId) currentNode = parent - } while (currentNode.children.isEmpty()) + } while (currentNode.isEmpty()) } internal fun update(oldPath: String, newPath: String) { @@ -157,7 +157,7 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { } } - latestNode.children.find { it.contentId == nameId }?.let { + latestNode.findChild(nameId)?.let { if (index == latestElement) return it latestNode = it } ?: return null @@ -202,7 +202,28 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { } private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) { - val children: MutableSet = mutableSetOf() + private var children: MutableList? = null + + internal fun findChild(nameId: Int): FilePathNode? { + // If search of child node will be slow, replace SmartList to THashSet + // For now SmartList reduce 500Kb memory on IDEA project + return children?.find { it.contentId == nameId } + } + + internal fun addChild(newNode: FilePathNode) { + createChildrenList() + children!!.add(newNode) + } + + internal fun removeChild(node: FilePathNode) { + children?.remove(node) + } + + internal fun isEmpty() = children == null || children!!.isEmpty() + + private fun createChildrenList() { + if (children == null) children = SmartList() + } override fun toString(): String { val buffer = StringBuilder() @@ -213,7 +234,7 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) { val name = this@VirtualFileUrlManagerImpl.fileNameStore.getNameForId(contentId) buffer.append("$prefix $name\n") - val iterator = children.iterator() + val iterator = children?.iterator() ?: return while (iterator.hasNext()) { val next = iterator.next() if (iterator.hasNext()) { @@ -225,5 +246,4 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() { } } } - } \ No newline at end of file