diff --git a/Telegram/NotificationService/BUILD b/Telegram/NotificationService/BUILD index edac695af6..5564908392 100644 --- a/Telegram/NotificationService/BUILD +++ b/Telegram/NotificationService/BUILD @@ -21,6 +21,7 @@ swift_library( "//submodules/rlottie:RLottieBinding", "//submodules/GZip:GZip", "//submodules/PersistentStringHash:PersistentStringHash", + "//submodules/Utils/RangeSet", ], visibility = [ diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index bb521192d2..0a067efc1c 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -15,6 +15,7 @@ import PersistentStringHash import CallKit import AppLockState import NotificationsPresentationData +import RangeSet private let queue = Queue() @@ -1187,7 +1188,7 @@ private final class NotificationServiceHandler { fetchMediaSignal = Signal { subscriber in final class DataValue { var data = Data() - var totalSize: Int64? + var missingRanges = RangeSet(0 ..< Int64.max) } let collectedData = Atomic(value: DataValue()) @@ -1217,12 +1218,22 @@ private final class NotificationServiceHandler { useMainConnection: true ).start(next: { result in switch result { - case let .dataPart(_, data, _, _): + case let .dataPart(offset, data, dataRange, _): var isCompleted = false let _ = collectedData.modify { current in let current = current - current.data.append(data) - if let totalSize = current.totalSize, Int64(current.data.count) >= totalSize { + + let fillRange = Int(offset) ..< (Int(offset) + data.count) + if current.data.count < fillRange.upperBound { + current.data.count = fillRange.upperBound + } + current.data.withUnsafeMutableBytes { buffer -> Void in + let bytes = buffer.baseAddress!.assumingMemoryBound(to: UInt8.self) + data.copyBytes(to: bytes.advanced(by: Int(offset)), from: Int(dataRange.lowerBound) ..< Int(dataRange.upperBound)) + } + current.missingRanges.remove(contentsOf: Int64(fillRange.lowerBound) ..< Int64(fillRange.upperBound)) + + if current.missingRanges.isEmpty { isCompleted = true } return current @@ -1235,8 +1246,8 @@ private final class NotificationServiceHandler { var isCompleted = false let _ = collectedData.modify { current in let current = current - current.totalSize = size - if Int64(current.data.count) >= size { + current.missingRanges.remove(contentsOf: size ..< Int64.max) + if current.missingRanges.isEmpty { isCompleted = true } return current diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift index 75c01e9917..9d815f9921 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerFrameSource.swift @@ -330,7 +330,7 @@ private final class AnimatedStickerDirectFrameSourceCache { return .notFound } - self.file.seek(position: Int64(index * 4 * 2)) + let _ = self.file.seek(position: Int64(index * 4 * 2)) var offset: Int32 = 0 var length: Int32 = 0 if self.file.read(&offset, 4) != 4 { @@ -384,12 +384,12 @@ private final class AnimatedStickerDirectFrameSourceCache { return } - strongSelf.file.seek(position: Int64(index * 4 * 2)) + let _ = strongSelf.file.seek(position: Int64(index * 4 * 2)) var offset = Int32(currentSize) var length = Int32(compressedData.data.count) let _ = strongSelf.file.write(&offset, count: 4) let _ = strongSelf.file.write(&length, count: 4) - strongSelf.file.seek(position: Int64(currentSize)) + let _ = strongSelf.file.seek(position: Int64(currentSize)) compressedData.data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Void in if let baseAddress = buffer.baseAddress { let _ = strongSelf.file.write(baseAddress, count: Int(length)) @@ -427,12 +427,12 @@ private final class AnimatedStickerDirectFrameSourceCache { return } - strongSelf.file.seek(position: Int64(index * 4 * 2)) + let _ = strongSelf.file.seek(position: Int64(index * 4 * 2)) var offset = Int32(currentSize) var length = Int32(compressedData.count) let _ = strongSelf.file.write(&offset, count: 4) let _ = strongSelf.file.write(&length, count: 4) - strongSelf.file.seek(position: Int64(currentSize)) + let _ = strongSelf.file.seek(position: Int64(currentSize)) compressedData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Void in if let baseAddress = buffer.baseAddress { let _ = strongSelf.file.write(baseAddress, count: Int(length)) @@ -502,7 +502,7 @@ private final class AnimatedStickerDirectFrameSourceCache { switch rangeResult { case let .range(range): - self.file.seek(position: Int64(range.lowerBound)) + let _ = self.file.seek(position: Int64(range.lowerBound)) let length = range.upperBound - range.lowerBound let compressedData = self.file.readData(count: length) if compressedData.count != length { diff --git a/submodules/AnimatedStickerNode/Sources/VideoStickerFrameSource.swift b/submodules/AnimatedStickerNode/Sources/VideoStickerFrameSource.swift index 3331868289..d599a9bb3a 100644 --- a/submodules/AnimatedStickerNode/Sources/VideoStickerFrameSource.swift +++ b/submodules/AnimatedStickerNode/Sources/VideoStickerFrameSource.swift @@ -100,7 +100,7 @@ private final class VideoStickerFrameSourceCache { return true } - self.file.seek(position: 0) + let _ = self.file.seek(position: 0) var frameRate: Int32 = 0 if self.file.read(&frameRate, 4) != 4 { return false @@ -113,7 +113,7 @@ private final class VideoStickerFrameSourceCache { } self.frameRate = frameRate - self.file.seek(position: 4) + let _ = self.file.seek(position: 4) var frameCount: Int32 = 0 if self.file.read(&frameCount, 4) != 4 { @@ -144,7 +144,7 @@ private final class VideoStickerFrameSourceCache { return .notFound } - self.file.seek(position: Int64(8 + index * 4 * 2)) + let _ = self.file.seek(position: Int64(8 + index * 4 * 2)) var offset: Int32 = 0 var length: Int32 = 0 if self.file.read(&offset, 4) != 4 { @@ -167,11 +167,11 @@ private final class VideoStickerFrameSourceCache { } func storeFrameRateAndCount(frameRate: Int, frameCount: Int) { - self.file.seek(position: 0) + let _ = self.file.seek(position: 0) var frameRate = Int32(frameRate) let _ = self.file.write(&frameRate, count: 4) - self.file.seek(position: 4) + let _ = self.file.seek(position: 4) var frameCount = Int32(frameCount) let _ = self.file.write(&frameCount, count: 4) } @@ -203,12 +203,12 @@ private final class VideoStickerFrameSourceCache { return } - strongSelf.file.seek(position: Int64(8 + index * 4 * 2)) + let _ = strongSelf.file.seek(position: Int64(8 + index * 4 * 2)) var offset = Int32(currentSize) var length = Int32(compressedData.count) let _ = strongSelf.file.write(&offset, count: 4) let _ = strongSelf.file.write(&length, count: 4) - strongSelf.file.seek(position: Int64(currentSize)) + let _ = strongSelf.file.seek(position: Int64(currentSize)) compressedData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Void in if let baseAddress = buffer.baseAddress { let _ = strongSelf.file.write(baseAddress, count: Int(length)) @@ -226,7 +226,7 @@ private final class VideoStickerFrameSourceCache { switch rangeResult { case let .range(range): - self.file.seek(position: Int64(range.lowerBound)) + let _ = self.file.seek(position: Int64(range.lowerBound)) let length = range.upperBound - range.lowerBound let compressedData = self.file.readData(count: length) if compressedData.count != length { diff --git a/submodules/LottieMeshSwift/Sources/Buffer.swift b/submodules/LottieMeshSwift/Sources/Buffer.swift index cff168947f..3601d4f89a 100644 --- a/submodules/LottieMeshSwift/Sources/Buffer.swift +++ b/submodules/LottieMeshSwift/Sources/Buffer.swift @@ -75,7 +75,7 @@ public final class MeshWriteBuffer { } public func seek(offset: Int) { - self.file.seek(position: Int64(offset)) + let _ = self.file.seek(position: Int64(offset)) self.offset = offset } } diff --git a/submodules/ManagedFile/Sources/ManagedFile.swift b/submodules/ManagedFile/Sources/ManagedFile.swift index 34ddd4eadc..66ad61cb81 100644 --- a/submodules/ManagedFile/Sources/ManagedFile.swift +++ b/submodules/ManagedFile/Sources/ManagedFile.swift @@ -99,12 +99,14 @@ public final class ManagedFile { return result } - public func seek(position: Int64) { + @discardableResult + public func seek(position: Int64) -> Bool { if let queue = self.queue { assert(queue.isCurrent()) } assert(!self.isClosed) - lseek(self.fd, position, SEEK_SET) + let result = lseek(self.fd, position, SEEK_SET) + return result == position } public func truncate(count: Int64) { diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index 8cba2107f6..99364741a6 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -139,7 +139,7 @@ public final class MediaBox { private let statusQueue = Queue() private let concurrentQueue = Queue.concurrentDefaultQueue() - private let dataQueue = Queue() + private let dataQueue = Queue(name: "MediaBox-Data") private let dataFileManager: MediaBoxFileManager private let cacheQueue = Queue() private let timeBasedCleanup: TimeBasedCleanup @@ -209,6 +209,58 @@ public final class MediaBox { let _ = self.ensureDirectoryCreated //self.updateResourceIndex() + + /*#if DEBUG + self.dataQueue.async { + for _ in 0 ..< 5 { + let tempFile = TempBox.shared.tempFile(fileName: "file") + print("MediaBox test: file \(tempFile.path)") + let queue2 = Queue.concurrentDefaultQueue() + if let fileContext = MediaBoxFileContextV2Impl(queue: self.dataQueue, manager: self.dataFileManager, storageBox: self.storageBox, resourceId: tempFile.path.data(using: .utf8)!, path: tempFile.path + "_complete", partialPath: tempFile.path + "_partial", metaPath: tempFile.path + "_partial" + ".meta") { + let _ = fileContext.fetched( + range: 0 ..< Int64.max, + priority: .default, + fetch: { ranges in + return ranges + |> filter { !$0.isEmpty } + |> take(1) + |> castError(MediaResourceDataFetchError.self) + |> mapToSignal { _ in + return Signal { subscriber in + queue2.async { + subscriber.putNext(.resourceSizeUpdated(524288)) + } + queue2.async { + subscriber.putNext(.resourceSizeUpdated(393216)) + } + queue2.async { + subscriber.putNext(.resourceSizeUpdated(655360)) + } + queue2.async { + subscriber.putNext(.resourceSizeUpdated(169608)) + } + queue2.async { + subscriber.putNext(.dataPart(resourceOffset: 131072, data: Data(repeating: 0xbb, count: 38536), range: 0 ..< 38536, complete: true)) + } + queue2.async { + subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(repeating: 0xaa, count: 131072), range: 0 ..< 131072, complete: false)) + } + + return EmptyDisposable + } + } + }, + error: { _ in + }, + completed: { + assert(try! Data(contentsOf: URL(fileURLWithPath: tempFile.path + "_complete")) == Data(repeating: 0xaa, count: 131072) + Data(repeating: 0xbb, count: 38536)) + let _ = fileContext.addReference() + } + ) + } + } + } + #endif*/ } public func setMaxStoreTimes(general: Int32, shortLived: Int32, gigabytesLimit: Int32) { @@ -688,7 +740,7 @@ public final class MediaBox { let clippedLowerBound = min(completeSize, max(0, range.lowerBound)) let clippedUpperBound = min(completeSize, max(0, range.upperBound)) if clippedLowerBound < clippedUpperBound && (clippedUpperBound - clippedLowerBound) <= 64 * 1024 * 1024 { - file.seek(position: clippedLowerBound) + let _ = file.seek(position: clippedLowerBound) let data = file.readData(count: Int(clippedUpperBound - clippedLowerBound)) subscriber.putNext((data, true)) } else { @@ -725,7 +777,7 @@ public final class MediaBox { subscriber.putNext((Data(), true)) subscriber.putCompletion() } else if clippedUpperBound <= fileSize && (clippedUpperBound - clippedLowerBound) <= 64 * 1024 * 1024 { - file.seek(position: Int64(clippedLowerBound)) + let _ = file.seek(position: Int64(clippedLowerBound)) let resultData = file.readData(count: Int(clippedUpperBound - clippedLowerBound)) subscriber.putNext((resultData, true)) subscriber.putCompletion() diff --git a/submodules/Postbox/Sources/MediaBoxFile.swift b/submodules/Postbox/Sources/MediaBoxFile.swift index ba0d687a3b..44472bd1ce 100644 --- a/submodules/Postbox/Sources/MediaBoxFile.swift +++ b/submodules/Postbox/Sources/MediaBoxFile.swift @@ -88,7 +88,7 @@ final class MediaBoxPartialFile { guard let clippedRange = fileMap.contains(range) else { return nil } - fd.seek(position: Int64(clippedRange.lowerBound)) + let _ = fd.seek(position: Int64(clippedRange.lowerBound)) return fd.readData(count: Int(clippedRange.upperBound - clippedRange.lowerBound)) } @@ -227,7 +227,7 @@ final class MediaBoxPartialFile { do { try self.fd.access { fd in - fd.seek(position: offset) + let _ = fd.seek(position: offset) let written = data.withUnsafeBytes { rawBytes -> Int in let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) @@ -330,7 +330,7 @@ final class MediaBoxPartialFile { do { var result: Data? try self.fd.access { fd in - fd.seek(position: Int64(actualRange.lowerBound)) + let _ = fd.seek(position: Int64(actualRange.lowerBound)) var data = Data(count: actualRange.count) let dataCount = data.count let readBytes = data.withUnsafeMutableBytes { rawBytes -> Int in diff --git a/submodules/Postbox/Sources/MediaBoxFileContextV2Impl.swift b/submodules/Postbox/Sources/MediaBoxFileContextV2Impl.swift index 560920279c..fb866c9146 100644 --- a/submodules/Postbox/Sources/MediaBoxFileContextV2Impl.swift +++ b/submodules/Postbox/Sources/MediaBoxFileContextV2Impl.swift @@ -421,21 +421,28 @@ final class MediaBoxFileContextV2Impl: MediaBoxFileContext { private func processWrite(resourceOffset: Int64, data: Data, dataRange: Range) { if let destinationFile = self.destinationFile { do { + var success = true try destinationFile.access { fd in - fd.seek(position: resourceOffset) - let written = data.withUnsafeBytes { rawBytes -> Int in - let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) - - return fd.write(bytes.advanced(by: Int(dataRange.lowerBound)), count: dataRange.count) + if fd.seek(position: resourceOffset) { + let written = data.withUnsafeBytes { rawBytes -> Int in + let bytes = rawBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) + + return fd.write(bytes.advanced(by: Int(dataRange.lowerBound)), count: dataRange.count) + } + assert(written == dataRange.count) + } else { + success = false } - assert(written == dataRange.count) } - - let range: Range = resourceOffset ..< (resourceOffset + Int64(dataRange.count)) - self.fileMap.fill(range) - self.fileMap.serialize(manager: self.manager, to: self.metaPath) - - self.storageBox.update(id: self.resourceId, size: self.fileMap.sum) + if success { + let range: Range = resourceOffset ..< (resourceOffset + Int64(dataRange.count)) + self.fileMap.fill(range) + self.fileMap.serialize(manager: self.manager, to: self.metaPath) + + self.storageBox.update(id: self.resourceId, size: self.fileMap.sum) + } else { + postboxLog("MediaBoxFileContextV2Impl: error seeking file to \(resourceOffset) at \(self.partialPath)") + } } catch let e { postboxLog("MediaBoxFileContextV2Impl: error writing file at \(self.partialPath): \(e)") } diff --git a/submodules/Postbox/Sources/MediaBoxFileManager.swift b/submodules/Postbox/Sources/MediaBoxFileManager.swift index a98a2ae0a1..bc963b8e4c 100644 --- a/submodules/Postbox/Sources/MediaBoxFileManager.swift +++ b/submodules/Postbox/Sources/MediaBoxFileManager.swift @@ -32,8 +32,8 @@ final class MediaBoxFileManager { return self.file.readData(count: count) } - func seek(position: Int64) { - self.file.seek(position: position) + func seek(position: Int64) -> Bool { + return self.file.seek(position: position) } } diff --git a/submodules/Postbox/Sources/MediaBoxFileMap.swift b/submodules/Postbox/Sources/MediaBoxFileMap.swift index d353eb61de..e81872da84 100644 --- a/submodules/Postbox/Sources/MediaBoxFileMap.swift +++ b/submodules/Postbox/Sources/MediaBoxFileMap.swift @@ -203,7 +203,7 @@ final class MediaBoxFileMap { } let _ = try? fileItem.access { file in - file.seek(position: 0) + let _ = file.seek(position: 0) let buffer = WriteBuffer() var magic: UInt32 = 0x7bac1487 buffer.write(&magic, offset: 0, length: 4) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index d40af9242a..f3a36a73be 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -181,9 +181,17 @@ private func collectExternalShareItems(strings: PresentationStrings, dateTimeFor case .progress: return .progress case let .done(data): - if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: fileData) { + guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) else { + return .progress + } + if let image = UIImage(data: fileData) { return .done(.image(image)) } else { + #if DEBUG + if "".isEmpty { + return .done(.file(URL(fileURLWithPath: data.path), "image.bin", "application/octet-stream")) + } + #endif return .progress } } diff --git a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift index 5c52b35437..f28a29dc5f 100644 --- a/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift +++ b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift @@ -387,7 +387,7 @@ public func cacheVideoStickerFrames(path: String, size: CGSize, cacheKey: String } if frameCount > 0 { - file.seek(position: 4) + let _ = file.seek(position: 4) let _ = file.write(&frameCount, count: 4) } diff --git a/submodules/TelegramCore/Sources/Network/FetchV2.swift b/submodules/TelegramCore/Sources/Network/FetchV2.swift index e4a05c99f8..14d489c6d5 100644 --- a/submodules/TelegramCore/Sources/Network/FetchV2.swift +++ b/submodules/TelegramCore/Sources/Network/FetchV2.swift @@ -647,8 +647,8 @@ private final class FetchImpl { Logger.shared.log("FetchV2", "\(self.loggingIdentifier): setting known size to \(resultingSize)") self.knownSize = resultingSize } - Logger.shared.log("FetchV2", "\(self.loggingIdentifier): reporting resource size \(fetchRange.lowerBound + actualLength)") - self.onNext(.resourceSizeUpdated(fetchRange.lowerBound + actualLength)) + Logger.shared.log("FetchV2", "\(self.loggingIdentifier): reporting resource size \(resultingSize)") + self.onNext(.resourceSizeUpdated(resultingSize)) } state.completedRanges.formUnion(RangeSet(partRange)) diff --git a/submodules/TelegramCore/Sources/Network/MultipartUpload.swift b/submodules/TelegramCore/Sources/Network/MultipartUpload.swift index 53b79f1e65..0bff10aa24 100644 --- a/submodules/TelegramCore/Sources/Network/MultipartUpload.swift +++ b/submodules/TelegramCore/Sources/Network/MultipartUpload.swift @@ -186,7 +186,7 @@ private final class MultipartUploadManager { self.bigTotalParts = nil } else { self.bigParts = false - self.defaultPartSize = 16 * 1024 + self.defaultPartSize = 128 * 1024 self.bigTotalParts = nil } } @@ -317,7 +317,7 @@ private final class MultipartUploadManager { switch resourceData { case let .resourceData(data): if let file = ManagedFile(queue: nil, path: data.path, mode: .read) { - file.seek(position: Int64(partOffset)) + let _ = file.seek(position: Int64(partOffset)) let data = file.readData(count: Int(partSize)) if data.count == partSize { partData = data diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 8995477dff..aef9b83c44 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -172,6 +172,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case sendWhenOnlineTip = 38 case chatWallpaperLightPreviewTip = 39 case chatWallpaperDarkPreviewTip = 40 + case displayChatListContacts = 41 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -389,6 +390,10 @@ private struct ApplicationSpecificNoticeKeys { static func sendWhenOnlineTip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.sendWhenOnlineTip.key) } + + static func displayChatListContacts() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.displayChatListContacts.key) + } } public struct ApplicationSpecificNotice { @@ -1420,6 +1425,26 @@ public struct ApplicationSpecificNotice { } } + public static func displayChatListContacts(accountManager: AccountManager) -> Signal { + return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.displayChatListContacts()) + |> map { view -> Bool in + if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { + return true + } else { + return false + } + } + } + + public static func setDisplayChatListContacts(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Void in + if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { + transaction.setNotice(ApplicationSpecificNoticeKeys.displayChatListContacts(), entry) + } + } + |> ignoreValues + } + public static func reset(accountManager: AccountManager) -> Signal { return accountManager.transaction { transaction -> Void in } diff --git a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift index 2d15c25129..29fcb45f51 100644 --- a/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift +++ b/submodules/TelegramUI/Components/AnimationCache/Sources/AnimationCache.swift @@ -605,10 +605,10 @@ private final class AnimationCacheItemWriterImpl: AnimationCacheItemWriter { let metadataPosition = file.position() let contentLength = Int(metadataPosition) - contentLengthOffset - 4 - file.seek(position: Int64(contentLengthOffset)) + let _ = file.seek(position: Int64(contentLengthOffset)) file.write(UInt32(contentLength)) - file.seek(position: metadataPosition) + let _ = file.seek(position: metadataPosition) file.write(UInt32(self.frames.count)) for frame in self.frames { file.write(Float32(frame.duration))