Files
BitTorrentSwift/BitTorrent/File Manager/MultiFileHandle.swift
T

129 lines
4.0 KiB
Swift

//
// MultiFileHandle.swift
// BitTorrent
//
// Created by Ben Davis on 23/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import Foundation
protocol FileHandleProtocol {
var offsetInFile: UInt64 { get }
func readData(ofLength length: Int) -> Data
func write(_ data: Data)
func seek(toFileOffset offset: UInt64)
func seekToEndOfFile() -> UInt64
func synchronizeFile()
}
extension FileHandle: FileHandleProtocol {}
class MultiFileHandle: FileHandleProtocol {
private typealias File = (handle: FileHandleProtocol, offset: UInt64, length: UInt64)
private let files: [File]
private var fileIndex: Int = 0
var offsetInFile: UInt64 {
return currentFile.offset + currentFile.handle.offsetInFile
}
private var currentFile: File { return files[fileIndex] }
private var remainingInCurrentFile: UInt64 { return currentFile.offset + currentFile.length - offsetInFile }
convenience init(fileHandles: [FileHandleProtocol]) {
var fileLengths: [UInt64] = []
for handle in fileHandles {
let length = handle.seekToEndOfFile()
handle.seek(toFileOffset: 0)
fileLengths.append(length)
}
self.init(fileHandles: fileHandles, fileLengths: fileLengths)
}
private init(fileHandles: [FileHandleProtocol], fileLengths: [UInt64]) {
self.files = MultiFileHandle.createFiles(fileHandles: fileHandles, fileLengths: fileLengths)
}
private static func createFiles(fileHandles: [FileHandleProtocol], fileLengths: [UInt64]) -> [File] {
var result: [File] = []
var offset: UInt64 = 0
for i in 0 ..< fileHandles.count {
let handle = fileHandles[i]
let length = fileLengths[i]
let element = File(handle: handle, offset: offset, length: length)
offset += length
result.append(element)
}
return result
}
func readData(ofLength length: Int) -> Data {
let finalOffset = offsetInFile + UInt64(length)
var result = Data()
while offsetInFile != finalOffset {
result += readData(until: finalOffset)
}
return result
}
private func readData(until offset: UInt64) -> Data {
let length = min(remainingInCurrentFile, offset - offsetInFile)
let result = currentFile.handle.readData(ofLength: Int(length))
if currentFile.handle.offsetInFile == currentFile.length {
incrementCurrentFile()
}
return result
}
func write(_ data: Data) {
var remaining: Data? = data
while let r = remaining {
remaining = writeDataToEndOfCurrentFile(r)
}
}
private func writeDataToEndOfCurrentFile(_ data: Data) -> Data? {
guard remainingInCurrentFile >= data.count else {
let dataToWrite = data.correctingIndicies[0 ..< Int(remainingInCurrentFile)]
let remaining = data.correctingIndicies[Int(remainingInCurrentFile) ..< data.count]
currentFile.handle.write(dataToWrite)
incrementCurrentFile()
return remaining
}
currentFile.handle.write(data)
return nil
}
private func incrementCurrentFile() {
guard (fileIndex + 1) < files.count else { return }
fileIndex += 1
currentFile.handle.seek(toFileOffset: 0)
}
func seek(toFileOffset offset: UInt64) {
for i in 0 ..< files.count {
fileIndex = i
if (currentFile.offset + currentFile.length) > offset {
let fileOffset = offset - currentFile.offset
currentFile.handle.seek(toFileOffset: fileOffset)
break
}
}
}
func seekToEndOfFile() -> UInt64 {
fileIndex = files.count - 1
_ = currentFile.handle.seekToEndOfFile()
return offsetInFile
}
func synchronizeFile() {
for file in files {
file.handle.synchronizeFile()
}
}
}