mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[workspace-model] Optimize memory consumption by new VirtualFileUrlManagerImpl
GitOrigin-RevId: 97900049061297e1c32feb8c79570f4a6f0f2abf
This commit is contained in:
committed by
intellij-monorepo-bot
parent
325c9cf27e
commit
25af0077d0
+2
-2
@@ -29,7 +29,7 @@ class VirtualFileNameStoreTest {
|
|||||||
Assert.assertEquals(3, store.generateIdForName("c"))
|
Assert.assertEquals(3, store.generateIdForName("c"))
|
||||||
store.removeName("b")
|
store.removeName("b")
|
||||||
store.removeName("a")
|
store.removeName("a")
|
||||||
Assert.assertEquals(2, store.generateIdForName("e"))
|
Assert.assertEquals(4, store.generateIdForName("e"))
|
||||||
Assert.assertEquals(1, store.generateIdForName("f"))
|
Assert.assertEquals(5, store.generateIdForName("f"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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<Int> = 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+23
-15
@@ -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.
|
// 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
|
package com.intellij.workspace.ide
|
||||||
|
|
||||||
import com.intellij.util.containers.BidirectionalMap
|
import gnu.trove.THashMap
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
|
||||||
internal class VirtualFileNameStore {
|
internal class VirtualFileNameStore {
|
||||||
private val generator = IdGenerator()
|
private val generator = IdGenerator()
|
||||||
private val nameStore = BidirectionalMap<String, IdPerCount>()
|
private val name2IdStore = THashMap<String, IdPerCount>()
|
||||||
|
private val id2NameStore = THashMap<Int, String>()
|
||||||
|
|
||||||
fun generateIdForName(name: String): Int {
|
fun generateIdForName(name: String): Int {
|
||||||
val idPerCount = nameStore[name]
|
val idPerCount = name2IdStore[name]
|
||||||
if (idPerCount != null) {
|
if (idPerCount != null) {
|
||||||
idPerCount.usageCount++
|
idPerCount.usageCount++
|
||||||
return idPerCount.id
|
return idPerCount.id
|
||||||
} else {
|
} else {
|
||||||
val id = generator.generateId()
|
val id = generator.generateId()
|
||||||
nameStore[name] = IdPerCount(id, 1)
|
name2IdStore[name] = IdPerCount(id, 1)
|
||||||
|
id2NameStore[id] = name
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeName(name: String) {
|
fun removeName(name: String) {
|
||||||
val idPerCount = nameStore[name] ?: return
|
val idPerCount = name2IdStore[name] ?: return
|
||||||
if (idPerCount.usageCount == 1L) {
|
if (idPerCount.usageCount == 1L) {
|
||||||
nameStore.remove(name)
|
name2IdStore.remove(name)
|
||||||
generator.releaseId(idPerCount.id)
|
id2NameStore.remove(idPerCount.id)
|
||||||
} else {
|
} else {
|
||||||
idPerCount.usageCount--
|
idPerCount.usageCount--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNameForId(id: Int): String? {
|
fun getNameForId(id: Int): String? = id2NameStore[id]
|
||||||
val list = nameStore.getKeysByValue(IdPerCount(id, 1)) ?: return null
|
|
||||||
if (list.isEmpty()) return null
|
|
||||||
assert(list.size == 1)
|
|
||||||
return list[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getIdForName(name: String) = nameStore[name]?.id
|
fun getIdForName(name: String) = name2IdStore[name]?.id
|
||||||
|
|
||||||
@TestOnly
|
@TestOnly
|
||||||
fun clear() {
|
fun clear() {
|
||||||
nameStore.clear()
|
name2IdStore.clear()
|
||||||
|
id2NameStore.clear()
|
||||||
generator.clear()
|
generator.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,4 +56,14 @@ private data class IdPerCount(val id: Int, var usageCount: Long) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode() = 31 * id.hashCode()
|
override fun hashCode() = 31 * id.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class IdGenerator {
|
||||||
|
private var generator: Int = 0
|
||||||
|
fun generateId() = ++generator
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
fun clear() {
|
||||||
|
generator = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+34
-14
@@ -4,16 +4,18 @@ package com.intellij.workspace.ide
|
|||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.util.SmartList
|
||||||
import com.intellij.workspace.api.VirtualFileUrl
|
import com.intellij.workspace.api.VirtualFileUrl
|
||||||
import com.intellij.workspace.api.VirtualFileUrlManager
|
import com.intellij.workspace.api.VirtualFileUrlManager
|
||||||
|
import gnu.trove.THashMap
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
||||||
private val idGenerator= IdGenerator()
|
private val idGenerator= IdGenerator()
|
||||||
private val EMPTY_URL = VirtualFileUrl(0, this)
|
private val EMPTY_URL = VirtualFileUrl(0, this)
|
||||||
private val fileNameStore = VirtualFileNameStore()
|
private val fileNameStore = VirtualFileNameStore()
|
||||||
private val id2NodeMapping = HashMap<Int, FilePathNode>()
|
private val id2NodeMapping = THashMap<Int, FilePathNode>()
|
||||||
private var segmentId2RootNodeMapping = mutableMapOf<Int, FilePathNode>()
|
private var segmentId2RootNodeMapping = THashMap<Int, FilePathNode>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getInstance(project: Project): VirtualFileUrlManager = project.service<VirtualFileUrlManagerImpl>()
|
fun getInstance(project: Project): VirtualFileUrlManager = project.service<VirtualFileUrlManagerImpl>()
|
||||||
@@ -82,12 +84,12 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val node = latestNode.children.find { it.contentId == nameId }
|
val node = latestNode.findChild(nameId)
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
val nodeId = idGenerator.generateId()
|
val nodeId = idGenerator.generateId()
|
||||||
val newNode = FilePathNode(nodeId, nameId, latestNode)
|
val newNode = FilePathNode(nodeId, nameId, latestNode)
|
||||||
id2NodeMapping[nodeId] = newNode
|
id2NodeMapping[nodeId] = newNode
|
||||||
latestNode.children.add(newNode)
|
latestNode.addChild(newNode)
|
||||||
latestNode = newNode
|
latestNode = newNode
|
||||||
// If it's the latest name of folder or files, save entity Id as node value
|
// If it's the latest name of folder or files, save entity Id as node value
|
||||||
if (index == latestElement) return VirtualFileUrl(nodeId, this)
|
if (index == latestElement) return VirtualFileUrl(nodeId, this)
|
||||||
@@ -106,27 +108,25 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
|||||||
println("File not found")
|
println("File not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (node.children.isNotEmpty()) return
|
if (!node.isEmpty()) return
|
||||||
|
|
||||||
var currentNode: FilePathNode = node
|
var currentNode: FilePathNode = node
|
||||||
do {
|
do {
|
||||||
val parent = currentNode.parent
|
val parent = currentNode.parent
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
if (currentNode === findRootNode(currentNode.contentId) && currentNode.children.isEmpty()) {
|
if (currentNode === findRootNode(currentNode.contentId) && currentNode.isEmpty()) {
|
||||||
removeNameUsage(currentNode.contentId)
|
removeNameUsage(currentNode.contentId)
|
||||||
idGenerator.releaseId(currentNode.nodeId)
|
|
||||||
id2NodeMapping.remove(currentNode.nodeId)
|
id2NodeMapping.remove(currentNode.nodeId)
|
||||||
segmentId2RootNodeMapping.remove(currentNode.contentId)
|
segmentId2RootNodeMapping.remove(currentNode.contentId)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.children.remove(currentNode)
|
parent.removeChild(currentNode)
|
||||||
removeNameUsage(currentNode.contentId)
|
removeNameUsage(currentNode.contentId)
|
||||||
idGenerator.releaseId(currentNode.nodeId)
|
|
||||||
id2NodeMapping.remove(currentNode.nodeId)
|
id2NodeMapping.remove(currentNode.nodeId)
|
||||||
currentNode = parent
|
currentNode = parent
|
||||||
} while (currentNode.children.isEmpty())
|
} while (currentNode.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun update(oldPath: String, newPath: String) {
|
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
|
if (index == latestElement) return it
|
||||||
latestNode = it
|
latestNode = it
|
||||||
} ?: return null
|
} ?: return null
|
||||||
@@ -202,7 +202,28 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) {
|
private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) {
|
||||||
val children: MutableSet<FilePathNode> = mutableSetOf()
|
private var children: MutableList<FilePathNode>? = 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 {
|
override fun toString(): String {
|
||||||
val buffer = StringBuilder()
|
val buffer = StringBuilder()
|
||||||
@@ -213,7 +234,7 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
|||||||
private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) {
|
private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) {
|
||||||
val name = this@VirtualFileUrlManagerImpl.fileNameStore.getNameForId(contentId)
|
val name = this@VirtualFileUrlManagerImpl.fileNameStore.getNameForId(contentId)
|
||||||
buffer.append("$prefix $name\n")
|
buffer.append("$prefix $name\n")
|
||||||
val iterator = children.iterator()
|
val iterator = children?.iterator() ?: return
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val next = iterator.next()
|
val next = iterator.next()
|
||||||
if (iterator.hasNext()) {
|
if (iterator.hasNext()) {
|
||||||
@@ -225,5 +246,4 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user