mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
174 lines
4.7 KiB
Swift
174 lines
4.7 KiB
Swift
import Foundation
|
|
import SwiftSignalKit
|
|
import ManagedFile
|
|
|
|
public final class MediaBoxFileManager {
|
|
public enum Mode {
|
|
case read
|
|
case readwrite
|
|
}
|
|
|
|
public enum AccessError: Error {
|
|
case generic
|
|
}
|
|
|
|
final class Item {
|
|
final class Accessor {
|
|
private let file: ManagedFile
|
|
|
|
init(file: ManagedFile) {
|
|
self.file = file
|
|
}
|
|
|
|
func write(_ data: UnsafeRawPointer, count: Int) -> Int {
|
|
return self.file.write(data, count: count)
|
|
}
|
|
|
|
func read(_ data: UnsafeMutableRawPointer, _ count: Int) -> Int {
|
|
return self.file.read(data, count)
|
|
}
|
|
|
|
func readData(count: Int) -> Data {
|
|
return self.file.readData(count: count)
|
|
}
|
|
|
|
func seek(position: Int64) -> Bool {
|
|
return self.file.seek(position: position)
|
|
}
|
|
}
|
|
|
|
weak var manager: MediaBoxFileManager?
|
|
let path: String
|
|
let mode: Mode
|
|
|
|
weak var context: ItemContext?
|
|
|
|
init(manager: MediaBoxFileManager, path: String, mode: Mode) {
|
|
self.manager = manager
|
|
self.path = path
|
|
self.mode = mode
|
|
}
|
|
|
|
deinit {
|
|
if let manager = self.manager, let context = self.context {
|
|
manager.discardItemContext(context: context)
|
|
}
|
|
}
|
|
|
|
func access(_ f: (Accessor) throws -> Void) throws {
|
|
if let context = self.context {
|
|
try f(Accessor(file: context.file))
|
|
} else {
|
|
if let manager = self.manager {
|
|
if let context = manager.takeContext(path: self.path, mode: self.mode) {
|
|
self.context = context
|
|
try f(Accessor(file: context.file))
|
|
} else {
|
|
throw AccessError.generic
|
|
}
|
|
} else {
|
|
throw AccessError.generic
|
|
}
|
|
}
|
|
}
|
|
|
|
func sync() {
|
|
if let context = self.context {
|
|
context.sync()
|
|
}
|
|
}
|
|
}
|
|
|
|
final class ItemContext {
|
|
let id: Int
|
|
let path: String
|
|
let mode: Mode
|
|
let file: ManagedFile
|
|
|
|
private var isDisposed: Bool = false
|
|
|
|
init?(id: Int, path: String, mode: Mode) {
|
|
let mappedMode: ManagedFile.Mode
|
|
switch mode {
|
|
case .read:
|
|
mappedMode = .read
|
|
case .readwrite:
|
|
mappedMode = .readwrite
|
|
}
|
|
|
|
guard let file = ManagedFile(queue: nil, path: path, mode: mappedMode) else {
|
|
return nil
|
|
}
|
|
self.file = file
|
|
|
|
self.id = id
|
|
self.path = path
|
|
self.mode = mode
|
|
}
|
|
|
|
deinit {
|
|
assert(self.isDisposed)
|
|
}
|
|
|
|
func dispose() {
|
|
if !self.isDisposed {
|
|
self.isDisposed = true
|
|
self.file._unsafeClose()
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
}
|
|
|
|
func sync() {
|
|
self.file.sync()
|
|
}
|
|
}
|
|
|
|
private let queue: Queue?
|
|
private var contexts: [Int: ItemContext] = [:]
|
|
private var nextItemId: Int = 0
|
|
private let maxOpenFiles: Int
|
|
|
|
public init(queue: Queue?) {
|
|
self.queue = queue
|
|
self.maxOpenFiles = 16
|
|
}
|
|
|
|
func open(path: String, mode: Mode) -> Item? {
|
|
if let queue = self.queue {
|
|
assert(queue.isCurrent())
|
|
}
|
|
|
|
return Item(manager: self, path: path, mode: mode)
|
|
}
|
|
|
|
private func takeContext(path: String, mode: Mode) -> ItemContext? {
|
|
if let queue = self.queue {
|
|
assert(queue.isCurrent())
|
|
}
|
|
|
|
if self.contexts.count > self.maxOpenFiles {
|
|
if let minKey = self.contexts.keys.min(), let context = self.contexts[minKey] {
|
|
self.discardItemContext(context: context)
|
|
}
|
|
}
|
|
|
|
let id = self.nextItemId
|
|
self.nextItemId += 1
|
|
let context = ItemContext(id: id, path: path, mode: mode)
|
|
self.contexts[id] = context
|
|
return context
|
|
}
|
|
|
|
private func discardItemContext(context: ItemContext) {
|
|
if let queue = self.queue {
|
|
assert(queue.isCurrent())
|
|
}
|
|
|
|
if let context = self.contexts.removeValue(forKey: context.id) {
|
|
context.dispose()
|
|
}
|
|
}
|
|
}
|
|
|