All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.nacular.doodle.utils.LeastRecentlyUsedCache.kt Maven / Gradle / Ivy

package io.nacular.doodle.utils


/**
 * Created by Nicholas Eddy on 3/22/20.
 */
public class LeastRecentlyUsedCache(private val maxSize: Int): MutableMap {
    private inner class Entry(var value: V, var key: K) {
        var left : Entry? = null
        var right: Entry? = null
    }

    private val hashMap = mutableMapOf()
    private var start   = null as LeastRecentlyUsedCache.Entry?
    private var end     = null as LeastRecentlyUsedCache.Entry?

    // FIXME: File Kotlin bug for this, which fails w/ ClassCastException whenever the key isn't found
//    override operator fun get(key: K): V? = hashMap[key]?.let {
//        removeNode(it)
//        addAtTop  (it)
//        return it.value
//    }

    override operator fun get(key: K): V? {
        return hashMap[key]?.let {
            removeNode(it)
            addAtTop  (it)
            return it.value
        }
    }


    override val entries: MutableSet>
        get() = hashMap.entries.map {
        object: MutableMap.MutableEntry {
            override var key   = it.key
            override var value = it.value.value
            override fun setValue(newValue: V): V {
                val result = it.value.value

                it.value.value = newValue

                return result
            }
        }
    }.toMutableSet()

    override val keys  : MutableSet  get() = hashMap.keys
    override val size  : Int            get() = hashMap.size
    override val values: MutableList get() = hashMap.values.map { it.value }.toMutableList()

    override fun containsKey(key: K): Boolean = hashMap.containsKey(key)

    override fun containsValue(value: V): Boolean = value in values

    override fun isEmpty(): Boolean = hashMap.isEmpty()

    override fun clear() {
        hashMap.clear()
        start = null
        end   = null
    }

    override fun put(key: K, value: V): V? {
        var result = value

        when (val entry = hashMap[key]) {
            null -> {
                val newNode = Entry(key = key, value = value)

                // We have reached maximum size so need to make room for new element.
                if (hashMap.size >= maxSize) {
                    end?.let { end ->
                        hashMap.remove(end.key)
                        removeNode(end)
                        addAtTop(newNode)
                    }
                } else {
                    addAtTop(newNode)
                }

                if (end != null) {
                    hashMap[key] = newNode
                }
            }
            else -> {
                result = entry.value
                entry.value = value
                removeNode(entry)
                addAtTop(entry)
            }
        }

        return result
    }

    override fun putAll(from: Map) {
        from.forEach {
            this[it.key] = it.value
        }
    }

    override fun remove(key: K): V? = hashMap.remove(key)?.also {
        removeNode(it)
    }?.value


    private fun addAtTop(node: Entry) {
        node.left  = null
        node.right = start

        start?.let { it.left = node }

        start = node

        if (end == null) end = start
    }

    private fun removeNode(node: Entry) {
        when (val l = node.left) {
            null ->   start = node.right
            else -> l.right = node.right
        }
        when (val r = node.right) {
            null ->    end = node.left
            else -> r.left = node.left
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy