convert tabs to spaces

This commit is contained in:
Sami Samhuri 2016-08-11 00:11:51 -07:00
parent 137bb3fac0
commit fe4dc9a69a
3 changed files with 297 additions and 297 deletions

View file

@ -14,247 +14,247 @@ class CacheCreekTests: XCTestCase {
typealias Node = DoublyLinkedListNode typealias Node = DoublyLinkedListNode
func testSettingAndGettingItems() { func testSettingAndGettingItems() {
let cache = LRUCache() let cache = LRUCache()
cache.set(item: 123, forKey: "123") cache.set(item: 123, forKey: "123")
XCTAssert(cache.count == 1) XCTAssert(cache.count == 1)
XCTAssert(cache.item(forKey: "123") == 123) XCTAssert(cache.item(forKey: "123") == 123)
XCTAssert(cache[123] == nil) XCTAssert(cache[123] == nil)
cache[234] = "234" cache[234] = "234"
XCTAssert(cache.count == 2) XCTAssert(cache.count == 2)
XCTAssert(cache.item(forKey: 234) == "234") XCTAssert(cache.item(forKey: 234) == "234")
// Test setting/getting an array // Test setting/getting an array
let array = [1, 2, 3, 4, 5] let array = [1, 2, 3, 4, 5]
cache[5] = array cache[5] = array
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
if let fetchedArray: [Int] = cache.item(forKey: 5) { if let fetchedArray: [Int] = cache.item(forKey: 5) {
XCTAssert(fetchedArray == array) XCTAssert(fetchedArray == array)
} }
else { else {
XCTFail("Expected an int array") XCTFail("Expected an int array")
} }
let testStruct = TestStruct(name: "Testing", value: Int(arc4random_uniform(100000))) let testStruct = TestStruct(name: "Testing", value: Int(arc4random_uniform(100000)))
cache["TestingStruct"] = testStruct cache["TestingStruct"] = testStruct
guard let fetchedStruct: TestStruct = cache.item(forKey: "TestingStruct") else { guard let fetchedStruct: TestStruct = cache.item(forKey: "TestingStruct") else {
XCTFail() XCTFail()
return return
} }
XCTAssert(testStruct.name == fetchedStruct.name) XCTAssert(testStruct.name == fetchedStruct.name)
XCTAssert(testStruct.value == fetchedStruct.value) XCTAssert(testStruct.value == fetchedStruct.value)
} }
func testDifferentKindsOfKeys() { func testDifferentKindsOfKeys() {
let cache = LRUCache() let cache = LRUCache()
let floatKey: Float = 123.456 let floatKey: Float = 123.456
cache.set(item: 123.456, forKey: floatKey) cache.set(item: 123.456, forKey: floatKey)
XCTAssert(cache.item(forKey: floatKey) as Double? == .Some(123.456)) XCTAssert(cache.item(forKey: floatKey) as Double? == .Some(123.456))
cache[floatKey] = 456.789 cache[floatKey] = 456.789
XCTAssert(cache.count == 1) XCTAssert(cache.count == 1)
XCTAssert(cache[floatKey] as? Double == .Some(456.789)) XCTAssert(cache[floatKey] as? Double == .Some(456.789))
cache.set(item: "123.456", forKey: "123.456") cache.set(item: "123.456", forKey: "123.456")
XCTAssert(cache.count == 2) XCTAssert(cache.count == 2)
XCTAssert(cache.item(forKey: "123.456") as String? == .Some("123.456")) XCTAssert(cache.item(forKey: "123.456") as String? == .Some("123.456"))
let boolKey = true let boolKey = true
cache.set(item: true, forKey: boolKey) cache.set(item: true, forKey: boolKey)
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
XCTAssert(cache.item(forKey: boolKey) as Bool? == .Some(true)) XCTAssert(cache.item(forKey: boolKey) as Bool? == .Some(true))
cache.removeItem(forKey: boolKey) cache.removeItem(forKey: boolKey)
XCTAssert(cache.count == 2) XCTAssert(cache.count == 2)
XCTAssert(cache.item(forKey: boolKey) as Bool? == .None) XCTAssert(cache.item(forKey: boolKey) as Bool? == .None)
} }
func testSettingAndGettingEnum() { func testSettingAndGettingEnum() {
let cache = LRUCache() let cache = LRUCache()
cache["ABC"] = TestEnum.ABC cache["ABC"] = TestEnum.ABC
cache["DEF"] = TestEnum.DEF("BlahBlahBlah") cache["DEF"] = TestEnum.DEF("BlahBlahBlah")
cache["GHI"] = TestEnum.GHI(-500) cache["GHI"] = TestEnum.GHI(-500)
guard let abc: TestEnum = cache.item(forKey: "ABC"), guard let abc: TestEnum = cache.item(forKey: "ABC"),
def: TestEnum = cache.item(forKey: "DEF"), def: TestEnum = cache.item(forKey: "DEF"),
ghi: TestEnum = cache.item(forKey: "GHI") ghi: TestEnum = cache.item(forKey: "GHI")
else { else {
XCTFail() XCTFail()
return return
} }
switch (abc, def, ghi) { switch (abc, def, ghi) {
case (.ABC, .DEF(let stringValue), .GHI(let intValue)): case (.ABC, .DEF(let stringValue), .GHI(let intValue)):
XCTAssert(stringValue == "BlahBlahBlah") XCTAssert(stringValue == "BlahBlahBlah")
XCTAssert(intValue == -500) XCTAssert(intValue == -500)
default: default:
XCTFail() XCTFail()
} }
} }
func testSubscripts() { func testSubscripts() {
let cache = LRUCache() let cache = LRUCache()
// Int subscript // Int subscript
cache[123] = 123 cache[123] = 123
XCTAssert(cache[123] as? Int == .Some(123)) XCTAssert(cache[123] as? Int == .Some(123))
XCTAssert(cache.count == 1) XCTAssert(cache.count == 1)
cache[123] = nil cache[123] = nil
XCTAssert(cache[123] as? Int == .None) XCTAssert(cache[123] as? Int == .None)
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
// String subscript // String subscript
cache["123"] = 123 cache["123"] = 123
XCTAssert(cache["123"] as? Int == .Some(123)) XCTAssert(cache["123"] as? Int == .Some(123))
XCTAssert(cache.count == 1) XCTAssert(cache.count == 1)
cache["123"] = nil cache["123"] = nil
XCTAssert(cache["123"] as? Int == .None) XCTAssert(cache["123"] as? Int == .None)
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
// Float subscript // Float subscript
let floatKey: Float = 3.14 let floatKey: Float = 3.14
cache[floatKey] = 123 cache[floatKey] = 123
XCTAssert(cache[floatKey] as? Int == .Some(123)) XCTAssert(cache[floatKey] as? Int == .Some(123))
XCTAssert(cache.count == 1) XCTAssert(cache.count == 1)
cache[floatKey] = nil cache[floatKey] = nil
XCTAssert(cache[floatKey] as? Int == .None) XCTAssert(cache[floatKey] as? Int == .None)
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
} }
func testRemovingItems() { func testRemovingItems() {
let cache = LRUCache() let cache = LRUCache()
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
cache.removeItem(forKey: 123) cache.removeItem(forKey: 123)
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
XCTAssert(cache[123] == nil) XCTAssert(cache[123] == nil)
cache[234] = nil cache[234] = nil
XCTAssert(cache.count == 2) XCTAssert(cache.count == 2)
XCTAssert(cache[234] == nil) XCTAssert(cache[234] == nil)
cache.removeAllItems() cache.removeAllItems()
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
NSNotificationCenter.defaultCenter().postNotificationName(UIApplicationDidReceiveMemoryWarningNotification, object: UIApplication.sharedApplication()) NSNotificationCenter.defaultCenter().postNotificationName(UIApplicationDidReceiveMemoryWarningNotification, object: UIApplication.sharedApplication())
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
NSNotificationCenter.defaultCenter().postNotificationName(UIApplicationDidEnterBackgroundNotification, object: UIApplication.sharedApplication()) NSNotificationCenter.defaultCenter().postNotificationName(UIApplicationDidEnterBackgroundNotification, object: UIApplication.sharedApplication())
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
// Make sure an unknown key doesn't have any weird side effects // Make sure an unknown key doesn't have any weird side effects
cache[567] = nil cache[567] = nil
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
cache.removeItem(forKey: 999) cache.removeItem(forKey: 999)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
} }
func testCountLimit() { func testCountLimit() {
let cache = LRUCache() let cache = LRUCache()
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 4) XCTAssert(cache.count == 4)
cache.countLimit = 3 cache.countLimit = 3
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
cache.removeAllItems() cache.removeAllItems()
XCTAssert(cache.count == 0) XCTAssert(cache.count == 0)
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
cache[567] = 567 cache[567] = 567
XCTAssert(cache.count == 3) XCTAssert(cache.count == 3)
cache.removeAllItems() cache.removeAllItems()
cache.set(item: 123, forKey: 123) cache.set(item: 123, forKey: 123)
cache.set(item: 234, forKey: 234) cache.set(item: 234, forKey: 234)
cache.set(item: 345, forKey: 345) cache.set(item: 345, forKey: 345)
cache.set(item: 456, forKey: 456) cache.set(item: 456, forKey: 456)
cache.countLimit = 2 cache.countLimit = 2
XCTAssert(cache.count == 2) XCTAssert(cache.count == 2)
} }
func testEmptyEviction() { func testEmptyEviction() {
// Make sure that an eviction on an empty dictionary doesn't crash // Make sure that an eviction on an empty dictionary doesn't crash
let cache = LRUCache() let cache = LRUCache()
cache.evictItems() cache.evictItems()
} }
func testObjCObjects() { func testObjCObjects() {
let cache = LRUCache() let cache = LRUCache()
let oldCache = NSCache() let oldCache = NSCache()
cache.set(item: oldCache, forKey: "InceptionCache") cache.set(item: oldCache, forKey: "InceptionCache")
guard let _: NSCache = cache.item(forKey: "InceptionCache") else { guard let _: NSCache = cache.item(forKey: "InceptionCache") else {
XCTFail("Expected an NSCache object") XCTFail("Expected an NSCache object")
return return
} }
} }
} }
private struct TestStruct { private struct TestStruct {
let name: String let name: String
let value: Int let value: Int
} }
private enum TestEnum { private enum TestEnum {
case ABC case ABC
case DEF(String) case DEF(String)
case GHI(Int) case GHI(Int)
} }

