Home telegram - media box file map
Post
Cancel

telegram - media box file map

class MediaBoxFileMap (对文件分片的包装)

1
2
3
4
fileprivate(set) var sum: Int32
private(set) var ranges: IndexSet
private(set) var truncationSize: Int32?
private(set) var progress: Float?

class ManagedFile

1
2
3
private let queue: Queue?
private let fd: Int32
private let mode: ManagedFileMode

class MediaBoxFileMissingRanges (管理分片请求)

1
2
3
private var requestedRanges = Bag<MediaBoxFileMissingRange>()
private var missingRangesFlattened = IndexSet()
private var missingRangesByPriority: [MediaBoxFetchPriority: IndexSet] = [:]

核心函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    private func updateRequestRanges(_ intervals: [(Range<Int>, MediaBoxFetchPriority)], fetch: ((Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>)?) {
        assert(self.queue.isCurrent())
        
        #if DEBUG
        for interval in intervals {
            assert(!interval.0.isEmpty)
        }
        #endif
        if intervals.isEmpty {
            if let (_, disposable) = self.currentFetch {
                self.currentFetch = nil
                disposable.dispose()
            }
        } else {
            if let (promise, _) = self.currentFetch {
                promise.set(.single(intervals))
            } else if let fetch = fetch {
                let promise = Promise<[(Range<Int>, MediaBoxFetchPriority)]>()
                let disposable = MetaDisposable()
                self.currentFetch = (promise, disposable)
                disposable.set((fetch(promise.get())
                |> deliverOn(self.queue)).start(next: { [weak self] data in
                    if let strongSelf = self {
                        switch data {
                            case .reset:
                                if !strongSelf.fileMap.ranges.isEmpty {
                                    strongSelf.reset()
                                }
                            case let .resourceSizeUpdated(size):
                                strongSelf.truncate(Int32(size))
                            case let .dataPart(resourceOffset, data, range, complete):
                                if !data.isEmpty {
                                    strongSelf.write(offset: Int32(resourceOffset), data: data, dataRange: range)
                                }
                                if complete {
                                    if let maxOffset = strongSelf.fileMap.ranges.rangeView.reversed().first?.upperBound {
                                        let maxValue = max(resourceOffset + range.count, maxOffset)
                                        strongSelf.truncate(Int32(maxValue))
                                    }
                                }
                            case let .replaceHeader(data, range):
                                strongSelf.write(offset: 0, data: data, dataRange: range)
                            case let .moveLocalFile(path):
                                strongSelf.moveLocalFile(tempPath: path)
                            case let .moveTempFile(file):
                                strongSelf.moveLocalFile(tempPath: file.path)
                                TempBox.shared.dispose(file)
                            case let .copyLocalItem(item):
                                strongSelf.copyLocalItem(item)
                            case let .progressUpdated(progress):
                                strongSelf.progressUpdated(progress)
                        }
                        if !strongSelf.processedAtLeastOneFetch {
                            strongSelf.processedAtLeastOneFetch = true
                            for request in strongSelf.dataRequests.copyItems() {
                                if request.waitingUntilAfterInitialFetch {
                                    request.waitingUntilAfterInitialFetch = false
                                    
                                    if strongSelf.fileMap.contains(request.range) {
                                        request.completion(MediaResourceData(path: strongSelf.path, offset: Int(request.range.lowerBound), size: request.range.count, complete: true))
                                    } else {
                                        request.completion(MediaResourceData(path: strongSelf.path, offset: Int(request.range.lowerBound), size: 0, complete: false))
                                    }
                                }
                            }
                        }
                    }
                }, error: { [weak self] e in
                    guard let strongSelf = self else {
                        return
                    }
                    for (error, _) in strongSelf.missingRanges.clear() {
                        error(e)
                    }
                }))
                promise.set(.single(intervals))
            }
        }
    }

fetchedResourceData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public func fetchedResourceData(_ resource: MediaResource, in range: Range<Int>, priority: MediaBoxFetchPriority = .default, parameters: MediaResourceFetchParameters?) -> Signal<Void, FetchResourceError> {
    return Signal { subscriber in
        let disposable = MetaDisposable()

        self.dataQueue.async {
            guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
                subscriber.putCompletion()
                return
            }

            let fetchResource = self.wrappedFetchResource.get()
// hcl 崩溃
            /*
             Fatal error: Can't form Range with upperBound < lowerBound
             Printing description of range:
             (Range<Int32>) range = 342486825..<2147483647
             */
            let fetchedDisposable = fileContext.fetched(range: Int32(range.lowerBound) ..< Int32(range.upperBound), priority: priority, fetch: { intervals in
                return fetchResource
                |> introduceError(MediaResourceDataFetchError.self)
                |> mapToSignal { fetch in
                    return fetch(resource, intervals, parameters)
                }
            }, error: { _ in
                subscriber.putCompletion()
            }, completed: {
                subscriber.putCompletion()
            })

            disposable.set(ActionDisposable {
                fetchedDisposable.dispose()
                releaseContext()
            })
        }

        return disposable
    }
}

resourceData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete) -> Signal<Data, NoError> {
        return Signal { subscriber in
            let disposable = MetaDisposable()
            
            self.dataQueue.async {
                guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
                    subscriber.putCompletion()
                    return
                }
                
                let range = Int32(range.lowerBound) ..< Int32(range.upperBound)
                
                let dataDisposable = fileContext.data(range: range, waitUntilAfterInitialFetch: false, next: { result in
                    if let file = ManagedFile(queue: self.dataQueue, path: result.path, mode: .read), let fileSize = file.getSize() {
                        if result.complete {
                            if result.offset + result.size <= fileSize {
                                if fileSize >= result.offset + result.size {
                                    file.seek(position: Int64(result.offset))
                                    let resultData = file.readData(count: result.size)
                                    subscriber.putNext(resultData)
                                    subscriber.putCompletion()
                                } else {
                                    assertionFailure("data.count >= result.offset + result.size")
                                }
                            } else {
                                assertionFailure()
                            }
                        } else {
                            switch mode {
                                case .complete:
                                    break
                                case .incremental:
                                    break
                                case .partial:
                                    subscriber.putNext(Data())
                            }
                        }
                    }
                })
                
                disposable.set(ActionDisposable {
                    dataDisposable.dispose()
                    releaseContext()
                })
            }
            
            return disposable
        }
    }
This post is licensed under CC BY 4.0 by the author.