<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2016 <NAME> (github.com/kean).

import Foundation
#if os(macOS)
    import Cocoa
#else
    import UIKit
#endif

/// In-memory image cache.
///
/// The implementation must be thread safe.
public protocol Caching: class {
    /// Accesses the image associated with the given key.
    subscript(key: AnyHashable) -> Image? { get set }
}

public extension Caching {
    /// Accesses the image associated with the given request.
    subscript(request: Request) -> Image? {
        get { return self[Request.cacheKey(for: request)] }
        set { self[Request.cacheKey(for: request)] = newValue }
    }
}

/// Auto-purging memory cache with LRU cleanup.
public final class Cache: Caching {
    // We don't use `NSCache` because it's not LRU
    
    private var map = [AnyHashable: Node<CachedImage>]()
    private let list = LinkedList<CachedImage>()
    private let lock = Lock()
    
    /// The maximum total cost that the cache can hold.
    public var costLimit: Int { didSet { lock.sync { trim() } } }
    
    /// The maximum number of items that the cache can hold.
    public var countLimit: Int { didSet { lock.sync { trim() } } }
    
    /// The total cost of items in the cache.
    public private(set) var totalCost = 0
    
    /// The total number of items in the cache.
    public var totalCount: Int { return map.count }
    
    deinit {
        #if os(iOS) || os(tvOS)
            NotificationCenter.default.removeObserver(self)
        #endif
    }
    
    /// Initializes `Cache`.
    /// - parameter costLimit: Default value is calculated based on the amount
    /// of the available memory.
    public init(costLimit: Int = Cache.defaultCostLimit(), countLimit: Int = Int.max) {
        self.costLimit = costLimit
        self.countLimit = countLimit
        #if os(iOS) || os(tvOS)
            NotificationCenter.default.addObserver(self, selector: #selector(Cache.removeAll), name: .UIApplicationDidReceiveMemoryWarning, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(Cache.didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
        #endif
    }
    
    private static func defaultCostLimit() -> Int {
        let physicalMemory = ProcessInfo.processInfo.physicalMemory
        let ratio = physicalMemory <= (1024 * 1024 * 512 /* 512 Mb */) ? 0.1 : 0.2
        let limit = physicalMemory / UInt64(1 / ratio)
        return limit > UInt64(Int.max) ? Int.max : Int(limit)
    }
    
    /// Accesses the image associated with the given key.
    public subscript(key: AnyHashable) -> Image? {
        get {
            lock.lock()  // faster than `sync()`
            defer { lock.unlock() }
            
            if let node = map[key] {
                // bubble node up to the head
                list.remove(node)
                list.append(node)
                return node.value.image
            }
            return nil
        }
        set {
            lock.lock() // faster than `sync()`
            defer { lock.unlock() }
            
            if let image = newValue {
                add(node: Node(value: CachedImage(image: image, cost: cost(image), key: key)))
                trim()
            } else if let node = map[key] {
                remove(node: node)
            }
        }
    }
    
    private func add(node: Node<CachedImage>) {
        list.append(node)
        map[node.value.key] = node
        totalCost += node.value.cost
    }
    
    private func remove(node: Node<CachedImage>) {
        list.remove(node)
        map[node.value.key] = nil
        totalCost -= node.value.cost
    }
    
    /// Removes all cached images.
    public dynamic func removeAll() {
        lock.sync {
            map.removeAll()
            list.removeAll()
            totalCost = 0
        }
    }
    
    private func trim() {
        _trim(toCost: costLimit)
        _trim(toCount: countLimit)
    }
    
    private dynamic func didEnterBackground() {
        // Remove most of the stored items when entering background.
        // This behaviour is similar to `NSCache` (which removes all
        // items). This feature is not documented and may be subject
        // to change in future Nuke versions.
        lock.sync {
            trim(toCost: Int(Double(costLimit) * 0.1))
            trim(toCount: Int(Double(countLimit) * 0.1))
        }
    }
    
    /// Removes least recently used items from the cache until the total cost
    /// of the remaining items is less than the given cost limit.<commit_msg>Fix new lock in didEnterBackground<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2016 <NAME> (github.com/kean).

import Foundation
#if os(macOS)
    import Cocoa
#else
    import UIKit
#endif

/// In-memory image cache.
///
/// The implementation must be thread safe.
public protocol Caching: class {
    /// Accesses the image associated with the given key.
    subscript(key: AnyHashable) -> Image? { get set }
}

public extension Caching {
    /// Accesses the image associated with the given request.
    subscript(request: Request) -> Image? {
        get { return self[Request.cacheKey(for: request)] }
        set { self[Request.cacheKey(for: request)] = newValue }
    }
}

/// Auto-purging memory cache with LRU cleanup.
public final class Cache: Caching {
    // We don't use `NSCache` because it's not LRU
    
    private var map = [AnyHashable: Node<CachedImage>]()
    private let list = LinkedList<CachedImage>()
    private let lock = Lock()
    
    /// The maximum total cost that the cache can hold.
    public var costLimit: Int { didSet { lock.sync { trim() } } }
    
    /// The maximum number of items that the cache can hold.
    public var countLimit: Int { didSet { lock.sync { trim() } } }
    
    /// The total cost of items in the cache.
    public private(set) var totalCost = 0
    
    /// The total number of items in the cache.
    public var totalCount: Int { return map.count }
    
    deinit {
        #if os(iOS) || os(tvOS)
            NotificationCenter.default.removeObserver(self)
        #endif
    }
    
    /// Initializes `Cache`.
    /// - parameter costLimit: Default value is calculated based on the amount
    /// of the available memory.
    public init(costLimit: Int = Cache.defaultCostLimit(), countLimit: Int = Int.max) {
        self.costLimit = costLimit
        self.countLimit = countLimit
        #if os(iOS) || os(tvOS)
            NotificationCenter.default.addObserver(self, selector: #selector(Cache.removeAll), name: .UIApplicationDidReceiveMemoryWarning, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(Cache.didEnterBackground), name: .UIApplicationDidEnterBackground, object: nil)
        #endif
    }
    
    private static func defaultCostLimit() -> Int {
        let physicalMemory = ProcessInfo.processInfo.physicalMemory
        let ratio = physicalMemory <= (1024 * 1024 * 512 /* 512 Mb */) ? 0.1 : 0.2
        let limit = physicalMemory / UInt64(1 / ratio)
        return limit > UInt64(Int.max) ? Int.max : Int(limit)
    }
    
    /// Accesses the image associated with the given key.
    public subscript(key: AnyHashable) -> Image? {
        get {
            lock.lock()  // faster than `sync()`
            defer { lock.unlock() }
            
            if let node = map[key] {
                // bubble node up to the head
                list.remove(node)
                list.append(node)
                return node.value.image
            }
            return nil
        }
        set {
            lock.lock() // faster than `sync()`
            defer { lock.unlock() }
            
            if let image = newValue {
                add(node: Node(value: CachedImage(image: image, cost: cost(image), key: key)))
                trim()
            } else if let node = map[key] {
                remove(node: node)
            }
        }
    }
    
    private func add(node: Node<CachedImage>) {
        list.append(node)
        map[node.value.key] = node
        totalCost += node.value.cost
    }
    
    private func remove(node: Node<CachedImage>) {
        list.remove(node)
        map[node.value.key] = nil
        totalCost -= node.value.cost
    }
    
    /// Removes all cached images.
    public dynamic func removeAll() {
        lock.sync {
            map.removeAll()
            list.removeAll()
            totalCost = 0
        }
    }
    
    private func trim() {
        _trim(toCost: costLimit)
        _trim(toCount: countLimit)
    }
    
    private dynamic func didEnterBackground() {
        // Remove most of the stored items when entering background.
        // This behaviour is similar to `NSCache` (which removes all
        // items). This feature is not documented and may be subject
        // to change in future Nuke versions.
        lock.sync {
            _trim(toCost: Int(Double(costLimit) * 0.1))
            _trim(toCount: Int(Double(countLimit) * 0.1))
        }
    }
    
    /// Removes least recently used items from the cache until the total cost
    /// of the remaining items is less than the given cost limit.
{'fraction_non_alphanumeric': 0.10348193947282786, 'fraction_numerical': 0.007159127888057273, 'mean_word_length': 1.9560756652773326, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '7177544', 'n_tokens_mistral': 2634, 'n_tokens_neox': 2531, 'n_words': 817}