View file

@ -13,33 +13,33 @@ import Foundation
/// ///
/// `AnyKey` is a simple struct that conforms to `Hashable` to allow any other `Hashable` key to be used in the cache dictionary /// `AnyKey` is a simple struct that conforms to `Hashable` to allow any other `Hashable` key to be used in the cache dictionary
struct AnyKey: Hashable { struct AnyKey: Hashable {
/// The underlying value /// The underlying value
private let underlying: Any private let underlying: Any
/// The hashing function /// The hashing function
private let hashValueFunc: () -> Int private let hashValueFunc: () -> Int
/// The equality function /// The equality function
private let equalityFunc: (Any) -> Bool private let equalityFunc: (Any) -> Bool
init<T: Hashable>(_ key: T) { init<T: Hashable>(_ key: T) {
underlying = key underlying = key
// Capture the key's hashability and equatability using closures. // Capture the key's hashability and equatability using closures.
// The Key shares the hash of the underlying value. // The Key shares the hash of the underlying value.
hashValueFunc = { key.hashValue } hashValueFunc = { key.hashValue }
// The Key is equal to a Key of the same underlying type, // The Key is equal to a Key of the same underlying type,
// whose underlying value is "==" to ours. // whose underlying value is "==" to ours.
equalityFunc = { equalityFunc = {
if let other = $0 as? T { if let other = $0 as? T {
return key == other return key == other
} }
return false return false
} }
} }
/// `Hashable` protocol conformance /// `Hashable` protocol conformance
var hashValue: Int { return hashValueFunc() } var hashValue: Int { return hashValueFunc() }
} }
func ==(x: AnyKey, y: AnyKey) -> Bool { func ==(x: AnyKey, y: AnyKey) -> Bool {
return x.equalityFunc(y.underlying) return x.equalityFunc(y.underlying)
} }

