feat: EPUB阅读器搜索、注释、CFI模块及大书远距跳转优化
- 实现EPUB阅读器搜索功能及选中注释功能 - 优化CFI模块,修复代码审查发现的11个问题 - 实现大书远距目录跳转与后台补全优化方案 - 优化设置面板与章节运行时联动 - 重构及大量改进优化
This commit is contained in:
@@ -1,19 +1,15 @@
|
||||
import Foundation
|
||||
|
||||
/// 后台补全优先级策略
|
||||
///
|
||||
/// 控制后台元数据解析的优先级排序,确保当前阅读区段优先补全
|
||||
struct RDEPUBBackgroundPriorityPolicy {
|
||||
/// 热区半径:当前章节附近的范围
|
||||
|
||||
let hotRadius: Int
|
||||
/// 温区半径:远距跳转锚点附近的范围
|
||||
|
||||
let warmRadius: Int
|
||||
/// 最大温区跳转锚点数量
|
||||
|
||||
let maxWarmJumpAnchors: Int
|
||||
/// 冷区份额:每轮补全中冷区任务的比例
|
||||
|
||||
let coldLaneShare: Double
|
||||
|
||||
/// 默认策略
|
||||
static let `default` = RDEPUBBackgroundPriorityPolicy(
|
||||
hotRadius: 24,
|
||||
warmRadius: 96,
|
||||
@@ -21,7 +17,6 @@ struct RDEPUBBackgroundPriorityPolicy {
|
||||
coldLaneShare: 0.15
|
||||
)
|
||||
|
||||
/// 根据总章节数动态计算策略
|
||||
static func adaptive(totalBuildableChapters: Int) -> RDEPUBBackgroundPriorityPolicy {
|
||||
let hotRadius = min(max(12, Int(sqrt(Double(totalBuildableChapters)))), 48)
|
||||
let warmRadius = min(max(hotRadius * 3, 32), 192)
|
||||
@@ -34,7 +29,6 @@ struct RDEPUBBackgroundPriorityPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
/// 优先级带
|
||||
enum RDEPUBPriorityBand: Int, Comparable {
|
||||
case hot = 0
|
||||
case warmPrimary = 1
|
||||
@@ -46,39 +40,32 @@ enum RDEPUBPriorityBand: Int, Comparable {
|
||||
}
|
||||
}
|
||||
|
||||
/// 温区跳转锚点
|
||||
struct RDEPUBWarmJumpAnchor {
|
||||
let spineIndex: Int
|
||||
let timestamp: CFAbsoluteTime
|
||||
let sequenceNumber: Int
|
||||
}
|
||||
|
||||
/// 元数据解析工作项
|
||||
struct RDEPUBMetadataParseWorkItem {
|
||||
let spineIndex: Int
|
||||
let generation: Int
|
||||
let priorityBand: RDEPUBPriorityBand
|
||||
|
||||
/// 排序键
|
||||
var sortKey: (bandRank: Int, distanceToCurrent: Int, distanceToNewestJump: Int, spineIndex: Int) {
|
||||
(priorityBand.rawValue, 0, 0, spineIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/// 后台补全优先级管理器
|
||||
final class RDEPUBBackgroundPriorityManager {
|
||||
|
||||
private unowned let context: RDEPUBReaderContext
|
||||
|
||||
/// 当前策略
|
||||
private(set) var policy: RDEPUBBackgroundPriorityPolicy
|
||||
|
||||
/// 温区跳转锚点列表(按时间倒序)
|
||||
private var warmAnchors: [RDEPUBWarmJumpAnchor] = []
|
||||
|
||||
/// 当前 generation
|
||||
private(set) var currentGeneration: Int = 0
|
||||
|
||||
/// 冷区游标
|
||||
private var coldCursor: Int = 0
|
||||
|
||||
init(context: RDEPUBReaderContext) {
|
||||
@@ -86,12 +73,10 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
self.policy = .default
|
||||
}
|
||||
|
||||
/// 更新策略
|
||||
func updatePolicy(_ newPolicy: RDEPUBBackgroundPriorityPolicy) {
|
||||
policy = newPolicy
|
||||
}
|
||||
|
||||
/// 添加温区跳转锚点
|
||||
func addWarmAnchor(spineIndex: Int) {
|
||||
let anchor = RDEPUBWarmJumpAnchor(
|
||||
spineIndex: spineIndex,
|
||||
@@ -101,13 +86,12 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
|
||||
warmAnchors.insert(anchor, at: 0)
|
||||
|
||||
// 保留最近 N 个锚点
|
||||
if warmAnchors.count > policy.maxWarmJumpAnchors {
|
||||
warmAnchors = Array(warmAnchors.prefix(policy.maxWarmJumpAnchors))
|
||||
}
|
||||
|
||||
currentGeneration += 1
|
||||
coldCursor = 0 // 重置冷区游标
|
||||
coldCursor = 0
|
||||
|
||||
RDEPUBBackgroundTrace.log(
|
||||
"PriorityManager",
|
||||
@@ -115,7 +99,6 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
)
|
||||
}
|
||||
|
||||
/// 生成优先级排序的 spineIndex 列表
|
||||
func makeMetadataPriorityOrder(
|
||||
allBuildableIndices: [Int],
|
||||
currentSpineIndex: Int?,
|
||||
@@ -124,7 +107,6 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
let uncachedIndices = allBuildableIndices.filter { !cachedSpineIndices.contains($0) }
|
||||
guard !uncachedIndices.isEmpty else { return [] }
|
||||
|
||||
// 为每个 spineIndex 计算优先级带
|
||||
let items = uncachedIndices.map { spineIndex -> (spineIndex: Int, band: RDEPUBPriorityBand) in
|
||||
let band = classifySpineIndex(
|
||||
spineIndex: spineIndex,
|
||||
@@ -133,33 +115,29 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
return (spineIndex, band)
|
||||
}
|
||||
|
||||
// 排序
|
||||
let sorted = items.sorted { lhs, rhs in
|
||||
// 先按优先级带排序
|
||||
|
||||
if lhs.band != rhs.band {
|
||||
return lhs.band < rhs.band
|
||||
}
|
||||
|
||||
// 同一带内按距离排序
|
||||
let lhsDistanceToCurrent = currentSpineIndex.map { abs(lhs.spineIndex - $0) } ?? Int.max
|
||||
let rhsDistanceToCurrent = currentSpineIndex.map { abs(rhs.spineIndex - $0) } ?? Int.max
|
||||
if lhsDistanceToCurrent != rhsDistanceToCurrent {
|
||||
return lhsDistanceToCurrent < rhsDistanceToCurrent
|
||||
}
|
||||
|
||||
// 距离相同时按 spineIndex 排序
|
||||
return lhs.spineIndex < rhs.spineIndex
|
||||
}
|
||||
|
||||
return sorted.map { $0.spineIndex }
|
||||
}
|
||||
|
||||
/// 分类 spineIndex 到优先级带
|
||||
private func classifySpineIndex(
|
||||
spineIndex: Int,
|
||||
currentSpineIndex: Int?
|
||||
) -> RDEPUBPriorityBand {
|
||||
// 检查是否在热区
|
||||
|
||||
if let current = currentSpineIndex {
|
||||
let distance = abs(spineIndex - current)
|
||||
if distance <= policy.hotRadius {
|
||||
@@ -167,7 +145,6 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否在温区
|
||||
for (index, anchor) in warmAnchors.enumerated() {
|
||||
let distance = abs(spineIndex - anchor.spineIndex)
|
||||
if distance <= policy.warmRadius {
|
||||
@@ -175,16 +152,13 @@ final class RDEPUBBackgroundPriorityManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 冷区
|
||||
return .cold
|
||||
}
|
||||
|
||||
/// 获取当前温区锚点(用于后台任务调度)
|
||||
func currentWarmAnchors() -> [RDEPUBWarmJumpAnchor] {
|
||||
warmAnchors
|
||||
}
|
||||
|
||||
/// 重置状态
|
||||
func reset() {
|
||||
warmAnchors.removeAll()
|
||||
currentGeneration = 0
|
||||
|
||||
Reference in New Issue
Block a user