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

commonJsMain.com.seiko.imageloader.util.LruCache.kt Maven / Gradle / Ivy

The newest version!
package com.seiko.imageloader.util

import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.internal.SynchronizedObject
import kotlinx.coroutines.internal.synchronized

@OptIn(InternalCoroutinesApi::class)
actual open class LruCache actual constructor(maxSize: Int) {

    private var _maxSize = 0
    private var _size = 0
    private var _putCount = 0
    private var _createCount = 0
    private var _evictionCount = 0
    private var _hitCount = 0
    private var _missCount = 0

    private val map: LinkedHashMap

    private val syncObject = SynchronizedObject()

    actual fun size() = synchronized(syncObject) { _size }

    actual fun maxSize() = synchronized(syncObject) { _maxSize }

    init {
        require(maxSize > 0) { "maxSize <= 0" }
        this._maxSize = maxSize
        this.map = LinkedHashMap(0, 0.75f)
    }

    open fun resize(maxSize: Int) {
        require(maxSize > 0) { "maxSize <= 0" }
        synchronized(syncObject) { this._maxSize = maxSize }
        trimToSize(maxSize)
    }

    actual operator fun get(key: K): V? {
        var mapValue: V?
        synchronized(syncObject) {
            mapValue = map[key]
            if (mapValue != null) {
                _hitCount++
                return mapValue
            }
            _missCount++
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */
        val createdValue: V = create(key) ?: return null
        synchronized(syncObject) {
            _createCount++
            mapValue = map.put(key, createdValue)
            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue!!)
            } else {
                _size += safeSizeOf(key, createdValue)
            }
        }
        return if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue)
            mapValue
        } else {
            trimToSize(_maxSize)
            createdValue
        }
    }

    actual fun put(key: K, value: V): V? {
        var previous: V?
        synchronized(syncObject) {
            _putCount++
            _size += safeSizeOf(key, value)
            previous = map.put(key, value)
            previous?.let {
                _size -= safeSizeOf(key, it)
            }
        }
        previous?.let {
            entryRemoved(false, key, it, value)
        }
        trimToSize(_maxSize)
        return previous
    }

    open fun trimToSize(maxSize: Int) {
        while (true) {
            var key: K? = null
            var value: V? = null
            synchronized(syncObject) {
                check(!(_size < 0 || map.isEmpty() && _size != 0)) {
                    (
                        this::class.simpleName +
                            ".sizeOf() is reporting inconsistent results!"
                        )
                }
                if (_size <= maxSize || map.isEmpty()) {
                    return
                }
                val (key1, value1) = map.entries.iterator().next()
                key = key1
                value = value1
                map.remove(key)
                _size -= safeSizeOf(key!!, value!!)
                _evictionCount++
            }
            entryRemoved(true, key!!, value!!, null)
        }
    }

    actual fun remove(key: K): V? {
        var previous: V?
        synchronized(syncObject) {
            previous = map.remove(key)
            previous?.let {
                _size -= safeSizeOf(key, it)
            }
        }
        previous?.let {
            entryRemoved(false, key, it, null)
        }
        return previous
    }

    protected actual open fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) = Unit

    protected actual open fun create(key: K): V? = null

    private fun safeSizeOf(key: K, value: V): Int {
        val result: Int = sizeOf(key, value)
        check(result >= 0) { "Negative size: $key=$value" }
        return result
    }

    protected actual open fun sizeOf(key: K, value: V): Int = 1

    actual fun evictAll() {
        trimToSize(-1) // -1 will evict 0-sized elements
    }

    actual fun snapshot(): MutableMap {
        val copy = LinkedHashMap()
        synchronized(syncObject) {
            map.entries.forEach { (key, value) ->
                copy[key] = value
            }
        }
        return copy
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy