diff --git a/Package.swift b/Package.swift index 302b327..47b6113 100644 --- a/Package.swift +++ b/Package.swift @@ -13,23 +13,24 @@ let package = Package( .macOS(.v13) ], products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "DataCacheKit", targets: ["DataCacheKit"]), ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], + dependencies: [], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "DataCacheKit", - dependencies: []), + dependencies: ["LRUCache"]), .testTarget( name: "DataCacheKitTests", dependencies: ["DataCacheKit"]), + + .target( + name: "LRUCache", + dependencies: []), + .testTarget( + name: "LRUCacheTests", + dependencies: ["LRUCache"]), ] ) diff --git a/Sources/DataCacheKit/MemoryCache.swift b/Sources/DataCacheKit/MemoryCache.swift index 3303462..36b538a 100644 --- a/Sources/DataCacheKit/MemoryCache.swift +++ b/Sources/DataCacheKit/MemoryCache.swift @@ -1,5 +1,6 @@ import Foundation import OSLog +import LRUCache public actor MemoryCache: Caching { public nonisolated let options: Options diff --git a/Sources/DataCacheKit/Utils/LRUCache.swift b/Sources/LRUCache/LRUCache.swift similarity index 84% rename from Sources/DataCacheKit/Utils/LRUCache.swift rename to Sources/LRUCache/LRUCache.swift index e324d7b..87af5ee 100644 --- a/Sources/DataCacheKit/Utils/LRUCache.swift +++ b/Sources/LRUCache/LRUCache.swift @@ -1,36 +1,41 @@ // https://github.com/swiftlang/swift-corelibs-foundation/blob/25d044f2c4ceb635d9f714f588673fd7a29790c1/Sources/Foundation/NSCache.swift import os -struct LRUCache: ~Copyable, Sendable { - var totalCostLimit: Int { +public struct LRUCache: ~Copyable, Sendable { + public var totalCostLimit: Int { get { entries.withLock { $0.totalCostLimit } } nonmutating set { entries.withLock { $0.totalCostLimit = newValue } } } - var countLimit: Int { + public var countLimit: Int { get { entries.withLock { $0.countLimit } } nonmutating set { entries.withLock { $0.countLimit = newValue } } } private let entries = OSAllocatedUnfairLock(initialState: .init()) - func value(forKey key: Key) -> Value? { + public init() {} + + public func value(forKey key: Key) -> Value? { entries.withLock { entries in - entries.values[key]?.value + guard let entry = entries.values[key] else { return nil } + entries.remove(entry) + entries.insert(entry) + return entry.value } } - func setValue(_ value: Value, forKey key: Key) { + public func setValue(_ value: Value, forKey key: Key) { setValue(value, forKey: key, cost: 0) } - func setValue(_ value: Value, forKey key: Key, cost: Int) { + public func setValue(_ value: Value, forKey key: Key, cost: Int) { entries.withLock { entries in entries.set(value, forKey: key, cost: cost) } } - func removeValue(forKey key: Key) { + public func removeValue(forKey key: Key) { entries.withLock { entries in if let entry = entries.values.removeValue(forKey: key) { entries.totalCost -= entry.cost @@ -39,7 +44,7 @@ struct LRUCache: ~Copyable, Sendable } } - func removeAllValues() { + public func removeAllValues() { entries.withLock { entiries in entiries.removeAll() } @@ -47,7 +52,7 @@ struct LRUCache: ~Copyable, Sendable } extension LRUCache { - subscript (_ key: Key, cost cost: Int = 0) -> Value? { + public subscript (_ key: Key, cost cost: Int = 0) -> Value? { get { value(forKey: key) } @@ -155,6 +160,9 @@ private extension LRUCache { if entry === tail { tail = oldPrev } + + entry.next = nil + entry.prev = nil } mutating func removeAll() { diff --git a/Tests/DataCacheKitTests/LRUCacheTests.swift b/Tests/LRUCacheTests/LRUCacheTests.swift similarity index 91% rename from Tests/DataCacheKitTests/LRUCacheTests.swift rename to Tests/LRUCacheTests/LRUCacheTests.swift index 0c8f9df..c423a67 100644 --- a/Tests/DataCacheKitTests/LRUCacheTests.swift +++ b/Tests/LRUCacheTests/LRUCacheTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import DataCacheKit +@testable import LRUCache import Foundation struct LRUCacheTests { @@ -75,6 +75,46 @@ struct LRUCacheTests { #expect(cache[3] == 3) } + @Test + func testCountLimitAccess() { + let cache = LRUCache() + + // Given + cache.countLimit = 2 + + // When + cache[1] = 1 + cache[2] = 2 + + _ = cache[1] + cache[3] = 3 + + // Then + #expect(cache[1] == 1) + #expect(cache[2] == nil) + #expect(cache[3] == 3) + } + + @Test + func testCountLimitAccessNSCache() { + let cache = NSCacheWrapper() + + // Given + cache.countLimit = 2 + + // When + cache[1] = 1 + cache[2] = 2 + + _ = cache[1] + cache[3] = 3 + + // Then + #expect(cache[1] == 1) + #expect(cache[2] == nil) + #expect(cache[3] == 3) + } + @Test func testCountLimitWithCost1() { let cache = LRUCache()