Refactor RangeSet for 32-bit support

This commit is contained in:
Ali 2022-05-11 23:41:57 +04:00
parent 3f6dabad1e
commit fc89536b5c
21 changed files with 134 additions and 80 deletions

View File

@ -20,7 +20,8 @@ swift_library(
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
"//submodules/MeshAnimationCache:MeshAnimationCache"
"//submodules/MeshAnimationCache:MeshAnimationCache",
"//submodules/Utils/RangeSet:RangeSet",
],
visibility = [
"//visibility:public",

View File

@ -6,6 +6,7 @@ import UIKit
import AsyncDisplayKit
import TelegramAudio
import UniversalMediaPlayer
import RangeSet
public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId {
case peer(PeerId)
@ -200,7 +201,7 @@ public protocol UniversalVideoManager: AnyObject {
func addPlaybackCompleted(id: AnyHashable, _ f: @escaping () -> Void) -> Int
func removePlaybackCompleted(id: AnyHashable, index: Int)
func statusSignal(content: UniversalVideoContent) -> Signal<MediaPlayerStatus?, NoError>
func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(IndexSet, Int64)?, NoError>
func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(RangeSet<Int64>, Int64)?, NoError>
}
public enum AudioRecordingState: Equatable {

View File

@ -8,11 +8,12 @@ import Display
import TelegramAudio
import UniversalMediaPlayer
import AVFoundation
import RangeSet
public protocol UniversalVideoContentNode: AnyObject {
var ready: Signal<Void, NoError> { get }
var status: Signal<MediaPlayerStatus, NoError> { get }
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> { get }
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> { get }
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
@ -105,8 +106,8 @@ public final class UniversalVideoNode: ASDisplayNode {
return self._status.get()
}
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
public var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
public var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
return self._bufferingStatus.get()
}

View File

@ -40,6 +40,7 @@ swift_library(
"//submodules/UndoUI:UndoUI",
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
"//submodules/TranslateUI:TranslateUI",
"//submodules/Utils/RangeSet:RangeSet",
],
visibility = [
"//visibility:public",

View File

@ -7,6 +7,7 @@ import Postbox
import Display
import UniversalMediaPlayer
import TelegramPresentationData
import RangeSet
private let textFont = Font.with(size: 13.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
@ -255,7 +256,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}))
}
func setBufferingStatusSignal(_ status: Signal<(IndexSet, Int64)?, NoError>?) {
func setBufferingStatusSignal(_ status: Signal<(RangeSet<Int64>, Int64)?, NoError>?) {
self.scrubberNode.bufferingStatus = status
}

View File

@ -19,6 +19,7 @@ swift_library(
"//submodules/FFMpegBinding:FFMpegBinding",
"//submodules/RingBuffer:RingBuffer",
"//submodules/YuvConversion:YuvConversion",
"//submodules/Utils/RangeSet:RangeSet",
],
visibility = [
"//visibility:public",

View File

@ -2,6 +2,7 @@ import Foundation
import AsyncDisplayKit
import Display
import SwiftSignalKit
import RangeSet
public enum MediaPlayerScrubbingNodeCap {
case square
@ -218,7 +219,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
private let containerNode: ASDisplayNode
private let foregroundNode: ASImageNode
private var ranges: (IndexSet, Int64)?
private var ranges: (RangeSet<Int64>, Int64)?
init(color: UIColor, lineCap: MediaPlayerScrubbingNodeCap, lineHeight: CGFloat) {
self.color = color
@ -239,7 +240,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
self.addSubnode(self.containerNode)
}
func updateStatus(_ ranges: IndexSet, _ size: Int64) {
func updateStatus(_ ranges: RangeSet<Int64>, _ size: Int64) {
self.ranges = (ranges, size)
if !self.bounds.width.isZero {
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.15, curve: .easeInOut))
@ -249,7 +250,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
if let ranges = self.ranges, !ranges.0.isEmpty, ranges.1 != 0 {
for range in ranges.0.rangeView {
for range in ranges.0.ranges {
let rangeWidth = min(size.width, (CGFloat(range.count) / CGFloat(ranges.1)) * size.width)
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: rangeWidth, height: size.height)))
transition.updateAlpha(node: self.foregroundNode, alpha: abs(size.width - rangeWidth) < 1.0 ? 0.0 : 1.0)
@ -358,9 +359,9 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
}
private var bufferingStatusDisposable: Disposable?
private var bufferingStatusValuePromise = Promise<(IndexSet, Int64)?>()
private var bufferingStatusValuePromise = Promise<(RangeSet<Int64>, Int64)?>()
public var bufferingStatus: Signal<(IndexSet, Int64)?, NoError>? {
public var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError>? {
didSet {
if let bufferingStatus = self.bufferingStatus {
self.bufferingStatusValuePromise.set(bufferingStatus)

View File

@ -25,6 +25,7 @@ swift_library(
"//submodules/AppBundle:AppBundle",
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
"//submodules/Svg:Svg",
"//submodules/Utils/RangeSet:RangeSet",
],
visibility = [
"//visibility:public",

View File

@ -18,6 +18,7 @@ import ImageTransparency
import AppBundle
import MusicAlbumArtResources
import Svg
import RangeSet
private enum ResourceFileData {
case data(Data)
@ -1671,7 +1672,7 @@ public func chatMessagePhotoStatus(context: AccountContext, messageId: MessageId
context.account.postbox.mediaBox.resourceRangesStatus(largestRepresentation.resource)
)
|> map { status, rangeStatus -> MediaResourceStatus in
if rangeStatus.contains(integersIn: Int(range.lowerBound) ..< Int(range.upperBound)) {
if rangeStatus.isSuperset(of: RangeSet<Int64>(range)) {
return .Local
}

View File

@ -1,6 +1,7 @@
import Foundation
import SwiftSignalKit
import ManagedFile
import RangeSet
private final class ResourceStatusContext {
var status: MediaResourceStatus?
@ -685,7 +686,7 @@ public final class MediaBox {
}
}
public func resourceRangesStatus(_ resource: MediaResource) -> Signal<IndexSet, NoError> {
public func resourceRangesStatus(_ resource: MediaResource) -> Signal<RangeSet<Int64>, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()

View File

@ -2,16 +2,17 @@ import Foundation
import SwiftSignalKit
import Crc32
import ManagedFile
import RangeSet
private final class MediaBoxFileMap {
fileprivate(set) var sum: Int64
private(set) var ranges: IndexSet
private(set) var ranges: RangeSet<Int64>
private(set) var truncationSize: Int64?
private(set) var progress: Float?
init() {
self.sum = 0
self.ranges = IndexSet()
self.ranges = RangeSet<Int64>()
self.truncationSize = nil
self.progress = nil
}
@ -34,7 +35,7 @@ private final class MediaBoxFileMap {
var count: Int32 = 0
var sum: Int64 = 0
var ranges: IndexSet = IndexSet()
var ranges = RangeSet<Int64>()
guard fd.read(&count, 4) == 4 else {
return nil
@ -74,7 +75,7 @@ private final class MediaBoxFileMap {
memcpy(&intervalLength, bytes.advanced(by: offset + 8), 8)
offset += 8 * 2
ranges.insert(integersIn: Int(intervalOffset) ..< Int(intervalOffset + intervalLength))
ranges.insert(contentsOf: intervalOffset ..< (intervalOffset + intervalLength))
sum += intervalLength
}
@ -95,7 +96,7 @@ private final class MediaBoxFileMap {
let crc: UInt32 = firstUInt32
var count: Int32 = 0
var sum: Int32 = 0
var ranges: IndexSet = IndexSet()
var ranges = RangeSet<Int64>()
guard fd.read(&count, 4) == 4 else {
return nil
@ -135,7 +136,7 @@ private final class MediaBoxFileMap {
memcpy(&intervalLength, bytes.advanced(by: offset + 4), 4)
offset += 8
ranges.insert(integersIn: Int(intervalOffset) ..< Int(intervalOffset + intervalLength))
ranges.insert(contentsOf: Int64(intervalOffset) ..< Int64(intervalOffset + intervalLength))
sum += intervalLength
}
@ -164,7 +165,7 @@ private final class MediaBoxFileMap {
var zero: Int32 = 0
buffer.write(&zero, offset: 0, length: 4)
let rangeView = self.ranges.rangeView
let rangeView = self.ranges.ranges
var count: Int32 = Int32(rangeView.count)
buffer.write(&count, offset: 0, length: 4)
@ -172,8 +173,8 @@ private final class MediaBoxFileMap {
buffer.write(&truncationSizeValue, offset: 0, length: 8)
for range in rangeView {
var intervalOffset = Int32(range.lowerBound)
var intervalLength = Int32(range.count)
var intervalOffset = range.lowerBound
var intervalLength = range.upperBound - range.lowerBound
buffer.write(&intervalOffset, offset: 0, length: 8)
buffer.write(&intervalLength, offset: 0, length: 8)
}
@ -184,10 +185,13 @@ private final class MediaBoxFileMap {
}
fileprivate func fill(_ range: Range<Int64>) {
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
let previousCount = self.ranges.count(in: intRange)
self.ranges.insert(integersIn: intRange)
self.sum += Int64(range.count - previousCount)
var previousCount: Int64 = 0
for intersectionRange in self.ranges.intersection(RangeSet<Int64>(range)).ranges {
previousCount += intersectionRange.upperBound - intersectionRange.lowerBound
}
self.ranges.insert(contentsOf: range)
self.sum += (range.upperBound - range.lowerBound) - previousCount
}
fileprivate func truncate(_ size: Int64) {
@ -199,21 +203,23 @@ private final class MediaBoxFileMap {
fileprivate func reset() {
self.truncationSize = nil
self.ranges.removeAll()
self.ranges = RangeSet<Int64>()
self.sum = 0
self.progress = nil
}
fileprivate func contains(_ range: Range<Int64>) -> Range<Int64>? {
let maxValue: Int
let maxValue: Int64
if let truncationSize = self.truncationSize {
maxValue = Int(truncationSize)
maxValue = truncationSize
} else {
maxValue = Int.max
maxValue = Int64.max
}
let intRange: Range<Int> = Int(range.lowerBound) ..< min(maxValue, Int(range.upperBound))
if self.ranges.contains(integersIn: intRange) {
return Int64(intRange.lowerBound) ..< Int64(intRange.upperBound)
let clippedRange: Range<Int64> = range.lowerBound ..< min(maxValue, range.upperBound)
let clippedRangeSet = RangeSet<Int64>(clippedRange)
if self.ranges.isSuperset(of: clippedRangeSet) {
return clippedRange
} else {
return nil
}
@ -243,7 +249,7 @@ final class MediaBoxPartialFile {
fileprivate let fileMap: MediaBoxFileMap
private var dataRequests = Bag<MediaBoxPartialFileDataRequest>()
private let missingRanges: MediaBoxFileMissingRanges
private let rangeStatusRequests = Bag<((IndexSet) -> Void, () -> Void)>()
private let rangeStatusRequests = Bag<((RangeSet<Int64>) -> Void, () -> Void)>()
private let statusRequests = Bag<((MediaResourceStatus) -> Void, Int64?)>()
private let fullRangeRequests = Bag<Disposable>()
@ -263,7 +269,7 @@ final class MediaBoxPartialFile {
self.fd = fd
if let fileMap = MediaBoxFileMap(fd: self.metadataFd) {
if !fileMap.ranges.isEmpty {
let upperBound = fileMap.ranges[fileMap.ranges.endIndex]
let upperBound = fileMap.ranges.ranges.last!.upperBound
if let actualSize = fileSize(path, useTotalFileAllocatedSize: false) {
if upperBound > actualSize {
self.fileMap = MediaBoxFileMap()
@ -460,8 +466,8 @@ final class MediaBoxPartialFile {
assertionFailure()
removeIndices.append((index, request))
} else {
let intRange: Range<Int> = Int(request.range.lowerBound) ..< Int(min(maxValue, request.range.upperBound))
if self.fileMap.ranges.contains(integersIn: intRange) {
let intRange: Range<Int64> = request.range.lowerBound ..< min(maxValue, request.range.upperBound)
if self.fileMap.ranges.isSuperset(of: RangeSet<Int64>(intRange)) {
removeIndices.append((index, request))
}
}
@ -650,7 +656,7 @@ final class MediaBoxPartialFile {
}
}
func rangeStatus(next: @escaping (IndexSet) -> Void, completed: @escaping () -> Void) -> Disposable {
func rangeStatus(next: @escaping (RangeSet<Int64>) -> Void, completed: @escaping () -> Void) -> Disposable {
assert(self.queue.isCurrent())
next(self.fileMap.ranges)
@ -758,7 +764,7 @@ final class MediaBoxPartialFile {
strongSelf.write(offset: resourceOffset, data: data, dataRange: range)
}
if complete {
if let maxOffset = strongSelf.fileMap.ranges.rangeView.reversed().first?.upperBound {
if let maxOffset = strongSelf.fileMap.ranges.ranges.reversed().first?.upperBound {
let maxValue = max(resourceOffset + Int64(range.count), Int64(maxOffset))
strongSelf.truncate(maxValue)
}
@ -807,15 +813,14 @@ final class MediaBoxPartialFile {
private final class MediaBoxFileMissingRange {
var range: Range<Int64>
let priority: MediaBoxFetchPriority
var remainingRanges: IndexSet
var remainingRanges: RangeSet<Int64>
let error: (MediaResourceDataFetchError) -> Void
let completion: () -> Void
init(range: Range<Int64>, priority: MediaBoxFetchPriority, error: @escaping (MediaResourceDataFetchError) -> Void, completion: @escaping () -> Void) {
self.range = range
self.priority = priority
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
self.remainingRanges = IndexSet(integersIn: intRange)
self.remainingRanges = RangeSet<Int64>(range)
self.error = error
self.completion = completion
}
@ -824,8 +829,8 @@ private final class MediaBoxFileMissingRange {
private final class MediaBoxFileMissingRanges {
private var requestedRanges = Bag<MediaBoxFileMissingRange>()
private var missingRangesFlattened = IndexSet()
private var missingRangesByPriority: [MediaBoxFetchPriority: IndexSet] = [:]
private var missingRangesFlattened = RangeSet<Int64>()
private var missingRangesByPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
func clear() -> [((MediaResourceDataFetchError) -> Void, () -> Void)] {
let errorsAndCompletions = self.requestedRanges.copyItems().map({ ($0.error, $0.completion) })
@ -838,14 +843,14 @@ private final class MediaBoxFileMissingRanges {
}
private func missingRequestedIntervals() -> [(Range<Int64>, MediaBoxFetchPriority)] {
var intervalsByPriority: [MediaBoxFetchPriority: IndexSet] = [:]
var remainingIntervals = IndexSet()
var intervalsByPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
var remainingIntervals = RangeSet<Int64>()
for item in self.requestedRanges.copyItems() {
var requestedInterval = IndexSet(integersIn: Int(item.range.lowerBound) ..< Int(item.range.upperBound))
var requestedInterval = RangeSet<Int64>(item.range)
requestedInterval.formIntersection(self.missingRangesFlattened)
if !requestedInterval.isEmpty {
if intervalsByPriority[item.priority] == nil {
intervalsByPriority[item.priority] = IndexSet()
intervalsByPriority[item.priority] = RangeSet<Int64>()
}
intervalsByPriority[item.priority]?.formUnion(requestedInterval)
remainingIntervals.formUnion(requestedInterval)
@ -857,9 +862,9 @@ private final class MediaBoxFileMissingRanges {
for priority in intervalsByPriority.keys.sorted(by: { $0.rawValue > $1.rawValue }) {
let currentIntervals = intervalsByPriority[priority]!.intersection(remainingIntervals)
remainingIntervals.subtract(currentIntervals)
for range in currentIntervals.rangeView {
for range in currentIntervals.ranges {
if !range.isEmpty {
result.append((Int64(range.lowerBound) ..< Int64(range.upperBound), priority))
result.append((range, priority))
}
}
}
@ -868,17 +873,16 @@ private final class MediaBoxFileMissingRanges {
}
func fill(_ range: Range<Int64>) -> ([(Range<Int64>, MediaBoxFetchPriority)], [() -> Void])? {
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
if self.missingRangesFlattened.intersects(integersIn: intRange) {
self.missingRangesFlattened.remove(integersIn: intRange)
if self.missingRangesFlattened.intersects(range) {
self.missingRangesFlattened.remove(contentsOf: range)
for priority in self.missingRangesByPriority.keys {
self.missingRangesByPriority[priority]!.remove(integersIn: intRange)
self.missingRangesByPriority[priority]!.remove(contentsOf: range)
}
var completions: [() -> Void] = []
for (index, item) in self.requestedRanges.copyItemsWithIndices() {
if item.range.overlaps(range) {
item.remainingRanges.remove(integersIn: intRange)
item.remainingRanges.remove(contentsOf: range)
if item.remainingRanges.isEmpty {
self.requestedRanges.remove(index)
completions.append(item.completion)
@ -904,15 +908,15 @@ private final class MediaBoxFileMissingRanges {
}
private func update(fileMap: MediaBoxFileMap) -> [(Range<Int64>, MediaBoxFetchPriority)]? {
var byPriority: [MediaBoxFetchPriority: IndexSet] = [:]
var flattened = IndexSet()
var byPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
var flattened = RangeSet<Int64>()
for item in self.requestedRanges.copyItems() {
let intRange: Range<Int> = Int(item.range.lowerBound) ..< Int(item.range.upperBound)
let intRange: Range<Int64> = item.range
if byPriority[item.priority] == nil {
byPriority[item.priority] = IndexSet()
byPriority[item.priority] = RangeSet<Int64>()
}
byPriority[item.priority]!.insert(integersIn: intRange)
flattened.insert(integersIn: intRange)
byPriority[item.priority]!.insert(contentsOf: intRange)
flattened.insert(contentsOf: intRange)
}
for priority in byPriority.keys {
byPriority[priority]!.subtract(fileMap.ranges)
@ -1041,10 +1045,10 @@ final class MediaBoxFileContext {
}
}
func rangeStatus(next: @escaping (IndexSet) -> Void, completed: @escaping () -> Void) -> Disposable {
func rangeStatus(next: @escaping (RangeSet<Int64>) -> Void, completed: @escaping () -> Void) -> Disposable {
switch self.content {
case let .complete(_, size):
next(IndexSet(integersIn: 0 ..< Int(size)))
next(RangeSet<Int64>(0 ..< size))
completed()
return EmptyDisposable
case let .partial(file):

View File

@ -326,6 +326,17 @@ private enum MultipartFetchSource {
case .revalidate:
return .fail(.revalidateMediaReference)
case let .location(parsedLocation):
#if DEBUG
switch parsedLocation {
case let .inputDocumentFileLocation(id, _, _, _):
if id == 5467475273110788326 {
return Signal<Data, MultipartFetchDownloadError>.single(Data(count: Int(limit))) |> delay(0.01, queue: .concurrentDefaultQueue())
}
default:
break
}
#endif
return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground)
|> mapError { error -> MultipartFetchDownloadError in
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {

View File

@ -269,6 +269,7 @@ swift_library(
"//submodules/BrowserUI:BrowserUI",
"//submodules/PremiumUI:PremiumUI",
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
"//submodules/Utils/RangeSet:RangeSet",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -155,10 +155,19 @@ private final class InlineStickerItemLayer: SimpleLayer {
}
}
private var didRequestFrame = false
private func loadNextFrame() {
guard let frameSource = self.frameSource else {
return
}
if self.contents != nil {
return
}
if self.didRequestFrame {
return
}
self.didRequestFrame = true
frameSource.with { [weak self] impl in
if let animationFrame = impl.takeFrame(draw: true) {
var image: UIImage?
@ -191,6 +200,9 @@ private final class InlineStickerItemLayer: SimpleLayer {
guard let strongSelf = self else {
return
}
if strongSelf.contents != nil {
return
}
strongSelf.contents = image.cgImage
}
}
@ -464,9 +476,10 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
/*if item.context.sharedContext.immediateExperimentalUISettings.inlineStickers*/ do {
var currentCount = 0
let updatedString = NSMutableAttributedString(attributedString: attributedText)
var startIndex = updatedString.string.startIndex
while true {
var hadUpdates = false
updatedString.string.enumerateSubstrings(in: updatedString.string.startIndex ..< updatedString.string.endIndex, options: [.byComposedCharacterSequences]) { substring, substringRange, _, stop in
updatedString.string.enumerateSubstrings(in: startIndex ..< updatedString.string.endIndex, options: [.byComposedCharacterSequences]) { substring, substringRange, _, stop in
if let substring = substring {
let emoji = substring.basicEmoji.0
@ -477,14 +490,22 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
if let emojiFile = emojiFile {
updatedString.replaceCharacters(in: NSRange(substringRange, in: updatedString.string), with: NSAttributedString(string: "[\u{00a0}\u{00a0}\u{00a0}\u{00a0}\u{00a0}]", attributes: [NSAttributedString.Key("Attribute__EmbeddedItem"): InlineStickerItem(file: emojiFile), NSAttributedString.Key.foregroundColor: UIColor.clear.cgColor]))
let currentDict = updatedString.attributes(at: NSRange(substringRange, in: updatedString.string).lowerBound, effectiveRange: nil)
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
updatedAttributes[NSAttributedString.Key("Attribute__EmbeddedItem")] = InlineStickerItem(file: emojiFile)
let insertString = NSAttributedString(string: "[\u{00a0}\u{00a0}\u{00a0}]", attributes: updatedAttributes)
//updatedString.insert(insertString, at: NSRange(substringRange, in: updatedString.string).upperBound)
updatedString.replaceCharacters(in: NSRange(substringRange, in: updatedString.string), with: insertString)
startIndex = substringRange.lowerBound
currentCount += 1
hadUpdates = true
stop = true
}
}
}
if !hadUpdates || currentCount >= 10 {
if !hadUpdates || currentCount >= 1000 {
break
}
}
@ -701,7 +722,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
itemLayer.isVisibleForAnimations = self.isVisibleForAnimations
}
itemLayer.frame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 1.0).center, size: CGSize()).insetBy(dx: -11.0, dy: -11.0)
itemLayer.frame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 0.0).center, size: CGSize()).insetBy(dx: -12.0, dy: -12.0)
}
}
}

View File

@ -12,6 +12,7 @@ import AccountContext
import PhotoResources
import AppBundle
import ManagedAnimationNode
import RangeSet
private func generateBackground(theme: PresentationTheme) -> UIImage? {
return generateImage(CGSize(width: 20.0, height: 10.0 + 8.0), rotatedContext: { size, context in
@ -401,7 +402,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
strongSelf.currentFileReference = fileReference
if let size = fileReference.media.size {
strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource)
|> map { ranges -> (IndexSet, Int64) in
|> map { ranges -> (RangeSet<Int64>, Int64) in
return (ranges, size)
}
} else {

View File

@ -22,6 +22,7 @@ swift_library(
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/AppBundle:AppBundle",
"//submodules/Utils/RangeSet:RangeSet",
],
visibility = [
"//visibility:public",

View File

@ -10,6 +10,7 @@ import UniversalMediaPlayer
import AccountContext
import PhotoResources
import UIKitRuntimeUtils
import RangeSet
public enum NativeVideoContentId: Hashable {
case message(UInt32, MediaId)
@ -130,8 +131,8 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
}
}
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
return self._bufferingStatus.get()
}

View File

@ -10,6 +10,7 @@ import UniversalMediaPlayer
import TelegramAudio
import AccountContext
import PhotoResources
import RangeSet
public enum PlatformVideoContentId: Hashable {
case message(MessageId, UInt32, MediaId)
@ -132,8 +133,8 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
return self._status.get()
}
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
return self._bufferingStatus.get()
}

View File

@ -10,6 +10,7 @@ import LegacyComponents
import UniversalMediaPlayer
import AccountContext
import PhotoResources
import RangeSet
public final class SystemVideoContent: UniversalVideoContent {
public let id: AnyHashable
@ -50,8 +51,8 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
return self._status.get()
}
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
return self._bufferingStatus.get()
}

View File

@ -4,6 +4,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import UniversalMediaPlayer
import AccountContext
import RangeSet
private final class UniversalVideoContentSubscriber {
let id: Int32
@ -28,11 +29,11 @@ private final class UniversalVideoContentHolder {
var statusValue: MediaPlayerStatus?
var bufferingStatusDisposable: Disposable?
var bufferingStatusValue: (IndexSet, Int64)?
var bufferingStatusValue: (RangeSet<Int64>, Int64)?
var playbackCompletedIndex: Int?
init(content: UniversalVideoContent, contentNode: UniversalVideoContentNode & ASDisplayNode, statusUpdated: @escaping (MediaPlayerStatus?) -> Void, bufferingStatusUpdated: @escaping ((IndexSet, Int64)?) -> Void, playbackCompleted: @escaping () -> Void) {
init(content: UniversalVideoContent, contentNode: UniversalVideoContentNode & ASDisplayNode, statusUpdated: @escaping (MediaPlayerStatus?) -> Void, bufferingStatusUpdated: @escaping ((RangeSet<Int64>, Int64)?) -> Void, playbackCompleted: @escaping () -> Void) {
self.content = content
self.contentNode = contentNode
@ -131,7 +132,7 @@ private final class UniversalVideoContentHolder {
private final class UniversalVideoContentHolderCallbacks {
let playbackCompleted = Bag<() -> Void>()
let status = Bag<(MediaPlayerStatus?) -> Void>()
let bufferingStatus = Bag<((IndexSet, Int64)?) -> Void>()
let bufferingStatus = Bag<((RangeSet<Int64>, Int64)?) -> Void>()
var isEmpty: Bool {
return self.playbackCompleted.isEmpty && self.status.isEmpty && self.bufferingStatus.isEmpty
@ -278,7 +279,7 @@ public final class UniversalVideoManagerImpl: UniversalVideoManager {
} |> runOn(Queue.mainQueue())
}
public func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(IndexSet, Int64)?, NoError> {
public func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(RangeSet<Int64>, Int64)?, NoError> {
return Signal { subscriber in
var callbacks: UniversalVideoContentHolderCallbacks
if let current = self.holderCallbacks[content.id] {

View File

@ -10,6 +10,7 @@ import UniversalMediaPlayer
import LegacyComponents
import AccountContext
import PhotoResources
import RangeSet
public final class WebEmbedVideoContent: UniversalVideoContent {
public let id: AnyHashable
@ -50,8 +51,8 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
return self._status.get()
}
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
return self._bufferingStatus.get()
}