<commit_before>package com.github.panpf.sketch.cache.internal

import android.content.ComponentCallbacks2
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.cache.BitmapPool
import com.github.panpf.sketch.decode.internal.logString
import com.github.panpf.sketch.decode.internal.samplingSize
import com.github.panpf.sketch.decode.internal.samplingSizeForRegion
import com.github.panpf.sketch.util.Logger
import com.github.panpf.sketch.util.allocationByteCountCompat
import com.github.panpf.sketch.util.format
import com.github.panpf.sketch.util.formatFileSize
import com.github.panpf.sketch.util.getTrimLevelName
import com.github.panpf.sketch.util.isAndSupportHardware
import com.github.panpf.sketch.util.recycle.AttributeStrategy
import com.github.panpf.sketch.util.recycle.LruPoolStrategy
import com.github.panpf.sketch.util.recycle.SizeConfigStrategy
import com.github.panpf.sketch.util.toHexString
import java.util.concurrent.atomic.AtomicInteger

/**
 * Release the cached [Bitmap] reuse pool according to the least-used rule
 */
class LruBitmapPool constructor(
    override val maxSize: Long,
    val allowedConfigs: Set<Bitmap.Config?> =
        Bitmap.Config.values().run {
            if (Build.VERSION.SDK_INT >= 19) {
                listOf(null).plus(this).toSet()
            } else {
                this.toSet()
            }
        }
) : BitmapPool {

    companion object {
        private const val MODULE = "LruBitmapPool"
    }

    private var _size: Long = 0L
    private var hits = 0
    private var misses = 0
    private var puts = 0
    private var evictions = 0
    private val strategy: LruPoolStrategy =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            SizeConfigStrategy()
        } else {
            AttributeStrategy()
        }
    private val getCount = AtomicInteger()
    private val hitCount = AtomicInteger()

    override var logger: Logger? = null
    override val size: Long
        get() = _size

    override fun put(bitmap: Bitmap): Boolean {
        if (bitmap.isRecycled) {
            logger?.w(MODULE, "put. Reject. Recycled, ${bitmap.logString}")
            return false
        }
        if (!bitmap.isMutable) {
            logger?.w(MODULE, "put. Reject. Immutable, ${bitmap.logString}")
            return false
        }
        val bitmapSize = strategy.getSize(bitmap).toLong()
        if (bitmapSize > maxSize * 0.7f) {
            logger?.w(MODULE) {
                "put. Reject. Too big ${bitmapSize.formatFileSize()}, maxSize ${maxSize.formatFileSize()}, ${bitmap.logString}"
            }
            return false
        }
        if (!allowedConfigs.contains(bitmap.config)) {
            logger?.w(MODULE) {
                "put. Reject. Disallowed config: ${bitmap.config}, ${bitmap.logString}"
            }
            return false
        }

        synchronized(this) {
            strategy.put(bitmap)
            puts++
            this._size += bitmapSize
            trimToSize(maxSize)
            logger?.d(MODULE) {
                "put. Successful. ${size.formatFileSize()}. ${bitmap.logString}"
            }
        }
        return true
    }

    override fun getDirty(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
        // Config will be null for non public config types, which can lead to transformations naively passing in
        // null as the requested config here. See issue #194.
        return synchronized(this) {
            strategy[width, height, config].apply {
                val getCount = getCount.addAndGet(1)
                val hitCount = if (this != null) {
                    hitCount.addAndGet(1)
                } else {
                    hitCount.get()
                }
                if (getCount == Int.MAX_VALUE || hitCount == Int.MAX_VALUE) {
                    this@LruBitmapPool.getCount.set(0)
                    this@LruBitmapPool.hitCount.set(0)
                }
                if (this == null) {
                    misses++
                } else {
                    hits++
                    _size -= strategy.getSize(this)<commit_msg>improve: Clear enough space before put<commit_after>package com.github.panpf.sketch.cache.internal

import android.content.ComponentCallbacks2
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import com.github.panpf.sketch.decode.internal.ImageFormat
import com.github.panpf.sketch.cache.BitmapPool
import com.github.panpf.sketch.decode.internal.logString
import com.github.panpf.sketch.decode.internal.samplingSize
import com.github.panpf.sketch.decode.internal.samplingSizeForRegion
import com.github.panpf.sketch.util.Logger
import com.github.panpf.sketch.util.allocationByteCountCompat
import com.github.panpf.sketch.util.format
import com.github.panpf.sketch.util.formatFileSize
import com.github.panpf.sketch.util.getTrimLevelName
import com.github.panpf.sketch.util.isAndSupportHardware
import com.github.panpf.sketch.util.recycle.AttributeStrategy
import com.github.panpf.sketch.util.recycle.LruPoolStrategy
import com.github.panpf.sketch.util.recycle.SizeConfigStrategy
import com.github.panpf.sketch.util.toHexString
import java.util.concurrent.atomic.AtomicInteger

/**
 * Release the cached [Bitmap] reuse pool according to the least-used rule
 */
class LruBitmapPool constructor(
    override val maxSize: Long,
    val allowedConfigs: Set<Bitmap.Config?> =
        Bitmap.Config.values().run {
            if (Build.VERSION.SDK_INT >= 19) {
                listOf(null).plus(this).toSet()
            } else {
                this.toSet()
            }
        }
) : BitmapPool {

    companion object {
        private const val MODULE = "LruBitmapPool"
    }

    private var _size: Long = 0L
    private var hits = 0
    private var misses = 0
    private var puts = 0
    private var evictions = 0
    private val strategy: LruPoolStrategy =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            SizeConfigStrategy()
        } else {
            AttributeStrategy()
        }
    private val getCount = AtomicInteger()
    private val hitCount = AtomicInteger()

    override var logger: Logger? = null
    override val size: Long
        get() = _size

    override fun put(bitmap: Bitmap): Boolean {
        if (bitmap.isRecycled) {
            logger?.w(MODULE, "put. Reject. Recycled, ${bitmap.logString}")
            return false
        }
        if (!bitmap.isMutable) {
            logger?.w(MODULE, "put. Reject. Immutable, ${bitmap.logString}")
            return false
        }
        val bitmapSize = strategy.getSize(bitmap).toLong()
        if (bitmapSize > maxSize * 0.7f) {
            logger?.w(MODULE) {
                "put. Reject. Too big ${bitmapSize.formatFileSize()}, maxSize ${maxSize.formatFileSize()}, ${bitmap.logString}"
            }
            return false
        }
        if (!allowedConfigs.contains(bitmap.config)) {
            logger?.w(MODULE) {
                "put. Reject. Disallowed config: ${bitmap.config}, ${bitmap.logString}"
            }
            return false
        }

        synchronized(this) {
            trimToSize(maxSize - bitmapSize)
            strategy.put(bitmap)
            puts++
            this._size += bitmapSize
            logger?.d(MODULE) {
                "put. Successful. ${size.formatFileSize()}. ${bitmap.logString}"
            }
        }
        return true
    }

    override fun getDirty(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
        // Config will be null for non public config types, which can lead to transformations naively passing in
        // null as the requested config here. See issue #194.
        return synchronized(this) {
            strategy[width, height, config].apply {
                val getCount = getCount.addAndGet(1)
                val hitCount = if (this != null) {
                    hitCount.addAndGet(1)
                } else {
                    hitCount.get()
                }
                if (getCount == Int.MAX_VALUE || hitCount == Int.MAX_VALUE) {
                    this@LruBitmapPool.getCount.set(0)
                    this@LruBitmapPool.hitCount.set(0)
                }
                if (this == null) {
                    misses++
                } else {
                    hits++
                    _size -= strategy.getSize(this)
{'fraction_non_alphanumeric': 0.10051186598417869, 'fraction_numerical': 0.003955328059562588, 'mean_word_length': 2.122775154377043, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '536059', 'n_tokens_mistral': 2454, 'n_tokens_neox': 2396, 'n_words': 527}