import Foundation final class RDEPUBChapterOffsetMap { let fragmentOffsets: [String: Int] let pageStartOffsets: [Int] let pageEndOffsets: [Int] private let cfiMapLock = NSLock() private var _cfiMap: RDEPUBCFIMap? var cfiMap: RDEPUBCFIMap? { cfiMapLock.lock() defer { cfiMapLock.unlock() } return _cfiMap } let chapterText: String? init( fragmentOffsets: [String: Int], pageStartOffsets: [Int], pageEndOffsets: [Int], cfiMap: RDEPUBCFIMap?, chapterText: String? ) { self.fragmentOffsets = fragmentOffsets self.pageStartOffsets = pageStartOffsets self.pageEndOffsets = pageEndOffsets self._cfiMap = cfiMap self.chapterText = chapterText } func updateCFIMap(_ cfiMap: RDEPUBCFIMap) { cfiMapLock.lock() _cfiMap = cfiMap cfiMapLock.unlock() } func chapterOffset(forFragmentID fragmentID: String) -> Int? { return fragmentOffsets[fragmentID] } func chapterOffset(forCFI rawCFI: String?) -> Int? { guard let cfi = RDEPUBCFICompatibility.parseLossy(rawCFI) else { return nil } let resolved = RDEPUBCFIResolver.resolve(cfi) let lastOffset = max((pageEndOffsets.max() ?? 0), 0) return RDEPUBCFIRecoveryEngine.recover( cfi: cfi, cfiMap: cfiMap, chapterText: chapterText, fragmentOffsets: fragmentOffsets, fallbackOffset: resolved.chapterOffset, lastOffset: lastOffset )?.chapterOffset } func pageIndex(forChapterOffset offset: Int) -> Int? { for i in 0..= pageStartOffsets[i] && offset <= pageEndOffsets[i] { return i } } return nil } }