View file

@ -13,167 +13,167 @@ import Foundation
/// It is designed to work similar to the `NSCache`, but with native Swift support. /// It is designed to work similar to the `NSCache`, but with native Swift support.
/// ///
public class LRUCache { public class LRUCache {
// MARK: - Private variables // MARK: - Private variables
/// An array of `NSNotificationCenter` observers that need to be removed upon deinitialization /// An array of `NSNotificationCenter` observers that need to be removed upon deinitialization
private var notificationObservers: [NSObjectProtocol] = [] private var notificationObservers: [NSObjectProtocol] = []
/// The list of cached items. Most recently used item at head, least recently used item at tail. /// The list of cached items. Most recently used item at head, least recently used item at tail.
private var items: DoublyLinkedList = DoublyLinkedList() private var items: DoublyLinkedList = DoublyLinkedList()
/// Maps keys of cached items to nodes in the linked list. /// Maps keys of cached items to nodes in the linked list.
private var keyToNodeMap: [AnyKey:DoublyLinkedListNode] = [:] private var keyToNodeMap: [AnyKey:DoublyLinkedListNode] = [:]
// MARK: - Public variables // MARK: - Public variables
/// The number of items in the cache. /// The number of items in the cache.
public var count: Int { public var count: Int {
return items.count return items.count
} }
/// The limit of the amount of items that can be held in the cache. This defaults to 0, which means there is no limit. /// The limit of the amount of items that can be held in the cache. This defaults to 0, which means there is no limit.
public var countLimit: Int = 0 { public var countLimit: Int = 0 {
didSet { didSet {
assert(countLimit >= 0) assert(countLimit >= 0)
evictItems() evictItems()
} }
} }
// MARK: - Initialization methods // MARK: - Initialization methods
public init() { public init() {
let removalBlock = { [unowned self] (_: NSNotification) in let removalBlock = { [unowned self] (_: NSNotification) in
self.removeAllItems() self.removeAllItems()
} }
var notificationObserver = NSNotificationCenter.defaultCenter() var notificationObserver = NSNotificationCenter.defaultCenter()
.addObserverForName(UIApplicationDidReceiveMemoryWarningNotification, .addObserverForName(UIApplicationDidReceiveMemoryWarningNotification,
object: UIApplication.sharedApplication(), object: UIApplication.sharedApplication(),
queue: nil, queue: nil,
usingBlock: removalBlock) usingBlock: removalBlock)
notificationObservers.append(notificationObserver) notificationObservers.append(notificationObserver)
notificationObserver = NSNotificationCenter.defaultCenter() notificationObserver = NSNotificationCenter.defaultCenter()
.addObserverForName(UIApplicationDidEnterBackgroundNotification, .addObserverForName(UIApplicationDidEnterBackgroundNotification,
object: UIApplication.sharedApplication(), object: UIApplication.sharedApplication(),
queue: nil, queue: nil,
usingBlock: removalBlock) usingBlock: removalBlock)
notificationObservers.append(notificationObserver) notificationObservers.append(notificationObserver)
} }
deinit { deinit {
notificationObservers.forEach { notificationObservers.forEach {
NSNotificationCenter.defaultCenter().removeObserver($0) NSNotificationCenter.defaultCenter().removeObserver($0)
} }
} }
// MARK: - Internal methods // MARK: - Internal methods
/// Evicts items if the `countLimit` has been reached. /// Evicts items if the `countLimit` has been reached.
/// ///
func evictItems() { func evictItems() {
guard countLimit > 0 else { return } guard countLimit > 0 else { return }
while items.count > countLimit { while items.count > countLimit {
if let node = items.removeLast() { if let node = items.removeLast() {
keyToNodeMap[node.key] = nil keyToNodeMap[node.key] = nil
} }
} }
} }
// MARK: - Public methods // MARK: - Public methods
/// Adds an item to the cache for any given `Hashable` key. /// Adds an item to the cache for any given `Hashable` key.
/// ///
/// - parameter item: The item to be cached /// - parameter item: The item to be cached
/// - parameter key: The key with which to cache the item /// - parameter key: The key with which to cache the item
/// ///
public func set<K: Hashable>(item item: Any, forKey key: K) { public func set<K: Hashable>(item item: Any, forKey key: K) {
let key = AnyKey(key) let key = AnyKey(key)
if let existingNode = keyToNodeMap[key] { if let existingNode = keyToNodeMap[key] {
items.remove(node: existingNode) items.remove(node: existingNode)
} }
let node = items.prepend(key: key, value: item) let node = items.prepend(key: key, value: item)
keyToNodeMap[key] = node keyToNodeMap[key] = node
evictItems() evictItems()
} }
/// Gets an item from the cache if it exists for a given `Hashable` key. /// Gets an item from the cache if it exists for a given `Hashable` key.
/// This method uses generics to infer the type that should be returned. /// This method uses generics to infer the type that should be returned.
/// ///
/// Note: Even if an item exists for the key, but does not match the given type, it will return `nil`. /// Note: Even if an item exists for the key, but does not match the given type, it will return `nil`.
/// ///
/// - parameter key: The key whose item should be fetched /// - parameter key: The key whose item should be fetched
/// - returns: The item from the cache if it exists, or `nil` if an item could not be found /// - returns: The item from the cache if it exists, or `nil` if an item could not be found
/// ///
public func item<T, K: Hashable>(forKey key: K) -> T? { public func item<T, K: Hashable>(forKey key: K) -> T? {
let key = AnyKey(key) let key = AnyKey(key)
if let node = keyToNodeMap[key], let item = node.value as? T { if let node = keyToNodeMap[key], let item = node.value as? T {
items.moveToHead(node: node) items.moveToHead(node: node)
return item return item
} }
return nil return nil
} }
/// Discards an item for a given `Hashable` key. /// Discards an item for a given `Hashable` key.
/// ///
/// - parameter key: The key whose item should be removed /// - parameter key: The key whose item should be removed
/// ///
public func removeItem<K: Hashable>(forKey key: K) { public func removeItem<K: Hashable>(forKey key: K) {
let key = AnyKey(key) let key = AnyKey(key)
if let node = keyToNodeMap[key] { if let node = keyToNodeMap[key] {
items.remove(node: node) items.remove(node: node)
keyToNodeMap[key] = nil keyToNodeMap[key] = nil
} }
} }
/// Clears the entire cache. /// Clears the entire cache.
/// ///
public func removeAllItems() { public func removeAllItems() {
items.removeAll() items.removeAll()
keyToNodeMap.removeAll() keyToNodeMap.removeAll()
} }
// MARK: - Subscript methods // MARK: - Subscript methods
// TODO: Consolidate these subscript methods once subscript generics with constraints are supported // TODO: Consolidate these subscript methods once subscript generics with constraints are supported
/// A subscript method that allows `Int` key subscripts. /// A subscript method that allows `Int` key subscripts.
/// ///
public subscript(key: Int) -> Any? { public subscript(key: Int) -> Any? {
get { get {
return item(forKey: key) return item(forKey: key)
} }
set { set {
if let newValue = newValue { if let newValue = newValue {
set(item: newValue, forKey: key) set(item: newValue, forKey: key)
} }
else { else {
removeItem(forKey: key) removeItem(forKey: key)
} }
} }
} }
/// A subscript method that allows `Float` key subscripts. /// A subscript method that allows `Float` key subscripts.
/// ///
public subscript(key: Float) -> Any? { public subscript(key: Float) -> Any? {
get { get {
return item(forKey: key) return item(forKey: key)
} }
set { set {
if let newValue = newValue { if let newValue = newValue {
set(item: newValue, forKey: key) set(item: newValue, forKey: key)
} }
else { else {
removeItem(forKey: key) removeItem(forKey: key)
} }
} }
} }
/// A subscript method that allows `String` key subscripts. /// A subscript method that allows `String` key subscripts.
/// ///
public subscript(key: String) -> Any? { public subscript(key: String) -> Any? {
get { get {
return item(forKey: key) return item(forKey: key)
} }
set { set {
if let newValue = newValue { if let newValue = newValue {
set(item: newValue, forKey: key) set(item: newValue, forKey: key)
} }
else { else {
removeItem(forKey: key) removeItem(forKey: key)
} }
} }
} }
} }