119 lines
4.4 KiB
Swift
119 lines
4.4 KiB
Swift
import Foundation
|
|
|
|
/// Adaptive chunking logic for sync payloads
|
|
enum ChunkingStrategy {
|
|
/// Maximum payload size target in bytes (100MB — server allows 500MB, nginx unlimited)
|
|
static let maxChunkSizeBytes = 100_000_000
|
|
|
|
/// Base overhead for conversation wrapper JSON structure
|
|
static let baseOverhead = 200
|
|
|
|
/// Estimate the JSON payload size for a single message
|
|
static func estimateMessageSize(_ msg: iMessage) -> Int {
|
|
var size = 500 // Base overhead for JSON structure and fixed fields
|
|
|
|
if let text = msg.text { size += text.utf8.count }
|
|
if let attributedBody = msg.attributedBody { size += attributedBody.utf8.count }
|
|
|
|
for attachment in msg.attachments {
|
|
size += 100
|
|
if let data = attachment.data { size += data.utf8.count }
|
|
if let filename = attachment.filename { size += filename.utf8.count }
|
|
if let mimeType = attachment.mimeType { size += mimeType.utf8.count }
|
|
if let transferName = attachment.transferName { size += transferName.utf8.count }
|
|
}
|
|
|
|
if let styleId = msg.expressiveSendStyleId { size += styleId.utf8.count }
|
|
if let guid = msg.associatedMessageGuid { size += guid.utf8.count }
|
|
if let replyGuid = msg.replyToGuid { size += replyGuid.utf8.count }
|
|
if let originGuid = msg.threadOriginatorGuid { size += originGuid.utf8.count }
|
|
if let groupTitle = msg.groupTitle { size += groupTitle.utf8.count }
|
|
if let bundleId = msg.balloonBundleId { size += bundleId.utf8.count }
|
|
if let senderName = msg.senderDisplayName { size += senderName.utf8.count }
|
|
|
|
return size
|
|
}
|
|
|
|
/// Maximum number of messages per batch request during initial sync.
|
|
/// Configurable via config.json key `imessageMaxMessagesPerBatch`.
|
|
static var maxMessagesPerBatch: Int {
|
|
let v = UserDefaults.standard.integer(forKey: "imessageMaxMessagesPerBatch")
|
|
return v > 0 ? v : 300
|
|
}
|
|
|
|
/// Maximum number of conversations per batch request during initial sync.
|
|
/// Configurable via config.json key `imessageMaxConversationsPerBatch`.
|
|
static var maxConversationsPerBatch: Int {
|
|
let v = UserDefaults.standard.integer(forKey: "imessageMaxConversationsPerBatch")
|
|
return v > 0 ? v : 5
|
|
}
|
|
|
|
/// Chunk a full list of conversation payloads into batches that stay
|
|
/// under `maxMessagesPerBatch` and `maxConversationsPerBatch`.
|
|
/// Each returned group is suitable for one `syncMessagesBatch` call.
|
|
static func chunkConversationPayloads<T>(
|
|
_ payloads: [T],
|
|
messageCount: (T) -> Int
|
|
) -> [[T]] {
|
|
guard !payloads.isEmpty else { return [] }
|
|
|
|
var chunks: [[T]] = []
|
|
var current: [T] = []
|
|
var currentMessages = 0
|
|
|
|
for p in payloads {
|
|
let msgs = messageCount(p)
|
|
let wouldExceed = currentMessages + msgs > maxMessagesPerBatch
|
|
|| current.count + 1 > maxConversationsPerBatch
|
|
if wouldExceed && !current.isEmpty {
|
|
chunks.append(current)
|
|
current = []
|
|
currentMessages = 0
|
|
}
|
|
current.append(p)
|
|
currentMessages += msgs
|
|
}
|
|
if !current.isEmpty {
|
|
chunks.append(current)
|
|
}
|
|
return chunks
|
|
}
|
|
|
|
/// Create adaptive chunks based on payload size estimation
|
|
static func createAdaptiveChunks(_ messages: [iMessage]) -> [[iMessage]] {
|
|
guard !messages.isEmpty else { return [] }
|
|
|
|
var chunks: [[iMessage]] = []
|
|
var currentChunk: [iMessage] = []
|
|
var currentSize = baseOverhead
|
|
|
|
for msg in messages {
|
|
let msgSize = estimateMessageSize(msg)
|
|
|
|
if msgSize > maxChunkSizeBytes {
|
|
if !currentChunk.isEmpty {
|
|
chunks.append(currentChunk)
|
|
currentChunk = []
|
|
currentSize = baseOverhead
|
|
}
|
|
chunks.append([msg])
|
|
continue
|
|
}
|
|
|
|
if currentSize + msgSize > maxChunkSizeBytes && !currentChunk.isEmpty {
|
|
chunks.append(currentChunk)
|
|
currentChunk = []
|
|
currentSize = baseOverhead
|
|
}
|
|
|
|
currentChunk.append(msg)
|
|
currentSize += msgSize
|
|
}
|
|
|
|
if !currentChunk.isEmpty {
|
|
chunks.append(currentChunk)
|
|
}
|
|
|
|
return chunks
|
|
}
|
|
}
|