Skip to content
This repository has been archived by the owner on Feb 7, 2020. It is now read-only.

By ID #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions FlatStore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
4B24C5582196021300932155 /* FlatBatchUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B24C5552196021100932155 /* FlatBatchUpdate.swift */; };
4B24C5592196021300932155 /* FlatRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B24C5562196021200932155 /* FlatRef.swift */; };
4B59A5042337E49F0088E921 /* FlatStore+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B59A5032337E49F0088E921 /* FlatStore+Identifiable.swift */; };
4B68395223708286002FFC5A /* InMemoryEntityStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B68395123708286002FFC5A /* InMemoryEntityStorage.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -39,6 +40,7 @@
4B24C5552196021100932155 /* FlatBatchUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatBatchUpdate.swift; sourceTree = "<group>"; };
4B24C5562196021200932155 /* FlatRef.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlatRef.swift; sourceTree = "<group>"; };
4B59A5032337E49F0088E921 /* FlatStore+Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FlatStore+Identifiable.swift"; sourceTree = "<group>"; };
4B68395123708286002FFC5A /* InMemoryEntityStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryEntityStorage.swift; sourceTree = "<group>"; };
4BF9E4BA21A0202A006AE786 /* Playground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Playground.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -87,6 +89,7 @@
4B24C5552196021100932155 /* FlatBatchUpdate.swift */,
4B24C5562196021200932155 /* FlatRef.swift */,
4B24C5542196021100932155 /* FlatStore.swift */,
4B68395123708286002FFC5A /* InMemoryEntityStorage.swift */,
4B59A5032337E49F0088E921 /* FlatStore+Identifiable.swift */,
4B24C53E219600B000932155 /* Info.plist */,
);
Expand Down Expand Up @@ -213,6 +216,7 @@
buildActionMask = 2147483647;
files = (
4B24C5582196021300932155 /* FlatBatchUpdate.swift in Sources */,
4B68395223708286002FFC5A /* InMemoryEntityStorage.swift in Sources */,
4B24C5592196021300932155 /* FlatRef.swift in Sources */,
4B59A5042337E49F0088E921 /* FlatStore+Identifiable.swift in Sources */,
4B24C5572196021200932155 /* FlatStore.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>FlatStoreTests</key>
<dict>
<key>testPerformanceExample()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.073941</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@
<dict>
<key>runDestinationsByUUID</key>
<dict>
<key>03A8D90B-CE32-4076-A881-2DAE0795E48B</key>
<dict>
<key>localComputer</key>
<dict>
<key>busSpeedInMHz</key>
<integer>400</integer>
<key>cpuCount</key>
<integer>1</integer>
<key>cpuKind</key>
<string>6-Core Intel Core i9</string>
<key>cpuSpeedInMHz</key>
<integer>2900</integer>
<key>logicalCPUCoresPerPackage</key>
<integer>12</integer>
<key>modelCode</key>
<string>MacBookPro15,3</string>
<key>physicalCPUCoresPerPackage</key>
<integer>6</integer>
<key>platformIdentifier</key>
<string>com.apple.platform.macosx</string>
</dict>
<key>targetArchitecture</key>
<string>x86_64</string>
<key>targetDevice</key>
<dict>
<key>modelCode</key>
<string>iPhone12,5</string>
<key>platformIdentifier</key>
<string>com.apple.platform.iphonesimulator</string>
</dict>
</dict>
<key>8D614F5D-019F-4C43-82A7-F29441FAEA6C</key>
<dict>
<key>localComputer</key>
Expand Down
27 changes: 18 additions & 9 deletions FlatStore/FlatBatchUpdate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public final class FlatBatchUpdatesContext {

// private let store: FlatStore

var buffer: [FlatStoreAnyIdentifier : Any] = [:]
var buffer = InMemoryEntityStorage()

private let store: FlatStore

public init(store: FlatStore) {
Expand All @@ -35,21 +36,29 @@ public final class FlatBatchUpdatesContext {

public func set<T: FlatStoreObjectType>(value: T) -> FlatStoreObjectIdentifier<T> {
let key = value.id
buffer[key.asAny] = value
buffer.update(inTable: T.FlatStoreID.tableName) { (table) in
table.byID[key.raw] = value
}
return key
}

public func set<T : Sequence>(values: T) -> [FlatStoreObjectIdentifier<T.Element>] where T.Element : FlatStoreObjectType {
return
values.map { value -> FlatStoreObjectIdentifier<T.Element> in
let key = value.id
buffer[key.asAny] = value
return key
public func set<T : Sequence>(values: T) -> [FlatStoreObjectIdentifier<T.Element>] where T.Element : FlatStoreObjectType {

var ids: [FlatStoreObjectIdentifier<T.Element>] = []

buffer.update(inTable: T.Element.FlatStoreID.tableName) { (table) in
values.forEach { value in
table.byID[value.id.raw] = value
ids.append(value.id)
}
}

return ids
}

public func get<T: FlatStoreObjectType>(by key: FlatStoreObjectIdentifier<T>) -> T? {
if let transientObject = (buffer[key.asAny] as? T) {

if let transientObject = (buffer.table(name: T.FlatStoreID.tableName)?.byID[key.raw] as? T) {
return transientObject
}
if let object = store.get(by: key) {
Expand Down
116 changes: 63 additions & 53 deletions FlatStore/FlatStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import Foundation

public struct FlatStoreAnyIdentifier : Hashable {

public let typeName: String
public let raw: AnyHashable
public let tableName: String
public let id: AnyHashable
public let notificationName: Notification.Name

public init(typeName: String, rawID: AnyHashable) {
self.typeName = typeName
self.raw = rawID
self.notificationName = .init(rawValue: "\(typeName)|\(rawID)")
public init(tableName: String, id: AnyHashable) {
self.tableName = tableName
self.id = id
self.notificationName = .init(rawValue: "\(tableName)|\(id)")
}

}
Expand All @@ -41,6 +41,10 @@ public struct FlatStoreObjectIdentifier<T : FlatStoreObjectType> : Hashable, Cus
return raw
}

public static var tableName: String {
typeName
}

public static var typeName: String {
String.init(reflecting: T.self)
}
Expand All @@ -58,7 +62,10 @@ public struct FlatStoreObjectIdentifier<T : FlatStoreObjectType> : Hashable, Cus

public init(_ raw: T.RawIDType) {
self.raw = raw
self.asAny = FlatStoreAnyIdentifier(typeName: Self.typeName, rawID: .init(raw))
self.asAny = FlatStoreAnyIdentifier(
tableName: Self.typeName,
id: .init(raw)
)
}

public var description: String {
Expand Down Expand Up @@ -92,7 +99,12 @@ public final class FlatStoreNotificationToken : NotificationTokenType {
}
}

public protocol FlatStoreObjectType {
public protocol _FlatStoreObjectType {

var notificationName: Notification.Name { get }
}

public protocol FlatStoreObjectType: _FlatStoreObjectType {

associatedtype RawIDType : Hashable
var rawID: RawIDType { get }
Expand All @@ -111,35 +123,31 @@ extension FlatStoreObjectType {
return FlatStoreObjectIdentifier<Self>.init(rawID)
}

public var notificationName: Notification.Name {
id.notificationName
}

}

public protocol PersistentStoreType {

typealias Storage = [FlatStoreAnyIdentifier : Any]

var rawStorage: Storage { get }
var allItemCount: Int { get }

func updateStorage(_ update: (inout Storage) -> ())

public struct EntityTable {
public var byID: [AnyHashable : Any] = [:]
}

public final class InMemoryPersistentStore: PersistentStoreType {
public protocol EntityStorageType {

public var allItemCount: Int {
return rawStorage.count
}
typealias Storage = [AnyHashable : EntityTable]

public var rawStorage: Storage = [:]
var allItemCount: Int { get }

public init() {

}
func allItems() -> [Any]

public func updateStorage(_ update: (inout Storage) -> ()) {
update(&rawStorage)
}
mutating func update(inTable name: String, update: (inout EntityTable) -> Void)

func table(name: String) -> EntityTable?

mutating func removeAll()

mutating func merge(inMemoryStorage: InMemoryEntityStorage)
}

// MARK: FlatStore
Expand All @@ -150,7 +158,7 @@ open class FlatStore {
storage.allItemCount
}

private let storage: PersistentStoreType
private var storage: EntityStorageType

private let notificationCenter: NotificationCenter = .init()

Expand All @@ -160,7 +168,7 @@ open class FlatStore {

private let lock = NSRecursiveLock()

public init(persistentStore: PersistentStoreType = InMemoryPersistentStore()) {
public init(persistentStore: EntityStorageType = InMemoryEntityStorage()) {
self.storage = persistentStore
notificationQueue.maxConcurrentOperationCount = 1
}
Expand All @@ -177,33 +185,33 @@ extension FlatStore {

public func get<T: FlatStoreObjectType>(by id: FlatStoreObjectIdentifier<T>) -> T? {
lock.lock(); defer { lock.unlock() }
return storage.rawStorage[id.asAny] as? T
return storage.table(name: T.FlatStoreID.tableName)?.byID[id.raw] as? T
}

public func get<S: Sequence, T: FlatStoreObjectType>(by ids: S) -> [T] where S.Element == FlatStoreObjectIdentifier<T> {
lock.lock(); defer { lock.unlock() }
return ids.compactMap { key in
storage.rawStorage[key.asAny] as? T
storage.table(name: T.FlatStoreID.tableName)?.byID[key.raw] as? T
}
}

public func get(by id: FlatStoreAnyIdentifier) -> Any? {
lock.lock(); defer { lock.unlock() }
return storage.rawStorage[id]
return storage.table(name: id.tableName)?.byID[id.id]
}

@discardableResult
public func set<T: FlatStoreObjectType>(value: T) -> T.CachingRef {

let key = value.id
let id = value.id

lock.lock()
storage.updateStorage { (store) in
_ = store[key.asAny] = value
storage.update(inTable: T.FlatStoreID.tableName) { (table) in
table.byID[id.raw] = value
}
lock.unlock()

let notification = makeSeparatedNotificationName(key.notificationName)
let notification = makeSeparatedNotificationName(id.notificationName)
notificationQueue.addOperation {
self.notificationCenter.post(name: notification, object: value)
}
Expand All @@ -226,21 +234,23 @@ extension FlatStore {
let key = value.id

lock.lock()
storage.updateStorage { (store) in
_ = store.removeValue(forKey: key.asAny)
storage.update(inTable: T.FlatStoreID.tableName) { (table) in
table.byID.removeValue(forKey: key.raw)
}
lock.unlock()

let notification = makeSeparatedNotificationName(key.notificationName)
notificationQueue.addOperation {
self.notificationCenter.post(name: notification, object: value)
}

dispatchUpdateNotification(name: key.notificationName, value: value)
}

public func deleteAll() {
lock.lock(); defer { lock.unlock() }
storage.updateStorage { (store) in
_ = store.removeAll()
storage.removeAll()
}

func dispatchUpdateNotification(name: Notification.Name, value: Any) {
let notification = makeSeparatedNotificationName(name)
notificationQueue.addOperation {
self.notificationCenter.post(name: notification, object: value)
}
}

Expand All @@ -251,8 +261,7 @@ extension FlatStore {

public func allObjects<T: FlatStoreObjectType>(type: T.Type) -> [T] {
lock.lock(); defer { lock.unlock() }
let typeName = T.FlatStoreID.typeName
return storage.rawStorage.filter { $0.key.typeName == typeName }.map { $0.value } as! [T]
return storage.table(name: T.FlatStoreID.tableName)?.byID.map { $0.value } as! [T]
}

}
Expand Down Expand Up @@ -389,12 +398,13 @@ extension FlatStore {

let context = FlatBatchUpdatesContext(store: self)
let u = try updates(self, context)

storage.updateStorage { (store) in
for item in context.buffer {
store[item.key] = item.value
}
storage.merge(inMemoryStorage: context.buffer)

for item in context.buffer.allItems() as! [_FlatStoreObjectType] {
dispatchUpdateNotification(name: item.notificationName, value: item)
}

return u

}
Expand Down
Loading