mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Merge branch 'master' into gift-resale
This commit is contained in:
commit
66e8f140c1
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -29,3 +29,6 @@ url=../tgcalls.git
|
||||
[submodule "third-party/dav1d/dav1d"]
|
||||
path = third-party/dav1d/dav1d
|
||||
url = https://github.com/ali-fareed/dav1d.git
|
||||
[submodule "third-party/td/td"]
|
||||
path = third-party/td/td
|
||||
url = https://github.com/tdlib/td
|
||||
|
@ -14115,3 +14115,70 @@ Sorry for the inconvenience.";
|
||||
"WebApp.ImportData.AccountHeader" = "ACCOUNT TO IMPORT DATA FROM";
|
||||
"WebApp.ImportData.CreatedOn" = "created on %@";
|
||||
"WebApp.ImportData.Import" = "Import";
|
||||
|
||||
"CallList.ToastCallLinkCopied.Text" = "Call link copied";
|
||||
"CallList.ToastCallLinkCopied.Action" = "View Call";
|
||||
"CallList.NewCall" = "Start New Call";
|
||||
"CallList.NewCallLink" = "Create Call Link";
|
||||
|
||||
"Chat.SendStarsToBecomeTopInfo" = "Send %@ or more to highlight your profile";
|
||||
|
||||
"VideoChat.RevokeLink" = "Revoke Link";
|
||||
|
||||
"InviteLink.GroupCallLinkHelp" = "Anyone on Telegram can join your call by following the link below.";
|
||||
"InviteLink.CallLinkTitle" = "Call Link";
|
||||
"InviteLink.CreatedGroupCallFooter" = "Be the first to join the call and add people from there. [Open Call >](open_call)";
|
||||
"InviteLink.QRCode.InfoGroupCall" = "Everyone on Telegram can scan this code to join your group call.";
|
||||
|
||||
"Call.GenericGroupCallTitle" = "Group Call";
|
||||
|
||||
"VideoChat.EncryptionKeyLabel" = "End-to-end encrypted";
|
||||
"VideoChat.EncryptionKeyText" = "These four emojis represent the call's encryption key. They must match for all participants and change when someone joins or leaves.";
|
||||
"VideoChat.EncryptionKeyDone" = "Close";
|
||||
"VideoChat.InviteMember" = "Add Member";
|
||||
|
||||
"VideoChat.GroupCallTitle" = "Group Call";
|
||||
|
||||
"Chat.ViewGroupCall" = "JOIN GROUP CALL";
|
||||
|
||||
"NewCall.SearchPlaceholder" = "Search for contacts or usernames";
|
||||
"NewCall.VideoOption" = "Call with video enabled";
|
||||
"NewCall.ActionCallSingle" = "Call %@";
|
||||
"NewCall.ActionCallMultiple" = "Call";
|
||||
|
||||
"Chat.ToastCallLinkExpired.Text" = "This link is no longer active";
|
||||
|
||||
"Chat.CallMessage.GroupCallParticipantCount_1" = "1 person";
|
||||
"Chat.CallMessage.GroupCallParticipantCount_any" = "%d people";
|
||||
|
||||
"Chat.CallMessage.DeclinedGroupCall" = "Declined Group Call";
|
||||
"Chat.CallMessage.MissedGroupCall" = "Missed Group Call";
|
||||
"Chat.CallMessage.CancelledGroupCall" = "Cancelled Group Call";
|
||||
"Chat.CallMessage.IncomingGroupCall" = "Incoming Group Call";
|
||||
"Chat.CallMessage.OutgoingGroupCall" = "Outgoing Group Call";
|
||||
|
||||
"Invitation.GroupCall" = "Group Call";
|
||||
"Invitation.JoinGroupCall" = "Join Group Call";
|
||||
"Invitation.PublicGroup" = "public group";
|
||||
"Invitation.PrivateGroup" = "private group";
|
||||
|
||||
"Invitation.GroupCall.Text" = "You are invited to join a group call.";
|
||||
|
||||
"Invitation.Group.AlreadyJoinedSingle" = "**%@** already joined this group.";
|
||||
"Invitation.Group.AlreadyJoinedMultiple" = "%@ already joined this group.";
|
||||
"Invitation.Group.AlreadyJoinedMultipleWithCount_1" = "{} and **%d** other person already joined this group.";
|
||||
"Invitation.Group.AlreadyJoinedMultipleWithCount_any" = "{} and **%d** other people already joined this group.";
|
||||
|
||||
"Invitation.GroupCall.AlreadyJoinedSingle" = "**%@** already joined this call.";
|
||||
"Invitation.GroupCall.AlreadyJoinedMultiple" = "%@ already joined this call.";
|
||||
"Invitation.GroupCall.AlreadyJoinedMultipleWithCount_1" = "{} and **%d** other person already joined this call.";
|
||||
"Invitation.GroupCall.AlreadyJoinedMultipleWithCount_any" = "{} and **%d** other people already joined this call.";
|
||||
|
||||
"Call.ShareLink" = "Share Call Link";
|
||||
"Call.AddMemberTitle" = "Add Member";
|
||||
|
||||
"Call.IncomingGroupCallTitle.Single" = "%@";
|
||||
"Call.IncomingGroupCallTitle.Multiple_1" = "{} and 1 other";
|
||||
"Call.IncomingGroupCallTitle.Multiple_any" = "{} and %d others";
|
||||
|
||||
"GroupCall.RevokeLinkText" = "Are you sure you want to revoke this link? Once you do, no one will be able to join the call using it.";
|
||||
|
@ -99,6 +99,12 @@ public enum ContactListPeer: Equatable {
|
||||
}
|
||||
|
||||
public final class ContactSelectionControllerParams {
|
||||
public enum MultipleSelectionMode {
|
||||
case disabled
|
||||
case possible
|
||||
case always
|
||||
}
|
||||
|
||||
public let context: AccountContext
|
||||
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
public let mode: ContactSelectionControllerMode
|
||||
@ -107,7 +113,7 @@ public final class ContactSelectionControllerParams {
|
||||
public let options: Signal<[ContactListAdditionalOption], NoError>
|
||||
public let displayDeviceContacts: Bool
|
||||
public let displayCallIcons: Bool
|
||||
public let multipleSelection: Bool
|
||||
public let multipleSelection: MultipleSelectionMode
|
||||
public let requirePhoneNumbers: Bool
|
||||
public let allowChannelsInSearch: Bool
|
||||
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
||||
@ -124,7 +130,7 @@ public final class ContactSelectionControllerParams {
|
||||
options: Signal<[ContactListAdditionalOption], NoError> = .single([]),
|
||||
displayDeviceContacts: Bool = false,
|
||||
displayCallIcons: Bool = false,
|
||||
multipleSelection: Bool = false,
|
||||
multipleSelection: MultipleSelectionMode = .disabled,
|
||||
requirePhoneNumbers: Bool = false,
|
||||
allowChannelsInSearch: Bool = false,
|
||||
confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) },
|
||||
|
@ -425,9 +425,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
isVideo = conferenceCall.flags.contains(.isVideo)
|
||||
if message.flags.contains(.Incoming) {
|
||||
hasIncoming = true
|
||||
//TODO:localize
|
||||
let missedTimeout: Int32
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
missedTimeout = 5
|
||||
#else
|
||||
missedTimeout = 30
|
||||
@ -463,8 +462,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
|
||||
peersString.append(", ")
|
||||
}
|
||||
if peer.id == item.context.account.peerId {
|
||||
//TODO:localize
|
||||
peersString += "You"
|
||||
peersString += item.presentationData.strings.DialogList_You
|
||||
} else {
|
||||
peersString += peer.compactDisplayTitle
|
||||
}
|
||||
|
@ -108,7 +108,8 @@ public final class CallListController: TelegramBaseController {
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
if case .tab = self.mode {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed))
|
||||
//self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed))
|
||||
self.navigationItem.rightBarButtonItem = nil
|
||||
|
||||
let icon: UIImage?
|
||||
if useSpecialTabBarIcons() {
|
||||
@ -191,7 +192,7 @@ public final class CallListController: TelegramBaseController {
|
||||
}
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed))
|
||||
//self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed))
|
||||
case .navigation:
|
||||
if self.editingMode {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed))
|
||||
@ -296,9 +297,8 @@ public final class CallListController: TelegramBaseController {
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
self.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
if case .undo = action {
|
||||
openCall()
|
||||
}
|
||||
@ -383,7 +383,8 @@ public final class CallListController: TelegramBaseController {
|
||||
})
|
||||
} else {
|
||||
strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true)
|
||||
strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true)
|
||||
//strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true)
|
||||
strongSelf.navigationItem.setRightBarButton(nil, animated: true)
|
||||
}
|
||||
case .navigation:
|
||||
if strongSelf.editingMode {
|
||||
@ -399,9 +400,9 @@ public final class CallListController: TelegramBaseController {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, createGroupCall: { [weak self] in
|
||||
}, openNewCall: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.createGroupCall(peerIds: [], isVideo: false)
|
||||
strongSelf.callPressed()
|
||||
}
|
||||
})
|
||||
|
||||
@ -515,8 +516,7 @@ public final class CallListController: TelegramBaseController {
|
||||
return
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let options = [ContactListAdditionalOption(title: "New Call Link", icon: .generic(PresentationResourcesItemList.linkIcon(presentationData.theme)!), action: { [weak self] in
|
||||
let options = [ContactListAdditionalOption(title: self.presentationData.strings.CallList_NewCallLink, icon: .generic(PresentationResourcesItemList.linkIcon(presentationData.theme)!), action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -652,7 +652,8 @@ public final class CallListController: TelegramBaseController {
|
||||
switch self.mode {
|
||||
case .tab:
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true)
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true)
|
||||
self.navigationItem.setRightBarButton(nil, animated: true)
|
||||
//self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true)
|
||||
case .navigation:
|
||||
self.navigationItem.setLeftBarButton(nil, animated: true)
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true)
|
||||
@ -738,8 +739,7 @@ public final class CallListController: TelegramBaseController {
|
||||
self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self)
|
||||
default:
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
self.present(textAlertController(context: self.context, title: nil, text: "An error occurred", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
self.present(textAlertController(context: self.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -67,16 +67,16 @@ final class CallListNodeInteraction {
|
||||
let delete: ([EngineMessage.Id]) -> Void
|
||||
let updateShowCallsTab: (Bool) -> Void
|
||||
let openGroupCall: (EnginePeer.Id) -> Void
|
||||
let createGroupCall: () -> Void
|
||||
let openNewCall: () -> Void
|
||||
|
||||
init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
init(setMessageIdWithRevealedOptions: @escaping (EngineMessage.Id?, EngineMessage.Id?) -> Void, call: @escaping (EngineMessage) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, delete: @escaping ([EngineMessage.Id]) -> Void, updateShowCallsTab: @escaping (Bool) -> Void, openGroupCall: @escaping (EnginePeer.Id) -> Void, openNewCall: @escaping () -> Void) {
|
||||
self.setMessageIdWithRevealedOptions = setMessageIdWithRevealedOptions
|
||||
self.call = call
|
||||
self.openInfo = openInfo
|
||||
self.delete = delete
|
||||
self.updateShowCallsTab = updateShowCallsTab
|
||||
self.openGroupCall = openGroupCall
|
||||
self.createGroupCall = createGroupCall
|
||||
self.openNewCall = openNewCall
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,10 +125,9 @@ private func mappedInsertEntries(context: AccountContext, presentationData: Item
|
||||
}), directionHint: entry.directionHint)
|
||||
case let .displayTabInfo(_, text):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint)
|
||||
case .createGroupCall:
|
||||
//TODO:localize
|
||||
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", hasSeparator: false, sectionId: 1, noInsets: true, editing: false, action: {
|
||||
nodeInteraction.createGroupCall()
|
||||
case .openNewCall:
|
||||
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: {
|
||||
nodeInteraction.openNewCall()
|
||||
})
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||
case let .groupCall(peer, _, isActive):
|
||||
@ -150,10 +149,9 @@ private func mappedUpdateEntries(context: AccountContext, presentationData: Item
|
||||
}), directionHint: entry.directionHint)
|
||||
case let .displayTabInfo(_, text):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: 0), directionHint: entry.directionHint)
|
||||
case .createGroupCall:
|
||||
//TODO:localize
|
||||
let item = ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.linkIcon(presentationData.theme), title: "New Call Link", sectionId: 1, noInsets: true, editing: false, action: {
|
||||
nodeInteraction.createGroupCall()
|
||||
case .openNewCall:
|
||||
let item = ItemListPeerActionItem(presentationData: presentationData, style: showSettings ? .blocks : .plain, icon: PresentationResourcesRootController.callListCallIcon(presentationData.theme), title: presentationData.strings.CallList_NewCall, hasSeparator: false, sectionId: 1, height: .generic, noInsets: true, editing: false, action: {
|
||||
nodeInteraction.openNewCall()
|
||||
})
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint)
|
||||
case let .groupCall(peer, _, isActive):
|
||||
@ -224,7 +222,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
private let call: (EngineMessage) -> Void
|
||||
private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void
|
||||
private let createGroupCall: () -> Void
|
||||
private let openNewCall: () -> Void
|
||||
private let openInfo: (EnginePeer.Id, [EngineMessage]) -> Void
|
||||
private let emptyStateUpdated: (Bool) -> Void
|
||||
private let emptyStatePromise = Promise<Bool>()
|
||||
@ -234,7 +232,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
|
||||
private var previousContentOffset: ListViewVisibleContentOffset?
|
||||
|
||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, createGroupCall: @escaping () -> Void) {
|
||||
init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, openNewCall: @escaping () -> Void) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
@ -243,7 +241,7 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
self.joinGroupCall = joinGroupCall
|
||||
self.openInfo = openInfo
|
||||
self.emptyStateUpdated = emptyStateUpdated
|
||||
self.createGroupCall = createGroupCall
|
||||
self.openNewCall = openNewCall
|
||||
self.currentState = CallListNodeState(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, disableAnimations: true, editing: false, messageIdWithRevealedOptions: nil)
|
||||
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
||||
|
||||
@ -447,11 +445,11 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
strongSelf.joinGroupCall(peerId, activeCall)
|
||||
}
|
||||
}))
|
||||
}, createGroupCall: { [weak self] in
|
||||
}, openNewCall: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.createGroupCall()
|
||||
strongSelf.openNewCall()
|
||||
})
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
@ -516,28 +514,18 @@ final class CallListControllerNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let canCreateGroupCall = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App())
|
||||
|> map { configuration -> Bool in
|
||||
var isConferencePossible = true
|
||||
if let data = configuration.data, let value = data["ios_enable_conference"] as? Double {
|
||||
isConferencePossible = value != 0.0
|
||||
}
|
||||
return isConferencePossible
|
||||
}
|
||||
|
||||
let callListNodeViewTransition = combineLatest(
|
||||
callListViewUpdate,
|
||||
self.statePromise.get(),
|
||||
groupCalls,
|
||||
showCallsTab,
|
||||
currentGroupCallPeerId,
|
||||
canCreateGroupCall
|
||||
currentGroupCallPeerId
|
||||
)
|
||||
|> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId, canCreateGroupCall) -> Signal<CallListNodeListViewTransition, NoError> in
|
||||
|> mapToQueue { (updateAndType, state, groupCalls, showCallsTab, currentGroupCallPeerId) -> Signal<CallListNodeListViewTransition, NoError> in
|
||||
let (update, type) = updateAndType
|
||||
|
||||
let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, canCreateGroupCall: canCreateGroupCall && mode == .tab, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData)
|
||||
let processedView = CallListNodeView(originalView: update.view, filteredEntries: callListNodeEntriesForView(view: update.view, displayOpenNewCall: mode == .tab && type == .all, groupCalls: groupCalls, state: state, showSettings: showSettings, showCallsTab: showCallsTab, isRecentCalls: type == .all, currentGroupCallPeerId: currentGroupCallPeerId), presentationData: state.presentationData)
|
||||
let previous = previousView.swap(processedView)
|
||||
let previousType = previousType.swap(type)
|
||||
|
||||
|
@ -25,7 +25,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
enum SortIndex: Comparable {
|
||||
case displayTab
|
||||
case displayTabInfo
|
||||
case createGroupCall
|
||||
case openNewCall
|
||||
case groupCall(EnginePeer.Id, String)
|
||||
case message(EngineMessage.Index)
|
||||
case hole(EngineMessage.Index)
|
||||
@ -41,7 +41,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case .createGroupCall:
|
||||
case .openNewCall:
|
||||
switch rhs {
|
||||
case .displayTab, .displayTabInfo:
|
||||
return false
|
||||
@ -50,7 +50,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
case let .groupCall(lhsPeerId, lhsTitle):
|
||||
switch rhs {
|
||||
case .displayTab, .displayTabInfo, .createGroupCall:
|
||||
case .displayTab, .displayTabInfo, .openNewCall:
|
||||
return false
|
||||
case let .groupCall(rhsPeerId, rhsTitle):
|
||||
if lhsTitle == rhsTitle {
|
||||
@ -63,7 +63,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
case let .hole(lhsIndex):
|
||||
switch rhs {
|
||||
case .displayTab, .displayTabInfo, .groupCall, .createGroupCall:
|
||||
case .displayTab, .displayTabInfo, .groupCall, .openNewCall:
|
||||
return false
|
||||
case let .hole(rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
@ -72,7 +72,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
case let .message(lhsIndex):
|
||||
switch rhs {
|
||||
case .displayTab, .displayTabInfo, .groupCall, .createGroupCall:
|
||||
case .displayTab, .displayTabInfo, .groupCall, .openNewCall:
|
||||
return false
|
||||
case let .hole(rhsIndex):
|
||||
return lhsIndex < rhsIndex
|
||||
@ -86,7 +86,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
|
||||
case displayTab(PresentationTheme, String, Bool)
|
||||
case displayTabInfo(PresentationTheme, String)
|
||||
case createGroupCall
|
||||
case openNewCall
|
||||
case groupCall(peer: EnginePeer, editing: Bool, isActive: Bool)
|
||||
case messageEntry(topMessage: EngineMessage, messages: [EngineMessage], theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, editing: Bool, hasActiveRevealControls: Bool, displayHeader: Bool, missed: Bool)
|
||||
case holeEntry(index: EngineMessage.Index, theme: PresentationTheme)
|
||||
@ -97,8 +97,8 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
return .displayTab
|
||||
case .displayTabInfo:
|
||||
return .displayTabInfo
|
||||
case .createGroupCall:
|
||||
return .createGroupCall
|
||||
case .openNewCall:
|
||||
return .openNewCall
|
||||
case let .groupCall(peer, _, _):
|
||||
return .groupCall(peer.id, peer.compactDisplayTitle)
|
||||
case let .messageEntry(message, _, _, _, _, _, _, _, _):
|
||||
@ -114,7 +114,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
return .setting(0)
|
||||
case .displayTabInfo:
|
||||
return .setting(1)
|
||||
case .createGroupCall:
|
||||
case .openNewCall:
|
||||
return .setting(2)
|
||||
case let .groupCall(peer, _, _):
|
||||
return .groupCall(peer.id)
|
||||
@ -143,8 +143,8 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .createGroupCall:
|
||||
if case .createGroupCall = rhs {
|
||||
case .openNewCall:
|
||||
if case .openNewCall = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -212,7 +212,7 @@ enum CallListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] {
|
||||
func callListNodeEntriesForView(view: EngineCallList, displayOpenNewCall: Bool, groupCalls: [EnginePeer], state: CallListNodeState, showSettings: Bool, showCallsTab: Bool, isRecentCalls: Bool, currentGroupCallPeerId: EnginePeer.Id?) -> [CallListNodeEntry] {
|
||||
var result: [CallListNodeEntry] = []
|
||||
for entry in view.items {
|
||||
switch entry {
|
||||
@ -237,8 +237,8 @@ func callListNodeEntriesForView(view: EngineCallList, canCreateGroupCall: Bool,
|
||||
}
|
||||
}
|
||||
|
||||
if canCreateGroupCall {
|
||||
result.append(.createGroupCall)
|
||||
if displayOpenNewCall {
|
||||
result.append(.openNewCall)
|
||||
}
|
||||
|
||||
if showSettings {
|
||||
|
@ -298,9 +298,25 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
messageText = invoice.title
|
||||
case let action as TelegramMediaAction:
|
||||
switch action.action {
|
||||
case .conferenceCall:
|
||||
//TODO:localize
|
||||
messageText = "Group call"
|
||||
case let .conferenceCall(conferenceCall):
|
||||
let incoming = message.flags.contains(.Incoming)
|
||||
|
||||
let missedTimeout: Int32 = 30
|
||||
let currentTime = Int32(Date().timeIntervalSince1970)
|
||||
|
||||
if conferenceCall.flags.contains(.isMissed) {
|
||||
messageText = strings.Chat_CallMessage_DeclinedGroupCall
|
||||
} else if message.timestamp < currentTime - missedTimeout {
|
||||
messageText = strings.Chat_CallMessage_MissedGroupCall
|
||||
} else if conferenceCall.duration != nil {
|
||||
messageText = strings.Chat_CallMessage_CancelledGroupCall
|
||||
} else {
|
||||
if incoming {
|
||||
messageText = strings.Chat_CallMessage_IncomingGroupCall
|
||||
} else {
|
||||
messageText = strings.Chat_CallMessage_OutgoingGroupCall
|
||||
}
|
||||
}
|
||||
case let .phoneCall(_, discardReason, _, isVideo):
|
||||
hideAuthor = !isPeerGroup
|
||||
let incoming = message.flags.contains(.Incoming)
|
||||
|
@ -474,7 +474,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
|
||||
if let requestAddContact = self?.requestAddContact {
|
||||
requestAddContact(phoneNumber)
|
||||
}
|
||||
}, openPeer: { [weak self] peer in
|
||||
}, openPeer: { [weak self] peer, _ in
|
||||
if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch {
|
||||
requestOpenPeerFromSearch(peer)
|
||||
}
|
||||
|
@ -52,14 +52,14 @@ private enum ContactListSearchEntryId: Hashable {
|
||||
}
|
||||
|
||||
private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
case addContact(PresentationTheme, PresentationStrings, String)
|
||||
case peer(Int, PresentationTheme, PresentationStrings, ContactListPeer, EnginePeer.Presence?, ContactListSearchGroup, Bool, Bool)
|
||||
case addContact(theme: PresentationTheme, strings: PresentationStrings, phoneNumber: String)
|
||||
case peer(index: Int, theme: PresentationTheme, strings: PresentationStrings, peer: ContactListPeer, presence: EnginePeer.Presence?, group: ContactListSearchGroup, enabled: Bool, requiresPremiumForMessaging: Bool, displayCallIcons: Bool)
|
||||
|
||||
var stableId: ContactListSearchEntryId {
|
||||
switch self {
|
||||
case .addContact:
|
||||
return .addContact
|
||||
case let .peer(_, _, _, peer, _, _, _, _):
|
||||
case let .peer(_, _, _, peer, _, _, _, _, _):
|
||||
return .peerId(peer.id)
|
||||
}
|
||||
}
|
||||
@ -72,9 +72,9 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsPeer, lhsPresence, lhsGroup, lhsEnabled, lhsRequiresPremiumForMessaging):
|
||||
case let .peer(lhsIndex, lhsTheme, lhsStrings, lhsPeer, lhsPresence, lhsGroup, lhsEnabled, lhsRequiresPremiumForMessaging, lhsDisplayCallIcons):
|
||||
switch rhs {
|
||||
case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsPeer, rhsPresence, rhsGroup, rhsEnabled, rhsRequiresPremiumForMessaging):
|
||||
case let .peer(rhsIndex, rhsTheme, rhsStrings, rhsPeer, rhsPresence, rhsGroup, rhsEnabled, rhsRequiresPremiumForMessaging, rhsDisplayCallIcons):
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -103,6 +103,9 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
if lhsRequiresPremiumForMessaging != rhsRequiresPremiumForMessaging {
|
||||
return false
|
||||
}
|
||||
if lhsDisplayCallIcons != rhsDisplayCallIcons {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@ -114,23 +117,23 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
switch lhs {
|
||||
case .addContact:
|
||||
return true
|
||||
case let .peer(lhsIndex, _, _, _, _, _, _, _):
|
||||
case let .peer(lhsIndex, _, _, _, _, _, _, _, _):
|
||||
switch rhs {
|
||||
case .addContact:
|
||||
return false
|
||||
case let .peer(rhsIndex, _, _, _, _, _, _, _):
|
||||
case let .peer(rhsIndex, _, _, _, _, _, _, _, _):
|
||||
return lhsIndex < rhsIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func item(context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ListViewItem {
|
||||
func item(context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, isPeerEnabled: @escaping (ContactListPeer) -> Bool, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer, ContactsSearchContainerNode.OpenPeerAction) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ListViewItem {
|
||||
switch self {
|
||||
case let .addContact(theme, strings, phoneNumber):
|
||||
return ContactsAddItem(context: context, theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
addContact?(phoneNumber)
|
||||
})
|
||||
case let .peer(_, theme, strings, peer, presence, group, enabled, requiresPremiumForMessaging):
|
||||
case let .peer(_, theme, strings, peer, presence, group, enabled, requiresPremiumForMessaging, displayCallIcons):
|
||||
let header: ListViewItemHeader
|
||||
let status: ContactsPeerItemStatus
|
||||
switch group {
|
||||
@ -161,8 +164,16 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
case let .deviceContact(stableId, contact):
|
||||
peerItem = .deviceContact(stableId: stableId, contact: contact)
|
||||
}
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: peerItem, status: status, requiresPremiumForMessaging: requiresPremiumForMessaging, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
openPeer(peer)
|
||||
var additionalActions: [ContactsPeerItemAction] = []
|
||||
if displayCallIcons {
|
||||
additionalActions = [ContactsPeerItemAction(icon: .voiceCall, action: { _, sourceNode, gesture in
|
||||
openPeer(peer, .voiceCall)
|
||||
}), ContactsPeerItemAction(icon: .videoCall, action: { _, sourceNode, gesture in
|
||||
openPeer(peer, .videoCall)
|
||||
})]
|
||||
}
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .peer, peer: peerItem, status: status, requiresPremiumForMessaging: requiresPremiumForMessaging, enabled: enabled && isPeerEnabled(peer), selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), additionalActions: additionalActions, index: nil, header: header, action: { _ in
|
||||
openPeer(peer, .generic)
|
||||
}, disabledAction: { _ in
|
||||
if case let .peer(peer, _, _) = peer {
|
||||
openDisabledPeer(EnginePeer(peer), requiresPremiumForMessaging ? .premiumRequired : .generic)
|
||||
@ -187,12 +198,12 @@ struct ContactListSearchContainerTransition {
|
||||
let query: String
|
||||
}
|
||||
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, emptyResults: Bool, query: String, context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, emptyResults: Bool, query: String, context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, isPeerEnabled: @escaping (ContactListPeer) -> Bool, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer, ContactsSearchContainerNode.OpenPeerAction) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, addContact: addContact, openPeer: openPeer, openDisabledPeer: openDisabledPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, addContact: addContact, openPeer: openPeer, openDisabledPeer: openDisabledPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, isPeerEnabled: isPeerEnabled, addContact: addContact, openPeer: openPeer, openDisabledPeer: openDisabledPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, isPeerEnabled: isPeerEnabled, addContact: addContact, openPeer: openPeer, openDisabledPeer: openDisabledPeer, contextAction: contextAction), directionHint: nil) }
|
||||
|
||||
return ContactListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching, emptyResults: emptyResults, query: query)
|
||||
}
|
||||
@ -211,9 +222,16 @@ public struct ContactsSearchCategories: OptionSet {
|
||||
}
|
||||
|
||||
public final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
public enum OpenPeerAction {
|
||||
case generic
|
||||
case voiceCall
|
||||
case videoCall
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let isPeerEnabled: (ContactListPeer) -> Bool
|
||||
private let addContact: ((String) -> Void)?
|
||||
private let openPeer: (ContactListPeer) -> Void
|
||||
private let openPeer: (ContactListPeer, ContactsSearchContainerNode.OpenPeerAction) -> Void
|
||||
private let openDisabledPeer: (EnginePeer, ChatListDisabledPeerReason) -> Void
|
||||
private let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
@ -238,8 +256,9 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
return true
|
||||
}
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], displayCallIcons: Bool = false, isPeerEnabled: @escaping (ContactListPeer) -> Bool = { _ in true }, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer, ContactsSearchContainerNode.OpenPeerAction) -> Void, openDisabledPeer: @escaping (EnginePeer, ChatListDisabledPeerReason) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) {
|
||||
self.context = context
|
||||
self.isPeerEnabled = isPeerEnabled
|
||||
self.addContact = addContact
|
||||
self.openPeer = openPeer
|
||||
self.openDisabledPeer = openDisabledPeer
|
||||
@ -441,7 +460,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
entries.append(.peer(index, themeAndStrings.0, themeAndStrings.1, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), localPeersAndPresences.1[peer.id], .contacts, enabled, requiresPremiumForMessaging))
|
||||
entries.append(.peer(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence: localPeersAndPresences.1[peer.id], group: .contacts, enabled: enabled, requiresPremiumForMessaging: requiresPremiumForMessaging, displayCallIcons: displayCallIcons))
|
||||
if searchDeviceContacts, case let .user(user) = peer, let phone = user.phone {
|
||||
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
|
||||
}
|
||||
@ -483,7 +502,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.peer(index, themeAndStrings.0, themeAndStrings.1, .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), nil, .global, enabled, requiresPremiumForMessaging))
|
||||
entries.append(.peer(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), presence: nil, group: .global, enabled: enabled, requiresPremiumForMessaging: requiresPremiumForMessaging, displayCallIcons: displayCallIcons))
|
||||
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
|
||||
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
|
||||
}
|
||||
@ -518,7 +537,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
entries.append(.peer(index, themeAndStrings.0, themeAndStrings.1, .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), nil, .global, enabled, requiresPremiumForMessaging))
|
||||
entries.append(.peer(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .peer(peer: peer.peer, isGlobal: true, participantCount: peer.subscribers), presence: nil, group: .global, enabled: enabled, requiresPremiumForMessaging: requiresPremiumForMessaging, displayCallIcons: displayCallIcons))
|
||||
if searchDeviceContacts, let user = peer.peer as? TelegramUser, let phone = user.phone {
|
||||
existingNormalizedPhoneNumbers.insert(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone)))
|
||||
}
|
||||
@ -539,13 +558,13 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
entries.append(.peer(index, themeAndStrings.0, themeAndStrings.1, .deviceContact(stableId, contact.0), nil, .deviceContacts, true, false))
|
||||
entries.append(.peer(index: index, theme: themeAndStrings.0, strings: themeAndStrings.1, peer: .deviceContact(stableId, contact.0), presence: nil, group: .deviceContacts, enabled: true, requiresPremiumForMessaging: false, displayCallIcons: displayCallIcons))
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = addContact, isViablePhoneNumber(query) {
|
||||
entries.append(.addContact(themeAndStrings.0, themeAndStrings.1, query))
|
||||
entries.append(.addContact(theme: themeAndStrings.0, strings: themeAndStrings.1, phoneNumber: query))
|
||||
}
|
||||
|
||||
return (entries, query)
|
||||
@ -571,9 +590,9 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
}
|
||||
}
|
||||
|
||||
let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, emptyResults: items?.isEmpty ?? false, query: query, context: context, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, timeFormat: strongSelf.presentationData.dateTimeFormat, addContact: addContact, openPeer: { peer in
|
||||
let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, emptyResults: items?.isEmpty ?? false, query: query, context: context, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, timeFormat: strongSelf.presentationData.dateTimeFormat, isPeerEnabled: strongSelf.isPeerEnabled, addContact: addContact, openPeer: { peer, action in
|
||||
self?.listNode.clearHighlightAnimated(true)
|
||||
self?.openPeer(peer)
|
||||
self?.openPeer(peer, action)
|
||||
}, openDisabledPeer: { peer, reason in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -508,7 +508,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.deviceContacts], addContact: nil, openPeer: { [weak self] peer in
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.deviceContacts], addContact: nil, openPeer: { [weak self] peer, _ in
|
||||
if let strongSelf = self, case let .deviceContact(id, _) = peer {
|
||||
strongSelf.selectionState = strongSelf.selectionState.withSelectedContactId(id)
|
||||
strongSelf.requestDeactivateSearch?()
|
||||
|
@ -438,9 +438,8 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
icon = nil
|
||||
isUserInteractionEnabled = action != nil
|
||||
case let .starsReactions(topCount):
|
||||
//TODO:localize
|
||||
self.action = nil
|
||||
self.text = "Send \(topCount) or more to highlight your profile"
|
||||
self.text = self.presentationData.strings.Chat_SendStarsToBecomeTopInfo("\(topCount)").string
|
||||
self.targetSelectionIndex = nil
|
||||
icon = nil
|
||||
isUserInteractionEnabled = action != nil
|
||||
|
@ -492,10 +492,9 @@ public final class InviteLinkInviteController: ViewController {
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
//TODO:localize
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: "Revoke Link"),
|
||||
ActionSheetTextItem(title: presentationData.strings.GroupCall_RevokeLinkText),
|
||||
ActionSheetButtonItem(title: presentationData.strings.GroupInfo_InviteLink_RevokeLink, color: .destructive, action: { [weak self] in
|
||||
dismissAction()
|
||||
|
||||
@ -674,9 +673,8 @@ public final class InviteLinkInviteController: ViewController {
|
||||
}
|
||||
var entries: [InviteLinkInviteEntry] = []
|
||||
|
||||
//TODO:localize
|
||||
let helpText: String = "Anyone on Telegram can join your call by following the link below."
|
||||
entries.append(.header(title: "Call Link", text: helpText))
|
||||
let helpText: String = presentationData.strings.InviteLink_GroupCallLinkHelp
|
||||
entries.append(.header(title: presentationData.strings.InviteLink_CallLinkTitle, text: helpText))
|
||||
|
||||
let mainInvite: ExportedInvitation = .link(link: mainInvite?.link ?? "", title: nil, isPermanent: true, requestApproval: false, isRevoked: false, adminId: self.context.account.peerId, date: 0, startDate: nil, expireDate: nil, usageLimit: nil, count: nil, requestedCount: nil, pricing: nil)
|
||||
|
||||
|
@ -369,8 +369,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
}
|
||||
)
|
||||
//TODO:localize
|
||||
let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString("Be the first to join the call and add people from there. [Open Call >](open_call)", attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||
let justCreatedCallTextAttributedString = parseMarkdownIntoAttributedString(item.presentationData.strings.InviteLink_CreatedGroupCallFooter, attributes: markdownAttributes).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = justCreatedCallTextAttributedString.string.range(of: ">"), let chevronImage {
|
||||
justCreatedCallTextAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: justCreatedCallTextAttributedString.string))
|
||||
}
|
||||
|
@ -193,9 +193,7 @@ public func JoinLinkPreviewController(
|
||||
) -> ViewController {
|
||||
if let data = context.currentAppConfiguration.with({ $0 }).data, data["ios_killswitch_legacy_join_link"] != nil {
|
||||
return LegacyJoinLinkPreviewController(context: context, link: link, navigateToPeer: navigateToPeer, parentNavigationController: parentNavigationController, resolvedState: resolvedState)
|
||||
} else if case let .invite(invite) = resolvedState, !invite.flags.requestNeeded, !invite.flags.isBroadcast, !invite.flags.canRefulfillSubscription {
|
||||
//TODO:release
|
||||
|
||||
} else if case let .invite(invite) = resolvedState, !invite.flags.requestNeeded, !invite.flags.isBroadcast, !invite.flags.canRefulfillSubscription {
|
||||
var verificationStatus: JoinSubjectScreenMode.Group.VerificationStatus?
|
||||
if invite.flags.isFake {
|
||||
verificationStatus = .fake
|
||||
|
@ -253,8 +253,7 @@ public final class QrCodeScreen: ViewController {
|
||||
case .channel:
|
||||
text = self.presentationData.strings.InviteLink_QRCode_InfoChannel
|
||||
case .groupCall:
|
||||
//TODO:localize
|
||||
text = "Everyone on Telegram can scan this code to join your group call."
|
||||
text = self.presentationData.strings.InviteLink_QRCode_InfoGroupCall
|
||||
}
|
||||
case .chatFolder:
|
||||
title = self.presentationData.strings.InviteLink_QRCodeFolder_Title
|
||||
|
@ -1047,11 +1047,12 @@ private func selectivePrivacySettingsControllerEntries(presentationData: Present
|
||||
if let settingInfoText = settingInfoText {
|
||||
entries.append(.settingInfo(presentationData.theme, settingInfoText, settingInfoLink))
|
||||
}
|
||||
|
||||
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
||||
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
||||
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
||||
entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink))
|
||||
if case .phoneNumber = kind {
|
||||
entries.append(.phoneDiscoveryHeader(presentationData.theme, presentationData.strings.PrivacyPhoneNumberSettings_DiscoveryHeader))
|
||||
entries.append(.phoneDiscoveryEverybody(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenEverybody, state.phoneDiscoveryEnabled != false))
|
||||
entries.append(.phoneDiscoveryMyContacts(presentationData.theme, presentationData.strings.PrivacySettings_LastSeenContacts, state.phoneDiscoveryEnabled == false))
|
||||
entries.append(.phoneDiscoveryInfo(presentationData.theme, state.phoneDiscoveryEnabled != false ? presentationData.strings.PrivacyPhoneNumberSettings_CustomPublicLink("+\(phoneNumber)").string : presentationData.strings.PrivacyPhoneNumberSettings_CustomDisabledHelp, phoneLink))
|
||||
}
|
||||
|
||||
if case .voiceMessages = kind, !isPremium {
|
||||
|
||||
|
@ -498,14 +498,12 @@ public final class CallController: ViewController {
|
||||
}
|
||||
|
||||
static func openConferenceAddParticipant(context: AccountContext, disablePeerIds: [EnginePeer.Id], shareLink: (() -> Void)?, completion: @escaping ([(id: EnginePeer.Id, isVideo: Bool)]) -> Void) -> ViewController {
|
||||
//TODO:localize
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
|
||||
var options: [ContactListAdditionalOption] = []
|
||||
var openShareLinkImpl: (() -> Void)?
|
||||
if shareLink != nil {
|
||||
//TODO:localize
|
||||
options.append(ContactListAdditionalOption(title: "Share Call Link", icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: {
|
||||
options.append(ContactListAdditionalOption(title: presentationData.strings.Call_ShareLink, icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: {
|
||||
openShareLinkImpl?()
|
||||
}, clearHighlightAutomatically: false))
|
||||
}
|
||||
@ -515,11 +513,11 @@ public final class CallController: ViewController {
|
||||
updatedPresentationData: (initial: presentationData, signal: .single(presentationData)),
|
||||
mode: .generic,
|
||||
title: { strings in
|
||||
//TODO:localize
|
||||
return "Add Member"
|
||||
return strings.Call_AddMemberTitle
|
||||
},
|
||||
options: .single(options),
|
||||
displayCallIcons: true,
|
||||
multipleSelection: .disabled,
|
||||
confirmation: { peer in
|
||||
switch peer {
|
||||
case let .peer(peer, _, _):
|
||||
|
@ -765,8 +765,8 @@ public final class PresentationCallImpl: PresentationCall {
|
||||
self.localVideoEndpointId = nil
|
||||
self.remoteVideoEndpointId = nil
|
||||
|
||||
//TODO:localize
|
||||
self.callKitIntegration?.updateCallIsConference(uuid: self.internalId, title: self.conferenceTitle ?? "Group Call")
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.callKitIntegration?.updateCallIsConference(uuid: self.internalId, title: self.conferenceTitle ?? presentationData.strings.Call_GenericGroupCallTitle)
|
||||
}
|
||||
|
||||
func internal_markAsCanBeRemoved() {
|
||||
|
@ -210,6 +210,11 @@ private final class PendingConferenceInvitationContext {
|
||||
case ringing
|
||||
}
|
||||
|
||||
enum InvitationError {
|
||||
case generic
|
||||
case privacy(peer: EnginePeer?)
|
||||
}
|
||||
|
||||
private let engine: TelegramEngine
|
||||
private var requestDisposable: Disposable?
|
||||
private var stateDisposable: Disposable?
|
||||
@ -218,19 +223,12 @@ private final class PendingConferenceInvitationContext {
|
||||
private var hadMessage: Bool = false
|
||||
private var didNotifyEnded: Bool = false
|
||||
|
||||
init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void) {
|
||||
init(engine: TelegramEngine, reference: InternalGroupCallReference, peerId: PeerId, isVideo: Bool, onStateUpdated: @escaping (State) -> Void, onEnded: @escaping (Bool) -> Void, onError: @escaping (InvitationError) -> Void) {
|
||||
self.engine = engine
|
||||
self.requestDisposable = (engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo).startStrict(next: { [weak self] messageId in
|
||||
self.requestDisposable = ((engine.calls.inviteConferenceCallParticipant(reference: reference, peerId: peerId, isVideo: isVideo) |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let messageId else {
|
||||
if !self.didNotifyEnded {
|
||||
self.didNotifyEnded = true
|
||||
onEnded(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
self.messageId = messageId
|
||||
|
||||
onStateUpdated(.ringing)
|
||||
@ -296,6 +294,24 @@ private final class PendingConferenceInvitationContext {
|
||||
}
|
||||
}
|
||||
})
|
||||
}, error: { [weak self] error in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if !self.didNotifyEnded {
|
||||
self.didNotifyEnded = true
|
||||
onEnded(false)
|
||||
}
|
||||
|
||||
let mappedError: InvitationError
|
||||
switch error {
|
||||
case .privacy(let peer):
|
||||
mappedError = .privacy(peer: peer)
|
||||
default:
|
||||
mappedError = .generic
|
||||
}
|
||||
onError(mappedError)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -792,6 +808,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
private let e2eContext: ConferenceCallE2EContext?
|
||||
|
||||
private var lastErrorAlertTimestamp: Double = 0.0
|
||||
|
||||
init(
|
||||
accountContext: AccountContext,
|
||||
audioSession: ManagedAudioSession,
|
||||
@ -841,6 +859,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.isConference = isConference
|
||||
self.beginWithVideo = beginWithVideo
|
||||
self.keyPair = keyPair
|
||||
|
||||
if self.isConference && conferenceSourceId == nil {
|
||||
self.isMutedValue = .unmuted
|
||||
self.isMutedPromise.set(self.isMutedValue)
|
||||
self.stateValue.muteState = nil
|
||||
}
|
||||
|
||||
if let keyPair, let initialCall {
|
||||
self.e2eContext = ConferenceCallE2EContext(
|
||||
@ -1742,6 +1766,15 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
self.onMutedSpeechActivityDetected?(value)
|
||||
}
|
||||
}, isConference: self.isConference, audioIsActiveByDefault: audioIsActiveByDefault, isStream: self.isStream, sharedAudioDevice: self.sharedAudioContext?.audioDevice, encryptionContext: encryptionContext))
|
||||
|
||||
let isEffectivelyMuted: Bool
|
||||
switch self.isMutedValue {
|
||||
case let .muted(isPushToTalkActive):
|
||||
isEffectivelyMuted = !isPushToTalkActive
|
||||
case .unmuted:
|
||||
isEffectivelyMuted = false
|
||||
}
|
||||
genericCallContext.setIsMuted(isEffectivelyMuted)
|
||||
}
|
||||
|
||||
self.genericCallContext = genericCallContext
|
||||
@ -1889,6 +1922,14 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
} else {
|
||||
reference = .id(id: callInfo.id, accessHash: callInfo.accessHash)
|
||||
}
|
||||
|
||||
let isEffectivelyMuted: Bool
|
||||
switch self.isMutedValue {
|
||||
case let .muted(isPushToTalkActive):
|
||||
isEffectivelyMuted = !isPushToTalkActive
|
||||
case .unmuted:
|
||||
isEffectivelyMuted = false
|
||||
}
|
||||
|
||||
self.currentLocalSsrc = ssrc
|
||||
self.requestDisposable.set((self.accountContext.engine.calls.joinGroupCall(
|
||||
@ -1896,7 +1937,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
joinAs: self.joinAsPeerId,
|
||||
callId: callInfo.id,
|
||||
reference: reference,
|
||||
preferMuted: true,
|
||||
preferMuted: isEffectivelyMuted,
|
||||
joinPayload: joinPayload,
|
||||
peerAdminIds: peerAdminIds,
|
||||
inviteHash: self.invite,
|
||||
@ -1962,7 +2003,11 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
|
||||
self.updateSessionState(internalState: .established(info: joinCallResult.callInfo, connectionMode: joinCallResult.connectionMode, clientParams: clientParams, localSsrc: ssrc, initialState: joinCallResult.state), audioSessionControl: self.audioSessionControl)
|
||||
|
||||
self.e2eContext?.begin()
|
||||
if let e2eState = joinCallResult.e2eState {
|
||||
self.e2eContext?.begin(initialState: e2eState)
|
||||
} else {
|
||||
self.e2eContext?.begin(initialState: nil)
|
||||
}
|
||||
}, error: { [weak self] error in
|
||||
guard let self else {
|
||||
return
|
||||
@ -3536,6 +3581,32 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
||||
onEnded: { success in
|
||||
didEndAlready = true
|
||||
onEnded?(success)
|
||||
},
|
||||
onError: { [weak self] error in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let timestamp = CACurrentMediaTime()
|
||||
if self.lastErrorAlertTimestamp > timestamp - 1.0 {
|
||||
return
|
||||
}
|
||||
self.lastErrorAlertTimestamp = timestamp
|
||||
|
||||
let presentationData = self.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
|
||||
var errorText = presentationData.strings.Login_UnknownError
|
||||
switch error {
|
||||
case let .privacy(peer):
|
||||
if let peer {
|
||||
errorText = presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).string
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})
|
||||
]), on: .root, blockInteraction: false, completion: {})
|
||||
}
|
||||
)
|
||||
if !didEndAlready {
|
||||
|
@ -65,9 +65,9 @@ final class VideoChatActionButtonComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
enum MicrophoneState {
|
||||
enum MicrophoneState: Equatable {
|
||||
case connecting
|
||||
case muted
|
||||
case muted(forced: Bool)
|
||||
case unmuted
|
||||
case raiseHand
|
||||
case scheduled
|
||||
|
@ -550,7 +550,7 @@ final class VideoChatEncryptionKeyComponent: Component {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
#if DEBUG && true
|
||||
#if DEBUG && false
|
||||
if self.component == nil {
|
||||
self.mockStateTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
@ -614,11 +614,10 @@ final class VideoChatEncryptionKeyComponent: Component {
|
||||
)
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let collapsedTextSize = self.collapsedText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "End-to-end encrypted", font: Font.semibold(12.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyLabel, font: Font.semibold(12.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 1000.0, height: 1000.0)
|
||||
@ -627,7 +626,7 @@ final class VideoChatEncryptionKeyComponent: Component {
|
||||
let expandedTextSize = self.expandedText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(string: "These four emojis represent the call's encryption key. They must match for all participants and change when someone joins or leaves.", font: Font.regular(12.0), textColor: component.theme.list.itemPrimaryTextColor)),
|
||||
text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyText, font: Font.regular(12.0), textColor: component.theme.list.itemPrimaryTextColor)),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.3
|
||||
)),
|
||||
@ -638,7 +637,7 @@ final class VideoChatEncryptionKeyComponent: Component {
|
||||
let expandedButtonTextSize = self.expandedButtonText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Close", font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
text: .plain(NSAttributedString(string: component.strings.VideoChat_EncryptionKeyDone, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - expandedSideInset * 2.0, height: 1000.0)
|
||||
|
@ -182,7 +182,7 @@ final class VideoChatMicButtonComponent: Component {
|
||||
|
||||
enum Content: Equatable {
|
||||
case connecting
|
||||
case muted
|
||||
case muted(forced: Bool)
|
||||
case unmuted(pushToTalk: Bool)
|
||||
case raiseHand(isRaised: Bool)
|
||||
case scheduled(state: ScheduledState)
|
||||
@ -263,9 +263,13 @@ final class VideoChatMicButtonComponent: Component {
|
||||
switch component.content {
|
||||
case .connecting, .unmuted, .raiseHand, .scheduled:
|
||||
self.beginTrackingWasPushToTalk = false
|
||||
case .muted:
|
||||
self.beginTrackingWasPushToTalk = true
|
||||
component.updateUnmutedStateIsPushToTalk(true)
|
||||
case let .muted(forced):
|
||||
if forced {
|
||||
self.beginTrackingWasPushToTalk = false
|
||||
} else {
|
||||
self.beginTrackingWasPushToTalk = true
|
||||
component.updateUnmutedStateIsPushToTalk(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,8 +295,11 @@ final class VideoChatMicButtonComponent: Component {
|
||||
switch component.content {
|
||||
case .connecting:
|
||||
break
|
||||
case .muted:
|
||||
component.updateUnmutedStateIsPushToTalk(false)
|
||||
case let .muted(forced):
|
||||
if forced {
|
||||
} else {
|
||||
component.updateUnmutedStateIsPushToTalk(false)
|
||||
}
|
||||
case .unmuted:
|
||||
if self.beginTrackingWasPushToTalk {
|
||||
if timestamp < self.beginTrackingTimestamp + 0.15 {
|
||||
@ -340,8 +347,12 @@ final class VideoChatMicButtonComponent: Component {
|
||||
case .connecting:
|
||||
titleText = component.strings.VoiceChat_Connecting
|
||||
isEnabled = false
|
||||
case .muted:
|
||||
titleText = component.strings.VoiceChat_Unmute
|
||||
case let .muted(forced):
|
||||
if forced {
|
||||
titleText = component.strings.VoiceChat_MutedByAdmin
|
||||
} else {
|
||||
titleText = component.strings.VoiceChat_Unmute
|
||||
}
|
||||
case let .unmuted(isPushToTalk):
|
||||
titleText = isPushToTalk ? component.strings.VoiceChat_Live : component.strings.VoiceChat_Mute
|
||||
case let .raiseHand(isRaised):
|
||||
@ -433,7 +444,9 @@ final class VideoChatMicButtonComponent: Component {
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
case .muted, .unmuted, .raiseHand, .scheduled:
|
||||
let colors: [UIColor]
|
||||
if case .muted = component.content {
|
||||
if case .muted(forced: true) = component.content {
|
||||
colors = [UIColor(rgb: 0x3252EF), UIColor(rgb: 0xC64688)]
|
||||
} else if case .muted(forced: false) = component.content {
|
||||
colors = [UIColor(rgb: 0x0080FF), UIColor(rgb: 0x00A1FE)]
|
||||
} else if case .raiseHand = component.content {
|
||||
colors = [UIColor(rgb: 0x3252EF), UIColor(rgb: 0xC64688)]
|
||||
@ -606,7 +619,9 @@ final class VideoChatMicButtonComponent: Component {
|
||||
transition.setScale(view: blobView, scale: availableSize.width / 116.0)
|
||||
|
||||
let blobsColor: UIColor
|
||||
if case .muted = component.content {
|
||||
if case .muted(forced: true) = component.content {
|
||||
blobsColor = UIColor(rgb: 0x914BAD)
|
||||
} else if case .muted(forced: false) = component.content {
|
||||
blobsColor = UIColor(rgb: 0x0086FF)
|
||||
} else if case .raiseHand = component.content {
|
||||
blobsColor = UIColor(rgb: 0x914BAD)
|
||||
@ -657,7 +672,9 @@ final class VideoChatMicButtonComponent: Component {
|
||||
}
|
||||
|
||||
let glowColor: UIColor
|
||||
if case .muted = component.content {
|
||||
if case .muted(forced: true) = component.content {
|
||||
glowColor = UIColor(rgb: 0x3252EF)
|
||||
} else if case .muted(forced: false) = component.content {
|
||||
glowColor = UIColor(rgb: 0x0086FF)
|
||||
} else if case .raiseHand = component.content {
|
||||
glowColor = UIColor(rgb: 0x3252EF)
|
||||
|
@ -1282,18 +1282,7 @@ final class VideoChatParticipantsComponent: Component {
|
||||
let invitedPeer = component.invitedPeers[i - self.listParticipants.count]
|
||||
participantPeerId = invitedPeer.peer.id
|
||||
|
||||
let subtitle: PeerListItemComponent.Subtitle
|
||||
//TODO:localize
|
||||
switch invitedPeer.state {
|
||||
case .none:
|
||||
subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral)
|
||||
case .connecting:
|
||||
subtitle = PeerListItemComponent.Subtitle(text: "connecting...", color: .neutral)
|
||||
case .requesting:
|
||||
subtitle = PeerListItemComponent.Subtitle(text: "requesting...", color: .neutral)
|
||||
case .ringing:
|
||||
subtitle = PeerListItemComponent.Subtitle(text: "invited", color: .neutral)
|
||||
}
|
||||
let subtitle: PeerListItemComponent.Subtitle = PeerListItemComponent.Subtitle(text: component.strings.VoiceChat_StatusInvited, color: .neutral)
|
||||
|
||||
let rightAccessoryComponent: AnyComponent<Empty> = AnyComponent(VideoChatParticipantInvitedStatusComponent(
|
||||
theme: component.theme
|
||||
@ -1861,11 +1850,10 @@ final class VideoChatParticipantsComponent: Component {
|
||||
let iconType: VideoChatListInviteComponent.Icon
|
||||
switch inviteOption.type {
|
||||
case let .invite(isMultiple):
|
||||
//TODO:localize
|
||||
if isMultiple {
|
||||
inviteText = component.strings.VoiceChat_InviteMember
|
||||
} else {
|
||||
inviteText = "Add Member"
|
||||
inviteText = component.strings.VideoChat_InviteMember
|
||||
}
|
||||
iconType = .addUser
|
||||
case .shareLink:
|
||||
|
@ -685,9 +685,8 @@ final class VideoChatScreenComponent: Component {
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with { $0 }
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
return false
|
||||
}), in: .current)
|
||||
case .openCall:
|
||||
@ -2038,11 +2037,10 @@ final class VideoChatScreenComponent: Component {
|
||||
maxTitleWidth -= 110.0
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(VideoChatTitleComponent(
|
||||
title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? "Group Call",
|
||||
title: self.callState?.title ?? self.peer?.debugDisplayTitle ?? environment.strings.VideoChat_GroupCallTitle,
|
||||
status: idleTitleStatusText,
|
||||
isRecording: self.callState?.recordingStartTimestamp != nil,
|
||||
strings: environment.strings,
|
||||
@ -2555,9 +2553,12 @@ final class VideoChatScreenComponent: Component {
|
||||
micButtonContent = .unmuted(pushToTalk: self.isPushToTalkActive)
|
||||
actionButtonMicrophoneState = .unmuted
|
||||
} else {
|
||||
micButtonContent = .muted
|
||||
actionButtonMicrophoneState = .muted
|
||||
micButtonContent = .muted(forced: false)
|
||||
actionButtonMicrophoneState = .muted(forced: false)
|
||||
}
|
||||
} else if isConference {
|
||||
micButtonContent = .muted(forced: true)
|
||||
actionButtonMicrophoneState = .muted(forced: true)
|
||||
} else {
|
||||
micButtonContent = .raiseHand(isRaised: callState.raisedHand)
|
||||
actionButtonMicrophoneState = .raiseHand
|
||||
@ -2708,6 +2709,18 @@ final class VideoChatScreenComponent: Component {
|
||||
} else if let expandedParticipantsVideoState = self.expandedParticipantsVideoState, !expandedParticipantsVideoState.isUIHidden {
|
||||
displayVideoControlButton = false
|
||||
}
|
||||
if case .audio = videoControlButtonContent {
|
||||
if let (availableOutputs, _) = self.audioOutputState {
|
||||
if availableOutputs.count <= 0 {
|
||||
displayVideoControlButton = false
|
||||
}
|
||||
} else {
|
||||
displayVideoControlButton = false
|
||||
}
|
||||
}
|
||||
if videoControlButtonContent == videoButtonContent {
|
||||
displayVideoControlButton = false
|
||||
}
|
||||
|
||||
let videoControlButtonSize = self.videoControlButton.update(
|
||||
transition: transition,
|
||||
|
@ -129,7 +129,6 @@ extension VideoChatScreenComponent.View {
|
||||
if let participant {
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if groupCall.invitePeer(participant.peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if case let .channel(channel) = self.peer, case .broadcast = channel.info {
|
||||
@ -242,7 +241,6 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if groupCall.invitePeer(peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if case let .channel(channel) = self.peer, case .broadcast = channel.info {
|
||||
@ -315,7 +313,6 @@ extension VideoChatScreenComponent.View {
|
||||
}
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if groupCall.invitePeer(peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if case let .channel(channel) = self.peer, case .broadcast = channel.info {
|
||||
|
@ -1256,7 +1256,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
if let participant = participant {
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if strongSelf.call.invitePeer(participant.peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
@ -1365,7 +1364,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
}
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if strongSelf.call.invitePeer(peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
@ -1434,7 +1432,6 @@ final class VoiceChatControllerImpl: ViewController, VoiceChatController {
|
||||
}
|
||||
dismissController?()
|
||||
|
||||
//TODO:release
|
||||
if strongSelf.call.invitePeer(peer.id, isVideo: false) {
|
||||
let text: String
|
||||
if let channel = strongSelf.peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
|
@ -79,7 +79,7 @@ public final class ConferenceCallE2EContext {
|
||||
self.synchronizeRemovedParticipantsTimer?.invalidate()
|
||||
}
|
||||
|
||||
func begin() {
|
||||
func begin(initialState: JoinGroupCallResult.E2EState?) {
|
||||
self.scheduledSynchronizeRemovedParticipantsAfterPoll = true
|
||||
self.synchronizeRemovedParticipantsTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
@ -88,6 +88,13 @@ public final class ConferenceCallE2EContext {
|
||||
self.synchronizeRemovedParticipants()
|
||||
})
|
||||
|
||||
if let initialState {
|
||||
self.e2ePoll0Offset = initialState.subChain0.offset
|
||||
self.e2ePoll1Offset = initialState.subChain1.offset
|
||||
self.addE2EBlocks(blocks: initialState.subChain0.blocks, subChainId: 0)
|
||||
self.addE2EBlocks(blocks: initialState.subChain1.blocks, subChainId: 1)
|
||||
}
|
||||
|
||||
self.e2ePoll(subChainId: 0)
|
||||
self.e2ePoll(subChainId: 1)
|
||||
}
|
||||
@ -182,7 +189,6 @@ public final class ConferenceCallE2EContext {
|
||||
self.e2eEncryptionKeyHashValue.set(outEmoji.isEmpty ? nil : outEmoji)
|
||||
|
||||
for outBlock in outBlocks {
|
||||
//TODO:release queue
|
||||
let _ = self.engine.calls.sendConferenceCallBroadcast(callId: self.callId, accessHash: self.accessHash, block: outBlock).startStandalone()
|
||||
}
|
||||
}
|
||||
@ -400,7 +406,6 @@ public final class ConferenceCallE2EContext {
|
||||
}
|
||||
|
||||
func kickPeer(id: EnginePeer.Id) {
|
||||
//TODO:release
|
||||
if !self.pendingKickPeers.contains(id) {
|
||||
self.pendingKickPeers.append(id)
|
||||
|
||||
@ -426,9 +431,9 @@ public final class ConferenceCallE2EContext {
|
||||
})
|
||||
}
|
||||
|
||||
public func begin() {
|
||||
public func begin(initialState: JoinGroupCallResult.E2EState?) {
|
||||
self.impl.with { impl in
|
||||
impl.begin()
|
||||
impl.begin(initialState: initialState)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,11 +587,22 @@ public struct JoinGroupCallResult {
|
||||
case rtc
|
||||
case broadcast(isExternalStream: Bool)
|
||||
}
|
||||
|
||||
public struct E2EState {
|
||||
public let subChain0: (offset: Int, blocks: [Data])
|
||||
public let subChain1: (offset: Int, blocks: [Data])
|
||||
|
||||
public init(subChain0: (offset: Int, blocks: [Data]), subChain1: (offset: Int, blocks: [Data])) {
|
||||
self.subChain0 = subChain0
|
||||
self.subChain1 = subChain1
|
||||
}
|
||||
}
|
||||
|
||||
public var callInfo: GroupCallInfo
|
||||
public var state: GroupCallParticipantsContext.State
|
||||
public var connectionMode: ConnectionMode
|
||||
public var jsonParams: String
|
||||
public var e2eState: E2EState?
|
||||
}
|
||||
|
||||
public class JoinGroupCallE2E {
|
||||
@ -791,7 +802,7 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||
connectionMode = .broadcast(isExternalStream: false)
|
||||
}
|
||||
|
||||
return account.postbox.transaction { transaction -> JoinGroupCallResult in
|
||||
return account.postbox.transaction { transaction -> Signal<JoinGroupCallResult, InternalJoinError> in
|
||||
if let peerId {
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||
if let cachedData = cachedData as? CachedChannelData {
|
||||
@ -806,6 +817,9 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||
|
||||
var state = state
|
||||
|
||||
var e2eSubChain1State: (offset: Int, blocks: [Data])?
|
||||
var e2eSubChain0State: (offset: Int, blocks: [Data])?
|
||||
|
||||
for update in updates.allUpdates {
|
||||
switch update {
|
||||
case let .updateGroupCallParticipants(_, participants, _):
|
||||
@ -852,21 +866,41 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .updateGroupCallChainBlocks(_, subChainId, blocks, nextOffset):
|
||||
if subChainId == 0 {
|
||||
e2eSubChain0State = (offset: Int(nextOffset), blocks: blocks.map { $0.makeData() })
|
||||
} else {
|
||||
e2eSubChain1State = (offset: Int(nextOffset), blocks: blocks.map { $0.makeData() })
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var e2eState: JoinGroupCallResult.E2EState?
|
||||
if let e2eSubChain0State, let e2eSubChain1State {
|
||||
e2eState = JoinGroupCallResult.E2EState(
|
||||
subChain0: e2eSubChain0State,
|
||||
subChain1: e2eSubChain1State
|
||||
)
|
||||
}
|
||||
|
||||
if generateE2E != nil && e2eState == nil {
|
||||
return .fail(.error(.generic))
|
||||
}
|
||||
|
||||
state.participants.sort(by: { GroupCallParticipantsContext.Participant.compare(lhs: $0, rhs: $1, sortAscending: state.sortAscending) })
|
||||
|
||||
return JoinGroupCallResult(
|
||||
return .single(JoinGroupCallResult(
|
||||
callInfo: parsedCall,
|
||||
state: state,
|
||||
connectionMode: connectionMode,
|
||||
jsonParams: parsedClientParams
|
||||
)
|
||||
jsonParams: parsedClientParams,
|
||||
e2eState: e2eState
|
||||
))
|
||||
}
|
||||
|> castError(InternalJoinError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -882,13 +916,19 @@ func _internal_joinGroupCall(account: Account, peerId: PeerId?, joinAs: PeerId?,
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal<MessageId?, NoError> {
|
||||
public enum InviteConferenceCallParticipantError {
|
||||
case generic
|
||||
case privacy(peer: EnginePeer?)
|
||||
}
|
||||
|
||||
func _internal_inviteConferenceCallParticipant(account: Account, reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal<MessageId, InviteConferenceCallParticipantError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputUser? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputUser)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<MessageId?, NoError> in
|
||||
|> castError(InviteConferenceCallParticipantError.self)
|
||||
|> mapToSignal { inputPeer -> Signal<MessageId, InviteConferenceCallParticipantError> in
|
||||
guard let inputPeer else {
|
||||
return .complete()
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
@ -897,17 +937,26 @@ func _internal_inviteConferenceCallParticipant(account: Account, reference: Inte
|
||||
}
|
||||
return account.network.request(Api.functions.phone.inviteConferenceCallParticipant(flags: flags, call: reference.apiInputGroupCall, userId: inputPeer))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
|
||||
return .single(nil)
|
||||
|> `catch` { error -> Signal<Api.Updates?, InviteConferenceCallParticipantError> in
|
||||
if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||
return account.postbox.transaction { transaction -> InviteConferenceCallParticipantError in
|
||||
return .privacy(peer: transaction.getPeer(peerId).flatMap(EnginePeer.init))
|
||||
}
|
||||
|> castError(InviteConferenceCallParticipantError.self)
|
||||
|> mapToSignal { error -> Signal<Api.Updates?, InviteConferenceCallParticipantError> in
|
||||
return .fail(error)
|
||||
}
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<MessageId?, NoError> in
|
||||
|> mapToSignal { result -> Signal<MessageId, InviteConferenceCallParticipantError> in
|
||||
if let result {
|
||||
account.stateManager.addUpdates(result)
|
||||
if let message = result.messageIds.first {
|
||||
return .single(message)
|
||||
}
|
||||
}
|
||||
return .single(nil)
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ public extension TelegramEngine {
|
||||
return _internal_sendConferenceCallBroadcast(account: self.account, callId: callId, accessHash: accessHash, block: block)
|
||||
}
|
||||
|
||||
public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal<EngineMessage.Id?, NoError> {
|
||||
public func inviteConferenceCallParticipant(reference: InternalGroupCallReference, peerId: EnginePeer.Id, isVideo: Bool) -> Signal<EngineMessage.Id, InviteConferenceCallParticipantError> {
|
||||
return _internal_inviteConferenceCallParticipant(account: self.account, reference: reference, peerId: peerId, isVideo: isVideo)
|
||||
}
|
||||
|
||||
|
@ -336,6 +336,8 @@ public enum PresentationResourceKey: Int32 {
|
||||
case peerStatusLockedImage
|
||||
case expandDownArrowImage
|
||||
case expandSmallDownArrowImage
|
||||
|
||||
case callListCallIcon
|
||||
}
|
||||
|
||||
public enum ChatExpiredStoryIndicatorType: Hashable {
|
||||
|
@ -199,4 +199,10 @@ public struct PresentationResourcesRootController {
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SortIcon"), color: .white)
|
||||
})
|
||||
}
|
||||
|
||||
public static func callListCallIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.callListCallIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call List/NewCallListIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -616,9 +616,27 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
}
|
||||
}
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
case .conferenceCall:
|
||||
//TODO:localize
|
||||
let titleString = "Group call"
|
||||
case let .conferenceCall(conferenceCall):
|
||||
var titleString: String
|
||||
let incoming = message.flags.contains(.Incoming)
|
||||
|
||||
let missedTimeout: Int32 = 30
|
||||
let currentTime = Int32(Date().timeIntervalSince1970)
|
||||
|
||||
if conferenceCall.flags.contains(.isMissed) {
|
||||
titleString = strings.Chat_CallMessage_DeclinedGroupCall
|
||||
} else if message.timestamp < currentTime - missedTimeout {
|
||||
titleString = strings.Chat_CallMessage_MissedGroupCall
|
||||
} else if conferenceCall.duration != nil {
|
||||
titleString = strings.Chat_CallMessage_CancelledGroupCall
|
||||
} else {
|
||||
if incoming {
|
||||
titleString = strings.Chat_CallMessage_IncomingGroupCall
|
||||
} else {
|
||||
titleString = strings.Chat_CallMessage_OutgoingGroupCall
|
||||
}
|
||||
}
|
||||
|
||||
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||
case let .groupPhoneCall(_, _, scheduleDate, duration):
|
||||
if let scheduleDate = scheduleDate {
|
||||
|
@ -153,8 +153,7 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
callDuration = conferenceCall.duration
|
||||
|
||||
if conferenceCall.otherParticipants.count > 0 {
|
||||
//TODO:localize
|
||||
peopleTextString = "\(conferenceCall.otherParticipants.count + 1) people"
|
||||
peopleTextString = item.presentationData.strings.Chat_CallMessage_GroupCallParticipantCount(Int32(conferenceCall.otherParticipants.count + 1))
|
||||
if let peer = item.message.author {
|
||||
peopleAvatars.append(peer)
|
||||
}
|
||||
@ -165,9 +164,8 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let missedTimeout: Int32
|
||||
#if DEBUG
|
||||
#if DEBUG && false
|
||||
missedTimeout = 5
|
||||
#else
|
||||
missedTimeout = 30
|
||||
@ -175,16 +173,16 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
let currentTime = Int32(Date().timeIntervalSince1970)
|
||||
if conferenceCall.flags.contains(.isMissed) {
|
||||
titleString = "Declined Group Call"
|
||||
titleString = item.presentationData.strings.Chat_CallMessage_DeclinedGroupCall
|
||||
} else if item.message.timestamp < currentTime - missedTimeout {
|
||||
titleString = "Missed Group Call"
|
||||
titleString = item.presentationData.strings.Chat_CallMessage_MissedGroupCall
|
||||
} else if conferenceCall.duration != nil {
|
||||
titleString = "Cancelled Group Call"
|
||||
titleString = item.presentationData.strings.Chat_CallMessage_CancelledGroupCall
|
||||
} else {
|
||||
if incoming {
|
||||
titleString = "Incoming Group Call"
|
||||
titleString = item.presentationData.strings.Chat_CallMessage_IncomingGroupCall
|
||||
} else {
|
||||
titleString = "Outgoing Group Call"
|
||||
titleString = item.presentationData.strings.Chat_CallMessage_OutgoingGroupCall
|
||||
}
|
||||
updateConferenceTimerEndTimeout = (item.message.timestamp + missedTimeout) - currentTime
|
||||
}
|
||||
@ -280,7 +278,7 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var avatarsWidth: CGFloat = 0.0
|
||||
if !peopleAvatars.isEmpty {
|
||||
avatarsWidth += avatarsLeftInset
|
||||
avatarsWidth += 1.0 * peopleAvatarSize + CGFloat(peopleAvatars.count - 1) * peopleAvatarSpacing
|
||||
avatarsWidth += 1.0 * peopleAvatarSize + CGFloat(min(3, peopleAvatars.count) - 1) * peopleAvatarSpacing
|
||||
avatarsWidth += avatarsRightInset
|
||||
labelsWidth += avatarsWidth
|
||||
}
|
||||
|
@ -475,8 +475,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent
|
||||
text = nil
|
||||
entities = nil
|
||||
case "telegram_call":
|
||||
//TODO:localize
|
||||
actionTitle = "JOIN GROUP CALL"
|
||||
actionTitle = item.presentationData.strings.Chat_ViewGroupCall
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -490,7 +490,7 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
contentHeight += 31.0
|
||||
|
||||
titleString = group.title
|
||||
subtitleString = group.isPublic ? "public group" : "private group"
|
||||
subtitleString = group.isPublic ? environment.strings.Invitation_PublicGroup : environment.strings.Invitation_PrivateGroup
|
||||
descriptionTextString = group.about
|
||||
|
||||
previewPeers = group.members
|
||||
@ -528,10 +528,9 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
}
|
||||
contentHeight += peerAvatarSize.height + 21.0
|
||||
case let .groupCall(groupCall):
|
||||
//TODO:localize
|
||||
titleString = "Group Call"
|
||||
titleString = environment.strings.Invitation_GroupCall
|
||||
subtitleString = nil
|
||||
descriptionTextString = "You are invited to join a group call."
|
||||
descriptionTextString = environment.strings.Invitation_GroupCall_Text
|
||||
|
||||
previewPeers = groupCall.members
|
||||
totalMemberCount = groupCall.totalMemberCount
|
||||
@ -691,12 +690,11 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
if !previewPeers.isEmpty {
|
||||
contentHeight += 11.0
|
||||
|
||||
//TODO:localize
|
||||
let previewPeersString: String
|
||||
switch component.mode {
|
||||
case .group:
|
||||
if previewPeers.count == 1 {
|
||||
previewPeersString = "**\(previewPeers[0].compactDisplayTitle)** already joined this group."
|
||||
previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedSingle(previewPeers[0].compactDisplayTitle).string
|
||||
} else {
|
||||
let firstPeers = previewPeers.prefix(upTo: 2)
|
||||
let peersTextArray = firstPeers.map { "**\($0.compactDisplayTitle)**" }
|
||||
@ -717,14 +715,14 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
if totalMemberCount > firstPeers.count {
|
||||
previewPeersString = "\(peersText) and **\(totalMemberCount - firstPeers.count)** other people already joined this group."
|
||||
previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedMultipleWithCount(Int32(totalMemberCount - firstPeers.count)).replacingOccurrences(of: "{}", with: peersText)
|
||||
} else {
|
||||
previewPeersString = "\(peersText) already joined this group."
|
||||
previewPeersString = environment.strings.Invitation_Group_AlreadyJoinedMultiple(peersText).string
|
||||
}
|
||||
}
|
||||
case .groupCall:
|
||||
if previewPeers.count == 1 {
|
||||
previewPeersString = "**\(previewPeers[0].compactDisplayTitle)** already joined this call."
|
||||
previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedSingle(previewPeers[0].compactDisplayTitle).string
|
||||
} else {
|
||||
let firstPeers = previewPeers.prefix(upTo: 2)
|
||||
let peersTextArray = firstPeers.map { "**\($0.compactDisplayTitle)**" }
|
||||
@ -745,9 +743,9 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
}
|
||||
}
|
||||
if totalMemberCount > firstPeers.count {
|
||||
previewPeersString = "\(peersText) and **\(totalMemberCount - firstPeers.count)** other people already joined this call."
|
||||
previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedMultipleWithCount(Int32(totalMemberCount - firstPeers.count)).replacingOccurrences(of: "{}", with: peersText)
|
||||
} else {
|
||||
previewPeersString = "\(peersText) already joined this call."
|
||||
previewPeersString = environment.strings.Invitation_GroupCall_AlreadyJoinedMultiple(peersText).string
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -854,8 +852,7 @@ private final class JoinSubjectScreenComponent: Component {
|
||||
case .group:
|
||||
actionButtonTitle = environment.strings.Invitation_JoinGroup
|
||||
case .groupCall:
|
||||
//TODO:localize
|
||||
actionButtonTitle = "Join Group Call"
|
||||
actionButtonTitle = environment.strings.Invitation_JoinGroupCall
|
||||
}
|
||||
let actionButtonSize = self.actionButton.update(
|
||||
transition: transition,
|
||||
|
@ -11118,7 +11118,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
giftsContext?.updateSorting(sorting == .date ? .value : .date)
|
||||
})))
|
||||
|
||||
if hasPinnedGifts && hasVisibility {
|
||||
if hasPinnedGifts {
|
||||
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Reorder, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { _, f in
|
||||
|
@ -1330,7 +1330,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
if self.hasGlobalSearch {
|
||||
categories.insert(.global)
|
||||
}
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: self.updatedPresentationData, onlyWriteable: true, categories: categories, addContact: nil, openPeer: { [weak self] peer in
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: self.updatedPresentationData, onlyWriteable: true, categories: categories, addContact: nil, openPeer: { [weak self] peer, _ in
|
||||
if let strongSelf = self {
|
||||
var updated = false
|
||||
var count = 0
|
||||
|
@ -1722,7 +1722,7 @@ final class StoryItemSetContainerSendMessage {
|
||||
case .contact:
|
||||
let theme = component.theme
|
||||
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
|
||||
let contactsController = component.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: component.context, updatedPresentationData: updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
|
||||
let contactsController = component.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: component.context, updatedPresentationData: updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: .always))
|
||||
contactsController.presentScheduleTimePicker = { [weak self, weak view] completion in
|
||||
guard let self, let view else {
|
||||
return
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Call List/NewCallListIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call List/NewCallListIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "newcall_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
submodules/TelegramUI/Images.xcassets/Call List/NewCallListIcon.imageset/newcall_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call List/NewCallListIcon.imageset/newcall_30.pdf
vendored
Normal file
Binary file not shown.
@ -1977,6 +1977,22 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
self.maybeCheckForUpdates()
|
||||
|
||||
SharedDisplayLinkDriver.shared.updateForegroundState(self.isActiveValue)
|
||||
|
||||
func cancelWindowPanGestures(view: UIView) {
|
||||
if let gestureRecognizers = view.gestureRecognizers {
|
||||
for recognizer in gestureRecognizers {
|
||||
if let recognizer = recognizer as? WindowPanRecognizer {
|
||||
recognizer.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for subview in view.subviews {
|
||||
cancelWindowPanGestures(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
//cancelWindowPanGestures(view: self.mainWindow.hostView.containerView)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
@ -2164,14 +2180,18 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
|
||||
|
||||
let internalId = CallSessionManager.getStableIncomingUUID(peerId: fromPeerId.id._internalGetInt64Value(), messageId: messageId.id)
|
||||
|
||||
//TODO:localize
|
||||
var displayTitle = "\(fromTitle)"
|
||||
var strings: PresentationStrings = defaultPresentationStrings
|
||||
let _ = (self.sharedContextPromise.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { sharedApplicationContext in
|
||||
strings = sharedApplicationContext.sharedContext.currentPresentationData.with { $0.strings }
|
||||
})
|
||||
|
||||
let displayTitle: String
|
||||
if let memberCountString = payloadJson["member_count"] as? String, let memberCount = Int(memberCountString) {
|
||||
if memberCount == 1 {
|
||||
displayTitle.append(" and 1 other")
|
||||
} else {
|
||||
displayTitle.append(" and \(memberCount) others")
|
||||
}
|
||||
displayTitle = strings.Call_IncomingGroupCallTitle_Multiple(Int32(memberCount)).replacingOccurrences(of: "{}", with: fromTitle)
|
||||
} else {
|
||||
displayTitle = strings.Call_IncomingGroupCallTitle_Single(fromTitle).string
|
||||
}
|
||||
|
||||
callKitIntegration.reportIncomingCall(
|
||||
|
@ -2898,7 +2898,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
if let currentGroupCallController = self.context.sharedContext as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
|
||||
if let currentGroupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == conferenceCall.callId {
|
||||
self.context.sharedContext.navigateToCurrentCall()
|
||||
return
|
||||
}
|
||||
@ -2932,8 +2932,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.context.sharedContext.openCreateGroupCallUI(context: self.context, peerIds: conferenceCall.otherParticipants, parentController: self)
|
||||
default:
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
self.present(textAlertController(context: self.context, title: nil, text: "An error occurred", actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
self.present(textAlertController(context: self.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
})
|
||||
}, longTap: { [weak self] action, params in
|
||||
@ -7513,17 +7512,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
navigationController.pushViewController(self, animated: false)
|
||||
|
||||
let updatedLayout = self.validLayout
|
||||
let updatedFrame = self.view.frame
|
||||
|
||||
if let initialLayout, let updatedLayout, transition.isAnimated {
|
||||
let initialView = self.view.superview
|
||||
let updatedFrame = self.view.convert(self.view.bounds, to: navigationController.view)
|
||||
navigationController.view.addSubview(self.view)
|
||||
|
||||
self.view.clipsToBounds = true
|
||||
self.view.frame = initialFrame
|
||||
self.containerLayoutUpdated(initialLayout, transition: .immediate)
|
||||
self.containerLayoutUpdated(updatedLayout, transition: transition)
|
||||
|
||||
self.chatDisplayNode.historyNode.layer.animateScaleX(from: initialLayout.size.width / updatedLayout.size.width, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.chatDisplayNode.historyNode.layer.animatePosition(from: CGPoint(x: (updatedLayout.size.width - initialLayout.size.width) / 2.0, y: 0.0), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
self.chatDisplayNode.inputPanelBackgroundNode.layer.removeAllAnimations()
|
||||
self.chatDisplayNode.inputPanelBackgroundNode.layer.animatePosition(from: CGPoint(x: 0.0, y: self.chatDisplayNode.inputPanelNode?.frame.height ?? 45.0), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
self.view.layer.animate(from: 14.0, to: updatedLayout.deviceMetrics.screenCornerRadius, keyPath: "cornerRadius", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.4)
|
||||
|
||||
transition.updateFrame(view: self.view, frame: updatedFrame, completion: { _ in
|
||||
|
@ -190,7 +190,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
let inputPanelContainerNode: ChatInputPanelContainer
|
||||
private let inputPanelOverlayNode: SparseNode
|
||||
private let inputPanelClippingNode: SparseNode
|
||||
private let inputPanelBackgroundNode: NavigationBackgroundNode
|
||||
let inputPanelBackgroundNode: NavigationBackgroundNode
|
||||
|
||||
private var navigationBarBackgroundContent: WallpaperBubbleBackgroundNode?
|
||||
private var inputPanelBackgroundContent: WallpaperBubbleBackgroundNode?
|
||||
@ -1880,9 +1880,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
displayMode = .aspectFit
|
||||
} else if case .compact = layout.metrics.widthClass {
|
||||
if layout.size.width < layout.size.height && layout.size.height < layout.deviceMetrics.screenSize.height {
|
||||
wallpaperBounds.size.height = layout.deviceMetrics.screenSize.height
|
||||
wallpaperBounds.size = layout.deviceMetrics.screenSize
|
||||
} else if layout.size.width > layout.size.height && layout.size.height < layout.deviceMetrics.screenSize.width {
|
||||
wallpaperBounds.size.height = layout.deviceMetrics.screenSize.width
|
||||
wallpaperBounds.size = layout.deviceMetrics.screenSize
|
||||
}
|
||||
}
|
||||
self.backgroundNode.updateLayout(size: wallpaperBounds.size, displayMode: displayMode, transition: transition)
|
||||
@ -2296,13 +2296,21 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
self.navigateButtons.update(rect: apparentNavigateButtonsFrame, within: layout.size, transition: transition)
|
||||
|
||||
if let titleAccessoryPanelNode = self.titleAccessoryPanelNode, let titleAccessoryPanelFrame, !titleAccessoryPanelNode.frame.equalTo(titleAccessoryPanelFrame) {
|
||||
let previousFrame = titleAccessoryPanelNode.frame
|
||||
titleAccessoryPanelNode.frame = titleAccessoryPanelFrame
|
||||
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
|
||||
if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width {
|
||||
} else {
|
||||
transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height))
|
||||
}
|
||||
}
|
||||
|
||||
if let chatTranslationPanel = self.chatTranslationPanel, let translationPanelFrame, !chatTranslationPanel.frame.equalTo(translationPanelFrame) {
|
||||
let previousFrame = chatTranslationPanel.frame
|
||||
chatTranslationPanel.frame = translationPanelFrame
|
||||
transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: -translationPanelFrame.height))
|
||||
if transition.isAnimated && previousFrame.width != translationPanelFrame.width {
|
||||
} else {
|
||||
transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: -translationPanelFrame.height))
|
||||
}
|
||||
}
|
||||
|
||||
if let chatImportStatusPanel = self.chatImportStatusPanel, let importStatusPanelFrame, !chatImportStatusPanel.frame.equalTo(importStatusPanelFrame) {
|
||||
|
@ -424,7 +424,7 @@ extension ChatControllerImpl {
|
||||
let _ = currentLocationController.swap(controller)
|
||||
})
|
||||
case .contact:
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true, requirePhoneNumbers: true))
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: .always, requirePhoneNumbers: true))
|
||||
contactsController.presentScheduleTimePicker = { [weak self] completion in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentScheduleTimePicker(completion: completion)
|
||||
@ -1620,7 +1620,7 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
func presentContactPicker() {
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
|
||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, updatedPresentationData: self.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: .always))
|
||||
contactsController.navigationPresentation = .modal
|
||||
self.chatDisplayNode.dismissInput()
|
||||
self.effectiveNavigationController?.pushViewController(contactsController)
|
||||
|
@ -120,7 +120,7 @@ final class ComposeControllerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global], addContact: nil, openPeer: { [weak self] peer in
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global], addContact: nil, openPeer: { [weak self] peer, _ in
|
||||
if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch, case let .peer(peer, _, _) = peer {
|
||||
requestOpenPeerFromSearch(peer.id)
|
||||
}
|
||||
|
@ -265,6 +265,10 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
||||
if isCall && count == 0 {
|
||||
self.titleView.title = CounterControllerTitle(title: self.params.title ?? self.presentationData.strings.Compose_NewGroupTitle, counter: nil)
|
||||
} else {
|
||||
var count = count
|
||||
if isCall {
|
||||
count += 1
|
||||
}
|
||||
self.titleView.title = CounterControllerTitle(title: self.params.title ?? self.presentationData.strings.Compose_NewGroupTitle, counter: "\(count)/\(maxCount)")
|
||||
}
|
||||
if self.rightNavigationButton == nil && !isCall {
|
||||
|
@ -131,11 +131,10 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
}, checkOptionTitle: nil)
|
||||
case let .groupCreation(isCall):
|
||||
if isCall {
|
||||
//TODO:localize
|
||||
placeholder = "Search for contacts or usernames"
|
||||
placeholder = self.presentationData.strings.NewCall_SearchPlaceholder
|
||||
self.footerPanelNode = FooterPanelNode(theme: self.presentationData.theme, strings: self.presentationData.strings, action: {
|
||||
proceedImpl?()
|
||||
}, checkOptionTitle: isCall ? "Call with video enabled" : nil)
|
||||
}, checkOptionTitle: self.presentationData.strings.NewCall_VideoOption)
|
||||
} else {
|
||||
placeholder = self.presentationData.strings.Compose_TokenListPlaceholder
|
||||
self.footerPanelNode = nil
|
||||
@ -483,15 +482,14 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
count = contactListNode.selectionState?.selectedPeerIndices.count ?? 0
|
||||
}
|
||||
if case let .groupCreation(isCall) = self.mode, isCall {
|
||||
//TODO:localize
|
||||
if count == 0 {
|
||||
// Don't set anything to prevent state update
|
||||
} else if count <= 1 {
|
||||
let callTitle: String
|
||||
if case let .contacts(contactListNode) = self.contentNode, let peer = contactListNode.selectedPeers.first, case let .peer(peer, _, _) = peer {
|
||||
callTitle = "Call \(EnginePeer(peer).compactDisplayTitle)"
|
||||
callTitle = self.presentationData.strings.NewCall_ActionCallSingle(EnginePeer(peer).compactDisplayTitle).string
|
||||
} else {
|
||||
callTitle = "Call"
|
||||
callTitle = self.presentationData.strings.NewCall_ActionCallMultiple
|
||||
}
|
||||
footerPanelNode.content = FooterPanelNode.Content(title: callTitle, badge: "")
|
||||
} else {
|
||||
|
@ -40,7 +40,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
private let options: Signal<[ContactListAdditionalOption], NoError>
|
||||
private let displayDeviceContacts: Bool
|
||||
private let displayCallIcons: Bool
|
||||
private let multipleSelection: Bool
|
||||
private let multipleSelection: ContactSelectionControllerParams.MultipleSelectionMode
|
||||
private let requirePhoneNumbers: Bool
|
||||
private let allowChannelsInSearch: Bool
|
||||
|
||||
@ -153,15 +153,17 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
})
|
||||
|
||||
if !params.multipleSelection {
|
||||
if self.multipleSelection != .always {
|
||||
self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search, activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
})
|
||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||
}
|
||||
|
||||
if params.multipleSelection {
|
||||
if self.multipleSelection == .always {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.beginSearch))
|
||||
} else if self.multipleSelection == .possible {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Select, style: .plain, target: self, action: #selector(self.beginSelection))
|
||||
}
|
||||
|
||||
self.getCurrentSendMessageContextMediaPreview = { [weak self] in
|
||||
@ -220,7 +222,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = ContactSelectionControllerNode(context: self.context, mode: self.mode, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection, requirePhoneNumbers: self.requirePhoneNumbers, allowChannelsInSearch: self.allowChannelsInSearch, isPeerEnabled: self.isPeerEnabled)
|
||||
self.displayNode = ContactSelectionControllerNode(context: self.context, mode: self.mode, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection == .always, requirePhoneNumbers: self.requirePhoneNumbers, allowChannelsInSearch: self.allowChannelsInSearch, isPeerEnabled: self.isPeerEnabled)
|
||||
self._ready.set(self.contactsNode.contactListNode.ready)
|
||||
|
||||
self.contactsNode.navigationBar = self.navigationBar
|
||||
@ -229,8 +231,8 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
self?.deactivateSearch()
|
||||
}
|
||||
|
||||
self.contactsNode.requestOpenPeerFromSearch = { [weak self] peer in
|
||||
self?.openPeer(peer: peer, action: .generic, node: nil, gesture: nil)
|
||||
self.contactsNode.requestOpenPeerFromSearch = { [weak self] peer, action in
|
||||
self?.openPeer(peer: peer, action: action, node: nil, gesture: nil)
|
||||
}
|
||||
|
||||
self.contactsNode.contactListNode.activateSearch = { [weak self] in
|
||||
@ -335,7 +337,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
if let searchContentNode = self.searchContentNode as? NavigationBarSearchContentNode {
|
||||
self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode)
|
||||
self.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring))
|
||||
} else if self.multipleSelection {
|
||||
} else if self.multipleSelection == .always {
|
||||
let contentNode = ContactsSearchNavigationContentNode(presentationData: self.presentationData, dismissSearch: { [weak self] in
|
||||
if let strongSelf = self, let navigationBar = strongSelf.navigationBar, let searchContentNode = strongSelf.searchContentNode as? ContactsSearchNavigationContentNode {
|
||||
searchContentNode.deactivate()
|
||||
|
@ -37,7 +37,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
var navigationBar: NavigationBar?
|
||||
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((ContactListPeer) -> Void)?
|
||||
var requestOpenPeerFromSearch: ((ContactListPeer, ContactListAction) -> Void)?
|
||||
var requestOpenDisabledPeerFromSearch: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||
var requestMultipleAction: ((_ silent: Bool, _ scheduleTime: Int32?, _ parameters: ChatSendMessageActionSheetController.SendParameters?) -> Void)?
|
||||
var dismiss: (() -> Void)?
|
||||
@ -258,7 +258,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
categories.insert(.channels)
|
||||
}
|
||||
|
||||
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, displayCallIcons: self.displayCallIcons, isPeerEnabled: self.isPeerEnabled, addContact: nil, openPeer: { [weak self] peer, action in
|
||||
if let strongSelf = self {
|
||||
var updated = false
|
||||
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
||||
@ -285,7 +285,16 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
if updated {
|
||||
strongSelf.requestDeactivateSearch?()
|
||||
} else {
|
||||
strongSelf.requestOpenPeerFromSearch?(peer)
|
||||
let mappedAction: ContactListAction
|
||||
switch action {
|
||||
case .generic:
|
||||
mappedAction = .generic
|
||||
case .voiceCall:
|
||||
mappedAction = .voiceCall
|
||||
case .videoCall:
|
||||
mappedAction = .videoCall
|
||||
}
|
||||
strongSelf.requestOpenPeerFromSearch?(peer, mappedAction)
|
||||
}
|
||||
}
|
||||
}, openDisabledPeer: { [weak self] peer, reason in
|
||||
@ -330,7 +339,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
categories.insert(.channels)
|
||||
}
|
||||
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||
self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, displayCallIcons: self.displayCallIcons, isPeerEnabled: self.isPeerEnabled, addContact: nil, openPeer: { [weak self] peer, action in
|
||||
if let strongSelf = self {
|
||||
var updated = false
|
||||
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
||||
@ -357,7 +366,16 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
if updated {
|
||||
strongSelf.requestDeactivateSearch?()
|
||||
} else {
|
||||
strongSelf.requestOpenPeerFromSearch?(peer)
|
||||
let mappedAction: ContactListAction
|
||||
switch action {
|
||||
case .generic:
|
||||
mappedAction = .generic
|
||||
case .voiceCall:
|
||||
mappedAction = .voiceCall
|
||||
case .videoCall:
|
||||
mappedAction = .videoCall
|
||||
}
|
||||
strongSelf.requestOpenPeerFromSearch?(peer, mappedAction)
|
||||
}
|
||||
}
|
||||
}, openDisabledPeer: { [weak self] peer, reason in
|
||||
|
@ -394,6 +394,11 @@ func openResolvedUrlImpl(
|
||||
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] resolvedCallLink in
|
||||
if let currentGroupCallController = context.sharedContext.currentGroupCallController as? VoiceChatController, case let .group(groupCall) = currentGroupCallController.call, let currentCallId = groupCall.callId, currentCallId == resolvedCallLink.id {
|
||||
context.sharedContext.navigateToCurrentCall()
|
||||
return
|
||||
}
|
||||
|
||||
navigationController?.pushViewController(context.sharedContext.makeJoinSubjectScreen(context: context, mode: JoinSubjectScreenMode.groupCall(JoinSubjectScreenMode.GroupCall(
|
||||
id: resolvedCallLink.id,
|
||||
accessHash: resolvedCallLink.accessHash,
|
||||
@ -407,8 +412,7 @@ func openResolvedUrlImpl(
|
||||
if case .chat = urlContext {
|
||||
elevatedLayout = false
|
||||
}
|
||||
//TODO:localize
|
||||
present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: "This link is no longer active"), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in
|
||||
present(UndoOverlayController(presentationData: presentationData, content: .linkRevoked(text: presentationData.strings.Chat_ToastCallLinkExpired_Text), elevatedLayout: elevatedLayout, animateInAsReplacement: false, action: { _ in
|
||||
return true
|
||||
}), nil)
|
||||
})
|
||||
|
@ -1963,10 +1963,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return
|
||||
}
|
||||
|
||||
let isVideo = controller?.isCallVideoOptionSelected ?? false
|
||||
|
||||
if peerIds.count == 1 {
|
||||
//TODO:release isVideo
|
||||
controller?.dismiss()
|
||||
self.performCall(context: context, parentController: parentController, peerId: peerIds[0], isVideo: false, began: {
|
||||
self.performCall(context: context, parentController: parentController, peerId: peerIds[0], isVideo: isVideo, began: {
|
||||
let _ = (context.sharedContext.hasOngoingCall.get()
|
||||
|> filter { $0 }
|
||||
|> timeout(1.0, queue: Queue.mainQueue(), alternate: .single(true))
|
||||
@ -1981,7 +1982,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
})
|
||||
})
|
||||
} else {
|
||||
self.createGroupCall(context: context, parentController: parentController, peerIds: peerIds, completion: {
|
||||
self.createGroupCall(context: context, parentController: parentController, peerIds: peerIds, isVideo: isVideo, completion: {
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
@ -2012,7 +2013,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
})
|
||||
}
|
||||
|
||||
private func createGroupCall(context: AccountContext, parentController: ViewController, peerIds: [EnginePeer.Id], completion: (() -> Void)? = nil) {
|
||||
private func createGroupCall(context: AccountContext, parentController: ViewController, peerIds: [EnginePeer.Id], isVideo: Bool, completion: (() -> Void)? = nil) {
|
||||
parentController.view.endEditing(true)
|
||||
|
||||
var cancelImpl: (() -> Void)?
|
||||
@ -2058,7 +2059,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
isStream: false
|
||||
),
|
||||
reference: .id(id: call.callInfo.id, accessHash: call.callInfo.accessHash),
|
||||
beginWithVideo: false,
|
||||
beginWithVideo: isVideo,
|
||||
invitePeerIds: peerIds
|
||||
)
|
||||
completion?()
|
||||
@ -2080,9 +2081,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
if let result {
|
||||
switch result {
|
||||
case .linkCopied:
|
||||
//TODO:localize
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: "Call link copied.", customUndoText: "View Call", timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_linkcopied", scale: 0.08, colors: ["info1.info1.stroke": UIColor.clear, "info2.info2.Fill": UIColor.clear], title: nil, text: presentationData.strings.CallList_ToastCallLinkCopied_Text, customUndoText: presentationData.strings.CallList_ToastCallLinkCopied_Action, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { action in
|
||||
if case .undo = action {
|
||||
openCall()
|
||||
}
|
||||
|
@ -899,6 +899,7 @@ public final class OngoingGroupCallContext {
|
||||
}
|
||||
return OngoingGroupCallRequestedVideoChannel(
|
||||
audioSsrc: channel.audioSsrc,
|
||||
userId: channel.peerId,
|
||||
endpointId: channel.endpointId,
|
||||
ssrcGroups: channel.ssrcGroups.map { group in
|
||||
return OngoingGroupCallSsrcGroup(
|
||||
|
1
third-party/td/td
vendored
Submodule
1
third-party/td/td
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a03a90470d6fca9a5a3db747ba3f3e4a465b5fe7
|
194
third-party/td/td/.clang-format
vendored
194
third-party/td/td/.clang-format
vendored
@ -1,194 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: None # All
|
||||
AllowShortIfStatementsOnASingleLine: Never # WithoutElse
|
||||
AllowShortLambdasOnASingleLine: Inline # All
|
||||
AllowShortLoopsOnASingleLine: false # true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
# AttributeMacros:
|
||||
# - __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BitFieldColonSpacing: Both
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakAfterAttributes: Never
|
||||
# BreakAfterJavaFieldAnnotations: false
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma # BeforeColon
|
||||
BreakInheritanceList: BeforeComma # BeforeColon
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120 # 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- Q_FOREACH_THIS_LIST_MUST_BE_NON_EMPTY
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 0
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: false
|
||||
# InsertTrailingCommas: None
|
||||
IntegerLiteralSeparator:
|
||||
Binary: 0
|
||||
Decimal: 0
|
||||
Hex: 0
|
||||
# JavaScriptQuotes: Leave
|
||||
# JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
# ObjCBinPackProtocolList: Never
|
||||
# ObjCBlockIndentWidth: 2
|
||||
# ObjCBreakBeforeNestedBlockParam: true
|
||||
# ObjCSpaceAfterProperty: false
|
||||
# ObjCSpaceBeforeProtocolList: true
|
||||
PackConstructorInitializers: NextLine
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Right # Left
|
||||
PPIndentWidth: -1
|
||||
QualifierAlignment: Leave
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: false # true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 0 # 1
|
||||
SortIncludes: CaseInsensitive # CaseSensitive
|
||||
# SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: Lexicographic # LexicographicNumeric
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: 1 # -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 100 # 8
|
||||
UseTab: Never
|
||||
...
|
39
third-party/td/td/.gitattributes
vendored
39
third-party/td/td/.gitattributes
vendored
@ -1,39 +0,0 @@
|
||||
* text=auto
|
||||
|
||||
*.cpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.hpp text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.h text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.c text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.tl text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.mm text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.txt text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.sh text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=lf
|
||||
*.php text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.ps1 text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent eol=crlf
|
||||
*.cmake text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.md text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.in text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.html text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
|
||||
*.java text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.py text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.js text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.patch text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.swift text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.pbxproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.cs text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.xaml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.appxmanifest text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.vsixmanifest text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.nuspec text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.targets text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.json text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.csproj text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.sln text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.xml text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
*.config text whitespace=blank-at-eol,space-before-tab,blank-at-eof,tab-in-indent
|
||||
|
||||
sqlite/sqlite/* linguist-vendored
|
||||
|
||||
*.pfx binary
|
||||
*.png binary
|
8
third-party/td/td/.gitignore
vendored
8
third-party/td/td/.gitignore
vendored
@ -1,8 +0,0 @@
|
||||
**/*build*/
|
||||
**/.*.swp
|
||||
**/.DS_Store
|
||||
**/auto/
|
||||
docs/
|
||||
/tdlib/
|
||||
vcpkg/
|
||||
.clang-tidy
|
1520
third-party/td/td/CHANGELOG.md
vendored
1520
third-party/td/td/CHANGELOG.md
vendored
File diff suppressed because it is too large
Load Diff
74
third-party/td/td/CMake/AddCXXCompilerFlag.cmake
vendored
74
third-party/td/td/CMake/AddCXXCompilerFlag.cmake
vendored
@ -1,74 +0,0 @@
|
||||
# - Adds a compiler flag if it is supported by the compiler
|
||||
#
|
||||
# This function checks that the supplied compiler flag is supported and then
|
||||
# adds it to the corresponding compiler flags
|
||||
#
|
||||
# add_cxx_compiler_flag(<FLAG> [<VARIANT>])
|
||||
#
|
||||
# - Example
|
||||
#
|
||||
# include(AddCXXCompilerFlag)
|
||||
# add_cxx_compiler_flag(-Wall)
|
||||
# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)
|
||||
# Requires CMake 2.6+
|
||||
|
||||
if (__add_cxx_compiler_flag)
|
||||
return()
|
||||
endif()
|
||||
set(__add_cxx_compiler_flag INCLUDED)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
function(mangle_compiler_flag FLAG OUTPUT)
|
||||
string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG)
|
||||
string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG})
|
||||
set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE)
|
||||
endfunction(mangle_compiler_flag)
|
||||
|
||||
function(add_cxx_compiler_flag FLAG)
|
||||
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
|
||||
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||
else()
|
||||
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
|
||||
endif()
|
||||
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
|
||||
if (DEFINED OLD_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
if (${MANGLED_FLAG_NAME})
|
||||
set(VARIANT ${ARGV1})
|
||||
if (ARGV1)
|
||||
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(add_required_cxx_compiler_flag FLAG)
|
||||
string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG})
|
||||
mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME)
|
||||
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
|
||||
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME})
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||
if (${MANGLED_FLAG_NAME})
|
||||
set(VARIANT ${ARGV1})
|
||||
if (ARGV1)
|
||||
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||
else()
|
||||
message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler")
|
||||
endif()
|
||||
endfunction()
|
62
third-party/td/td/CMake/FindAtomics.cmake
vendored
62
third-party/td/td/CMake/FindAtomics.cmake
vendored
@ -1,62 +0,0 @@
|
||||
# Original issue:
|
||||
# * https://gitlab.kitware.com/cmake/cmake/-/issues/23021#note_1098733
|
||||
#
|
||||
# For reference:
|
||||
# * https://gcc.gnu.org/wiki/Atomic/GCCMM
|
||||
#
|
||||
# riscv64 specific:
|
||||
# * https://lists.debian.org/debian-riscv/2022/01/msg00009.html
|
||||
#
|
||||
# ATOMICS_FOUND - system has C++ atomics
|
||||
# ATOMICS_LIBRARIES - libraries needed to use C++ atomics
|
||||
|
||||
if (ATOMICS_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# RISC-V only has 32-bit and 64-bit atomic instructions. GCC is supposed
|
||||
# to convert smaller atomics to those larger ones via masking and
|
||||
# shifting like LLVM, but it's a known bug that it does not. This means
|
||||
# anything that wants to use atomics on 1-byte or 2-byte types needs
|
||||
# to link atomic library, but not 4-byte or 8-byte (though it does no harm).
|
||||
set(ATOMIC_CODE
|
||||
"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
std::atomic<std::uint8_t> n8{0}; // riscv64
|
||||
std::atomic<std::uint64_t> n64{0}; // armel, mipsel, powerpc
|
||||
int main() {
|
||||
++n8;
|
||||
++n64;
|
||||
}")
|
||||
|
||||
set(ATOMICS_LIBS " " "-latomic")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
set(ATOMICS_LIBS "${ATOMICS_LIBS}" /usr/pkg/gcc12/x86_64--netbsd/lib/libatomic.so /usr/pkg/gcc12/i486--netbsdelf/lib/libatomic.so)
|
||||
endif()
|
||||
|
||||
foreach (ATOMICS_LIBRARY ${ATOMICS_LIBS})
|
||||
unset(ATOMICS_FOUND CACHE)
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${ATOMICS_LIBRARY}")
|
||||
check_cxx_source_compiles("${ATOMIC_CODE}" ATOMICS_FOUND)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
if (ATOMICS_FOUND)
|
||||
if (NOT ATOMICS_LIBRARY STREQUAL " ")
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Atomics DEFAULT_MSG ATOMICS_LIBRARY)
|
||||
set(ATOMICS_LIBRARIES "${ATOMICS_LIBRARY}" CACHE STRING "Atomic library" FORCE)
|
||||
else()
|
||||
set(ATOMICS_LIBRARIES "" CACHE STRING "Atomic operations library" FORCE)
|
||||
endif()
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if (Atomics_FIND_REQUIRED AND NOT ATOMICS_FOUND)
|
||||
message(FATAL_ERROR "Atomic operations library isn't found.")
|
||||
endif()
|
||||
|
||||
unset(ATOMICS_LIBRARY)
|
||||
unset(ATOMICS_LIBS)
|
||||
unset(ATOMIC_CODE)
|
25
third-party/td/td/CMake/FindReadline.cmake
vendored
25
third-party/td/td/CMake/FindReadline.cmake
vendored
@ -1,25 +0,0 @@
|
||||
if (APPLE)
|
||||
find_path(READLINE_INCLUDE_DIR readline/readline.h /opt/homebrew/opt/readline/include /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include NO_DEFAULT_PATH)
|
||||
endif()
|
||||
find_path(READLINE_INCLUDE_DIR readline/readline.h)
|
||||
|
||||
if (APPLE)
|
||||
find_library(READLINE_LIBRARY readline /opt/homebrew/opt/readline/lib /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib NO_DEFAULT_PATH)
|
||||
endif()
|
||||
find_library(READLINE_LIBRARY readline)
|
||||
|
||||
if (READLINE_INCLUDE_DIR AND READLINE_LIBRARY AND NOT GNU_READLINE_FOUND)
|
||||
set(CMAKE_REQUIRED_INCLUDES "${READLINE_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_LIBRARIES "${READLINE_LIBRARY}")
|
||||
include(CheckCXXSourceCompiles)
|
||||
unset(GNU_READLINE_FOUND CACHE)
|
||||
check_cxx_source_compiles("#include <stdio.h>\n#include <readline/readline.h>\nint main() { rl_replace_line(\"\", 0); }" GNU_READLINE_FOUND)
|
||||
if (NOT GNU_READLINE_FOUND)
|
||||
unset(READLINE_INCLUDE_DIR CACHE)
|
||||
unset(READLINE_LIBRARY CACHE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Readline DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARY)
|
||||
mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY)
|
99
third-party/td/td/CMake/GeneratePkgConfig.cmake
vendored
99
third-party/td/td/CMake/GeneratePkgConfig.cmake
vendored
@ -1,99 +0,0 @@
|
||||
function(get_relative_link OUTPUT PATH)
|
||||
if (PATH MATCHES "^[$]<[$]<CONFIG:DEBUG>:")
|
||||
set(${OUTPUT} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
string(REGEX REPLACE "^[$]<[$]<NOT:[$]<CONFIG:DEBUG>>:(.*)>$" "\\1" PATH "${PATH}")
|
||||
|
||||
get_filename_component(NAME "${PATH}" NAME_WE)
|
||||
if (IS_ABSOLUTE ${PATH})
|
||||
get_filename_component(DIRECTORY_NAME "${PATH}" DIRECTORY)
|
||||
if (WIN32)
|
||||
set(${OUTPUT} "-l\"${DIRECTORY_NAME}/${NAME}\"" PARENT_SCOPE)
|
||||
else()
|
||||
get_filename_component(FULL_NAME "${PATH}" NAME)
|
||||
set(${OUTPUT} "-L\"${DIRECTORY_NAME}\" -l:${FULL_NAME}" PARENT_SCOPE)
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (NOT WIN32 AND NAME MATCHES "^lib")
|
||||
string(REGEX REPLACE "^lib" "-l" LINK "${NAME}")
|
||||
elseif (NAME MATCHES "^-")
|
||||
set(LINK "${NAME}")
|
||||
else()
|
||||
string(CONCAT LINK "-l" "${NAME}")
|
||||
endif()
|
||||
set(${OUTPUT} "${LINK}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(generate_pkgconfig TARGET DESCRIPTION)
|
||||
# message("Generating pkg-config for ${TARGET}")
|
||||
get_filename_component(PREFIX "${CMAKE_INSTALL_PREFIX}" REALPATH)
|
||||
|
||||
get_target_property(LIST "${TARGET}" LINK_LIBRARIES)
|
||||
set(REQS "")
|
||||
set(LIBS "")
|
||||
foreach (LIB ${LIST})
|
||||
if (TARGET "${LIB}")
|
||||
set(HAS_REQS 1)
|
||||
list(APPEND REQS "${LIB}")
|
||||
else()
|
||||
set(HAS_LIBS 1)
|
||||
get_relative_link(LINK "${LIB}")
|
||||
if (NOT LINK EQUAL "")
|
||||
list(APPEND LIBS "${LINK}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (HAS_REQS)
|
||||
set(REQUIRES "")
|
||||
foreach (REQ ${REQS})
|
||||
set(REQUIRES "${REQUIRES} ${REQ}")
|
||||
endforeach()
|
||||
set(REQUIRES "Requires.private:${REQUIRES}\n")
|
||||
endif()
|
||||
if (HAS_LIBS)
|
||||
set(LIBRARIES "")
|
||||
list(REVERSE LIBS)
|
||||
list(REMOVE_DUPLICATES LIBS)
|
||||
foreach (LIB ${LIBS})
|
||||
set(LIBRARIES " ${LIB}${LIBRARIES}")
|
||||
endforeach()
|
||||
set(LIBRARIES "Libs.private:${LIBRARIES}\n")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig")
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" CONTENT
|
||||
"prefix=${PREFIX}
|
||||
|
||||
Name: ${TARGET}
|
||||
Description: ${DESCRIPTION}
|
||||
Version: ${PROJECT_VERSION}
|
||||
|
||||
CFlags: -I\"${PKGCONFIG_INCLUDEDIR}\"
|
||||
Libs: -L\"${PKGCONFIG_LIBDIR}\" -l${TARGET}
|
||||
${REQUIRES}${LIBRARIES}")
|
||||
|
||||
get_target_property(LIBRARY_TYPE "${TARGET}" TYPE)
|
||||
if (LIBRARY_TYPE STREQUAL "STATIC_LIBRARY" OR LIBRARY_TYPE STREQUAL "SHARED_LIBRARY")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${TARGET}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
elseif (LIBRARY_TYPE STREQUAL "INTERFACE_LIBRARY")
|
||||
# TODO: support interface libraries
|
||||
else()
|
||||
message(FATAL_ERROR "Don't know how to handle ${TARGET} of type ${LIBRARY_TYPE}")
|
||||
endif()
|
||||
endfunction()
|
@ -1,127 +0,0 @@
|
||||
# - Returns a version string from Git
|
||||
#
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar>)
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
#
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if (__get_git_revision_description)
|
||||
return()
|
||||
endif()
|
||||
set(__get_git_revision_description YES)
|
||||
|
||||
# We must run the following at "include" time, not at function call time,
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This will return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while (NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if (cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_git_dir_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
set(${_git_dir_var} "${git_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
if (NOT GIT_DIR STREQUAL "")
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_CURRENT_SOURCE_DIR}" "${GIT_DIR}")
|
||||
if (_relative_to_source_dir MATCHES "^[.][.]")
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
endif()
|
||||
if (GIT_DIR STREQUAL "")
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if ((NOT IS_DIRECTORY ${GIT_DIR}) AND Git_FOUND)
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise, the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (NOT out STREQUAL "")
|
||||
# If out is non-empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
if (NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if (NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
endfunction()
|
@ -1,43 +0,0 @@
|
||||
#
|
||||
# Internal file for GetGitRevisionDescription.cmake
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if (HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if (EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if (PACKED_REFS MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if (NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
@ -1,14 +0,0 @@
|
||||
function(prevent_in_source_build)
|
||||
get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
|
||||
get_filename_component(REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH)
|
||||
|
||||
if (REAL_BINARY_DIR STREQUAL REAL_SOURCE_DIR)
|
||||
message(" Out-of-source build must be used. Remove the files already")
|
||||
message(" created by CMake and rerun CMake from a new directory:")
|
||||
message(" rm -rf CMakeFiles CMakeCache.txt")
|
||||
message(" mkdir build")
|
||||
message(" cd build")
|
||||
message(" cmake ..")
|
||||
message(FATAL_ERROR "In-source build failed.")
|
||||
endif()
|
||||
endfunction()
|
185
third-party/td/td/CMake/TdSetUpCompiler.cmake
vendored
185
third-party/td/td/CMake/TdSetUpCompiler.cmake
vendored
@ -1,185 +0,0 @@
|
||||
# Configures C++17 compiler, setting TDLib-specific compilation options.
|
||||
|
||||
function(td_set_up_compiler)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1 PARENT_SCOPE)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON PARENT_SCOPE)
|
||||
|
||||
include(illumos)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(GCC 1)
|
||||
set(GCC 1 PARENT_SCOPE)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CLANG 1)
|
||||
set(CLANG 1 PARENT_SCOPE)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
set(INTEL 1)
|
||||
set(INTEL 1 PARENT_SCOPE)
|
||||
elseif (NOT MSVC)
|
||||
message(FATAL_ERROR "Compiler isn't supported")
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if (GCC OR CLANG OR INTEL)
|
||||
if (WIN32 AND INTEL)
|
||||
set(STD17_FLAG /Qstd=c++17)
|
||||
else()
|
||||
set(STD17_FLAG -std=c++17)
|
||||
endif()
|
||||
if (GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0))
|
||||
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler to at least GCC 7.0.")
|
||||
endif()
|
||||
if (CLANG AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0))
|
||||
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler to at least clang 5.0.")
|
||||
endif()
|
||||
check_cxx_compiler_flag(${STD17_FLAG} HAVE_STD17)
|
||||
elseif (MSVC)
|
||||
set(HAVE_STD17 MSVC_VERSION>=1914) # MSVC 2017 version 15.7
|
||||
endif()
|
||||
|
||||
if (NOT HAVE_STD17)
|
||||
message(FATAL_ERROR "No C++17 support in the compiler. Please upgrade the compiler.")
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
|
||||
string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
endif()
|
||||
add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17 /utf-8 /GR- /W4 /wd4100 /wd4127 /wd4324 /wd4505 /wd4814 /wd4702 /bigobj")
|
||||
elseif (CLANG OR GCC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD17_FLAG} -fno-omit-frame-pointer -fno-exceptions -fno-rtti")
|
||||
if (APPLE)
|
||||
set(TD_LINKER_FLAGS "-Wl,-dead_strip")
|
||||
if (NOT CMAKE_BUILD_TYPE MATCHES "Deb")
|
||||
set(TD_LINKER_FLAGS "${TD_LINKER_FLAGS},-x,-S")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
set(TD_LINKER_FLAGS "-Wl,-z,ignore")
|
||||
elseif (EMSCRIPTEN)
|
||||
set(TD_LINKER_FLAGS "-Wl,--gc-sections")
|
||||
elseif (ANDROID)
|
||||
set(TD_LINKER_FLAGS "-Wl,--gc-sections -Wl,--exclude-libs,ALL -Wl,--icf=safe")
|
||||
else()
|
||||
set(TD_LINKER_FLAGS "-Wl,--gc-sections -Wl,--exclude-libs,ALL")
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TD_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TD_LINKER_FLAGS}")
|
||||
|
||||
if (WIN32 OR CYGWIN)
|
||||
if (GCC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
|
||||
endif()
|
||||
endif()
|
||||
elseif (INTEL)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD17_FLAG}")
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DPSAPI_VERSION=1 -DNOMINMAX -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
if (CYGWIN)
|
||||
add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096)
|
||||
endif()
|
||||
|
||||
# _FILE_OFFSET_BITS is broken in Android NDK r15, r15b and r17 and doesn't work prior to Android 7.0
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
|
||||
# _GNU_SOURCE might not be defined by g++
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lsocket -lnsl")
|
||||
if (ILLUMOS)
|
||||
add_definitions(-DTD_ILLUMOS=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(AddCXXCompilerFlag)
|
||||
if (NOT MSVC)
|
||||
add_cxx_compiler_flag("-Wall")
|
||||
add_cxx_compiler_flag("-Wextra")
|
||||
add_cxx_compiler_flag("-Wimplicit-fallthrough=2")
|
||||
add_cxx_compiler_flag("-Wpointer-arith")
|
||||
add_cxx_compiler_flag("-Wcast-qual")
|
||||
add_cxx_compiler_flag("-Wsign-compare")
|
||||
add_cxx_compiler_flag("-Wduplicated-branches")
|
||||
add_cxx_compiler_flag("-Wduplicated-cond")
|
||||
add_cxx_compiler_flag("-Walloc-zero")
|
||||
add_cxx_compiler_flag("-Wlogical-op")
|
||||
add_cxx_compiler_flag("-Wno-tautological-compare")
|
||||
add_cxx_compiler_flag("-Wpointer-arith")
|
||||
add_cxx_compiler_flag("-Wvla")
|
||||
add_cxx_compiler_flag("-Wnon-virtual-dtor")
|
||||
add_cxx_compiler_flag("-Wno-unused-parameter")
|
||||
add_cxx_compiler_flag("-Wconversion")
|
||||
add_cxx_compiler_flag("-Wno-sign-conversion")
|
||||
add_cxx_compiler_flag("-Wc++17-compat-pedantic")
|
||||
add_cxx_compiler_flag("-Wdeprecated")
|
||||
add_cxx_compiler_flag("-Wno-unused-command-line-argument")
|
||||
add_cxx_compiler_flag("-Qunused-arguments")
|
||||
add_cxx_compiler_flag("-Wno-unknown-warning-option")
|
||||
add_cxx_compiler_flag("-Wodr")
|
||||
add_cxx_compiler_flag("-flto-odr-type-merging")
|
||||
add_cxx_compiler_flag("-Wno-psabi")
|
||||
add_cxx_compiler_flag("-Wunused-member-function")
|
||||
add_cxx_compiler_flag("-Wunused-private-field")
|
||||
|
||||
# add_cxx_compiler_flag("-Werror")
|
||||
|
||||
# add_cxx_compiler_flag("-Wcast-align")
|
||||
|
||||
#std::int32_t <-> int and off_t <-> std::size_t/std::int64_t
|
||||
# add_cxx_compiler_flag("-Wuseless-cast")
|
||||
|
||||
#external headers like openssl
|
||||
# add_cxx_compiler_flag("-Wzero-as-null-pointer-constant")
|
||||
endif()
|
||||
|
||||
if (GCC)
|
||||
add_cxx_compiler_flag("-Wno-maybe-uninitialized") # too many false positives
|
||||
endif()
|
||||
if (WIN32 AND GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0))
|
||||
# warns about casts of function pointers returned by GetProcAddress
|
||||
add_cxx_compiler_flag("-Wno-cast-function-type")
|
||||
endif()
|
||||
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0))
|
||||
# warns about a lot of "return std::move", which are not redundant for compilers without fix for DR 1579, i.e. GCC 4.9 or clang 3.8
|
||||
# see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579
|
||||
add_cxx_compiler_flag("-Wno-redundant-move")
|
||||
endif()
|
||||
if (GCC AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0))
|
||||
add_cxx_compiler_flag("-Wno-stringop-overflow") # some false positives
|
||||
endif()
|
||||
if (CLANG AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5))
|
||||
# https://stackoverflow.com/questions/26744556/warning-returning-a-captured-reference-from-a-lambda
|
||||
add_cxx_compiler_flag("-Wno-return-stack-address")
|
||||
endif()
|
||||
if (GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0))
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104030
|
||||
add_cxx_compiler_flag("-Wbidi-chars=none")
|
||||
endif()
|
||||
|
||||
if (MINGW)
|
||||
add_cxx_compiler_flag("-ftrack-macro-expansion=0")
|
||||
endif()
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}" PARENT_SCOPE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" PARENT_SCOPE)
|
||||
endfunction()
|
278
third-party/td/td/CMake/iOS.cmake
vendored
278
third-party/td/td/CMake/iOS.cmake
vendored
@ -1,278 +0,0 @@
|
||||
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
|
||||
# files which are included with CMake 2.8.4
|
||||
# It has been altered for iOS development
|
||||
|
||||
# Options:
|
||||
#
|
||||
# IOS_PLATFORM = OS (default) or SIMULATOR
|
||||
# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
|
||||
# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
|
||||
# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
|
||||
#
|
||||
# IOS_ARCH = automatic(default) or "arch1;arch2" (e.q. "x86_64;arm64")
|
||||
# By default this value will be automatically chosen based on the IOS_PLATFORM value above.
|
||||
# If set manually, it will override the default and force to build those architectures only.
|
||||
#
|
||||
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
|
||||
# By default this location is automatically chosen based on the IOS_PLATFORM value above.
|
||||
# If set manually, it will override the default location and force the user of a particular Developer Platform
|
||||
#
|
||||
# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
|
||||
# By default this location is automatically chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
|
||||
# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
|
||||
# If set manually, this will force the use of a specific SDK version
|
||||
|
||||
# Macros:
|
||||
#
|
||||
# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE)
|
||||
# A convenience macro for setting xcode specific properties on targets
|
||||
# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1")
|
||||
#
|
||||
# find_host_package (PROGRAM ARGS)
|
||||
# A macro used to find executable programs on the host system, not within the iOS environment.
|
||||
# Thanks to the android-cmake project for providing the command
|
||||
|
||||
# Standard settings
|
||||
set (CMAKE_SYSTEM_NAME Darwin)
|
||||
set (CMAKE_SYSTEM_VERSION 1)
|
||||
set (UNIX True)
|
||||
set (APPLE True)
|
||||
set (IOS True)
|
||||
|
||||
# Required as of cmake 2.8.10
|
||||
set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
|
||||
|
||||
# Determine the cmake host system version so we know where to find the iOS SDKs
|
||||
find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
|
||||
if (CMAKE_UNAME)
|
||||
execute_process(COMMAND uname -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
|
||||
endif (CMAKE_UNAME)
|
||||
|
||||
# Force the compilers to gcc for iOS
|
||||
set (CMAKE_C_COMPILER /usr/bin/gcc)
|
||||
set (CMAKE_CXX_COMPILER /usr/bin/g++)
|
||||
set (CMAKE_AR ar CACHE FILEPATH "" FORCE)
|
||||
set (CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE)
|
||||
set (PKG_CONFIG_EXECUTABLE pkg-config CACHE FILEPATH "" FORCE)
|
||||
|
||||
# Setup iOS platform unless specified manually with IOS_PLATFORM
|
||||
if (NOT DEFINED IOS_PLATFORM)
|
||||
set (IOS_PLATFORM "OS")
|
||||
endif (NOT DEFINED IOS_PLATFORM)
|
||||
set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
|
||||
|
||||
# Check the platform selection and setup for developer root
|
||||
if (IOS_PLATFORM STREQUAL "OS")
|
||||
set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
|
||||
set (XCODE_IOS_PLATFORM ios)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
|
||||
|
||||
set (APPLE_IOS True)
|
||||
elseif (IOS_PLATFORM STREQUAL "SIMULATOR")
|
||||
set (SIMULATOR_FLAG true)
|
||||
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
|
||||
set (XCODE_IOS_PLATFORM ios-simulator)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
|
||||
|
||||
set (APPLE_IOS True)
|
||||
elseif (IOS_PLATFORM STREQUAL "WATCHOS")
|
||||
set (IOS_PLATFORM_LOCATION "WatchOS.platform")
|
||||
set (XCODE_IOS_PLATFORM watchos)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchos")
|
||||
|
||||
set (APPLE_WATCH True)
|
||||
elseif (IOS_PLATFORM STREQUAL "WATCHSIMULATOR")
|
||||
set (SIMULATOR_FLAG true)
|
||||
set (IOS_PLATFORM_LOCATION "WatchSimulator.platform")
|
||||
set (XCODE_IOS_PLATFORM watchos-simulator)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchsimulator")
|
||||
|
||||
set (APPLE_WATCH True)
|
||||
elseif (IOS_PLATFORM STREQUAL "TVOS")
|
||||
set (IOS_PLATFORM_LOCATION "AppleTvOS.platform")
|
||||
set (XCODE_IOS_PLATFORM tvos)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-appletvos")
|
||||
|
||||
set (APPLE_TV True)
|
||||
elseif (IOS_PLATFORM STREQUAL "TVSIMULATOR")
|
||||
set (SIMULATOR_FLAG true)
|
||||
set (IOS_PLATFORM_LOCATION "AppleTvSimulator.platform")
|
||||
set (XCODE_IOS_PLATFORM tvos-simulator)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-tvsimulator")
|
||||
|
||||
set (APPLE_TV True)
|
||||
elseif (IOS_PLATFORM STREQUAL "VISIONOS")
|
||||
set (IOS_PLATFORM_LOCATION "XROS.platform")
|
||||
set (XCODE_IOS_PLATFORM xros)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xros")
|
||||
|
||||
set (APPLE_VISION True)
|
||||
elseif (IOS_PLATFORM STREQUAL "VISIONSIMULATOR")
|
||||
set (SIMULATOR_FLAG true)
|
||||
set (IOS_PLATFORM_LOCATION "XRSimulator.platform")
|
||||
set (XCODE_IOS_PLATFORM xros-simulator)
|
||||
|
||||
# This causes the installers to properly locate the output libraries
|
||||
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-xrsimulator")
|
||||
|
||||
set (APPLE_VISION True)
|
||||
else (IOS_PLATFORM STREQUAL "OS")
|
||||
message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS, SIMULATOR, or WATCHOS.")
|
||||
endif ()
|
||||
|
||||
# All iOS/Darwin specific settings - some may be redundant
|
||||
set (CMAKE_SHARED_LIBRARY_PREFIX "lib")
|
||||
set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
|
||||
set (CMAKE_SHARED_MODULE_PREFIX "lib")
|
||||
set (CMAKE_SHARED_MODULE_SUFFIX ".so")
|
||||
set (CMAKE_MODULE_EXISTS 1)
|
||||
set (CMAKE_DL_LIBS "")
|
||||
|
||||
set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
|
||||
set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
|
||||
set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
|
||||
set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
|
||||
|
||||
if (IOS_DEPLOYMENT_TARGET)
|
||||
set (XCODE_IOS_PLATFORM_VERSION_FLAGS "-m${XCODE_IOS_PLATFORM}-version-min=${IOS_DEPLOYMENT_TARGET}")
|
||||
endif()
|
||||
|
||||
set (CMAKE_SHARED_LINKER_FLAGS_INIT "-fapplication-extension")
|
||||
set (CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS}")
|
||||
# Hidden visibility is required for cxx on iOS
|
||||
set (CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fvisibility-inlines-hidden")
|
||||
|
||||
set (CMAKE_C_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
|
||||
set (CMAKE_CXX_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fapplication-extension -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
|
||||
|
||||
set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
|
||||
set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names")
|
||||
set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names")
|
||||
set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
|
||||
set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
|
||||
set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
|
||||
|
||||
# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree
|
||||
# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache
|
||||
# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun)
|
||||
# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex
|
||||
if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
|
||||
find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
|
||||
endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
|
||||
|
||||
# Setup iOS deployment target
|
||||
set (IOS_DEPLOYMENT_TARGET ${IOS_DEPLOYMENT_TARGET} CACHE STRING "Minimum iOS version")
|
||||
|
||||
# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
|
||||
# Note Xcode 4.3 changed the installation location, choose the most recent one available
|
||||
execute_process(COMMAND /usr/bin/xcode-select -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
|
||||
set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
|
||||
if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
|
||||
if (EXISTS ${XCODE_POST_43_ROOT})
|
||||
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT})
|
||||
elseif (EXISTS ${XCODE_PRE_43_ROOT})
|
||||
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT})
|
||||
endif (EXISTS ${XCODE_POST_43_ROOT})
|
||||
endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
|
||||
set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
|
||||
|
||||
# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
|
||||
if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
|
||||
file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
|
||||
if (_CMAKE_IOS_SDKS)
|
||||
list (SORT _CMAKE_IOS_SDKS)
|
||||
list (REVERSE _CMAKE_IOS_SDKS)
|
||||
list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
|
||||
else (_CMAKE_IOS_SDKS)
|
||||
message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
|
||||
endif (_CMAKE_IOS_SDKS)
|
||||
message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
|
||||
endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
|
||||
set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
|
||||
|
||||
# Set the sysroot default to the most recent SDK
|
||||
set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
|
||||
|
||||
# Set the architectures unless specified manually with IOS_ARCH
|
||||
if (NOT DEFINED IOS_ARCH)
|
||||
if (IOS_PLATFORM STREQUAL "OS")
|
||||
set (IOS_ARCH "arm64")
|
||||
elseif (IOS_PLATFORM STREQUAL "SIMULATOR")
|
||||
set (IOS_ARCH "x86_64;arm64")
|
||||
elseif (IOS_PLATFORM STREQUAL "WATCHOS")
|
||||
set (IOS_ARCH "armv7k;arm64_32;arm64")
|
||||
|
||||
# Include C++ Standard Library for Xcode 15 builds.
|
||||
include_directories(SYSTEM "${CMAKE_IOS_SDK_ROOT}/usr/include/c++/v1")
|
||||
elseif (IOS_PLATFORM STREQUAL "WATCHSIMULATOR")
|
||||
set (IOS_ARCH "x86_64;arm64")
|
||||
|
||||
# Include C++ Standard Library for Xcode 15 builds.
|
||||
include_directories(SYSTEM "${CMAKE_IOS_SDK_ROOT}/usr/include/c++/v1")
|
||||
elseif (IOS_PLATFORM STREQUAL "TVOS")
|
||||
set (IOS_ARCH "arm64")
|
||||
elseif (IOS_PLATFORM STREQUAL "TVSIMULATOR")
|
||||
set (IOS_ARCH "x86_64;arm64")
|
||||
elseif (IOS_PLATFORM STREQUAL "VISIONOS")
|
||||
set (IOS_ARCH "arm64")
|
||||
elseif (IOS_PLATFORM STREQUAL "VISIONSIMULATOR")
|
||||
set (IOS_ARCH "x86_64;arm64")
|
||||
endif()
|
||||
endif()
|
||||
message (STATUS "The iOS architectures: ${IOS_ARCH}")
|
||||
|
||||
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
|
||||
|
||||
# Set the find root to the iOS developer roots and to user defined paths
|
||||
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root")
|
||||
|
||||
# default to searching for frameworks first
|
||||
set (CMAKE_FIND_FRAMEWORK FIRST)
|
||||
|
||||
# set up the default search directories for frameworks
|
||||
set (CMAKE_SYSTEM_FRAMEWORK_PATH
|
||||
${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
|
||||
${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
|
||||
${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
|
||||
)
|
||||
|
||||
# only search the iOS sdks, not the remainder of the host filesystem
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
# This little macro lets you set any Xcode specific property
|
||||
macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE)
|
||||
set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE})
|
||||
endmacro (set_xcode_property)
|
||||
|
||||
# This macro lets you find executable programs on the host system
|
||||
macro (find_host_package)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
|
||||
set (IOS FALSE)
|
||||
|
||||
find_package(${ARGN})
|
||||
|
||||
set (IOS TRUE)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endmacro (find_host_package)
|
10
third-party/td/td/CMake/illumos.cmake
vendored
10
third-party/td/td/CMake/illumos.cmake
vendored
@ -1,10 +0,0 @@
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
#
|
||||
# Determine if the host is running an illumos distribution:
|
||||
#
|
||||
execute_process(COMMAND /usr/bin/uname -o OUTPUT_VARIABLE UNAME_O OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if (UNAME_O STREQUAL "illumos")
|
||||
set(ILLUMOS 1)
|
||||
endif()
|
||||
endif()
|
1449
third-party/td/td/CMakeLists.txt
vendored
1449
third-party/td/td/CMakeLists.txt
vendored
File diff suppressed because it is too large
Load Diff
2473
third-party/td/td/Doxyfile
vendored
2473
third-party/td/td/Doxyfile
vendored
File diff suppressed because it is too large
Load Diff
23
third-party/td/td/LICENSE_1_0.txt
vendored
23
third-party/td/td/LICENSE_1_0.txt
vendored
@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
145
third-party/td/td/README.md
vendored
145
third-party/td/td/README.md
vendored
@ -1,145 +0,0 @@
|
||||
# TDLib
|
||||
|
||||
TDLib (Telegram Database library) is a cross-platform library for building [Telegram](https://telegram.org) clients. It can be easily used from almost any programming language.
|
||||
|
||||
## Table of Contents
|
||||
- [Features](#features)
|
||||
- [Examples and documentation](#usage)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Building](#building)
|
||||
- [Using in CMake C++ projects](#using-cxx)
|
||||
- [Using in Java projects](#using-java)
|
||||
- [Using in .NET projects](#using-dotnet)
|
||||
- [Using with other programming languages](#using-json)
|
||||
- [License](#license)
|
||||
|
||||
<a name="features"></a>
|
||||
## Features
|
||||
|
||||
`TDLib` has many advantages. Notably `TDLib` is:
|
||||
|
||||
* **Cross-platform**: `TDLib` can be used on Android, iOS, Windows, macOS, Linux, FreeBSD, OpenBSD, NetBSD, illumos, Windows Phone, WebAssembly, watchOS, tvOS, visionOS, Tizen, Cygwin. It should also work on other *nix systems with or without minimal effort.
|
||||
* **Multilanguage**: `TDLib` can be easily used with any programming language that is able to execute C functions. Additionally, it already has native Java (using `JNI`) bindings and .NET (using `C++/CLI` and `C++/CX`) bindings.
|
||||
* **Easy to use**: `TDLib` takes care of all network implementation details, encryption and local data storage.
|
||||
* **High-performance**: in the [Telegram Bot API](https://core.telegram.org/bots/api), each `TDLib` instance handles more than 25000 active bots simultaneously.
|
||||
* **Well-documented**: all `TDLib` API methods and public interfaces are fully documented.
|
||||
* **Consistent**: `TDLib` guarantees that all updates are delivered in the right order.
|
||||
* **Reliable**: `TDLib` remains stable on slow and unreliable Internet connections.
|
||||
* **Secure**: all local data is encrypted using a user-provided encryption key.
|
||||
* **Fully-asynchronous**: requests to `TDLib` don't block each other or anything else, responses are sent when they are available.
|
||||
|
||||
<a name="usage"></a>
|
||||
## Examples and documentation
|
||||
See our [Getting Started](https://core.telegram.org/tdlib/getting-started) tutorial for a description of basic TDLib concepts.
|
||||
|
||||
Take a look at our [examples](https://github.com/tdlib/td/blob/master/example/README.md#tdlib-usage-and-build-examples).
|
||||
|
||||
See a [TDLib build instructions generator](https://tdlib.github.io/td/build.html) for detailed instructions on how to build TDLib.
|
||||
|
||||
See description of our [JSON](#using-json), [C++](#using-cxx), [Java](#using-java) and [.NET](#using-dotnet) interfaces.
|
||||
|
||||
See the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html)
|
||||
for a list of all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||
|
||||
<a name="dependencies"></a>
|
||||
## Dependencies
|
||||
`TDLib` depends on:
|
||||
|
||||
* C++17 compatible compiler (Clang 5.0+, GCC 7.0+, MSVC 19.1+ (Visual Studio 2017.7+), Intel C++ Compiler 19+)
|
||||
* OpenSSL
|
||||
* zlib
|
||||
* gperf (build only)
|
||||
* CMake (3.10+, build only)
|
||||
* PHP (optional, for documentation generation)
|
||||
|
||||
<a name="building"></a>
|
||||
## Building
|
||||
|
||||
The simplest way to build `TDLib` is to use our [TDLib build instructions generator](https://tdlib.github.io/td/build.html).
|
||||
You need only to choose your programming language and target operating system to receive complete build instructions.
|
||||
|
||||
In general, you need to install all `TDLib` [dependencies](#dependencies), enter directory containing `TDLib` sources and compile them using CMake:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
To build `TDLib` on low memory devices you can run [SplitSource.php](https://github.com/tdlib/td/blob/master/SplitSource.php) script
|
||||
before compiling main `TDLib` source code and compile only needed targets:
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build . --target prepare_cross_compiling
|
||||
cd ..
|
||||
php SplitSource.php
|
||||
cd build
|
||||
cmake --build . --target tdjson
|
||||
cmake --build . --target tdjson_static
|
||||
cd ..
|
||||
php SplitSource.php --undo
|
||||
```
|
||||
In our tests clang 6.0 with libc++ required less than 500 MB of RAM per file and GCC 4.9/6.3 used less than 1 GB of RAM per file.
|
||||
|
||||
<a name="using-cxx"></a>
|
||||
## Using in CMake C++ projects
|
||||
For C++ projects that use CMake, the best approach is to build `TDLib` as part of your project or to install it system-wide.
|
||||
|
||||
There are several libraries that you could use in your CMake project:
|
||||
|
||||
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a JSON interface. This has a simple C interface, so it can be easily used with any programming language that is able to execute C functions.
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for more information.
|
||||
* Td::TdStatic — static library with C++ interface for general usage.
|
||||
See [ClientManager](https://core.telegram.org/tdlib/docs/classtd_1_1_client_manager.html) and [Client](https://core.telegram.org/tdlib/docs/classtd_1_1_client.html) documentation for more information.
|
||||
|
||||
For example, part of your CMakeLists.txt may look like this:
|
||||
```
|
||||
add_subdirectory(td)
|
||||
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||
```
|
||||
|
||||
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
|
||||
```
|
||||
find_package(Td 1.8.46 REQUIRED)
|
||||
target_link_libraries(YourTarget PRIVATE Td::TdStatic)
|
||||
```
|
||||
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/blob/master/example/cpp/CMakeLists.txt).
|
||||
|
||||
<a name="using-java"></a>
|
||||
## Using in Java projects
|
||||
`TDLib` provides native Java interface through JNI. To enable it, specify option `-DTD_ENABLE_JNI=ON` to CMake.
|
||||
|
||||
See [example/java](https://github.com/tdlib/td/tree/master/example/java) for example of using `TDLib` from Java and detailed build and usage instructions.
|
||||
|
||||
<a name="using-dotnet"></a>
|
||||
## Using in .NET projects
|
||||
`TDLib` provides native .NET interface through `C++/CLI` and `C++/CX`. To enable it, specify option `-DTD_ENABLE_DOTNET=ON` to CMake.
|
||||
.NET Core supports `C++/CLI` only since version 3.1 and only on Windows, so if older .NET Core is used or portability is needed, then `TDLib` JSON interface should be used through P/Invoke instead.
|
||||
|
||||
See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions.
|
||||
See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform".
|
||||
|
||||
When `TDLib` is built with `TD_ENABLE_DOTNET` option enabled, `C++` documentation is removed from some files. You need to checkout these files to return `C++` documentation back:
|
||||
```
|
||||
git checkout td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h
|
||||
```
|
||||
|
||||
<a name="using-json"></a>
|
||||
## Using from other programming languages
|
||||
`TDLib` provides efficient native C++, Java, and .NET interfaces.
|
||||
But for most use cases we suggest to use the JSON interface, which can be easily used with any programming language that is able to execute C functions.
|
||||
See [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html) documentation for detailed JSON interface description,
|
||||
the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of
|
||||
all available `TDLib` [methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||
|
||||
`TDLib` JSON interface adheres to semantic versioning and versions with the same major version number are binary and backward compatible, but the underlying `TDLib` API can be different for different minor and even patch versions.
|
||||
If you need to support different `TDLib` versions, then you can use a value of the `version` option to find exact `TDLib` version to use appropriate API methods.
|
||||
|
||||
See [example/python/tdjson_example.py](https://github.com/tdlib/td/blob/master/example/python/tdjson_example.py) for an example of such usage.
|
||||
|
||||
<a name="license"></a>
|
||||
## License
|
||||
`TDLib` is licensed under the terms of the Boost Software License. See [LICENSE_1_0.txt](http://www.boost.org/LICENSE_1_0.txt) for more information.
|
511
third-party/td/td/SplitSource.php
vendored
511
third-party/td/td/SplitSource.php
vendored
@ -1,511 +0,0 @@
|
||||
<?php
|
||||
|
||||
function disjoint_set_find(&$parents, $x) {
|
||||
if ($parents[$x] !== $x) {
|
||||
return $parents[$x] = disjoint_set_find($parents, $parents[$x]);
|
||||
}
|
||||
return $x;
|
||||
}
|
||||
|
||||
function disjoint_set_union(&$parents, $x, $y) {
|
||||
$x = disjoint_set_find($parents, $x);
|
||||
$y = disjoint_set_find($parents, $y);
|
||||
|
||||
if ($x !== $y) {
|
||||
if (rand(0, 1) == 0) {
|
||||
$parents[$x] = $y;
|
||||
} else {
|
||||
$parents[$y] = $x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function split_file($file, $chunks, $undo) {
|
||||
$cpp_name = "$file.cpp";
|
||||
|
||||
echo "Processing file $cpp_name".PHP_EOL;
|
||||
|
||||
$new_files = array();
|
||||
foreach (range(0, $chunks - 1) as $n) {
|
||||
$new_files[] = "$file$n.cpp";
|
||||
}
|
||||
|
||||
$is_generated = (strpos($file, 'td/generate/') === 0);
|
||||
|
||||
$cmake_file = $is_generated ? 'td/generate/CMakeLists.txt' : 'CMakeLists.txt';
|
||||
$cmake = file_get_contents($cmake_file);
|
||||
|
||||
$cmake_cpp_name = $cpp_name;
|
||||
$cmake_new_files = $new_files;
|
||||
if ($is_generated) {
|
||||
foreach ($cmake_new_files as &$file_ref) {
|
||||
$file_ref = str_replace('td/generate/auto/td', '${TD_AUTO_INCLUDE_DIR}', $file_ref);
|
||||
}
|
||||
$cmake_cpp_name = str_replace('td/generate/auto/td', '${TD_AUTO_INCLUDE_DIR}', $cmake_cpp_name);
|
||||
}
|
||||
|
||||
if ($undo) {
|
||||
foreach ($new_files as $file) {
|
||||
if (file_exists($file)) {
|
||||
echo "Unlinking ".$file.PHP_EOL;
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($cmake, $cmake_cpp_name) === false) {
|
||||
$cmake = str_replace(implode(PHP_EOL.' ', $cmake_new_files), $cmake_cpp_name, $cmake);
|
||||
file_put_contents($cmake_file, $cmake);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($cmake, $cmake_cpp_name) !== false) {
|
||||
$cmake = str_replace($cmake_cpp_name, implode(PHP_EOL.' ', $cmake_new_files), $cmake);
|
||||
file_put_contents($cmake_file, $cmake);
|
||||
}
|
||||
|
||||
if (!file_exists($cpp_name)) {
|
||||
echo "ERROR: skip nonexistent file $cpp_name".PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
$lines = file($cpp_name);
|
||||
$depth = 0;
|
||||
$target_depth = 1 + $is_generated;
|
||||
$is_static = false;
|
||||
$in_define = false;
|
||||
$in_comment = false;
|
||||
$current = '';
|
||||
$common = '';
|
||||
$functions = array();
|
||||
$namespace_begin = '';
|
||||
$namespace_end = '';
|
||||
foreach ($lines as $line) {
|
||||
$add_depth = strpos($line, 'namespace ') === 0 ? 1 : (strpos($line, '} // namespace') === 0 ? -1 : 0);
|
||||
if ($add_depth) {
|
||||
# namespace begin/end
|
||||
if ($add_depth > 0) {
|
||||
$depth += $add_depth;
|
||||
}
|
||||
if ($depth <= $target_depth) {
|
||||
if ($add_depth > 0) {
|
||||
$namespace_begin .= $line;
|
||||
} else {
|
||||
$namespace_end .= $line;
|
||||
}
|
||||
}
|
||||
if ($add_depth < 0) {
|
||||
$depth += $add_depth;
|
||||
}
|
||||
if ($is_static) {
|
||||
$common .= $current;
|
||||
} else {
|
||||
$functions[] = $current;
|
||||
}
|
||||
$common .= $line;
|
||||
$current = '';
|
||||
$is_static = false;
|
||||
$in_define = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($line, '#undef') === 0 && !trim($current)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($in_comment && strpos($line, '*/') === 0) {
|
||||
$in_comment = false;
|
||||
continue;
|
||||
}
|
||||
if (strpos($line, '/*') === 0) {
|
||||
$in_comment = true;
|
||||
}
|
||||
if ($in_comment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($depth !== $target_depth) {
|
||||
$common .= $line;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($line, 'static ') === 0 && $depth === $target_depth) {
|
||||
$is_static = true;
|
||||
}
|
||||
if (!trim($current) && strpos($line, '#define ') === 0) {
|
||||
$is_static = true;
|
||||
$in_define = true;
|
||||
}
|
||||
|
||||
$current .= $line;
|
||||
if ((strpos($line, '}') === 0 || ($in_define && !trim($line)) || preg_match('/^[a-z].*;\s*$/i', $line)) && $depth === $target_depth) {
|
||||
# block end
|
||||
if ($is_static) {
|
||||
$common .= $current;
|
||||
} else {
|
||||
$functions[] = $current;
|
||||
}
|
||||
$current = '';
|
||||
$is_static = false;
|
||||
$in_define = false;
|
||||
}
|
||||
}
|
||||
$current = trim($current);
|
||||
if (!empty($current)) {
|
||||
fwrite(STDERR, "ERROR: $current".PHP_EOL);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (count($functions) < $chunks) {
|
||||
fwrite(STDERR, "ERROR: file is too small to be split more".PHP_EOL);
|
||||
return;
|
||||
}
|
||||
|
||||
$deps = array(); // all functions from the same subarray must be in the same file
|
||||
$parents = array();
|
||||
foreach ($functions as $i => $f) {
|
||||
if (preg_match_all('/(?J)create_handler<(?<name>[A-Z][A-Za-z]*)>|'.
|
||||
'(?<name>[A-Z][A-Za-z]*) (final )?: public (Td::ResultHandler|Request)|'.
|
||||
'(CREATE_REQUEST|CREATE_NO_ARGS_REQUEST)[(](?<name>[A-Z][A-Za-z]*)|'.
|
||||
'(?<name>complete_pending_preauthentication_requests)|'.
|
||||
'(?<name>get_message_history_slice)|'.
|
||||
'(Up|Down)load(?!ManagerCallback)[a-zA-Z]+C(?<name>allback)|(up|down)load_[a-z_]*_c(?<name>allback)_|'.
|
||||
'(?<name>lazy_to_json)|'.
|
||||
'(?<name>LogEvent)[^sA]|'.
|
||||
'(?<name>parse)[(]|'.
|
||||
'(?<name>store)[(]/', $f, $matches, PREG_SET_ORDER)) {
|
||||
foreach ($matches as $match) {
|
||||
$name = $match['name'];
|
||||
if ($name === 'parse' || $name === 'store') {
|
||||
if ($is_generated) {
|
||||
continue;
|
||||
}
|
||||
$name = 'LogEvent';
|
||||
}
|
||||
$deps[$name][] = $i;
|
||||
}
|
||||
}
|
||||
$parents[$i] = $i;
|
||||
}
|
||||
|
||||
foreach ($deps as $func_ids) {
|
||||
foreach ($func_ids as $func_id) {
|
||||
disjoint_set_union($parents, $func_ids[0], $func_id);
|
||||
}
|
||||
}
|
||||
$sets = array();
|
||||
$set_sizes = array();
|
||||
foreach ($functions as $i => $f) {
|
||||
$parent = disjoint_set_find($parents, $i);
|
||||
if (!isset($sets[$parent])) {
|
||||
$sets[$parent] = '';
|
||||
$set_sizes[$parent] = 0;
|
||||
}
|
||||
$sets[$parent] .= $f;
|
||||
$set_sizes[$parent] += strlen($f);
|
||||
}
|
||||
arsort($set_sizes);
|
||||
|
||||
$files = array_fill(0, $chunks, '');
|
||||
$file_sizes = array_fill(0, $chunks, 0);
|
||||
foreach ($set_sizes as $parent => $size) {
|
||||
$file_id = array_search(min($file_sizes), $file_sizes);
|
||||
$files[$file_id] .= $sets[$parent];
|
||||
$file_sizes[$file_id] += $size;
|
||||
}
|
||||
|
||||
foreach ($files as $n => $f) {
|
||||
$new_content = $common.$namespace_begin.$f.$namespace_end;
|
||||
|
||||
$std_methods = array();
|
||||
preg_match_all('/std::[a-z_0-9]*|td::unique(?!_)/', $new_content, $std_methods);
|
||||
$std_methods = array_unique($std_methods[0]);
|
||||
|
||||
$needed_std_headers = array();
|
||||
$type_headers = array(
|
||||
'std::move' => '',
|
||||
'std::vector' => '',
|
||||
'std::string' => '',
|
||||
'std::uint32_t' => '',
|
||||
'std::int32_t' => '',
|
||||
'std::int64_t' => '',
|
||||
'td::unique' => 'algorithm',
|
||||
'std::count_if' => 'algorithm',
|
||||
'std::fill' => 'algorithm',
|
||||
'std::find' => 'algorithm',
|
||||
'std::is_sorted' => 'algorithm',
|
||||
'std::lower_bound' => 'algorithm',
|
||||
'std::max' => 'algorithm',
|
||||
'std::merge' => 'algorithm',
|
||||
'std::min' => 'algorithm',
|
||||
'std::partial_sort' => 'algorithm',
|
||||
'std::partition' => 'algorithm',
|
||||
'std::remove' => 'algorithm',
|
||||
'std::reverse' => 'algorithm',
|
||||
'std::rotate' => 'algorithm',
|
||||
'std::sort' => 'algorithm',
|
||||
'std::stable_sort' => 'algorithm',
|
||||
'std::upper_bound' => 'algorithm',
|
||||
'std::abs' => 'cmath',
|
||||
'std::isfinite' => 'cmath',
|
||||
'std::function' => 'functional',
|
||||
'std::greater' => 'functional',
|
||||
'std::reference_wrapper' => 'functional',
|
||||
'std::make_move_iterator' => 'iterator',
|
||||
'std::numeric_limits' => 'limits',
|
||||
'std::map' => 'map',
|
||||
'std::multimap' => 'map',
|
||||
'std::make_shared' => 'memory',
|
||||
'std::shared_ptr' => 'memory',
|
||||
'std::multiset' => 'set',
|
||||
'std::set' => 'set',
|
||||
'std::get' => 'tuple',
|
||||
'std::make_tuple' => 'tuple',
|
||||
'std::tie' => 'tuple',
|
||||
'std::tuple' => 'tuple',
|
||||
'std::decay_t' => 'type_traits',
|
||||
'std::is_same' => 'type_traits',
|
||||
'std::unordered_map' => 'unordered_map',
|
||||
'std::unordered_set' => 'unordered_set',
|
||||
'std::make_pair' => 'utility',
|
||||
'std::pair' => 'utility',
|
||||
'std::swap' => 'utility');
|
||||
foreach ($type_headers as $type => $header) {
|
||||
if (in_array($type, $std_methods)) {
|
||||
$std_methods = array_diff($std_methods, array($type));
|
||||
if ($header && !in_array($header, $needed_std_headers)) {
|
||||
$needed_std_headers[] = $header;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$std_methods) { // know all needed std headers
|
||||
$new_content = preg_replace_callback(
|
||||
'/#include <([a-z_]*)>/',
|
||||
function ($matches) use ($needed_std_headers) {
|
||||
if (in_array($matches[1], $needed_std_headers)) {
|
||||
return $matches[0];
|
||||
}
|
||||
return '';
|
||||
},
|
||||
$new_content
|
||||
);
|
||||
}
|
||||
|
||||
$td_methods = array(
|
||||
'AccentColorId' => 'AccentColorId',
|
||||
'account_manager[_(-](?![.]get[(][)])|AccountManager[^;>]' => 'AccountManager',
|
||||
'AffiliateType' => 'AffiliateType',
|
||||
'alarm_manager[_(-](?![.]get[(][)])|AlarmManager' => 'AlarmManager',
|
||||
'animations_manager[_(-](?![.]get[(][)])|AnimationsManager[^;>]' => 'AnimationsManager',
|
||||
'attach_menu_manager[_(-](?![.]get[(][)])|AttachMenuManager[^;>]' => 'AttachMenuManager',
|
||||
'audios_manager[_(-](?![.]get[(][)])|AudiosManager' => 'AudiosManager',
|
||||
'auth_manager[_(-](?![.]get[(][)])|AuthManager' => 'AuthManager',
|
||||
'AutoDownloadSettings|[a-z_]*auto_download_settings' => 'AutoDownloadSettings',
|
||||
'autosave_manager[_(-](?![.]get[(][)])|AutosaveManager' => 'AutosaveManager',
|
||||
'BackgroundId' => 'BackgroundId',
|
||||
'background_manager[_(-](?![.]get[(][)])|BackgroundManager' => 'BackgroundManager',
|
||||
'BackgroundType' => 'BackgroundType',
|
||||
'Birthdate' => 'Birthdate',
|
||||
'boost_manager[_(-](?![.]get[(][)])|BoostManager' => 'BoostManager',
|
||||
'bot_info_manager[_(-](?![.]get[(][)])|BotInfoManager' => 'BotInfoManager',
|
||||
'BotMenuButton|[a-z_]*_menu_button' => 'BotMenuButton',
|
||||
'send_bot_custom_query|answer_bot_custom_query|set_bot_updates_status' => 'BotQueries',
|
||||
'bot_recommendation_manager[_(-](?![.]get[(][)])|BotRecommendationManager' => 'BotRecommendationManager',
|
||||
'BotVerification' => 'BotVerification',
|
||||
'BotVerifierSettings' => 'BotVerifierSettings',
|
||||
'BusinessAwayMessage' => 'BusinessAwayMessage',
|
||||
'BusinessBotRights' => 'BusinessBotRights',
|
||||
'BusinessChatLink' => 'BusinessChatLink',
|
||||
'BusinessConnectedBot' => 'BusinessConnectedBot',
|
||||
'BusinessConnectionId' => 'BusinessConnectionId',
|
||||
'business_connection_manager[_(-](?![.]get[(][)])|BusinessConnectionManager' => 'BusinessConnectionManager',
|
||||
'BusinessGreetingMessage' => 'BusinessGreetingMessage',
|
||||
'BusinessInfo|business_info' => 'BusinessInfo',
|
||||
'BusinessIntro' => 'BusinessIntro',
|
||||
'business_manager[_(-](?![.]get[(][)])|BusinessManager' => 'BusinessManager',
|
||||
'BusinessRecipients' => 'BusinessRecipients',
|
||||
'BusinessWorkHours' => 'BusinessWorkHours',
|
||||
'callback_queries_manager[_(-](?![.]get[(][)])|CallbackQueriesManager' => 'CallbackQueriesManager',
|
||||
'CallId' => 'CallId',
|
||||
'call_manager[_(-](?![.]get[(][)])|CallManager' => 'CallManager',
|
||||
'ChannelId' => 'ChannelId',
|
||||
'channel_recommendation_manager[_(-](?![.]get[(][)])|ChannelRecommendationManager' => 'ChannelRecommendationManager',
|
||||
'ChatId' => 'ChatId',
|
||||
'chat_manager[_(-](?![.]get[(][)])|ChatManager([^ ;.]| [^*])' => 'ChatManager',
|
||||
'common_dialog_manager[_(-](?![.]get[(][)])|CommonDialogManager' => 'CommonDialogManager',
|
||||
'connection_state_manager[_(-](?![.]get[(][)])|ConnectionStateManager' => 'ConnectionStateManager',
|
||||
'country_info_manager[_(-](?![.]get[(][)])|CountryInfoManager' => 'CountryInfoManager',
|
||||
'CustomEmojiId' => 'CustomEmojiId',
|
||||
'device_token_manager[_(-](?![.]get[(][)])|DeviceTokenManager' => 'DeviceTokenManager',
|
||||
'DialogAction[^M]' => 'DialogAction',
|
||||
'dialog_action_manager[_(-](?![.]get[(][)])|DialogActionManager' => 'DialogActionManager',
|
||||
'DialogFilter[^A-Z]' => 'DialogFilter',
|
||||
'DialogFilterId' => 'DialogFilterId',
|
||||
'dialog_filter_manager[_(-](?![.]get[(][)])|DialogFilterManager' => 'DialogFilterManager',
|
||||
'DialogId' => 'DialogId',
|
||||
'dialog_invite_link_manager[_(-](?![.]get[(][)])|DialogInviteLinkManager' => 'DialogInviteLinkManager',
|
||||
'DialogListId' => 'DialogListId',
|
||||
'DialogLocation' => 'DialogLocation',
|
||||
'dialog_manager[_(-](?![.]get[(][)])|DialogManager' => 'DialogManager',
|
||||
'DialogParticipantFilter' => 'DialogParticipantFilter',
|
||||
'dialog_participant_manager[_(-](?![.]get[(][)])|DialogParticipantManager' => 'DialogParticipantManager',
|
||||
'DialogSource' => 'DialogSource',
|
||||
'DisallowedGiftsSettings' => 'DisallowedGiftsSettings',
|
||||
'documents_manager[_(-](?![.]get[(][)])|DocumentsManager' => 'DocumentsManager',
|
||||
'download_manager[_(-](?![.]get[(][)])|DownloadManager[^C]' => 'DownloadManager',
|
||||
'DownloadManagerCallback' => 'DownloadManagerCallback',
|
||||
'EmailVerification' => 'EmailVerification',
|
||||
'EmojiGroup' => 'EmojiGroup',
|
||||
'FactCheck' => 'FactCheck',
|
||||
'file_reference_manager[_(-](?![.]get[(][)])|FileReferenceManager|file_references[)]' => 'FileReferenceManager',
|
||||
'file_manager[_(-](?![.]get[(][)])|FileManager([^ ;.]| [^*])|update_file[)]' => 'files/FileManager',
|
||||
'FolderId' => 'FolderId',
|
||||
'forum_topic_manager[_(-](?![.]get[(][)])|ForumTopicManager' => 'ForumTopicManager',
|
||||
'game_manager[_(-](?![.]get[(][)])|GameManager' => 'GameManager',
|
||||
'G[(][)]|Global[^A-Za-z]' => 'Global',
|
||||
'GlobalPrivacySettings' => 'GlobalPrivacySettings',
|
||||
'GroupCallId' => 'GroupCallId',
|
||||
'group_call_manager[_(-](?![.]get[(][)])|GroupCallManager' => 'GroupCallManager',
|
||||
'hashtag_hints[_(-](?![.]get[(][)])|HashtagHints' => 'HashtagHints',
|
||||
'inline_message_manager[_(-](?![.]get[(][)])|InlineMessageManager' => 'InlineMessageManager',
|
||||
'inline_queries_manager[_(-](?![.]get[(][)])|InlineQueriesManager' => 'InlineQueriesManager',
|
||||
'InputBusinessChatLink' => 'InputBusinessChatLink',
|
||||
'language_pack_manager[_(-]|LanguagePackManager' => 'LanguagePackManager',
|
||||
'link_manager[_(-](?![.]get[(][)])|LinkManager' => 'LinkManager',
|
||||
'LogeventIdWithGeneration|add_log_event|delete_log_event|get_erase_log_event_promise|parse_time|store_time' => 'logevent/LogEventHelper',
|
||||
'MessageCopyOptions' => 'MessageCopyOptions',
|
||||
'MessageEffectId' => 'MessageEffectId',
|
||||
'MessageForwardInfo|LastForwardedMessageInfo|forward_info' => 'MessageForwardInfo',
|
||||
'MessageFullId' => 'MessageFullId',
|
||||
'MessageId' => 'MessageId',
|
||||
'message_import_manager[_(-](?![.]get[(][)])|MessageImportManager' => 'MessageImportManager',
|
||||
'message_query_manager[_(-](?![.]get[(][)])|MessageQueryManager' => 'MessageQueryManager',
|
||||
'MessageLinkInfo' => 'MessageLinkInfo',
|
||||
'MessageQuote' => 'MessageQuote',
|
||||
'MessageReaction|UnreadMessageReaction|[a-z_]*message[a-z_]*reaction|reload_paid_reaction_privacy|get_chosen_tags' => 'MessageReaction',
|
||||
'MessageReactor' => 'MessageReactor',
|
||||
'MessageSearchOffset' => 'MessageSearchOffset',
|
||||
'[a-z_]*_message_sender' => 'MessageSender',
|
||||
'messages_manager[_(-](?![.]get[(][)])|MessagesManager' => 'MessagesManager',
|
||||
'MessageThreadInfo' => 'MessageThreadInfo',
|
||||
'MessageTtl' => 'MessageTtl',
|
||||
'MissingInvitee' => 'MissingInvitee',
|
||||
'notification_manager[_(-](?![.]get[(][)])|NotificationManager|notifications[)]' => 'NotificationManager',
|
||||
'notification_settings_manager[_(-](?![.]get[(][)])|NotificationSettingsManager' => 'NotificationSettingsManager',
|
||||
'online_manager[_(-](?![.]get[(][)])|OnlineManager' => 'OnlineManager',
|
||||
'option_manager[_(-](?![.]get[(][)])|OptionManager' => 'OptionManager',
|
||||
'PaidReactionType' => 'PaidReactionType',
|
||||
'password_manager[_(-](?![.]get[(][)])|PasswordManager' => 'PasswordManager',
|
||||
'people_nearby_manager[_(-](?![.]get[(][)])|PeopleNearbyManager' => 'PeopleNearbyManager',
|
||||
'phone_number_manager[_(-](?![.]get[(][)])|PhoneNumberManager' => 'PhoneNumberManager',
|
||||
'PhotoSizeSource' => 'PhotoSizeSource',
|
||||
'poll_manager[_(-](?![.]get[(][)])|PollManager' => 'PollManager',
|
||||
'privacy_manager[_(-](?![.]get[(][)])|PrivacyManager' => 'PrivacyManager',
|
||||
'promo_data_manager[_(-](?![.]get[(][)])|PromoDataManager' => 'PromoDataManager',
|
||||
'PublicDialogType|get_public_dialog_type' => 'PublicDialogType',
|
||||
'quick_reply_manager[_(-](?![.]get[(][)])|QuickReplyManager' => 'QuickReplyManager',
|
||||
'ReactionListType|[a-z_]*_reaction_list_type' => 'ReactionListType',
|
||||
'reaction_manager[_(-](?![.]get[(][)])|ReactionManager' => 'ReactionManager',
|
||||
'ReactionNotificationSettings' => 'ReactionNotificationSettings',
|
||||
'ReactionNotificationsFrom' => 'ReactionNotificationsFrom',
|
||||
'ReactionType|[a-z_]*_reaction_type' => 'ReactionType',
|
||||
'ReferralProgramInfo' => 'ReferralProgramInfo',
|
||||
'referral_program_manager[_(-](?![.]get[(][)])|ReferralProgramManager' => 'ReferralProgramManager',
|
||||
'ReferralProgramParameters' => 'ReferralProgramParameters',
|
||||
'RequestActor|RequestOnceActor' => 'RequestActor',
|
||||
'saved_messages_manager[_(-](?![.]get[(][)])|SavedMessagesManager' => 'SavedMessagesManager',
|
||||
'ScopeNotificationSettings|[a-z_]*_scope_notification_settings' => 'ScopeNotificationSettings',
|
||||
'SecretChatActor' => 'SecretChatActor',
|
||||
'secret_chats_manager[_(-]|SecretChatsManager' => 'SecretChatsManager',
|
||||
'secure_manager[_(-](?![.]get[(][)])|SecureManager' => 'SecureManager',
|
||||
'SentEmailCode' => 'SentEmailCode',
|
||||
'SharedDialog' => 'SharedDialog',
|
||||
'sponsored_message_manager[_(-](?![.]get[(][)])|SponsoredMessageManager' => 'SponsoredMessageManager',
|
||||
'StarAmount' => 'StarAmount',
|
||||
'StarGift[^A-Z]' => 'StarGift',
|
||||
'StarGiftAttribute' => 'StarGiftAttribute',
|
||||
'StarGiftId' => 'StarGiftId',
|
||||
'star_gift_manager[_(-](?![.]get[(][)])|StarGiftManager' => 'StarGiftManager',
|
||||
'StarGiftSettings' => 'StarGiftSettings',
|
||||
'star_manager[_(-](?![.]get[(][)])|StarManager' => 'StarManager',
|
||||
'StarSubscription[^P]' => 'StarSubscription',
|
||||
'StarSubscriptionPricing' => 'StarSubscriptionPricing',
|
||||
'state_manager[_(-](?![.]get[(][)])|StateManager' => 'StateManager',
|
||||
'statistics_manager[_(-](?![.]get[(][)])|StatisticsManager' => 'StatisticsManager',
|
||||
'StickerSetId' => 'StickerSetId',
|
||||
'stickers_manager[_(-](?![.]get[(][)])|StickersManager' => 'StickersManager',
|
||||
'storage_manager[_(-](?![.]get[(][)])|StorageManager' => 'StorageManager',
|
||||
'StoryId' => 'StoryId',
|
||||
'StoryListId' => 'StoryListId',
|
||||
'story_manager[_(-](?![.]get[(][)])|StoryManager' => 'StoryManager',
|
||||
'SuggestedAction|[a-z_]*_suggested_action' => 'SuggestedAction',
|
||||
'suggested_action_manager[_(-](?![.]get[(][)])|SuggestedActionManager' => 'SuggestedActionManager',
|
||||
'SynchronousRequests' => 'SynchronousRequests',
|
||||
'TargetDialogTypes' => 'TargetDialogTypes',
|
||||
'td_api' => 'td_api',
|
||||
'td_db[(][)]|TdDb[^A-Za-z]' => 'TdDb',
|
||||
'telegram_api' => 'telegram_api',
|
||||
'terms_of_service_manager[_(-](?![.]get[(][)])|TermsOfServiceManager' => 'TermsOfServiceManager',
|
||||
'theme_manager[_(-](?![.]get[(][)])|ThemeManager' => 'ThemeManager',
|
||||
'ThemeSettings' => 'ThemeSettings',
|
||||
'time_zone_manager[_(-](?![.]get[(][)])|TimeZoneManager' => 'TimeZoneManager',
|
||||
'TopDialogCategory|get_top_dialog_category' => 'TopDialogCategory',
|
||||
'top_dialog_manager[_(-](?![.]get[(][)])|TopDialogManager' => 'TopDialogManager',
|
||||
'translation_manager[_(-](?![.]get[(][)])|TranslationManager' => 'TranslationManager',
|
||||
'transcription_manager[_(-](?![.]get[(][)])|TranscriptionManager' => 'TranscriptionManager',
|
||||
'updates_manager[_(-](?![.]get[(][)])|UpdatesManager|get_difference[)]|updateSentMessage|dummyUpdate' => 'UpdatesManager',
|
||||
'UserId' => 'UserId',
|
||||
'user_manager[_(-](?![.]get[(][)])|UserManager([^ ;.]| [^*])' => 'UserManager',
|
||||
'UserStarGift' => 'UserStarGift',
|
||||
'video_notes_manager[_(-](?![.]get[(][)])|VideoNotesManager' => 'VideoNotesManager',
|
||||
'videos_manager[_(-](?![.]get[(][)])|VideosManager' => 'VideosManager',
|
||||
'voice_notes_manager[_(-](?![.]get[(][)])|VoiceNotesManager' => 'VoiceNotesManager',
|
||||
'web_app_manager[_(-](?![.]get[(][)])|WebAppManager' => 'WebAppManager',
|
||||
'WebAppOpenParameters' => 'WebAppOpenParameters',
|
||||
'WebPageId(Hash)?' => 'WebPageId',
|
||||
'web_pages_manager[_(-](?![.]get[(][)])|WebPagesManager' => 'WebPagesManager');
|
||||
|
||||
foreach ($td_methods as $pattern => $header) {
|
||||
if (strpos($cpp_name, $header) !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$include_name = '#include "td/telegram/'.$header.'.h"';
|
||||
if (strpos($new_content, $include_name) !== false && preg_match('/[^a-zA-Z0-9_]('.$pattern.')/', str_replace($include_name, '', $new_content)) === 0) {
|
||||
$new_content = str_replace($include_name, '', $new_content);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file_exists($new_files[$n]) || file_get_contents($new_files[$n]) !== $new_content) {
|
||||
echo "Writing file ".$new_files[$n].PHP_EOL;
|
||||
file_put_contents($new_files[$n], $new_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array('--help', $argv) || in_array('-h', $argv)) {
|
||||
echo "Usage: php SplitSource.php [OPTION]...\n".
|
||||
"Splits some source files to reduce a maximum amount of RAM needed for compiling a single file.\n".
|
||||
" -u, --undo Undo all source code changes.\n".
|
||||
" -h, --help Show this help.\n";
|
||||
exit(2);
|
||||
}
|
||||
|
||||
$undo = in_array('--undo', $argv) || in_array('-u', $argv);
|
||||
$files = array('td/telegram/ChatManager' => 10,
|
||||
'td/telegram/MessagesManager' => 50,
|
||||
'td/telegram/NotificationManager' => 10,
|
||||
'td/telegram/Requests' => 50,
|
||||
'td/telegram/StickersManager' => 10,
|
||||
'td/telegram/StoryManager' => 10,
|
||||
'td/telegram/UpdatesManager' => 10,
|
||||
'td/telegram/UserManager' => 10,
|
||||
'td/generate/auto/td/telegram/td_api' => 10,
|
||||
'td/generate/auto/td/telegram/td_api_json' => 10,
|
||||
'td/generate/auto/td/telegram/telegram_api' => 10);
|
||||
|
||||
foreach ($files as $file => $chunks) {
|
||||
split_file($file, $chunks, $undo);
|
||||
}
|
8
third-party/td/td/TdConfig.cmake
vendored
8
third-party/td/td/TdConfig.cmake
vendored
@ -1,8 +0,0 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
#TODO: write all external dependencies
|
||||
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/TdTargets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/TdTargets.cmake")
|
||||
endif()
|
||||
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/TdStaticTargets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/TdStaticTargets.cmake")
|
||||
endif()
|
93
third-party/td/td/benchmark/CMakeLists.txt
vendored
93
third-party/td/td/benchmark/CMakeLists.txt
vendored
@ -1,93 +0,0 @@
|
||||
if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.10"))
|
||||
message(FATAL_ERROR "CMake >= 3.10 is required")
|
||||
endif()
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
endif()
|
||||
|
||||
# TODO: all benchmarks in one file
|
||||
add_executable(bench_crypto bench_crypto.cpp)
|
||||
target_link_libraries(bench_crypto PRIVATE tdutils ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
|
||||
if (WIN32)
|
||||
if (MINGW)
|
||||
target_link_libraries(bench_crypto PRIVATE ws2_32 mswsock crypt32)
|
||||
else()
|
||||
target_link_libraries(bench_crypto PRIVATE ws2_32 Mswsock Crypt32)
|
||||
endif()
|
||||
endif()
|
||||
target_include_directories(bench_crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
add_executable(bench_actor bench_actor.cpp)
|
||||
target_link_libraries(bench_actor PRIVATE tdactor tdutils)
|
||||
|
||||
add_executable(bench_http bench_http.cpp)
|
||||
target_link_libraries(bench_http PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server bench_http_server.cpp)
|
||||
target_link_libraries(bench_http_server PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server_cheat bench_http_server_cheat.cpp)
|
||||
target_link_libraries(bench_http_server_cheat PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_server_fast bench_http_server_fast.cpp)
|
||||
target_link_libraries(bench_http_server_fast PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_http_reader bench_http_reader.cpp)
|
||||
target_link_libraries(bench_http_reader PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_handshake bench_handshake.cpp)
|
||||
target_link_libraries(bench_handshake PRIVATE tdmtproto tdutils)
|
||||
|
||||
add_executable(bench_db bench_db.cpp)
|
||||
target_link_libraries(bench_db PRIVATE tdactor tddb tdutils)
|
||||
|
||||
add_executable(bench_tddb bench_tddb.cpp)
|
||||
target_link_libraries(bench_tddb PRIVATE tdcore tddb tdutils)
|
||||
|
||||
add_executable(bench_misc bench_misc.cpp)
|
||||
target_link_libraries(bench_misc PRIVATE tdcore tdutils)
|
||||
|
||||
add_executable(check_proxy check_proxy.cpp)
|
||||
target_link_libraries(check_proxy PRIVATE tdclient tdutils)
|
||||
|
||||
add_executable(check_tls check_tls.cpp)
|
||||
target_link_libraries(check_tls PRIVATE tdutils)
|
||||
|
||||
add_executable(rmdir rmdir.cpp)
|
||||
target_link_libraries(rmdir PRIVATE tdutils)
|
||||
|
||||
add_executable(wget wget.cpp)
|
||||
target_link_libraries(wget PRIVATE tdnet tdutils)
|
||||
|
||||
add_executable(bench_empty bench_empty.cpp)
|
||||
target_link_libraries(bench_empty PRIVATE tdutils)
|
||||
|
||||
if (NOT WIN32 AND NOT CYGWIN)
|
||||
add_executable(bench_log bench_log.cpp)
|
||||
target_link_libraries(bench_log PRIVATE tdutils)
|
||||
|
||||
set_source_files_properties(bench_queue.cpp PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
|
||||
add_executable(bench_queue bench_queue.cpp)
|
||||
target_link_libraries(bench_queue PRIVATE tdutils)
|
||||
endif()
|
||||
|
||||
if (TD_TEST_FOLLY AND TD_WITH_ABSEIL)
|
||||
find_package(ABSL QUIET)
|
||||
find_package(folly QUIET)
|
||||
find_package(gflags QUIET)
|
||||
|
||||
if (ABSL_FOUND AND folly_FOUND)
|
||||
add_executable(memory-hashset-memprof EXCLUDE_FROM_ALL hashset_memory.cpp)
|
||||
target_compile_definitions(memory-hashset-memprof PRIVATE USE_MEMPROF=1)
|
||||
target_link_libraries(memory-hashset-memprof PRIVATE tdutils memprof_stat Folly::folly absl::flat_hash_map absl::hash)
|
||||
|
||||
add_executable(memory-hashset-os hashset_memory.cpp)
|
||||
target_compile_definitions(memory-hashset-os PRIVATE USE_MEMPROF=0)
|
||||
target_link_libraries(memory-hashset-os PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
|
||||
|
||||
add_executable(hashmap-build hashmap_build.cpp)
|
||||
target_link_libraries(hashmap-build PRIVATE tdutils Folly::folly absl::flat_hash_map absl::hash)
|
||||
endif()
|
||||
endif()
|
349
third-party/td/td/benchmark/bench_actor.cpp
vendored
349
third-party/td/td/benchmark/bench_actor.cpp
vendored
@ -1,349 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
|
||||
#if TD_MSVC
|
||||
#pragma comment(linker, "/STACK:16777216")
|
||||
#endif
|
||||
|
||||
struct TestActor final : public td::Actor {
|
||||
static td::int32 actor_count_;
|
||||
|
||||
void start_up() final {
|
||||
actor_count_++;
|
||||
stop();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
if (--actor_count_ == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
td::int32 TestActor::actor_count_;
|
||||
|
||||
namespace td {
|
||||
template <>
|
||||
class ActorTraits<TestActor> {
|
||||
public:
|
||||
static constexpr bool need_context = false;
|
||||
static constexpr bool need_start_up = true;
|
||||
};
|
||||
} // namespace td
|
||||
|
||||
class CreateActorBench final : public td::Benchmark {
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "CreateActor";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
scheduler_->create_actor_unsafe<TestActor>(0, "TestActor").release();
|
||||
}
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <int type>
|
||||
class RingBench final : public td::Benchmark {
|
||||
public:
|
||||
struct PassActor;
|
||||
|
||||
private:
|
||||
int actor_n_ = -1;
|
||||
int thread_n_ = -1;
|
||||
td::vector<td::ActorId<PassActor>> actor_array_;
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
static const char *types[] = {"later", "immediate", "raw", "tail", "lambda"};
|
||||
static_assert(0 <= type && type < 5, "");
|
||||
return PSTRING() << "Ring (send_" << types[type] << ") (threads_n = " << thread_n_ << ")";
|
||||
}
|
||||
|
||||
struct PassActor final : public td::Actor {
|
||||
int id = -1;
|
||||
td::ActorId<PassActor> next_actor;
|
||||
int start_n = 0;
|
||||
|
||||
void pass(int n) {
|
||||
// LOG(INFO) << "Pass: " << n;
|
||||
if (n == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
} else {
|
||||
if (type == 0) {
|
||||
send_closure_later(next_actor, &PassActor::pass, n - 1);
|
||||
} else if (type == 1) {
|
||||
send_closure(next_actor, &PassActor::pass, n - 1);
|
||||
} else if (type == 2) {
|
||||
send_event(next_actor, td::Event::raw(static_cast<td::uint32>(n - 1)));
|
||||
} else if (type == 3) {
|
||||
if (n % 5000 == 0) {
|
||||
send_closure_later(next_actor, &PassActor::pass, n - 1);
|
||||
} else {
|
||||
// TODO: it is three times faster than send_event
|
||||
// maybe send event could be further optimized?
|
||||
next_actor.get_actor_unsafe()->raw_event(td::Event::raw(static_cast<td::uint32>(n - 1)).data);
|
||||
}
|
||||
} else if (type == 4) {
|
||||
send_lambda(next_actor, [n, ptr = next_actor.get_actor_unsafe()] { ptr->pass(n - 1); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raw_event(const td::Event::Raw &raw) final {
|
||||
pass(static_cast<int>(raw.u32));
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
yield();
|
||||
}
|
||||
void wakeup() final {
|
||||
if (start_n != 0) {
|
||||
int n = start_n;
|
||||
start_n = 0;
|
||||
pass(n);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RingBench(int actor_n, int thread_n) : actor_n_(actor_n), thread_n_(thread_n) {
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(thread_n_, 0);
|
||||
|
||||
actor_array_ = td::vector<td::ActorId<PassActor>>(actor_n_);
|
||||
for (int i = 0; i < actor_n_; i++) {
|
||||
actor_array_[i] =
|
||||
scheduler_->create_actor_unsafe<PassActor>(thread_n_ ? i % thread_n_ : 0, "PassActor").release();
|
||||
actor_array_[i].get_actor_unsafe()->id = i;
|
||||
}
|
||||
for (int i = 0; i < actor_n_; i++) {
|
||||
actor_array_[i].get_actor_unsafe()->next_actor = actor_array_[(i + 1) % actor_n_];
|
||||
}
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
// first actor is on main_thread
|
||||
actor_array_[0].get_actor_unsafe()->start_n = td::max(n, 100);
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <int type>
|
||||
class QueryBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
static const char *types[] = {"callback", "immediate future", "delayed future", "dummy", "lambda", "lambda_future"};
|
||||
static_assert(0 <= type && type < 6, "");
|
||||
return PSTRING() << "QueryBench: " << types[type];
|
||||
}
|
||||
|
||||
class ClientActor final : public td::Actor {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
Callback() = default;
|
||||
Callback(const Callback &) = delete;
|
||||
Callback &operator=(const Callback &) = delete;
|
||||
Callback(Callback &&) = delete;
|
||||
Callback &operator=(Callback &&) = delete;
|
||||
virtual ~Callback() = default;
|
||||
virtual void on_result(int x) = 0;
|
||||
};
|
||||
explicit ClientActor(td::unique_ptr<Callback> callback) : callback_(std::move(callback)) {
|
||||
}
|
||||
void f(int x) {
|
||||
callback_->on_result(x * x);
|
||||
}
|
||||
void dummy(int x, int *y) {
|
||||
*y = x * x;
|
||||
}
|
||||
void f_immediate_promise(int x, td::PromiseActor<int> &&promise) {
|
||||
promise.set_value(x * x);
|
||||
}
|
||||
void f_promise(td::Promise<> promise) {
|
||||
promise.set_value(td::Unit());
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<Callback> callback_;
|
||||
};
|
||||
|
||||
class ServerActor final : public td::Actor {
|
||||
public:
|
||||
class ClientCallback final : public ClientActor::Callback {
|
||||
public:
|
||||
explicit ClientCallback(td::ActorId<ServerActor> server) : server_(server) {
|
||||
}
|
||||
void on_result(int x) final {
|
||||
send_closure(server_, &ServerActor::on_result, x);
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorId<ServerActor> server_;
|
||||
};
|
||||
void start_up() final {
|
||||
client_ = td::create_actor<ClientActor>("Client", td::make_unique<ClientCallback>(actor_id(this))).release();
|
||||
}
|
||||
|
||||
void on_result(int x) {
|
||||
CHECK(x == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
|
||||
void wakeup() final {
|
||||
while (true) {
|
||||
if (n_ < 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
return;
|
||||
}
|
||||
n_--;
|
||||
if (type == 0) {
|
||||
send_closure(client_, &ClientActor::f, n_);
|
||||
return;
|
||||
} else if (type == 1) {
|
||||
td::PromiseActor<int> promise;
|
||||
td::FutureActor<int> future;
|
||||
init_promise_future(&promise, &future);
|
||||
send_closure(client_, &ClientActor::f_immediate_promise, n_, std::move(promise));
|
||||
CHECK(!future.is_ready());
|
||||
CHECK(!future.empty());
|
||||
CHECK(future.get_state() == td::FutureActor<int>::State::Waiting);
|
||||
// int val = future.move_as_ok();
|
||||
// CHECK(val == n_ * n_);
|
||||
} else if (type == 2) {
|
||||
td::PromiseActor<int> promise;
|
||||
init_promise_future(&promise, &future_);
|
||||
future_.set_event(td::EventCreator::raw(actor_id(), static_cast<td::uint64>(1)));
|
||||
send_closure(client_, &ClientActor::f_immediate_promise, n_, std::move(promise));
|
||||
return;
|
||||
} else if (type == 3) {
|
||||
int res;
|
||||
send_closure(client_, &ClientActor::dummy, n_, &res);
|
||||
} else if (type == 4) {
|
||||
int val = 0;
|
||||
send_lambda(client_, [&] { val = n_ * n_; });
|
||||
CHECK(val == 0 || val == n_ * n_);
|
||||
} else if (type == 5) {
|
||||
send_closure(client_, &ClientActor::f_promise,
|
||||
td::PromiseCreator::lambda([actor_id = actor_id(this), n = n_](td::Unit) {
|
||||
send_closure(actor_id, &ServerActor::result, n * n);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run(int n) {
|
||||
n_ = n;
|
||||
wakeup();
|
||||
}
|
||||
|
||||
void raw_event(const td::Event::Raw &event) final {
|
||||
int val = future_.move_as_ok();
|
||||
CHECK(val == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
void result(int val) {
|
||||
CHECK(val == n_ * n_);
|
||||
wakeup();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorId<ClientActor> client_;
|
||||
int n_ = 0;
|
||||
td::FutureActor<int> future_;
|
||||
};
|
||||
|
||||
void start_up() final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
|
||||
server_ = scheduler_->create_actor_unsafe<ServerActor>(0, "Server");
|
||||
scheduler_->start();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
// first actor is on main_thread
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
send_closure(server_, &ServerActor::run, n);
|
||||
}
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
server_.release();
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
td::ActorOwn<ServerActor> server_;
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::init_openssl_threads();
|
||||
|
||||
bench(CreateActorBench());
|
||||
bench(RingBench<4>(504, 0));
|
||||
bench(RingBench<3>(504, 0));
|
||||
bench(RingBench<0>(504, 0));
|
||||
bench(RingBench<1>(504, 0));
|
||||
bench(RingBench<2>(504, 0));
|
||||
bench(QueryBench<5>());
|
||||
bench(QueryBench<4>());
|
||||
bench(QueryBench<3>());
|
||||
bench(QueryBench<2>());
|
||||
bench(QueryBench<1>());
|
||||
bench(QueryBench<0>());
|
||||
bench(RingBench<3>(504, 0));
|
||||
bench(RingBench<0>(504, 10));
|
||||
bench(RingBench<1>(504, 10));
|
||||
bench(RingBench<2>(504, 10));
|
||||
bench(RingBench<0>(504, 2));
|
||||
bench(RingBench<1>(504, 2));
|
||||
bench(RingBench<2>(504, 2));
|
||||
}
|
528
third-party/td/td/benchmark/bench_crypto.cpp
vendored
528
third-party/td/td/benchmark/bench_crypto.cpp
vendored
@ -1,528 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/UInt.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static constexpr std::size_t DATA_SIZE = 8 << 10;
|
||||
static constexpr std::size_t SHORT_DATA_SIZE = 64;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
|
||||
class SHA1Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA1 OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
unsigned char md[20];
|
||||
SHA1(data, DATA_SIZE, md);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class SHA1ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA1 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[20];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha1(td::Slice(data, SHORT_DATA_SIZE), md);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SHA256ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA256 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[32];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha256(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SHA512ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "SHA512 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[64];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::sha512(td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HmacSha256ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "HMAC-SHA256 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[32];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::hmac_sha256(td::Slice(data, td::min(static_cast<std::size_t>(32), SHORT_DATA_SIZE)),
|
||||
td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 32));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class HmacSha512ShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "HMAC-SHA512 [" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
unsigned char md[64];
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::hmac_sha512(td::Slice(data, td::min(static_cast<std::size_t>(64), SHORT_DATA_SIZE)),
|
||||
td::Slice(data, SHORT_DATA_SIZE), td::MutableSlice(md, 64));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesEcbBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES ECB OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::AesState state;
|
||||
state.init(td::as_slice(key), true);
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i <= n; i++) {
|
||||
size_t step = 16;
|
||||
for (size_t offset = 0; offset + step <= data_slice.size(); offset += step) {
|
||||
state.encrypt(data_slice.ubegin() + offset, data_slice.ubegin() + offset, static_cast<int>(step));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesIgeEncryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL encrypt [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesIgeState state;
|
||||
state.init(as_slice(key), as_slice(iv), true);
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.encrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesIgeDecryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL decrypt [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesIgeState state;
|
||||
state.init(as_slice(key), as_slice(iv), false);
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.decrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesCtrBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CTR OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesCtrState state;
|
||||
state.init(as_slice(key), as_slice(iv));
|
||||
for (int i = 0; i < n; i++) {
|
||||
state.encrypt(data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
class AesCtrOpenSSLBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CTR RAW OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(key.raw, sizeof(key));
|
||||
td::Random::secure_bytes(iv.raw, sizeof(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, key.raw, iv.raw);
|
||||
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
td::AesCtrState state;
|
||||
state.init(as_slice(key), as_slice(iv));
|
||||
for (int i = 0; i < n; i++) {
|
||||
int len = 0;
|
||||
auto int_size = static_cast<int>(DATA_SIZE);
|
||||
EVP_EncryptUpdate(ctx, data_slice.ubegin(), &len, data_slice.ubegin(), int_size);
|
||||
CHECK(len == int_size);
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class AesCbcDecryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CBC Decrypt OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::aes_cbc_decrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AesCbcEncryptBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt128 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES CBC Encrypt OpenSSL [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::aes_cbc_encrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool use_state>
|
||||
class AesIgeShortBench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[SHORT_DATA_SIZE];
|
||||
td::UInt256 key;
|
||||
td::UInt256 iv;
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "AES IGE OpenSSL " << (use_state ? "EVP" : "C ") << "[" << SHORT_DATA_SIZE << "B]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
td::Random::secure_bytes(as_mutable_slice(key));
|
||||
td::Random::secure_bytes(as_mutable_slice(iv));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::MutableSlice data_slice(data, SHORT_DATA_SIZE);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (use_state) {
|
||||
td::AesIgeState ige;
|
||||
ige.init(as_slice(key), as_slice(iv), false);
|
||||
ige.decrypt(data_slice, data_slice);
|
||||
} else {
|
||||
td::aes_ige_decrypt(as_slice(key), as_mutable_slice(iv), data_slice, data_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(Rand, "std_rand") {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= std::rand();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(CppRand, "mt19937_rand") {
|
||||
std::uint_fast32_t res = 0;
|
||||
std::mt19937 g(123);
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= g();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TdRand32, "td_rand_fast32") {
|
||||
td::uint32 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= td::Random::fast_uint32();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TdRandFast, "td_rand_fast") {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res ^= td::Random::fast(0, RAND_MAX);
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
BENCH(SslRand, "ssl_rand_int32") {
|
||||
std::vector<td::thread> v;
|
||||
std::atomic<td::uint32> sum{0};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v.emplace_back([&sum, n] {
|
||||
td::int32 res = 0;
|
||||
for (int j = 0; j < n; j++) {
|
||||
res ^= td::Random::secure_int32();
|
||||
}
|
||||
sum += res;
|
||||
});
|
||||
}
|
||||
for (auto &x : v) {
|
||||
x.join();
|
||||
}
|
||||
v.clear();
|
||||
td::do_not_optimize_away(sum.load());
|
||||
}
|
||||
#endif
|
||||
|
||||
BENCH(SslRandBuf, "ssl_rand_bytes") {
|
||||
td::int32 res = 0;
|
||||
std::array<td::int32, 1000> buf;
|
||||
for (int i = 0; i < n; i += static_cast<int>(buf.size())) {
|
||||
td::Random::secure_bytes(reinterpret_cast<td::uint8 *>(buf.data()), sizeof(buf[0]) * buf.size());
|
||||
for (auto x : buf) {
|
||||
res ^= x;
|
||||
}
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(Pbkdf2, "pbkdf2") {
|
||||
std::string password = "cucumber";
|
||||
std::string salt = "abcdefghijklmnopqrstuvw";
|
||||
std::string key(32, ' ');
|
||||
td::pbkdf2_sha256(password, salt, n, key);
|
||||
}
|
||||
|
||||
class Crc32Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "CRC32 zlib [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::uint64 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::crc32(td::Slice(data, DATA_SIZE));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
};
|
||||
|
||||
class Crc64Bench final : public td::Benchmark {
|
||||
public:
|
||||
alignas(64) unsigned char data[DATA_SIZE];
|
||||
|
||||
std::string get_description() const final {
|
||||
return PSTRING() << "CRC64 Anton [" << (DATA_SIZE >> 10) << "KB]";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(123));
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
td::uint64 res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::crc64(td::Slice(data, DATA_SIZE));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::init_openssl_threads();
|
||||
td::bench(AesCtrBench());
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
td::bench(AesCtrOpenSSLBench());
|
||||
#endif
|
||||
|
||||
td::bench(AesCbcDecryptBench());
|
||||
td::bench(AesCbcEncryptBench());
|
||||
td::bench(AesIgeShortBench<true>());
|
||||
td::bench(AesIgeShortBench<false>());
|
||||
td::bench(AesIgeEncryptBench());
|
||||
td::bench(AesIgeDecryptBench());
|
||||
td::bench(AesEcbBench());
|
||||
|
||||
td::bench(Pbkdf2Bench());
|
||||
td::bench(RandBench());
|
||||
td::bench(CppRandBench());
|
||||
td::bench(TdRand32Bench());
|
||||
td::bench(TdRandFastBench());
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::bench(SslRandBench());
|
||||
#endif
|
||||
td::bench(SslRandBufBench());
|
||||
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
|
||||
td::bench(SHA1Bench());
|
||||
#endif
|
||||
td::bench(SHA1ShortBench());
|
||||
td::bench(SHA256ShortBench());
|
||||
td::bench(SHA512ShortBench());
|
||||
td::bench(HmacSha256ShortBench());
|
||||
td::bench(HmacSha512ShortBench());
|
||||
td::bench(Crc32Bench());
|
||||
td::bench(Crc64Bench());
|
||||
}
|
244
third-party/td/td/benchmark/bench_db.cpp
vendored
244
third-party/td/td/benchmark/bench_db.cpp
vendored
@ -1,244 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/db/binlog/Binlog.h"
|
||||
#include "td/db/binlog/ConcurrentBinlog.h"
|
||||
#include "td/db/BinlogKeyValue.h"
|
||||
#include "td/db/DbKey.h"
|
||||
#include "td/db/SeqKeyValue.h"
|
||||
#include "td/db/SqliteConnectionSafe.h"
|
||||
#include "td/db/SqliteDb.h"
|
||||
#include "td/db/SqliteKeyValueAsync.h"
|
||||
#include "td/db/SqliteKeyValueSafe.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
template <class KeyValueT>
|
||||
class TdKvBench final : public td::Benchmark {
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
td::string name_;
|
||||
|
||||
public:
|
||||
explicit TdKvBench(td::string name) {
|
||||
name_ = std::move(name);
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name_;
|
||||
}
|
||||
|
||||
class Main final : public td::Actor {
|
||||
public:
|
||||
explicit Main(int n) : n_(n) {
|
||||
}
|
||||
|
||||
private:
|
||||
void loop() final {
|
||||
KeyValueT::destroy("test_tddb").ignore();
|
||||
|
||||
class Worker final : public Actor {
|
||||
public:
|
||||
Worker(int n, td::string db_name) : n_(n) {
|
||||
kv_.init(db_name).ensure();
|
||||
}
|
||||
|
||||
private:
|
||||
void loop() final {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
kv_.set(td::to_string(i % 10), td::to_string(i));
|
||||
}
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
int n_;
|
||||
KeyValueT kv_;
|
||||
};
|
||||
td::create_actor_on_scheduler<Worker>("Worker", 0, n_, "test_tddb").release();
|
||||
}
|
||||
int n_;
|
||||
};
|
||||
|
||||
void start_up_n(int n) final {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
scheduler_->create_actor_unsafe<Main>(1, "Main", n).release();
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
scheduler_->start();
|
||||
while (scheduler_->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler_->finish();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
scheduler_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <bool is_encrypted = false>
|
||||
class SqliteKVBench final : public td::Benchmark {
|
||||
td::SqliteDb db;
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "SqliteKV " << td::tag("is_encrypted", is_encrypted);
|
||||
}
|
||||
void start_up() final {
|
||||
td::string path = "testdb.sqlite";
|
||||
td::SqliteDb::destroy(path).ignore();
|
||||
if (is_encrypted) {
|
||||
db = td::SqliteDb::change_key(path, true, td::DbKey::password("cucumber"), td::DbKey::empty()).move_as_ok();
|
||||
} else {
|
||||
db = td::SqliteDb::open_with_key(path, true, td::DbKey::empty()).move_as_ok();
|
||||
}
|
||||
db.exec("PRAGMA encoding=\"UTF-8\"").ensure();
|
||||
db.exec("PRAGMA synchronous=NORMAL").ensure();
|
||||
db.exec("PRAGMA journal_mode=WAL").ensure();
|
||||
db.exec("PRAGMA temp_store=MEMORY").ensure();
|
||||
db.exec("DROP TABLE IF EXISTS KV").ensure();
|
||||
db.exec("CREATE TABLE IF NOT EXISTS KV (k BLOB PRIMARY KEY, v BLOB)").ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto stmt = db.get_statement("REPLACE INTO KV (k, v) VALUES(?1, ?2)").move_as_ok();
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto key = td::to_string(i % 10);
|
||||
auto value = td::to_string(i);
|
||||
stmt.bind_blob(1, key).ensure();
|
||||
stmt.bind_blob(2, value).ensure();
|
||||
stmt.step().ensure();
|
||||
CHECK(!stmt.can_step());
|
||||
stmt.reset();
|
||||
|
||||
if (i % 10 == 0) {
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
}
|
||||
}
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
}
|
||||
};
|
||||
|
||||
static td::Status init_db(td::SqliteDb &db) {
|
||||
TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\""));
|
||||
TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
|
||||
|
||||
TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY"));
|
||||
// TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
class SqliteKeyValueAsyncBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "SqliteKeyValueAsync";
|
||||
}
|
||||
void start_up() final {
|
||||
do_start_up().ensure();
|
||||
scheduler_->start();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto key = td::to_string(i % 10);
|
||||
auto value = td::to_string(i);
|
||||
sqlite_kv_async_->set(key, value, td::Auto());
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
scheduler_->run_main(0.1);
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
sqlite_kv_async_.reset();
|
||||
sqlite_kv_safe_.reset();
|
||||
sql_connection_->close_and_destroy();
|
||||
}
|
||||
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
|
||||
std::shared_ptr<td::SqliteKeyValueSafe> sqlite_kv_safe_;
|
||||
td::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
|
||||
|
||||
td::Status do_start_up() {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
td::string sql_db_name = "testdb.sqlite";
|
||||
td::SqliteDb::destroy(sql_db_name).ignore();
|
||||
td::SqliteDb::open_with_key(sql_db_name, true, td::DbKey::empty()).move_as_ok();
|
||||
|
||||
sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name, td::DbKey::empty());
|
||||
auto &db = sql_connection_->get();
|
||||
TRY_STATUS(init_db(db));
|
||||
|
||||
sqlite_kv_safe_ = std::make_shared<td::SqliteKeyValueSafe>("common", sql_connection_);
|
||||
sqlite_kv_async_ = create_sqlite_key_value_async(sqlite_kv_safe_, 0);
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
class SeqKvBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "SeqKvBench";
|
||||
}
|
||||
|
||||
td::SeqKeyValue kv;
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
kv.set(PSLICE() << i % 10, PSLICE() << i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <bool is_encrypted = false>
|
||||
class BinlogKeyValueBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "BinlogKeyValue " << td::tag("is_encrypted", is_encrypted);
|
||||
}
|
||||
|
||||
td::BinlogKeyValue<td::Binlog> kv;
|
||||
void start_up() final {
|
||||
td::SqliteDb::destroy("test_binlog").ignore();
|
||||
kv.init("test_binlog", is_encrypted ? td::DbKey::password("cucumber") : td::DbKey::empty()).ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
kv.set(td::to_string(i % 10), td::to_string(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
bench(TdKvBench<td::BinlogKeyValue<td::Binlog>>("BinlogKeyValue<Binlog>"));
|
||||
bench(TdKvBench<td::BinlogKeyValue<td::ConcurrentBinlog>>("BinlogKeyValue<ConcurrentBinlog>"));
|
||||
|
||||
bench(BinlogKeyValueBench<true>());
|
||||
bench(BinlogKeyValueBench<false>());
|
||||
bench(SqliteKVBench<false>());
|
||||
bench(SqliteKVBench<true>());
|
||||
bench(SqliteKeyValueAsyncBench());
|
||||
bench(SeqKvBench());
|
||||
}
|
8
third-party/td/td/benchmark/bench_empty.cpp
vendored
8
third-party/td/td/benchmark/bench_empty.cpp
vendored
@ -1,8 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
int main() {
|
||||
}
|
72
third-party/td/td/benchmark/bench_handshake.cpp
vendored
72
third-party/td/td/benchmark/bench_handshake.cpp
vendored
@ -1,72 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/mtproto/DhCallback.h"
|
||||
#include "td/mtproto/DhHandshake.h"
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
static td::int32 g = 3;
|
||||
static td::string prime_base64 =
|
||||
"xxyuucaxyQSObFIvcPE_c5gNQCOOPiHBSTTQN1Y9kw9IGYoKp8FAWCKUk9IlMPTb-jNvbgrJJROVQ67UTM58NyD9UfaUWHBaxozU_mtrE6vcl0ZRKW"
|
||||
"kyhFTxj6-MWV9kJHf-lrsqlB1bzR1KyMxJiAcI-ps3jjxPOpBgvuZ8-aSkppWBEFGQfhYnU7VrD2tBDbp02KhLKhSzFE4O8ShHVP0X7ZUNWWW0ud1G"
|
||||
"WC2xF40WnGvEZbDW_5yjko_vW5rk5Bj8Feg-vqD4f6n_Xu1wBQ3tKEn0e_lZ2VaFDOkphR8NgRX2NbEF7i5OFdBLJFS_b0-t8DSxBAMRnNjjuS_MW"
|
||||
"w";
|
||||
|
||||
class HandshakeBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "Handshake";
|
||||
}
|
||||
|
||||
class FakeDhCallback final : public td::mtproto::DhCallback {
|
||||
public:
|
||||
int is_good_prime(td::Slice prime_str) const final {
|
||||
auto it = cache.find(prime_str.str());
|
||||
if (it == cache.end()) {
|
||||
return -1;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
void add_good_prime(td::Slice prime_str) const final {
|
||||
cache[prime_str.str()] = 1;
|
||||
}
|
||||
void add_bad_prime(td::Slice prime_str) const final {
|
||||
cache[prime_str.str()] = 0;
|
||||
}
|
||||
mutable std::map<td::string, int> cache;
|
||||
} dh_callback;
|
||||
|
||||
void run(int n) final {
|
||||
td::mtproto::DhHandshake a;
|
||||
td::mtproto::DhHandshake b;
|
||||
auto prime = td::base64url_decode(prime_base64).move_as_ok();
|
||||
td::mtproto::DhHandshake::check_config(g, prime, &dh_callback).ensure();
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
a.set_config(g, prime);
|
||||
b.set_config(g, prime);
|
||||
b.set_g_a(a.get_g_b());
|
||||
a.set_g_a(b.get_g_b());
|
||||
a.run_checks(true, &dh_callback).ensure();
|
||||
b.run_checks(true, &dh_callback).ensure();
|
||||
auto a_key = a.gen_key();
|
||||
auto b_key = b.gen_key();
|
||||
CHECK(a_key.first == b_key.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::bench(HandshakeBench());
|
||||
}
|
77
third-party/td/td/benchmark/bench_http.cpp
vendored
77
third-party/td/td/benchmark/bench_http.cpp
vendored
@ -1,77 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpOutboundConnection.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/SslStream.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
|
||||
std::atomic<int> counter;
|
||||
|
||||
class HttpClient final : public td::HttpOutboundConnection::Callback {
|
||||
void start_up() final {
|
||||
td::IPAddress addr;
|
||||
addr.init_ipv4_port("127.0.0.1", 8082).ensure();
|
||||
auto fd = td::SocketFd::open(addr);
|
||||
LOG_CHECK(fd.is_ok()) << fd.error();
|
||||
connection_ = td::create_actor<td::HttpOutboundConnection>(
|
||||
"Connect", td::BufferedFd<td::SocketFd>(fd.move_as_ok()), td::SslStream{}, std::numeric_limits<size_t>::max(),
|
||||
0, 0, td::ActorOwn<td::HttpOutboundConnection::Callback>(actor_id(this)));
|
||||
yield();
|
||||
cnt_ = 100000;
|
||||
counter++;
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
if (--counter == 0) {
|
||||
td::Scheduler::instance()->finish();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
if (cnt_-- < 0) {
|
||||
return stop();
|
||||
}
|
||||
send_closure(connection_, &td::HttpOutboundConnection::write_next, td::BufferSlice("GET / HTTP/1.1\r\n\r\n"));
|
||||
send_closure(connection_, &td::HttpOutboundConnection::write_ok);
|
||||
LOG(INFO) << "SEND";
|
||||
}
|
||||
|
||||
void handle(td::unique_ptr<td::HttpQuery> result) final {
|
||||
loop();
|
||||
}
|
||||
|
||||
void on_connection_error(td::Status error) final {
|
||||
LOG(ERROR) << "ERROR: " << error;
|
||||
}
|
||||
|
||||
td::ActorOwn<td::HttpOutboundConnection> connection_;
|
||||
int cnt_ = 0;
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler->create_actor_unsafe<HttpClient>(0, "Client1").release();
|
||||
scheduler->create_actor_unsafe<HttpClient>(0, "Client2").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
119
third-party/td/td/benchmark/bench_http_reader.cpp
vendored
119
third-party/td/td/benchmark/bench_http_reader.cpp
vendored
@ -1,119 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/HttpReader.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/find_boundary.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
static std::string http_query = "GET / HTTP/1.1\r\nConnection:keep-alive\r\nhost:127.0.0.1:8080\r\n\r\n";
|
||||
static const size_t block_size = 2500;
|
||||
|
||||
class HttpReaderBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "HttpReaderBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
td::HttpQuery q;
|
||||
int parsed = 0;
|
||||
int sent = 0;
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
sent++;
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
while (true) {
|
||||
auto wait = http_reader_.read_next(&q).ok();
|
||||
if (wait != 0) {
|
||||
break;
|
||||
}
|
||||
parsed++;
|
||||
}
|
||||
}
|
||||
CHECK(parsed == sent);
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
http_reader_.init(&reader_, 10000, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class BufferBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "BufferBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
auto result = reader_.cut_head(http_query.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
}
|
||||
};
|
||||
|
||||
class FindBoundaryBench final : public td::Benchmark {
|
||||
std::string get_description() const final {
|
||||
return "FindBoundaryBench";
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
auto cnt = static_cast<int>(block_size / http_query.size());
|
||||
for (int i = 0; i < n; i += cnt) {
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
writer_.append(http_query);
|
||||
}
|
||||
reader_.sync_with_writer();
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
size_t len = 0;
|
||||
find_boundary(reader_.clone(), "\r\n\r\n", len);
|
||||
CHECK(size_t(len) + 4 == http_query.size());
|
||||
auto result = reader_.cut_head(len + 2);
|
||||
reader_.advance(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
td::ChainBufferWriter writer_;
|
||||
td::ChainBufferReader reader_;
|
||||
td::HttpReader http_reader_;
|
||||
|
||||
void start_up() final {
|
||||
writer_ = {};
|
||||
reader_ = writer_.extract_reader();
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
td::bench(BufferBench());
|
||||
td::bench(FindBoundaryBench());
|
||||
td::bench(HttpReaderBench());
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/HttpInboundConnection.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
class HelloWorld final : public td::HttpInboundConnection::Callback {
|
||||
public:
|
||||
void handle(td::unique_ptr<td::HttpQuery> query, td::ActorOwn<td::HttpInboundConnection> connection) final {
|
||||
// LOG(ERROR) << *query;
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
|
||||
auto res = hc.finish(content);
|
||||
LOG_IF(FATAL, res.is_error()) << res.error();
|
||||
send_closure(connection, &td::HttpInboundConnection::write_next, td::BufferSlice(res.ok()));
|
||||
send_closure(connection.release(), &td::HttpInboundConnection::write_ok);
|
||||
}
|
||||
void hangup() final {
|
||||
LOG(ERROR) << "CLOSE " << cnt--;
|
||||
stop();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 0;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
LOG(ERROR) << "ACCEPT " << cnt++;
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<td::HttpInboundConnection>(
|
||||
"HttpInboundConnection", scheduler_id, td::BufferedFd<td::SocketFd>(std::move(fd)), 1024 * 1024, 0, 0,
|
||||
td::create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id))
|
||||
.release();
|
||||
}
|
||||
void hangup() final {
|
||||
// may be it should be default?..
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
static int cnt = 0;
|
||||
|
||||
class HelloWorld final : public td::Actor {
|
||||
public:
|
||||
explicit HelloWorld(td::SocketFd socket_fd) : socket_fd_(std::move(socket_fd)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::SocketFd socket_fd_;
|
||||
|
||||
std::array<char, 1024> read_buf;
|
||||
size_t read_new_lines{0};
|
||||
|
||||
td::string hello_;
|
||||
td::string write_buf_;
|
||||
size_t write_pos_{0};
|
||||
|
||||
void start_up() final {
|
||||
td::Scheduler::subscribe(socket_fd_.get_poll_info().extract_pollable_fd(this));
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
hello_ = hc.finish(content).ok().str();
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
auto status = do_loop();
|
||||
if (status.is_error()) {
|
||||
td::Scheduler::unsubscribe(socket_fd_.get_poll_info().get_pollable_fd_ref());
|
||||
stop();
|
||||
LOG(ERROR) << "CLOSE: " << status;
|
||||
}
|
||||
}
|
||||
td::Status do_loop() {
|
||||
sync_with_poll(socket_fd_);
|
||||
TRY_STATUS(read_loop());
|
||||
TRY_STATUS(write_loop());
|
||||
if (can_close_local(socket_fd_)) {
|
||||
return td::Status::Error("CLOSE");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status write_loop() {
|
||||
while (can_write_local(socket_fd_) && write_pos_ < write_buf_.size()) {
|
||||
TRY_RESULT(written, socket_fd_.write(td::Slice(write_buf_).substr(write_pos_)));
|
||||
write_pos_ += written;
|
||||
if (write_pos_ == write_buf_.size()) {
|
||||
write_pos_ = 0;
|
||||
write_buf_.clear();
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status read_loop() {
|
||||
while (can_read_local(socket_fd_)) {
|
||||
TRY_RESULT(read_size, socket_fd_.read(td::MutableSlice(read_buf.data(), read_buf.size())));
|
||||
for (size_t i = 0; i < read_size; i++) {
|
||||
if (read_buf[i] == '\n') {
|
||||
read_new_lines++;
|
||||
if (read_new_lines == 2) {
|
||||
read_new_lines = 0;
|
||||
write_buf_.append(hello_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 0;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
LOG(ERROR) << "ACCEPT " << cnt++;
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id, std::move(fd)).release();
|
||||
}
|
||||
void hangup() final {
|
||||
// may be it should be default?..
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpHeaderCreator.h"
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/HttpReader.h"
|
||||
#include "td/net/TcpListener.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/BufferedFd.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
class HttpEchoConnection final : public td::Actor {
|
||||
public:
|
||||
explicit HttpEchoConnection(td::SocketFd fd) : fd_(std::move(fd)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::BufferedFd<td::SocketFd> fd_;
|
||||
td::HttpReader reader_;
|
||||
td::HttpQuery query_;
|
||||
void start_up() final {
|
||||
td::Scheduler::subscribe(fd_.get_poll_info().extract_pollable_fd(this));
|
||||
reader_.init(&fd_.input_buffer(), 1024 * 1024, 0);
|
||||
}
|
||||
void tear_down() final {
|
||||
td::Scheduler::unsubscribe_before_close(fd_.get_poll_info().get_pollable_fd_ref());
|
||||
fd_.close();
|
||||
}
|
||||
|
||||
void handle_query() {
|
||||
query_ = td::HttpQuery();
|
||||
td::HttpHeaderCreator hc;
|
||||
td::Slice content = "hello world";
|
||||
//auto content = td::BufferSlice("hello world");
|
||||
hc.init_ok();
|
||||
hc.set_keep_alive();
|
||||
hc.set_content_size(content.size());
|
||||
hc.add_header("Server", "TDLib/test");
|
||||
hc.add_header("Date", "Thu Dec 14 01:41:50 2017");
|
||||
hc.add_header("Content-Type:", "text/html");
|
||||
auto res = hc.finish(content);
|
||||
fd_.output_buffer().append(res.ok());
|
||||
}
|
||||
|
||||
void loop() final {
|
||||
sync_with_poll(fd_);
|
||||
auto status = [&] {
|
||||
TRY_STATUS(loop_read());
|
||||
TRY_STATUS(loop_write());
|
||||
return td::Status::OK();
|
||||
}();
|
||||
if (status.is_error() || can_close_local(fd_)) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
td::Status loop_read() {
|
||||
TRY_STATUS(fd_.flush_read());
|
||||
while (true) {
|
||||
TRY_RESULT(need, reader_.read_next(&query_));
|
||||
if (need == 0) {
|
||||
handle_query();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
td::Status loop_write() {
|
||||
TRY_STATUS(fd_.flush_write());
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
const int N = 8;
|
||||
class Server final : public td::TcpListener::Callback {
|
||||
public:
|
||||
void start_up() final {
|
||||
listener_ =
|
||||
td::create_actor<td::TcpListener>("Listener", 8082, td::ActorOwn<td::TcpListener::Callback>(actor_id(this)));
|
||||
}
|
||||
void accept(td::SocketFd fd) final {
|
||||
pos_++;
|
||||
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
|
||||
td::create_actor_on_scheduler<HttpEchoConnection>("HttpEchoConnection", scheduler_id, std::move(fd)).release();
|
||||
}
|
||||
void hangup() final {
|
||||
LOG(ERROR) << "Hanging up..";
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::ActorOwn<td::TcpListener> listener_;
|
||||
int pos_{0};
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(N, 0);
|
||||
scheduler->create_actor_unsafe<Server>(0, "Server").release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
160
third-party/td/td/benchmark/bench_log.cpp
vendored
160
third-party/td/td/benchmark/bench_log.cpp
vendored
@ -1,160 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
std::string create_tmp_file() {
|
||||
#if TD_ANDROID
|
||||
std::string name = "/data/local/tmp/large_file.txt";
|
||||
unlink(name.c_str());
|
||||
return name;
|
||||
#else
|
||||
char file_name[] = "largefileXXXXXX";
|
||||
int fd = mkstemp(file_name);
|
||||
if (fd == -1) {
|
||||
perror("Can't cretate temporary file");
|
||||
}
|
||||
CHECK(fd != -1);
|
||||
|
||||
close(fd);
|
||||
return file_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
class IostreamWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
std::ofstream stream;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "ostream (to file, no buf, no flush)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
stream.open(file_name_.c_str());
|
||||
CHECK(stream.is_open());
|
||||
// stream.rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
stream << "This is just for test" << 987654321 << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
stream.close();
|
||||
unlink(file_name_.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class FILEWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
FILE *file;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "std::fprintf (to file, no buf, no flush)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
file = fopen(file_name_.c_str(), "w");
|
||||
// setvbuf(file, buffer, _IOFBF, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
std::fprintf(file, "This is just for test%d\n", 987654321);
|
||||
// std::fflush(file);
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
std::fclose(file);
|
||||
unlink(file_name_.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_ANDROID
|
||||
#include <android/log.h>
|
||||
#define ALOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "XXX", __VA_ARGS__)
|
||||
class ALogWriteBench final : public td::Benchmark {
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "android_log";
|
||||
}
|
||||
void start_up() final {
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
ALOG("This is just for test%d\n", 987654321);
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class LogWriteBench final : public td::Benchmark {
|
||||
protected:
|
||||
std::string file_name_;
|
||||
std::ofstream stream;
|
||||
std::streambuf *old_buf;
|
||||
static constexpr std::size_t BUFFER_SIZE = 1 << 20;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
std::string get_description() const final {
|
||||
return "td_log (slow in debug mode)";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
file_name_ = create_tmp_file();
|
||||
stream.open(file_name_.c_str());
|
||||
CHECK(stream.is_open());
|
||||
old_buf = std::cerr.rdbuf(stream.rdbuf());
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
LOG(DEBUG) << "This is just for test" << 987654321;
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
stream.close();
|
||||
unlink(file_name_.c_str());
|
||||
std::cerr.rdbuf(old_buf);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
td::bench(LogWriteBench());
|
||||
#if TD_ANDROID
|
||||
td::bench(ALogWriteBench());
|
||||
#endif
|
||||
td::bench(IostreamWriteBench());
|
||||
td::bench(FILEWriteBench());
|
||||
}
|
841
third-party/td/td/benchmark/bench_misc.cpp
vendored
841
third-party/td/td/benchmark/bench_misc.cpp
vendored
@ -1,841 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/telegram/td_api.h"
|
||||
#include "td/telegram/telegram_api.h"
|
||||
#include "td/telegram/telegram_api.hpp"
|
||||
|
||||
#include "td/utils/algorithm.h"
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/Clocks.h"
|
||||
#include "td/utils/port/EventFd.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/port/RwMutex.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/ThreadSafeCounter.h"
|
||||
|
||||
#if !TD_WINDOWS
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
class F {
|
||||
td::uint32 ∑
|
||||
|
||||
public:
|
||||
explicit F(td::uint32 &sum) : sum(sum) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void operator()(const T &x) const {
|
||||
sum += static_cast<td::uint32>(reinterpret_cast<std::uintptr_t>(&x));
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(TlCall, "TL Call") {
|
||||
td::tl_object_ptr<td::telegram_api::Function> x = td::make_tl_object<td::telegram_api::account_getWallPapers>(0);
|
||||
td::uint32 res = 0;
|
||||
F f(res);
|
||||
for (int i = 0; i < n; i++) {
|
||||
downcast_call(*x, f);
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
static td::td_api::object_ptr<td::td_api::file> get_file_object() {
|
||||
return td::td_api::make_object<td::td_api::file>(
|
||||
12345, 123456, 123456,
|
||||
td::td_api::make_object<td::td_api::localFile>(
|
||||
"/android/data/0/data/org.telegram.data/files/photos/12345678901234567890_123.jpg", true, true, false, true,
|
||||
0, 123456, 123456),
|
||||
td::td_api::make_object<td::td_api::remoteFile>("abacabadabacabaeabacabadabacabafabacabadabacabaeabacabadabacaba",
|
||||
"abacabadabacabaeabacabadabacaba", false, true, 123456));
|
||||
}
|
||||
|
||||
BENCH(ToStringIntSmall, "to_string<int> small") {
|
||||
auto buf = td::StackAllocator::alloc(1000);
|
||||
td::StringBuilder sb(buf.as_slice());
|
||||
for (int i = 0; i < n; i++) {
|
||||
sb << td::Random::fast(0, 100);
|
||||
sb.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(ToStringIntBig, "to_string<int> big") {
|
||||
auto buf = td::StackAllocator::alloc(1000);
|
||||
td::StringBuilder sb(buf.as_slice());
|
||||
for (int i = 0; i < n; i++) {
|
||||
sb << 1234567890;
|
||||
sb.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(TlToStringUpdateFile, "TL to_string updateFile") {
|
||||
auto x = td::td_api::make_object<td::td_api::updateFile>(get_file_object());
|
||||
|
||||
std::size_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += to_string(x).size();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(TlToStringMessage, "TL to_string message") {
|
||||
auto x = td::td_api::make_object<td::td_api::message>();
|
||||
x->id_ = 123456000111;
|
||||
x->sender_id_ = td::td_api::make_object<td::td_api::messageSenderUser>(123456000112);
|
||||
x->chat_id_ = 123456000112;
|
||||
x->sending_state_ = td::td_api::make_object<td::td_api::messageSendingStatePending>(0);
|
||||
x->date_ = 1699999999;
|
||||
auto photo = td::td_api::make_object<td::td_api::photo>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
photo->sizes_.push_back(td::td_api::make_object<td::td_api::photoSize>(
|
||||
"a", get_file_object(), 160, 160,
|
||||
td::vector<td::int32>{10000, 20000, 30000, 50000, 70000, 90000, 120000, 150000, 180000, 220000}));
|
||||
}
|
||||
x->content_ = td::td_api::make_object<td::td_api::messagePhoto>(
|
||||
std::move(photo), td::td_api::make_object<td::td_api::formattedText>(), false, false, false);
|
||||
|
||||
std::size_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += to_string(x).size();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
BENCH(EventFd, "EventFd") {
|
||||
td::EventFd fd;
|
||||
fd.init();
|
||||
for (int i = 0; i < n; i++) {
|
||||
fd.release();
|
||||
fd.acquire();
|
||||
}
|
||||
fd.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
BENCH(NewInt, "new int + delete") {
|
||||
std::uintptr_t res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int *x = new int;
|
||||
res += reinterpret_cast<std::uintptr_t>(x);
|
||||
delete x;
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(NewObj, "new struct, then delete") {
|
||||
struct A {
|
||||
td::int32 a = 0;
|
||||
td::int32 b = 0;
|
||||
td::int32 c = 0;
|
||||
td::int32 d = 0;
|
||||
};
|
||||
std::uintptr_t res = 0;
|
||||
A **ptr = new A *[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
ptr[i] = new A();
|
||||
res += reinterpret_cast<std::uintptr_t>(ptr[i]);
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
delete ptr[i];
|
||||
}
|
||||
delete[] ptr;
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
BENCH(ThreadNew, "new struct, then delete in 2 threads") {
|
||||
NewObjBench a;
|
||||
NewObjBench b;
|
||||
td::thread ta([&] { a.run(n / 2); });
|
||||
td::thread tb([&] { b.run(n - n / 2); });
|
||||
ta.join();
|
||||
tb.join();
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
// Too hard for clang (?)
|
||||
BENCH(Time, "Clocks::monotonic") {
|
||||
double res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += td::Clocks::monotonic();
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
*/
|
||||
#if !TD_WINDOWS
|
||||
class PipeBench final : public td::Benchmark {
|
||||
public:
|
||||
int p[2];
|
||||
|
||||
td::string get_description() const final {
|
||||
return "pipe write + read int32";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
int res = pipe(p);
|
||||
CHECK(res == 0);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int val = 1;
|
||||
auto write_len = write(p[1], &val, sizeof(val));
|
||||
CHECK(write_len == sizeof(val));
|
||||
auto read_len = read(p[0], &val, sizeof(val));
|
||||
CHECK(read_len == sizeof(val));
|
||||
res += val;
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
class SemBench final : public td::Benchmark {
|
||||
sem_t sem;
|
||||
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "sem post + wait";
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
int err = sem_init(&sem, 0, 0);
|
||||
CHECK(err != -1);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sem_post(&sem);
|
||||
sem_wait(&sem);
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !TD_WINDOWS
|
||||
class UtimeBench final : public td::Benchmark {
|
||||
public:
|
||||
void start_up() final {
|
||||
td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok().close();
|
||||
}
|
||||
td::string get_description() const final {
|
||||
return "utime";
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
int err = utime("test", nullptr);
|
||||
CHECK(err >= 0);
|
||||
utimbuf buf;
|
||||
buf.modtime = 123;
|
||||
buf.actime = 321;
|
||||
err = utime("test", &buf);
|
||||
CHECK(err >= 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
BENCH(Pwrite, "pwrite") {
|
||||
auto fd = td::FileFd::open("test", td::FileFd::Create | td::FileFd::Write).move_as_ok();
|
||||
for (int i = 0; i < n; i++) {
|
||||
fd.pwrite("a", 0).ok();
|
||||
}
|
||||
fd.close();
|
||||
}
|
||||
|
||||
class CreateFileBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "create_file";
|
||||
}
|
||||
void start_up() final {
|
||||
td::mkdir("A").ensure();
|
||||
}
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
td::rmrf("A/").ignore();
|
||||
}
|
||||
};
|
||||
|
||||
class WalkPathBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return "walk_path";
|
||||
}
|
||||
void start_up_n(int n) final {
|
||||
td::mkdir("A").ensure();
|
||||
for (int i = 0; i < n; i++) {
|
||||
td::FileFd::open(PSLICE() << "A/" << i, td::FileFd::Write | td::FileFd::Create).move_as_ok().close();
|
||||
}
|
||||
}
|
||||
void run(int n) final {
|
||||
int cnt = 0;
|
||||
td::walk_path("A/", [&](td::CSlice path, auto type) {
|
||||
if (type == td::WalkPath::Type::EnterDir) {
|
||||
return;
|
||||
}
|
||||
td::stat(path).ok();
|
||||
cnt++;
|
||||
}).ignore();
|
||||
}
|
||||
void tear_down() final {
|
||||
td::rmrf("A/").ignore();
|
||||
}
|
||||
};
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
template <int ThreadN = 2>
|
||||
class AtomicReleaseIncBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicReleaseInc" << ThreadN;
|
||||
}
|
||||
|
||||
static std::atomic<td::uint64> a_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
a_.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
template <int ThreadN>
|
||||
std::atomic<td::uint64> AtomicReleaseIncBench<ThreadN>::a_;
|
||||
|
||||
template <int ThreadN = 2>
|
||||
class AtomicReleaseCasIncBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
|
||||
}
|
||||
|
||||
static std::atomic<td::uint64> a_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
auto value = a_.load(std::memory_order_relaxed);
|
||||
while (!a_.compare_exchange_strong(value, value + 1, std::memory_order_release, std::memory_order_relaxed)) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
template <int ThreadN>
|
||||
std::atomic<td::uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
|
||||
|
||||
template <int ThreadN>
|
||||
class RwMutexReadBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "RwMutexRead" << ThreadN;
|
||||
}
|
||||
td::RwMutex mutex_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
auto lock = mutex_.lock_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <int ThreadN>
|
||||
class RwMutexWriteBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "RwMutexWrite" << ThreadN;
|
||||
}
|
||||
td::RwMutex mutex_;
|
||||
void run(int n) final {
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < ThreadN; i++) {
|
||||
threads.emplace_back([&] {
|
||||
for (int i = 0; i < n / ThreadN; i++) {
|
||||
auto lock = mutex_.lock_write();
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadSafeCounterBench final : public td::Benchmark {
|
||||
static td::ThreadSafeCounter counter_;
|
||||
int thread_count_;
|
||||
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "ThreadSafeCounter" << thread_count_;
|
||||
}
|
||||
void run(int n) final {
|
||||
counter_.clear();
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < thread_count_; i++) {
|
||||
threads.emplace_back([n] {
|
||||
for (int i = 0; i < n; i++) {
|
||||
counter_.add(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
CHECK(counter_.sum() == n * thread_count_);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ThreadSafeCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||
}
|
||||
};
|
||||
td::ThreadSafeCounter ThreadSafeCounterBench::counter_;
|
||||
|
||||
template <bool StrictOrder>
|
||||
class AtomicCounterBench final : public td::Benchmark {
|
||||
static std::atomic<td::int64> counter_;
|
||||
int thread_count_;
|
||||
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "AtomicCounter" << thread_count_;
|
||||
}
|
||||
void run(int n) final {
|
||||
counter_.store(0);
|
||||
td::vector<td::thread> threads;
|
||||
for (int i = 0; i < thread_count_; i++) {
|
||||
threads.emplace_back([n] {
|
||||
for (int i = 0; i < n; i++) {
|
||||
counter_.fetch_add(1, StrictOrder ? std::memory_order_seq_cst : std::memory_order_relaxed);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
CHECK(counter_.load() == n * thread_count_);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit AtomicCounterBench(int thread_count) : thread_count_(thread_count) {
|
||||
}
|
||||
};
|
||||
template <bool StrictOrder>
|
||||
std::atomic<td::int64> AtomicCounterBench<StrictOrder>::counter_;
|
||||
|
||||
#endif
|
||||
|
||||
class IdDuplicateCheckerOld {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "Old";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS) {
|
||||
auto oldest_message_id = *saved_message_ids_.begin();
|
||||
if (message_id < oldest_message_id) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << oldest_message_id);
|
||||
}
|
||||
}
|
||||
if (saved_message_ids_.count(message_id) != 0) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
|
||||
saved_message_ids_.insert(message_id);
|
||||
if (saved_message_ids_.size() > MAX_SAVED_MESSAGE_IDS) {
|
||||
saved_message_ids_.erase(saved_message_ids_.begin());
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
template <size_t MAX_SAVED_MESSAGE_IDS>
|
||||
class IdDuplicateCheckerNew {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return PSTRING() << "New" << MAX_SAVED_MESSAGE_IDS;
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
auto insert_result = saved_message_ids_.insert(message_id);
|
||||
if (!insert_result.second) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = begin_it == insert_result.first;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << *saved_message_ids_.begin());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
class IdDuplicateCheckerNewOther {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "NewOther";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (!saved_message_ids_.insert(message_id).second) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = *begin_it == message_id;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << *saved_message_ids_.begin());
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
class IdDuplicateCheckerNewSimple {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return "NewSimple";
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
auto insert_result = saved_message_ids_.insert(message_id);
|
||||
if (!insert_result.second) {
|
||||
return td::Status::Error(1, "Ignore already processed message");
|
||||
}
|
||||
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS + 1) {
|
||||
auto begin_it = saved_message_ids_.begin();
|
||||
bool is_very_old = begin_it == insert_result.first;
|
||||
saved_message_ids_.erase(begin_it);
|
||||
if (is_very_old) {
|
||||
return td::Status::Error(2, "Ignore very old message");
|
||||
}
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
|
||||
std::set<td::uint64> saved_message_ids_;
|
||||
};
|
||||
|
||||
template <size_t max_size>
|
||||
class IdDuplicateCheckerArray {
|
||||
public:
|
||||
static td::string get_description() {
|
||||
return PSTRING() << "Array" << max_size;
|
||||
}
|
||||
td::Status check(td::uint64 message_id) {
|
||||
if (end_pos_ == 2 * max_size) {
|
||||
std::copy_n(&saved_message_ids_[max_size], max_size, &saved_message_ids_[0]);
|
||||
end_pos_ = max_size;
|
||||
}
|
||||
if (end_pos_ == 0 || message_id > saved_message_ids_[end_pos_ - 1]) {
|
||||
// fast path
|
||||
saved_message_ids_[end_pos_++] = message_id;
|
||||
return td::Status::OK();
|
||||
}
|
||||
if (end_pos_ >= max_size && message_id < saved_message_ids_[0]) {
|
||||
return td::Status::Error(2, PSLICE() << "Ignore very old message " << message_id
|
||||
<< " older than the oldest known message " << saved_message_ids_[0]);
|
||||
}
|
||||
auto it = std::lower_bound(&saved_message_ids_[0], &saved_message_ids_[end_pos_], message_id);
|
||||
if (*it == message_id) {
|
||||
return td::Status::Error(1, PSLICE() << "Ignore already processed message " << message_id);
|
||||
}
|
||||
std::copy_backward(it, &saved_message_ids_[end_pos_], &saved_message_ids_[end_pos_ + 1]);
|
||||
*it = message_id;
|
||||
++end_pos_;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<td::uint64, 2 * max_size> saved_message_ids_;
|
||||
std::size_t end_pos_ = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBench final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBench" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
checker_.check(i).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchRepeat final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchRepeat" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto iter = i >> 10;
|
||||
auto pos = i - (iter << 10);
|
||||
if (pos < 768) {
|
||||
if (iter >= 3 && pos == 0) {
|
||||
auto error = checker_.check((iter - 3) * 768 + pos);
|
||||
CHECK(error.error().code() == 2);
|
||||
}
|
||||
checker_.check(iter * 768 + pos).ensure();
|
||||
} else {
|
||||
checker_.check(iter * 768 + pos - 256).ensure_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchRepeatOnly final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchRepeatOnly" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto result = checker_.check(i & 255);
|
||||
CHECK(result.is_error() == (i >= 256));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchReverse final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchReverseAdd" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto pos = i & 255;
|
||||
checker_.check(i - pos + (255 - pos)).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class DuplicateCheckerBenchEvenOdd final : public td::Benchmark {
|
||||
td::string get_description() const final {
|
||||
return PSTRING() << "DuplicateCheckerBenchEvenOdd" << T::get_description();
|
||||
}
|
||||
void run(int n) final {
|
||||
T checker_;
|
||||
for (int i = 0; i < n; i++) {
|
||||
auto pos = i & 255;
|
||||
checker_.check(i - pos + (pos * 2) % 256 + (pos * 2) / 256).ensure();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BENCH(AddToTopStd, "add_to_top std") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
auto value = td::Random::fast(0, 9);
|
||||
auto it = std::find(v.begin(), v.end(), value);
|
||||
if (it == v.end()) {
|
||||
if (v.size() == 8) {
|
||||
v.back() = value;
|
||||
} else {
|
||||
v.push_back(value);
|
||||
}
|
||||
it = v.end() - 1;
|
||||
}
|
||||
std::rotate(v.begin(), it, it + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(AddToTopTd, "add_to_top td") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
td::add_to_top(v, 8, td::Random::fast(0, 9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(AnyOfStd, "any_of std") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
v.push_back(i);
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int rem = td::Random::fast(0, 127);
|
||||
res += static_cast<int>(std::any_of(v.begin(), v.end(), [rem](int x) { return (x & 127) == rem; }));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
BENCH(AnyOfTd, "any_of td") {
|
||||
td::vector<int> v;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
v.push_back(i);
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int rem = td::Random::fast(0, 127);
|
||||
res += static_cast<int>(td::any_of(v, [rem](int x) { return (x & 127) == rem; }));
|
||||
}
|
||||
td::do_not_optimize_away(res);
|
||||
}
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||
|
||||
td::bench(AnyOfStdBench());
|
||||
td::bench(AnyOfTdBench());
|
||||
|
||||
td::bench(ToStringIntSmallBench());
|
||||
td::bench(ToStringIntBigBench());
|
||||
|
||||
td::bench(AddToTopStdBench());
|
||||
td::bench(AddToTopTdBench());
|
||||
|
||||
td::bench(TlToStringUpdateFileBench());
|
||||
td::bench(TlToStringMessageBench());
|
||||
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchEvenOdd<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchReverse<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeatOnly<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerOld>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNewOther>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNewSimple>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBenchRepeat<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerOld>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<1000>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNewOther>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNewSimple>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<300>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<100>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerNew<10>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerArray<1000>>());
|
||||
td::bench(DuplicateCheckerBench<IdDuplicateCheckerArray<300>>());
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
for (int i = 1; i <= 16; i *= 2) {
|
||||
td::bench(ThreadSafeCounterBench(i));
|
||||
td::bench(AtomicCounterBench<false>(i));
|
||||
td::bench(AtomicCounterBench<true>(i));
|
||||
}
|
||||
|
||||
td::bench(AtomicReleaseIncBench<1>());
|
||||
td::bench(AtomicReleaseIncBench<2>());
|
||||
td::bench(AtomicReleaseCasIncBench<1>());
|
||||
td::bench(AtomicReleaseCasIncBench<2>());
|
||||
td::bench(RwMutexWriteBench<1>());
|
||||
td::bench(RwMutexReadBench<1>());
|
||||
td::bench(RwMutexWriteBench<2>());
|
||||
td::bench(RwMutexReadBench<2>());
|
||||
#endif
|
||||
#if !TD_WINDOWS
|
||||
td::bench(UtimeBench());
|
||||
#endif
|
||||
td::bench(WalkPathBench());
|
||||
td::bench(CreateFileBench());
|
||||
td::bench(PwriteBench());
|
||||
|
||||
td::bench(TlCallBench());
|
||||
#if !TD_THREAD_UNSUPPORTED
|
||||
td::bench(ThreadNewBench());
|
||||
#endif
|
||||
#if !TD_EVENTFD_UNSUPPORTED
|
||||
td::bench(EventFdBench());
|
||||
#endif
|
||||
td::bench(NewObjBench());
|
||||
td::bench(NewIntBench());
|
||||
#if !TD_WINDOWS
|
||||
td::bench(PipeBench());
|
||||
#endif
|
||||
#if TD_LINUX || TD_ANDROID || TD_TIZEN
|
||||
td::bench(SemBench());
|
||||
#endif
|
||||
}
|
932
third-party/td/td/benchmark/bench_queue.cpp
vendored
932
third-party/td/td/benchmark/bench_queue.cpp
vendored
@ -1,932 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/MpscPollableQueue.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/queue.h"
|
||||
#include "td/utils/Random.h"
|
||||
|
||||
// TODO: check system calls
|
||||
// TODO: all return values must be checked
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if TD_LINUX
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#define MODE std::memory_order_relaxed
|
||||
|
||||
// void set_affinity(int mask) {
|
||||
// pid_t pid = gettid();
|
||||
// int syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
|
||||
// if (syscallres) {
|
||||
// perror("Failed to set affinity");
|
||||
// }
|
||||
// }
|
||||
|
||||
using qvalue_t = int;
|
||||
|
||||
class Backoff {
|
||||
int cnt = 0;
|
||||
|
||||
public:
|
||||
bool next() {
|
||||
cnt++;
|
||||
if (cnt < 50) {
|
||||
return true;
|
||||
} else {
|
||||
td::usleep_for(1);
|
||||
return cnt < 500;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
// Just for testing, not production
|
||||
class PipeQueue {
|
||||
int input;
|
||||
int output;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
int new_pipe[2];
|
||||
int res = pipe(new_pipe);
|
||||
CHECK(res == 0);
|
||||
output = new_pipe[0];
|
||||
input = new_pipe[1];
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
auto len = write(input, &value, sizeof(value));
|
||||
CHECK(len == sizeof(value));
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res;
|
||||
auto len = read(output, &res, sizeof(res));
|
||||
CHECK(len == sizeof(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
close(input);
|
||||
close(output);
|
||||
}
|
||||
};
|
||||
|
||||
class VarQueue {
|
||||
std::atomic<qvalue_t> data{0};
|
||||
|
||||
public:
|
||||
void init() {
|
||||
data.store(-1, MODE);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
data.store(value, MODE);
|
||||
}
|
||||
|
||||
qvalue_t try_get() {
|
||||
__sync_synchronize(); // TODO: it is wrong place for barrier, but it results in fastest queue
|
||||
qvalue_t res = data.load(MODE);
|
||||
return res;
|
||||
}
|
||||
|
||||
void acquire() {
|
||||
data.store(-1, MODE);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res;
|
||||
Backoff backoff;
|
||||
|
||||
do {
|
||||
res = try_get();
|
||||
} while (res == -1 && (backoff.next(), true));
|
||||
acquire();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
}
|
||||
};
|
||||
|
||||
class SemQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
sem_wait(&sem);
|
||||
qvalue_t res = q.get();
|
||||
return res;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
|
||||
// HACK for benchmark
|
||||
void reader_flush() {
|
||||
}
|
||||
|
||||
void writer_flush() {
|
||||
}
|
||||
|
||||
void writer_put(qvalue_t value) {
|
||||
put(value);
|
||||
}
|
||||
|
||||
int reader_wait() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qvalue_t reader_get_unsafe() {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_LINUX
|
||||
class EventfdQueue {
|
||||
int fd;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
qvalue_t get() {
|
||||
td::int64 x;
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
CHECK(x == 1);
|
||||
return q.get();
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
const int queue_buf_size = 1 << 10;
|
||||
|
||||
class BufferQueue {
|
||||
struct node {
|
||||
qvalue_t val;
|
||||
char pad[64 - sizeof(std::atomic<qvalue_t>)];
|
||||
};
|
||||
node q[queue_buf_size];
|
||||
|
||||
struct Position {
|
||||
std::atomic<td::uint32> i{0};
|
||||
char pad[64 - sizeof(std::atomic<td::uint32>)];
|
||||
|
||||
td::uint32 local_read_i;
|
||||
td::uint32 local_write_i;
|
||||
char pad2[64 - sizeof(td::uint32) * 2];
|
||||
|
||||
void init() {
|
||||
i = 0;
|
||||
local_read_i = 0;
|
||||
local_write_i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Position writer;
|
||||
Position reader;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
writer.init();
|
||||
reader.init();
|
||||
}
|
||||
|
||||
bool reader_empty() {
|
||||
return reader.local_write_i == reader.local_read_i;
|
||||
}
|
||||
|
||||
bool writer_empty() {
|
||||
return writer.local_write_i == writer.local_read_i + queue_buf_size;
|
||||
}
|
||||
|
||||
int reader_ready() {
|
||||
return static_cast<int>(reader.local_write_i - reader.local_read_i);
|
||||
}
|
||||
|
||||
int writer_ready() {
|
||||
return static_cast<int>(writer.local_read_i + queue_buf_size - writer.local_write_i);
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe() {
|
||||
return q[reader.local_read_i++ & (queue_buf_size - 1)].val;
|
||||
}
|
||||
|
||||
void flush_reader() {
|
||||
reader.i.store(reader.local_read_i, std::memory_order_release);
|
||||
}
|
||||
|
||||
int update_reader() {
|
||||
reader.local_write_i = writer.i.load(std::memory_order_acquire);
|
||||
return reader_ready();
|
||||
}
|
||||
|
||||
void put_unsafe(qvalue_t val) {
|
||||
q[writer.local_write_i++ & (queue_buf_size - 1)].val = val;
|
||||
}
|
||||
|
||||
void flush_writer() {
|
||||
writer.i.store(writer.local_write_i, std::memory_order_release);
|
||||
}
|
||||
|
||||
int update_writer() {
|
||||
writer.local_read_i = reader.i.load(std::memory_order_acquire);
|
||||
return writer_ready();
|
||||
}
|
||||
|
||||
int wait_reader() {
|
||||
Backoff backoff;
|
||||
int res = 0;
|
||||
while (res == 0) {
|
||||
backoff.next();
|
||||
res = update_reader();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get_noflush() {
|
||||
if (!reader_empty()) {
|
||||
return get_unsafe();
|
||||
}
|
||||
|
||||
Backoff backoff;
|
||||
while (true) {
|
||||
backoff.next();
|
||||
if (update_reader()) {
|
||||
return get_unsafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
qvalue_t res = get_noflush();
|
||||
flush_reader();
|
||||
return res;
|
||||
}
|
||||
|
||||
void put_noflush(qvalue_t val) {
|
||||
if (!writer_empty()) {
|
||||
put_unsafe(val);
|
||||
return;
|
||||
}
|
||||
if (!update_writer()) {
|
||||
LOG(FATAL) << "Put strong failed";
|
||||
}
|
||||
put_unsafe(val);
|
||||
}
|
||||
|
||||
void put(qvalue_t val) {
|
||||
put_noflush(val);
|
||||
flush_writer();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
}
|
||||
};
|
||||
|
||||
#if TD_LINUX
|
||||
class BufferedFdQueue {
|
||||
int fd;
|
||||
std::atomic<int> wait_flag{0};
|
||||
BufferQueue q;
|
||||
char pad[64];
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
(void)pad[0];
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
void put_noflush(qvalue_t value) {
|
||||
q.put_noflush(value);
|
||||
}
|
||||
void flush_writer() {
|
||||
q.flush_writer();
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
void flush_reader() {
|
||||
q.flush_reader();
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe_flush() {
|
||||
qvalue_t res = q.get_unsafe();
|
||||
q.flush_reader();
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get_unsafe() {
|
||||
return q.get_unsafe();
|
||||
}
|
||||
|
||||
int wait_reader() {
|
||||
int res = 0;
|
||||
Backoff backoff;
|
||||
while (res == 0 && backoff.next()) {
|
||||
res = q.update_reader();
|
||||
}
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
while (!(res = q.update_reader())) {
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
__sync_synchronize();
|
||||
}
|
||||
wait_flag.store(0, MODE);
|
||||
return res;
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
if (!q.reader_empty()) {
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
|
||||
Backoff backoff;
|
||||
while (backoff.next()) {
|
||||
if (q.update_reader()) {
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
while (!q.update_reader()) {
|
||||
auto len = read(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
__sync_synchronize();
|
||||
}
|
||||
wait_flag.store(0, MODE);
|
||||
return get_unsafe_flush();
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
class FdQueue {
|
||||
int fd;
|
||||
std::atomic<int> wait_flag{0};
|
||||
VarQueue q;
|
||||
char pad[64];
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
fd = eventfd(0, 0);
|
||||
(void)pad[0];
|
||||
}
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
td::int64 x = 1;
|
||||
__sync_synchronize();
|
||||
if (wait_flag.load(MODE)) {
|
||||
auto len = write(fd, &x, sizeof(x));
|
||||
CHECK(len == sizeof(x));
|
||||
}
|
||||
}
|
||||
qvalue_t get() {
|
||||
// td::int64 x;
|
||||
// auto len = read(fd, &x, sizeof(x));
|
||||
// CHECK(len == sizeof(x));
|
||||
// return q.get();
|
||||
|
||||
Backoff backoff;
|
||||
qvalue_t res = -1;
|
||||
do {
|
||||
res = q.try_get();
|
||||
} while (res == -1 && backoff.next());
|
||||
if (res != -1) {
|
||||
q.acquire();
|
||||
return res;
|
||||
}
|
||||
|
||||
td::int64 x;
|
||||
wait_flag.store(1, MODE);
|
||||
__sync_synchronize();
|
||||
// while (res == -1 && read(fd, &x, sizeof(x)) == sizeof(x)) {
|
||||
// res = q.try_get();
|
||||
// }
|
||||
do {
|
||||
__sync_synchronize();
|
||||
res = q.try_get();
|
||||
} while (res == -1 && read(fd, &x, sizeof(x)) == sizeof(x));
|
||||
q.acquire();
|
||||
wait_flag.store(0, MODE);
|
||||
return res;
|
||||
}
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
class SemBackoffQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
Backoff backoff;
|
||||
int sem_flag = -1;
|
||||
do {
|
||||
sem_flag = sem_trywait(&sem);
|
||||
} while (sem_flag != 0 && backoff.next());
|
||||
if (sem_flag != 0) {
|
||||
sem_wait(&sem);
|
||||
}
|
||||
return q.get();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
|
||||
class SemCheatQueue {
|
||||
sem_t sem;
|
||||
VarQueue q;
|
||||
|
||||
public:
|
||||
void init() {
|
||||
q.init();
|
||||
sem_init(&sem, 0, 0);
|
||||
}
|
||||
|
||||
void put(qvalue_t value) {
|
||||
q.put(value);
|
||||
sem_post(&sem);
|
||||
}
|
||||
|
||||
qvalue_t get() {
|
||||
Backoff backoff;
|
||||
qvalue_t res = -1;
|
||||
do {
|
||||
res = q.try_get();
|
||||
} while (res == -1 && backoff.next());
|
||||
sem_wait(&sem);
|
||||
if (res != -1) {
|
||||
q.acquire();
|
||||
return res;
|
||||
}
|
||||
return q.get();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
q.destroy();
|
||||
sem_destroy(&sem);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class QueueBenchmark2 final : public td::Benchmark {
|
||||
QueueT client, server;
|
||||
int connections_n, queries_n;
|
||||
|
||||
int server_active_connections;
|
||||
int client_active_connections;
|
||||
td::vector<td::int64> server_conn;
|
||||
td::vector<td::int64> client_conn;
|
||||
|
||||
td::string name;
|
||||
|
||||
public:
|
||||
QueueBenchmark2(int connections_n, td::string name) : connections_n(connections_n), name(std::move(name)) {
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name;
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
client.init();
|
||||
server.init();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
client.destroy();
|
||||
server.destroy();
|
||||
}
|
||||
|
||||
void server_process(qvalue_t value) {
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(static_cast<td::uint32>(value) >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == server_conn[co]++);
|
||||
|
||||
client.writer_put(value);
|
||||
client.writer_flush();
|
||||
if (no + 1 >= queries_n) {
|
||||
server_active_connections--;
|
||||
}
|
||||
}
|
||||
|
||||
void *server_run(void *) {
|
||||
server_conn = td::vector<td::int64>(connections_n);
|
||||
server_active_connections = connections_n;
|
||||
|
||||
while (server_active_connections > 0) {
|
||||
int cnt = server.reader_wait();
|
||||
CHECK(cnt != 0);
|
||||
while (cnt-- > 0) {
|
||||
server_process(server.reader_get_unsafe());
|
||||
server.reader_flush();
|
||||
}
|
||||
// client.writer_flush();
|
||||
server.reader_flush();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void client_process(qvalue_t value) {
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(static_cast<td::uint32>(value) >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == client_conn[co]++);
|
||||
if (no + 1 < queries_n) {
|
||||
server.writer_put(value + 1);
|
||||
server.writer_flush();
|
||||
} else {
|
||||
client_active_connections--;
|
||||
}
|
||||
}
|
||||
|
||||
void *client_run(void *) {
|
||||
client_conn = td::vector<td::int64>(connections_n);
|
||||
client_active_connections = connections_n;
|
||||
CHECK(queries_n < (1 << 24));
|
||||
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.writer_put(static_cast<qvalue_t>(i) << 24);
|
||||
}
|
||||
server.writer_flush();
|
||||
|
||||
while (client_active_connections > 0) {
|
||||
int cnt = client.reader_wait();
|
||||
CHECK(cnt != 0);
|
||||
while (cnt-- > 0) {
|
||||
client_process(client.reader_get_unsafe());
|
||||
client.reader_flush();
|
||||
}
|
||||
// server.writer_flush();
|
||||
client.reader_flush();
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void *client_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark2 *>(arg)->client_run(nullptr);
|
||||
}
|
||||
|
||||
static void *server_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark2 *>(arg)->server_run(nullptr);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
pthread_t client_thread_id;
|
||||
pthread_t server_thread_id;
|
||||
|
||||
queries_n = (n + connections_n - 1) / connections_n;
|
||||
|
||||
pthread_create(&client_thread_id, nullptr, client_run_gateway, this);
|
||||
pthread_create(&server_thread_id, nullptr, server_run_gateway, this);
|
||||
|
||||
pthread_join(client_thread_id, nullptr);
|
||||
pthread_join(server_thread_id, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class QueueBenchmark final : public td::Benchmark {
|
||||
QueueT client, server;
|
||||
const int connections_n;
|
||||
int queries_n;
|
||||
|
||||
td::string name;
|
||||
|
||||
public:
|
||||
QueueBenchmark(int connections_n, td::string name) : connections_n(connections_n), name(std::move(name)) {
|
||||
}
|
||||
|
||||
td::string get_description() const final {
|
||||
return name;
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
client.init();
|
||||
server.init();
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
client.destroy();
|
||||
server.destroy();
|
||||
}
|
||||
|
||||
void *server_run(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
int active_connections = connections_n;
|
||||
while (active_connections > 0) {
|
||||
qvalue_t value = server.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
client.put(value);
|
||||
if (no + 1 >= queries_n) {
|
||||
active_connections--;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *client_run(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
CHECK(queries_n < (1 << 24));
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.put(static_cast<qvalue_t>(i) << 24);
|
||||
}
|
||||
int active_connections = connections_n;
|
||||
while (active_connections > 0) {
|
||||
qvalue_t value = client.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
if (no + 1 < queries_n) {
|
||||
server.put(value + 1);
|
||||
} else {
|
||||
active_connections--;
|
||||
}
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *client_run2(void *) {
|
||||
td::vector<td::int64> conn(connections_n);
|
||||
CHECK(queries_n < (1 << 24));
|
||||
for (int query = 0; query < queries_n; query++) {
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
server.put((static_cast<td::int64>(i) << 24) + query);
|
||||
}
|
||||
for (int i = 0; i < connections_n; i++) {
|
||||
qvalue_t value = client.get();
|
||||
int no = value & 0x00FFFFFF;
|
||||
auto co = static_cast<int>(value >> 24);
|
||||
CHECK(co >= 0 && co < connections_n);
|
||||
CHECK(no == conn[co]++);
|
||||
}
|
||||
}
|
||||
// system("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void *client_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark *>(arg)->client_run(nullptr);
|
||||
}
|
||||
|
||||
static void *server_run_gateway(void *arg) {
|
||||
return static_cast<QueueBenchmark *>(arg)->server_run(nullptr);
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
pthread_t client_thread_id;
|
||||
pthread_t server_thread_id;
|
||||
|
||||
queries_n = (n + connections_n - 1) / connections_n;
|
||||
|
||||
pthread_create(&client_thread_id, nullptr, client_run_gateway, this);
|
||||
pthread_create(&server_thread_id, nullptr, server_run_gateway, this);
|
||||
|
||||
pthread_join(client_thread_id, nullptr);
|
||||
pthread_join(server_thread_id, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <class QueueT>
|
||||
class RingBenchmark final : public td::Benchmark {
|
||||
static constexpr int QN = 504;
|
||||
|
||||
struct Thread {
|
||||
int int_id;
|
||||
pthread_t id;
|
||||
QueueT queue;
|
||||
Thread *next;
|
||||
char pad[64];
|
||||
|
||||
void *run() {
|
||||
qvalue_t value;
|
||||
do {
|
||||
int cnt = queue.reader_wait();
|
||||
CHECK(cnt == 1);
|
||||
value = queue.reader_get_unsafe();
|
||||
queue.reader_flush();
|
||||
|
||||
next->queue.writer_put(value - 1);
|
||||
next->queue.writer_flush();
|
||||
} while (value >= QN);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Thread q[QN];
|
||||
|
||||
public:
|
||||
static void *run_gateway(void *arg) {
|
||||
return static_cast<Thread *>(arg)->run();
|
||||
}
|
||||
|
||||
void start_up() final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
q[i].int_id = i;
|
||||
q[i].queue.init();
|
||||
q[i].next = &q[(i + 1) % QN];
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down() final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
q[i].queue.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void run(int n) final {
|
||||
for (int i = 0; i < QN; i++) {
|
||||
pthread_create(&q[i].id, nullptr, run_gateway, &q[i]);
|
||||
}
|
||||
|
||||
if (n < 1000) {
|
||||
n = 1000;
|
||||
}
|
||||
q[0].queue.writer_put(n);
|
||||
q[0].queue.writer_flush();
|
||||
|
||||
for (int i = 0; i < QN; i++) {
|
||||
pthread_join(q[i].id, nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
static void test_queue() {
|
||||
td::vector<td::thread> threads;
|
||||
static constexpr size_t THREAD_COUNT = 100;
|
||||
td::vector<td::MpscPollableQueue<int>> queues(THREAD_COUNT);
|
||||
for (auto &q : queues) {
|
||||
q.init();
|
||||
}
|
||||
for (size_t i = 0; i < THREAD_COUNT; i++) {
|
||||
threads.emplace_back([&q = queues[i]] {
|
||||
while (true) {
|
||||
auto ready_count = q.reader_wait_nonblock();
|
||||
while (ready_count-- > 0) {
|
||||
q.reader_get_unsafe();
|
||||
}
|
||||
q.reader_get_event_fd().wait(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (size_t iter = 0; iter < THREAD_COUNT; iter++) {
|
||||
td::usleep_for(100);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
queues[td::Random::fast(0, THREAD_COUNT - 1)].writer_put(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < THREAD_COUNT; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
int main() {
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
// test_queue();
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
// td::bench(RingBenchmark<SemQueue>());
|
||||
// td::bench(RingBenchmark<td::PollQueue<qvalue_t>>());
|
||||
|
||||
#define BENCH_Q2(Q, N) td::bench(QueueBenchmark2<Q<qvalue_t>>(N, #Q "(" #N ")"))
|
||||
|
||||
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
|
||||
BENCH_Q2(td::InfBackoffQueue, 1);
|
||||
BENCH_Q2(td::MpscPollableQueue, 1);
|
||||
BENCH_Q2(td::PollQueue, 1);
|
||||
|
||||
BENCH_Q2(td::InfBackoffQueue, 10);
|
||||
BENCH_Q2(td::MpscPollableQueue, 10);
|
||||
BENCH_Q2(td::PollQueue, 10);
|
||||
|
||||
BENCH_Q2(td::InfBackoffQueue, 100);
|
||||
BENCH_Q2(td::MpscPollableQueue, 100);
|
||||
BENCH_Q2(td::PollQueue, 100);
|
||||
|
||||
BENCH_Q2(td::PollQueue, 4);
|
||||
BENCH_Q2(td::PollQueue, 10);
|
||||
BENCH_Q2(td::PollQueue, 100);
|
||||
#endif
|
||||
|
||||
#define BENCH_Q(Q, N) td::bench(QueueBenchmark<Q>(N, #Q "(" #N ")"))
|
||||
|
||||
#if TD_LINUX
|
||||
BENCH_Q(BufferQueue, 1);
|
||||
BENCH_Q(BufferedFdQueue, 1);
|
||||
BENCH_Q(FdQueue, 1);
|
||||
#endif
|
||||
BENCH_Q(PipeQueue, 1);
|
||||
BENCH_Q(SemCheatQueue, 1);
|
||||
BENCH_Q(SemQueue, 1);
|
||||
BENCH_Q(VarQueue, 1);
|
||||
|
||||
#if TD_LINUX
|
||||
BENCH_Q(BufferQueue, 4);
|
||||
BENCH_Q(BufferQueue, 10);
|
||||
BENCH_Q(BufferQueue, 100);
|
||||
#endif
|
||||
#endif
|
||||
}
|
113
third-party/td/td/benchmark/bench_tddb.cpp
vendored
113
third-party/td/td/benchmark/bench_tddb.cpp
vendored
@ -1,113 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/telegram/DialogId.h"
|
||||
#include "td/telegram/MessageDb.h"
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/NotificationId.h"
|
||||
#include "td/telegram/ServerMessageId.h"
|
||||
#include "td/telegram/UserId.h"
|
||||
|
||||
#include "td/db/DbKey.h"
|
||||
#include "td/db/SqliteConnectionSafe.h"
|
||||
#include "td/db/SqliteDb.h"
|
||||
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/benchmark.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
static td::Status init_db(td::SqliteDb &db) {
|
||||
TRY_STATUS(db.exec("PRAGMA encoding=\"UTF-8\""));
|
||||
TRY_STATUS(db.exec("PRAGMA synchronous=NORMAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
|
||||
TRY_STATUS(db.exec("PRAGMA temp_store=MEMORY"));
|
||||
TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
class MessageDbBench final : public td::Benchmark {
|
||||
public:
|
||||
td::string get_description() const final {
|
||||
return "MessageDb";
|
||||
}
|
||||
void start_up() final {
|
||||
LOG(ERROR) << "START UP";
|
||||
do_start_up().ensure();
|
||||
scheduler_->start();
|
||||
}
|
||||
void run(int n) final {
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
for (int i = 0; i < n; i += 20) {
|
||||
auto dialog_id = td::DialogId(td::UserId(static_cast<td::int64>(td::Random::fast(1, 100))));
|
||||
auto message_id_raw = td::Random::fast(1, 100000);
|
||||
for (int j = 0; j < 20; j++) {
|
||||
auto message_id = td::MessageId{td::ServerMessageId{message_id_raw + j}};
|
||||
auto unique_message_id = td::ServerMessageId{i + 1};
|
||||
auto sender_dialog_id = td::DialogId(td::UserId(static_cast<td::int64>(td::Random::fast(1, 1000))));
|
||||
auto random_id = i + 1;
|
||||
auto ttl_expires_at = 0;
|
||||
auto data = td::BufferSlice(td::Random::fast(100, 299));
|
||||
|
||||
// use async on same thread.
|
||||
message_db_async_->add_message({dialog_id, message_id}, unique_message_id, sender_dialog_id, random_id,
|
||||
ttl_expires_at, 0, 0, "", td::NotificationId(), td::MessageId(), std::move(data),
|
||||
td::Promise<>());
|
||||
}
|
||||
}
|
||||
}
|
||||
void tear_down() final {
|
||||
scheduler_->run_main(0.1);
|
||||
{
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
sql_connection_.reset();
|
||||
message_db_sync_safe_.reset();
|
||||
message_db_async_.reset();
|
||||
}
|
||||
|
||||
scheduler_->finish();
|
||||
scheduler_.reset();
|
||||
LOG(ERROR) << "TEAR DOWN";
|
||||
}
|
||||
|
||||
private:
|
||||
td::unique_ptr<td::ConcurrentScheduler> scheduler_;
|
||||
std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
|
||||
std::shared_ptr<td::MessageDbSyncSafeInterface> message_db_sync_safe_;
|
||||
std::shared_ptr<td::MessageDbAsyncInterface> message_db_async_;
|
||||
|
||||
td::Status do_start_up() {
|
||||
scheduler_ = td::make_unique<td::ConcurrentScheduler>(1, 0);
|
||||
|
||||
auto guard = scheduler_->get_main_guard();
|
||||
|
||||
td::string sql_db_name = "testdb.sqlite";
|
||||
sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name, td::DbKey::empty());
|
||||
auto &db = sql_connection_->get();
|
||||
TRY_STATUS(init_db(db));
|
||||
|
||||
db.exec("BEGIN TRANSACTION").ensure();
|
||||
// version == 0 ==> db will be destroyed
|
||||
TRY_STATUS(init_message_db(db, 0));
|
||||
db.exec("COMMIT TRANSACTION").ensure();
|
||||
|
||||
message_db_sync_safe_ = td::create_message_db_sync(sql_connection_);
|
||||
message_db_async_ = td::create_message_db_async(message_db_sync_safe_, 0);
|
||||
return td::Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
td::bench(MessageDbBench());
|
||||
}
|
193
third-party/td/td/benchmark/check_proxy.cpp
vendored
193
third-party/td/td/benchmark/check_proxy.cpp
vendored
@ -1,193 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/telegram/Client.h"
|
||||
#include "td/telegram/td_api.h"
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/TsCerr.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
static void usage() {
|
||||
td::TsCerr() << "Tests specified MTProto-proxies, outputs working proxies to stdout; exits with code 0 if a working "
|
||||
"proxy was found.\n";
|
||||
td::TsCerr() << "Usage: check_proxy [options] server:port:secret [server2:port2:secret2 ...]\n";
|
||||
td::TsCerr() << "Options:\n";
|
||||
td::TsCerr() << " -v<N>\tSet verbosity level to N\n";
|
||||
td::TsCerr() << " -h/--help\tDisplay this information\n";
|
||||
td::TsCerr() << " -d/--dc-id\tIdentifier of a datacenter to which try to connect (default is 2)\n";
|
||||
td::TsCerr() << " -l/--proxy-list\tName of a file with proxies to check; one proxy per line\n";
|
||||
td::TsCerr() << " -t/--timeout\tMaximum overall timeout for the request (default is 10 seconds)\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int new_verbosity_level = VERBOSITY_NAME(FATAL);
|
||||
|
||||
td::vector<std::pair<td::string, td::td_api::object_ptr<td::td_api::testProxy>>> requests;
|
||||
|
||||
auto add_proxy = [&requests](td::string arg) {
|
||||
if (arg.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t offset = 0;
|
||||
if (arg[0] == '[') {
|
||||
auto end_ipv6_pos = arg.find(']');
|
||||
if (end_ipv6_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find end of IPv6 address in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
offset = end_ipv6_pos;
|
||||
}
|
||||
if (std::count(arg.begin() + offset, arg.end(), ':') == 3) {
|
||||
auto secret_domain_pos = arg.find(':', arg.find(':', offset) + 1) + 1;
|
||||
auto domain_pos = arg.find(':', secret_domain_pos);
|
||||
auto secret = arg.substr(secret_domain_pos, domain_pos - secret_domain_pos);
|
||||
auto domain = arg.substr(domain_pos + 1);
|
||||
auto r_decoded_secret = td::hex_decode(secret);
|
||||
if (r_decoded_secret.is_error()) {
|
||||
r_decoded_secret = td::base64url_decode(secret);
|
||||
if (r_decoded_secret.is_error()) {
|
||||
td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
}
|
||||
arg = arg.substr(0, secret_domain_pos) + td::base64url_encode(r_decoded_secret.ok() + domain);
|
||||
}
|
||||
|
||||
auto secret_pos = arg.rfind(':');
|
||||
if (secret_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find proxy port and secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto secret = arg.substr(secret_pos + 1);
|
||||
auto port_pos = arg.substr(0, secret_pos).rfind(':');
|
||||
if (port_pos == td::string::npos) {
|
||||
td::TsCerr() << "Error: failed to find proxy secret in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto r_port = td::to_integer_safe<td::int32>(arg.substr(port_pos + 1, secret_pos - port_pos - 1));
|
||||
if (r_port.is_error()) {
|
||||
td::TsCerr() << "Error: failed to parse proxy port in \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
auto port = r_port.move_as_ok();
|
||||
auto server = arg.substr(0, port_pos);
|
||||
if (server[0] == '[' && server.back() == ']') {
|
||||
server = server.substr(1, server.size() - 2);
|
||||
}
|
||||
|
||||
if (server.empty() || port <= 0 || port > 65536 || secret.empty()) {
|
||||
td::TsCerr() << "Error: proxy address to check is in wrong format: \"" << arg << "\"\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
requests.emplace_back(arg,
|
||||
td::td_api::make_object<td::td_api::testProxy>(
|
||||
server, port, td::td_api::make_object<td::td_api::proxyTypeMtproto>(secret), -1, -1));
|
||||
};
|
||||
|
||||
td::int32 dc_id = 2;
|
||||
double timeout = 10.0;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
td::string arg(argv[i]);
|
||||
|
||||
auto get_next_arg = [&i, &arg, argc, argv](bool is_optional = false) {
|
||||
CHECK(arg.size() >= 2);
|
||||
if (arg.size() == 2 || arg[1] == '-') {
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
return td::string(argv[++i]);
|
||||
}
|
||||
} else {
|
||||
if (arg.size() > 2) {
|
||||
return arg.substr(2);
|
||||
}
|
||||
}
|
||||
if (!is_optional) {
|
||||
td::TsCerr() << "Error: value is required after " << arg << "\n";
|
||||
usage();
|
||||
}
|
||||
return td::string();
|
||||
};
|
||||
|
||||
if (td::begins_with(arg, "-v")) {
|
||||
arg = get_next_arg(true);
|
||||
int new_verbosity = 1;
|
||||
while (arg[0] == 'v') {
|
||||
new_verbosity++;
|
||||
arg = arg.substr(1);
|
||||
}
|
||||
if (!arg.empty()) {
|
||||
new_verbosity += td::to_integer<int>(arg) - (new_verbosity == 1);
|
||||
}
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + new_verbosity;
|
||||
} else if (td::begins_with(arg, "-t") || arg == "--timeout") {
|
||||
timeout = td::to_double(get_next_arg());
|
||||
} else if (td::begins_with(arg, "-d") || arg == "--dc-id") {
|
||||
dc_id = td::to_integer<td::int32>(get_next_arg());
|
||||
} else if (td::begins_with(arg, "-l") || arg == "--proxy-list") {
|
||||
auto r_proxies = td::read_file_str(get_next_arg());
|
||||
if (r_proxies.is_error()) {
|
||||
td::TsCerr() << "Error: wrong file name specified\n";
|
||||
usage();
|
||||
}
|
||||
for (auto &proxy : td::full_split(r_proxies.ok(), '\n')) {
|
||||
add_proxy(td::trim(proxy));
|
||||
}
|
||||
} else if (arg[0] == '-') {
|
||||
usage();
|
||||
} else {
|
||||
add_proxy(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (requests.empty()) {
|
||||
td::TsCerr() << "Error: proxy address to check is not specified\n";
|
||||
usage();
|
||||
}
|
||||
|
||||
SET_VERBOSITY_LEVEL(new_verbosity_level);
|
||||
|
||||
td::ClientManager client_manager;
|
||||
auto client_id = client_manager.create_client_id();
|
||||
for (size_t i = 0; i < requests.size(); i++) {
|
||||
auto &request = requests[i].second;
|
||||
request->dc_id_ = dc_id;
|
||||
request->timeout_ = timeout;
|
||||
client_manager.send(client_id, i + 1, std::move(request));
|
||||
}
|
||||
size_t successful_requests = 0;
|
||||
size_t failed_requests = 0;
|
||||
|
||||
while (successful_requests + failed_requests != requests.size()) {
|
||||
auto response = client_manager.receive(100.0);
|
||||
CHECK(client_id == response.client_id);
|
||||
if (1 <= response.request_id && response.request_id <= requests.size()) {
|
||||
auto &proxy = requests[static_cast<size_t>(response.request_id - 1)].first;
|
||||
if (response.object->get_id() == td::td_api::error::ID) {
|
||||
LOG(ERROR) << proxy << ": " << to_string(response.object);
|
||||
failed_requests++;
|
||||
} else {
|
||||
std::cout << proxy << std::endl;
|
||||
successful_requests++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (successful_requests == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
336
third-party/td/td/benchmark/check_tls.cpp
vendored
336
third-party/td/td/benchmark/check_tls.cpp
vendored
@ -1,336 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/BigNum.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/Iocp.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SliceBuilder.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
static td::BigNumContext context;
|
||||
|
||||
static bool is_quadratic_residue(const td::BigNum &a) {
|
||||
// 2^255 - 19
|
||||
td::BigNum mod =
|
||||
td::BigNum::from_hex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed").move_as_ok();
|
||||
// (mod - 1) / 2 = 2^254 - 10
|
||||
td::BigNum pow =
|
||||
td::BigNum::from_hex("3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6").move_as_ok();
|
||||
|
||||
td::BigNum r;
|
||||
td::BigNum::mod_exp(r, a, pow, mod, context);
|
||||
td::BigNum one = td::BigNum::from_decimal("1").move_as_ok();
|
||||
td::BigNum::mod_add(r, r, one, mod, context);
|
||||
|
||||
td::string result = r.to_decimal();
|
||||
CHECK(result == "0" || result == "1" || result == "2");
|
||||
return result == "2";
|
||||
}
|
||||
|
||||
struct TlsInfo {
|
||||
td::vector<size_t> extension_list;
|
||||
td::vector<size_t> encrypted_application_data_length;
|
||||
};
|
||||
|
||||
td::Result<TlsInfo> test_tls(const td::string &url) {
|
||||
td::IPAddress address;
|
||||
TRY_STATUS(address.init_host_port(url, 443));
|
||||
TRY_RESULT(socket, td::SocketFd::open(address));
|
||||
|
||||
td::string request;
|
||||
|
||||
auto add_string = [&](td::Slice data) {
|
||||
request.append(data.data(), data.size());
|
||||
};
|
||||
auto add_random = [&](size_t length) {
|
||||
while (length-- > 0) {
|
||||
request += static_cast<char>(td::Random::secure_int32());
|
||||
}
|
||||
};
|
||||
auto add_length = [&](size_t length) {
|
||||
request += static_cast<char>(length / 256);
|
||||
request += static_cast<char>(length % 256);
|
||||
};
|
||||
auto add_key = [&] {
|
||||
td::string key(32, '\0');
|
||||
td::BigNum mod =
|
||||
td::BigNum::from_hex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed").move_as_ok();
|
||||
while (true) {
|
||||
td::Random::secure_bytes(key);
|
||||
key[31] = static_cast<char>(key[31] & 127);
|
||||
td::BigNum x = td::BigNum::from_le_binary(key);
|
||||
if (!is_quadratic_residue(x)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
td::BigNum y = x.clone();
|
||||
td::BigNum coef = td::BigNum::from_decimal("486662").move_as_ok();
|
||||
td::BigNum::mod_add(y, y, coef, mod, context);
|
||||
td::BigNum::mod_mul(y, y, x, mod, context);
|
||||
td::BigNum one = td::BigNum::from_decimal("1").move_as_ok();
|
||||
td::BigNum::mod_add(y, y, one, mod, context);
|
||||
td::BigNum::mod_mul(y, y, x, mod, context);
|
||||
// y = x^3 + 486662 * x^2 + x
|
||||
if (is_quadratic_residue(y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
request += key;
|
||||
};
|
||||
|
||||
const size_t MAX_GREASE = 7;
|
||||
char greases[MAX_GREASE];
|
||||
td::Random::secure_bytes(td::MutableSlice{greases, MAX_GREASE});
|
||||
for (auto &grease : greases) {
|
||||
grease = static_cast<char>((grease & 0xF0) + 0x0A);
|
||||
}
|
||||
for (size_t i = 1; i < MAX_GREASE; i += 2) {
|
||||
if (greases[i] == greases[i - 1]) {
|
||||
greases[i] = static_cast<char>(0x10 ^ greases[i]);
|
||||
}
|
||||
}
|
||||
auto add_grease = [&](size_t num) {
|
||||
auto c = greases[num];
|
||||
request += c;
|
||||
request += c;
|
||||
};
|
||||
|
||||
add_string("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03");
|
||||
add_random(32);
|
||||
add_string("\x20");
|
||||
add_random(32);
|
||||
add_string("\x00\x20");
|
||||
add_grease(0);
|
||||
add_string(
|
||||
"\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00"
|
||||
"\x2f\x00\x35\x01\x00\x01\x93");
|
||||
add_grease(2);
|
||||
add_string("\x00\x00\x00\x00");
|
||||
add_length(url.size() + 5);
|
||||
add_length(url.size() + 3);
|
||||
add_string("\x00");
|
||||
add_length(url.size());
|
||||
add_string(url);
|
||||
add_string("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08");
|
||||
add_grease(4);
|
||||
add_string(
|
||||
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68"
|
||||
"\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04"
|
||||
"\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29");
|
||||
add_grease(4);
|
||||
add_string("\x00\x01\x00\x00\x1d\x00\x20");
|
||||
add_key();
|
||||
add_string("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a");
|
||||
add_grease(6);
|
||||
add_string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02");
|
||||
add_grease(3);
|
||||
add_string("\x00\x01\x00\x00\x15");
|
||||
auto padding = 515 - static_cast<int>(request.size());
|
||||
CHECK(padding >= 0);
|
||||
add_length(padding);
|
||||
request.resize(517);
|
||||
|
||||
// LOG(ERROR) << td::format::as_hex_dump<0>(td::Slice(request));
|
||||
|
||||
TRY_STATUS(socket.write(request));
|
||||
|
||||
TlsInfo info;
|
||||
auto end_time = td::Time::now() + 3;
|
||||
td::string result;
|
||||
size_t pos = 0;
|
||||
size_t server_hello_length = 0;
|
||||
size_t encrypted_application_data_length_sum = 0;
|
||||
while (td::Time::now() < end_time) {
|
||||
static char buf[20000];
|
||||
TRY_RESULT(res, socket.read(td::MutableSlice{buf, sizeof(buf)}));
|
||||
if (res > 0) {
|
||||
auto read_length = [&]() -> size_t {
|
||||
CHECK(result.size() >= 2 + pos);
|
||||
pos += 2;
|
||||
return static_cast<unsigned char>(result[pos - 2]) * 256 + static_cast<unsigned char>(result[pos - 1]);
|
||||
};
|
||||
|
||||
result += td::Slice(buf, res).str();
|
||||
while (true) {
|
||||
#define CHECK_LENGTH(length) \
|
||||
if (pos + (length) > result.size()) { \
|
||||
break; \
|
||||
}
|
||||
#define EXPECT_STR(pos, str, error) \
|
||||
if (!begins_with(td::Slice(result).substr(pos), str)) { \
|
||||
return td::Status::Error(error); \
|
||||
}
|
||||
|
||||
if (pos == 0) {
|
||||
CHECK_LENGTH(3);
|
||||
EXPECT_STR(0, "\x16\x03\x03", "Non-TLS response or TLS <= 1.1");
|
||||
pos += 3;
|
||||
}
|
||||
if (pos == 3) {
|
||||
CHECK_LENGTH(2);
|
||||
server_hello_length = read_length();
|
||||
if (server_hello_length <= 39) {
|
||||
return td::Status::Error("Receive too short server hello");
|
||||
}
|
||||
}
|
||||
if (server_hello_length > 0) {
|
||||
if (pos == 5) {
|
||||
CHECK_LENGTH(server_hello_length);
|
||||
|
||||
EXPECT_STR(5, "\x02\x00", "Non-TLS response 2");
|
||||
EXPECT_STR(9, "\x03\x03", "Non-TLS response 3");
|
||||
|
||||
auto random_id = td::Slice(result.c_str() + 11, 32);
|
||||
if (random_id ==
|
||||
"\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07"
|
||||
"\x9e\x09\xe2\xc8\xa8\x33\x9c") {
|
||||
return td::Status::Error("TLS 1.3 servers returning HelloRetryRequest are not supported");
|
||||
}
|
||||
if (result[43] == '\x00') {
|
||||
return td::Status::Error("TLS <= 1.2: empty session_id");
|
||||
}
|
||||
EXPECT_STR(43, "\x20", "Non-TLS response 4");
|
||||
if (server_hello_length <= 75) {
|
||||
return td::Status::Error("Receive too short server hello 2");
|
||||
}
|
||||
EXPECT_STR(44, request.substr(44, 32), "TLS <= 1.2: expected mirrored session_id");
|
||||
EXPECT_STR(76, "\x13\x01\x00", "TLS <= 1.2: expected 0x1301 as a chosen cipher");
|
||||
pos += 74;
|
||||
size_t extensions_length = read_length();
|
||||
if (extensions_length + 76 != server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extensions length");
|
||||
}
|
||||
while (pos < 5 + server_hello_length - 4) {
|
||||
info.extension_list.push_back(read_length());
|
||||
size_t extension_length = read_length();
|
||||
if (pos + extension_length > 5 + server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extension length");
|
||||
}
|
||||
pos += extension_length;
|
||||
}
|
||||
if (pos != 5 + server_hello_length) {
|
||||
return td::Status::Error("Receive wrong extensions list");
|
||||
}
|
||||
}
|
||||
if (pos == 5 + server_hello_length) {
|
||||
CHECK_LENGTH(6);
|
||||
EXPECT_STR(pos, "\x14\x03\x03\x00\x01\x01", "Expected dummy ChangeCipherSpec");
|
||||
pos += 6;
|
||||
}
|
||||
if (pos == 11 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
if (pos == result.size()) {
|
||||
return info;
|
||||
}
|
||||
|
||||
CHECK_LENGTH(3);
|
||||
EXPECT_STR(pos, "\x17\x03\x03", "Expected encrypted application data");
|
||||
pos += 3;
|
||||
}
|
||||
if (pos == 14 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
CHECK_LENGTH(2);
|
||||
size_t encrypted_application_data_length = read_length();
|
||||
info.encrypted_application_data_length.push_back(encrypted_application_data_length);
|
||||
if (encrypted_application_data_length == 0) {
|
||||
return td::Status::Error("Receive empty encrypted application data");
|
||||
}
|
||||
}
|
||||
if (pos == 16 + server_hello_length + encrypted_application_data_length_sum) {
|
||||
CHECK_LENGTH(info.encrypted_application_data_length.back());
|
||||
pos += info.encrypted_application_data_length.back();
|
||||
encrypted_application_data_length_sum += info.encrypted_application_data_length.back() + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
td::usleep_for(10000);
|
||||
}
|
||||
}
|
||||
|
||||
// LOG(ERROR) << url << ":" << td::format::as_hex_dump<0>(td::Slice(result));
|
||||
return td::Status::Error("Failed to get response in 3 seconds");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
auto iocp = td::make_unique<td::detail::Iocp>();
|
||||
iocp->init();
|
||||
auto iocp_thread = td::thread([&iocp] { iocp->loop(); });
|
||||
td::detail::Iocp::Guard iocp_guard(iocp.get());
|
||||
#endif
|
||||
|
||||
td::vector<td::string> urls;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
urls.emplace_back(argv[i]);
|
||||
}
|
||||
for (auto &url : urls) {
|
||||
const int MAX_TRIES = 100;
|
||||
td::vector<std::map<size_t, int>> length_count;
|
||||
td::vector<size_t> extension_list;
|
||||
for (int i = 0; i < MAX_TRIES; i++) {
|
||||
auto r_tls_info = test_tls(url);
|
||||
if (r_tls_info.is_error()) {
|
||||
LOG(ERROR) << url << ": " << r_tls_info.error();
|
||||
break;
|
||||
} else {
|
||||
auto tls_info = r_tls_info.move_as_ok();
|
||||
if (length_count.size() < tls_info.encrypted_application_data_length.size()) {
|
||||
length_count.resize(tls_info.encrypted_application_data_length.size());
|
||||
}
|
||||
for (size_t t = 0; t < tls_info.encrypted_application_data_length.size(); t++) {
|
||||
length_count[t][tls_info.encrypted_application_data_length[t]]++;
|
||||
}
|
||||
if (i == 0) {
|
||||
extension_list = tls_info.extension_list;
|
||||
} else {
|
||||
if (extension_list != tls_info.extension_list) {
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 extension list has changed from " << extension_list << " to "
|
||||
<< tls_info.extension_list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MAX_TRIES - 1) {
|
||||
if (extension_list != td::vector<size_t>{51, 43} && extension_list != td::vector<size_t>{43, 51}) {
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 unsupported extension list " << extension_list;
|
||||
} else {
|
||||
td::string length_distribution = "|";
|
||||
for (size_t t = 0; t < length_count.size(); t++) {
|
||||
for (auto it : length_count[t]) {
|
||||
length_distribution += PSTRING()
|
||||
<< it.first << " : " << static_cast<int>(it.second * 100.0 / MAX_TRIES) << "%|";
|
||||
}
|
||||
if (t + 1 != length_count.size()) {
|
||||
length_distribution += " + |";
|
||||
}
|
||||
}
|
||||
LOG(ERROR) << url << ": TLS 1.3.0 with extensions " << extension_list << " and "
|
||||
<< (length_count.size() != 1 ? "unsupported " : "")
|
||||
<< "encrypted application data length distribution " << length_distribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
iocp->interrupt_loop();
|
||||
iocp_thread.join();
|
||||
#endif
|
||||
}
|
545
third-party/td/td/benchmark/hashmap_build.cpp
vendored
545
third-party/td/td/benchmark/hashmap_build.cpp
vendored
@ -1,545 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
|
||||
#ifdef SCOPE_EXIT
|
||||
#undef SCOPE_EXIT
|
||||
#endif
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <array>
|
||||
#include <folly/container/F14Map.h>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#define test_map td::FlatHashMap
|
||||
//#define test_map folly::F14FastMap
|
||||
//#define test_map absl::flat_hash_map
|
||||
//#define test_map std::map
|
||||
//#define test_map std::unordered_map
|
||||
|
||||
//#define CREATE_MAP(num) CREATE_MAP_IMPL(num)
|
||||
#define CREATE_MAP(num)
|
||||
|
||||
#define CREATE_MAP_IMPL(num) \
|
||||
int f_##num() { \
|
||||
test_map<td::int32, std::array<char, num>> m; \
|
||||
m.emplace(1, std::array<char, num>{}); \
|
||||
int sum = 0; \
|
||||
for (auto &it : m) { \
|
||||
sum += it.first; \
|
||||
} \
|
||||
auto it = m.find(1); \
|
||||
sum += it->first; \
|
||||
m.erase(it); \
|
||||
return sum; \
|
||||
} \
|
||||
int x_##num = f_##num()
|
||||
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
CREATE_MAP(__LINE__);
|
||||
|
||||
int main() {
|
||||
}
|
193
third-party/td/td/benchmark/hashset_memory.cpp
vendored
193
third-party/td/td/benchmark/hashset_memory.cpp
vendored
@ -1,193 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#if USE_MEMPROF
|
||||
#include "memprof/memprof_stat.h"
|
||||
#endif
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/FlatHashMap.h"
|
||||
#include "td/utils/FlatHashMapChunks.h"
|
||||
#include "td/utils/FlatHashTable.h"
|
||||
#include "td/utils/HashTableUtils.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/MapNode.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#ifdef SCOPE_EXIT
|
||||
#undef SCOPE_EXIT
|
||||
#endif
|
||||
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <array>
|
||||
#include <folly/container/F14Map.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
static int mem_stat_i = -1;
|
||||
static int mem_stat_cur = 0;
|
||||
|
||||
static bool use_memprof() {
|
||||
#if USE_MEMPROF
|
||||
return mem_stat_i < 0 && is_memprof_on();
|
||||
#else
|
||||
return mem_stat_i < 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static td::uint64 get_memory() {
|
||||
#if USE_MEMPROF
|
||||
if (use_memprof()) {
|
||||
return get_used_memory_size();
|
||||
}
|
||||
#endif
|
||||
CHECK(!use_memprof());
|
||||
return td::mem_stat().ok().resident_size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class Generator {
|
||||
public:
|
||||
T next() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
static size_t dyn_size() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class IntGenerator {
|
||||
public:
|
||||
T next() {
|
||||
return ++value;
|
||||
}
|
||||
static size_t dyn_size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T value{};
|
||||
};
|
||||
|
||||
template <>
|
||||
class Generator<td::int32> final : public IntGenerator<td::int32> {};
|
||||
template <>
|
||||
class Generator<td::int64> final : public IntGenerator<td::int64> {};
|
||||
|
||||
template <class T>
|
||||
class Generator<td::unique_ptr<T>> {
|
||||
public:
|
||||
td::unique_ptr<T> next() {
|
||||
return td::make_unique<T>();
|
||||
}
|
||||
static std::size_t dyn_size() {
|
||||
return sizeof(T);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class KeyT, class ValueT>
|
||||
static void measure(td::StringBuilder &sb, td::Slice name, td::Slice key_name, td::Slice value_name) {
|
||||
mem_stat_cur++;
|
||||
if (mem_stat_i >= 0 && mem_stat_cur != mem_stat_i) {
|
||||
return;
|
||||
}
|
||||
sb << name << "<" << key_name << "," << value_name << "> " << (use_memprof() ? "memprof" : "os") << "\n";
|
||||
std::size_t ideal_size = sizeof(KeyT) + sizeof(ValueT) + Generator<ValueT>::dyn_size();
|
||||
|
||||
sb << "\tempty:" << sizeof(T);
|
||||
struct Stat {
|
||||
int pi;
|
||||
double min_ratio;
|
||||
double max_ratio;
|
||||
};
|
||||
td::vector<Stat> stat;
|
||||
stat.reserve(1024);
|
||||
for (std::size_t size : {1000000u}) {
|
||||
Generator<KeyT> key_generator;
|
||||
Generator<ValueT> value_generator;
|
||||
auto start_mem = get_memory();
|
||||
T ht;
|
||||
auto ratio = [&] {
|
||||
auto end_mem = get_memory();
|
||||
auto used_mem = end_mem - start_mem;
|
||||
return static_cast<double>(used_mem) / (static_cast<double>(ideal_size) * static_cast<double>(ht.size()));
|
||||
};
|
||||
double min_ratio;
|
||||
double max_ratio;
|
||||
auto reset = [&] {
|
||||
min_ratio = 1e100;
|
||||
max_ratio = 0;
|
||||
};
|
||||
auto update = [&] {
|
||||
auto x = ratio();
|
||||
min_ratio = td::min(min_ratio, x);
|
||||
max_ratio = td::max(max_ratio, x);
|
||||
};
|
||||
reset();
|
||||
|
||||
int p = 10;
|
||||
int pi = 1;
|
||||
for (std::size_t i = 0; i < size; i++) {
|
||||
ht.emplace(key_generator.next(), value_generator.next());
|
||||
update();
|
||||
if ((i + 1) % p == 0) {
|
||||
stat.push_back(Stat{pi, min_ratio, max_ratio});
|
||||
reset();
|
||||
pi++;
|
||||
p *= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &s : stat) {
|
||||
sb << " 10^" << s.pi << ":" << s.min_ratio << "->" << s.max_ratio;
|
||||
}
|
||||
sb << '\n';
|
||||
}
|
||||
|
||||
template <std::size_t size>
|
||||
using Bytes = std::array<char, size>;
|
||||
|
||||
template <template <typename... Args> class T>
|
||||
void print_memory_stats(td::Slice name) {
|
||||
td::string big_buff(1 << 16, '\0');
|
||||
td::StringBuilder sb(big_buff, false);
|
||||
#define MEASURE(KeyT, ValueT) measure<T<KeyT, ValueT>, KeyT, ValueT>(sb, name, #KeyT, #ValueT);
|
||||
MEASURE(td::int32, td::int32);
|
||||
MEASURE(td::int64, td::unique_ptr<Bytes<360>>);
|
||||
if (!sb.as_cslice().empty()) {
|
||||
LOG(PLAIN) << '\n' << sb.as_cslice() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class KeyT, class ValueT, class HashT = td::Hash<KeyT>, class EqT = std::equal_to<KeyT>>
|
||||
using FlatHashMapImpl = td::FlatHashTable<td::MapNode<KeyT, ValueT>, HashT, EqT>;
|
||||
|
||||
#define FOR_EACH_TABLE(F) \
|
||||
F(FlatHashMapImpl) \
|
||||
F(folly::F14FastMap) \
|
||||
F(absl::flat_hash_map) \
|
||||
F(std::unordered_map) \
|
||||
F(std::map)
|
||||
#define BENCHMARK_MEMORY(T) print_memory_stats<T>(#T);
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Usage:
|
||||
// % benchmark/memory-hashset-os 0
|
||||
// Number of benchmarks = 10
|
||||
// % for i in {1..10}; do ./benchmark/memory-hashset-os $i; done
|
||||
if (argc > 1) {
|
||||
mem_stat_i = td::to_integer<td::int32>(td::Slice(argv[1]));
|
||||
}
|
||||
FOR_EACH_TABLE(BENCHMARK_MEMORY);
|
||||
if (mem_stat_i <= 0) {
|
||||
LOG(PLAIN) << "Number of benchmarks = " << mem_stat_cur << "\n";
|
||||
}
|
||||
}
|
45
third-party/td/td/benchmark/rmdir.cpp
vendored
45
third-party/td/td/benchmark/rmdir.cpp
vendored
@ -1,45 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 1) {
|
||||
return 1;
|
||||
}
|
||||
td::CSlice dir(argv[1]);
|
||||
int cnt = 0;
|
||||
auto status = td::walk_path(dir, [&](td::CSlice path, auto type) {
|
||||
if (type != td::WalkPath::Type::EnterDir) {
|
||||
cnt++;
|
||||
}
|
||||
auto type_name = [&] {
|
||||
switch (type) {
|
||||
case td::WalkPath::Type::EnterDir:
|
||||
return td::CSlice("Open");
|
||||
case td::WalkPath::Type::ExitDir:
|
||||
return td::CSlice("Exit");
|
||||
case td::WalkPath::Type::RegularFile:
|
||||
return td::CSlice("File");
|
||||
case td::WalkPath::Type::Symlink:
|
||||
return td::CSlice("Link");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return td::CSlice();
|
||||
}
|
||||
}();
|
||||
LOG(INFO) << type_name << ' ' << path;
|
||||
//if (is_dir) {
|
||||
// td::rmdir(path);
|
||||
//} else {
|
||||
// td::unlink(path);
|
||||
//}
|
||||
});
|
||||
LOG(INFO) << status << ": " << cnt;
|
||||
}
|
43
third-party/td/td/benchmark/wget.cpp
vendored
43
third-party/td/td/benchmark/wget.cpp
vendored
@ -1,43 +0,0 @@
|
||||
//
|
||||
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2025
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
#include "td/net/HttpQuery.h"
|
||||
#include "td/net/Wget.h"
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "td/actor/ConcurrentScheduler.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Promise.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
|
||||
td::VERBOSITY_NAME(fd) = VERBOSITY_NAME(INFO);
|
||||
|
||||
td::string url = (argc > 1 ? argv[1] : "https://telegram.org");
|
||||
auto timeout = 10;
|
||||
auto ttl = 3;
|
||||
auto prefer_ipv6 = (argc > 2 && td::string(argv[2]) == "-6");
|
||||
auto scheduler = td::make_unique<td::ConcurrentScheduler>(0, 0);
|
||||
scheduler
|
||||
->create_actor_unsafe<td::Wget>(0, "Client",
|
||||
td::PromiseCreator::lambda([](td::Result<td::unique_ptr<td::HttpQuery>> res) {
|
||||
if (res.is_error()) {
|
||||
LOG(FATAL) << res.error();
|
||||
}
|
||||
LOG(ERROR) << *res.ok();
|
||||
td::Scheduler::instance()->finish();
|
||||
}),
|
||||
url, td::Auto(), timeout, ttl, prefer_ipv6)
|
||||
.release();
|
||||
scheduler->start();
|
||||
while (scheduler->run_main(10)) {
|
||||
// empty
|
||||
}
|
||||
scheduler->finish();
|
||||
}
|
1139
third-party/td/td/build.html
vendored
1139
third-party/td/td/build.html
vendored
File diff suppressed because it is too large
Load Diff
321
third-party/td/td/example/README.md
vendored
321
third-party/td/td/example/README.md
vendored
@ -1,321 +0,0 @@
|
||||
# TDLib usage and build examples
|
||||
|
||||
This directory contains basic examples of TDLib usage from different programming languages and examples of library building for different platforms.
|
||||
If you are looking for documentation of all available TDLib methods, see the [td_api.tl](https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl) scheme or the
|
||||
automatically generated [HTML documentation](https://core.telegram.org/tdlib/docs/td__api_8h.html) for a list of all available TDLib
|
||||
[methods](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_function.html) and [classes](https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_object.html).
|
||||
Also, take a look at our [Getting Started](https://core.telegram.org/tdlib/getting-started) tutorial for a description of basic TDLib concepts.
|
||||
|
||||
TDLib can be easily used from almost any programming language on any platform. See a [TDLib build instructions generator](https://tdlib.github.io/td/build.html) for detailed instructions on how to build TDLib.
|
||||
Choose your preferred programming language to see examples of usage and a detailed description:
|
||||
|
||||
- [Python](#python)
|
||||
- [JavaScript](#javascript)
|
||||
- [Go](#go)
|
||||
- [Java](#java)
|
||||
- [Kotlin](#kotlin)
|
||||
- [C#](#csharp)
|
||||
- [C++](#cxx)
|
||||
- [Swift](#swift)
|
||||
- [Objective-C](#objective-c)
|
||||
- [Object Pascal](#object-pascal)
|
||||
- [Dart](#dart)
|
||||
- [Rust](#rust)
|
||||
- [Erlang](#erlang)
|
||||
- [PHP](#php)
|
||||
- [Lua](#lua)
|
||||
- [Ruby](#ruby)
|
||||
- [Crystal](#crystal)
|
||||
- [Haskell](#haskell)
|
||||
- [Nim](#nim)
|
||||
- [Clojure](#clojure)
|
||||
- [Emacs Lisp](#emacslisp)
|
||||
- [D](#d)
|
||||
- [Elixir](#elixir)
|
||||
- [Vala](#vala)
|
||||
- [1С](#1s)
|
||||
- [C](#c)
|
||||
- [G](#g)
|
||||
- [Other](#other)
|
||||
|
||||
<a name="python"></a>
|
||||
## Using TDLib in Python projects
|
||||
|
||||
TDLib can be used from Python through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
Convenient Python wrappers already exist for our JSON interface.
|
||||
|
||||
If you use Python >= 3.6, take a look at [python-telegram](https://github.com/alexander-akhmetov/python-telegram).
|
||||
The wrapper uses the full power of asyncio, has a good documentation and has several examples. It can be installed through pip or used in a Docker container.
|
||||
You can also try a fork [python-telegram](https://github.com/iTeam-co/pytglib) of this library.
|
||||
|
||||
If you want to use TDLib with asyncio and Python >= 3.9, take a look at [aiotdlib](https://github.com/pylakey/aiotdlib) or [Pytdbot](https://github.com/pytdbot/client).
|
||||
|
||||
For older Python versions you can use [pytdlib](https://github.com/pytdlib/pytdlib).
|
||||
This wrapper contains generator for TDLib API classes and basic interface for interaction with TDLib.
|
||||
|
||||
You can also check out [example/python/tdjson_example.py](https://github.com/tdlib/td/blob/master/example/python/tdjson_example.py),
|
||||
[tdlib-python](https://github.com/JunaidBabu/tdlib-python), or [Python Wrapper TDLib](https://github.com/alvhix/pywtdlib) for some basic examples of TDLib JSON interface integration with Python.
|
||||
|
||||
<a name="javascript"></a>
|
||||
## Using TDLib in JavaScript projects
|
||||
|
||||
TDLib can be compiled to WebAssembly and used in a browser from JavaScript. See [tdweb](https://github.com/tdlib/td/tree/master/example/web) as a convenient wrapper for TDLib in a browser
|
||||
and [telegram-react](https://github.com/evgeny-nadymov/telegram-react) as an example of a TDLib-based Telegram client.
|
||||
|
||||
See also [Svelte-tdweb-starter](https://github.com/gennadypolakov/svelte-tdweb-starter) - Svelte wrapper for tdweb, and [Telegram-Photoframe](https://github.com/lukefx/telegram-photoframe) - a web application that displays your preferred group or channel as Photoframe.
|
||||
|
||||
TDLib can be used from Node.js through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
Convenient Node.js wrappers already exist for our JSON interface.
|
||||
For example, take a look at [Airgram](https://github.com/airgram/airgram) – modern TDLib framework for TypeScript/JavaScript, or
|
||||
at [tdl](https://github.com/eilvelia/tdl), which provides a convenient, fully-asynchronous interface for interaction with TDLib and contains a bunch of examples.
|
||||
|
||||
You can also see [TdNode](https://github.com/puppy0cam/TdNode), [tglib](https://github.com/nodegin/tglib), [node-tdlib](https://github.com/wfjsw/node-tdlib), [tdlnode](https://github.com/fonbah/tdlnode),
|
||||
[Paper Plane](https://github.com/par6n/paper-plane), or [node-tlg](https://github.com/dilongfa/node-tlg) for other examples of TDLib JSON interface integration with Node.js.
|
||||
|
||||
See also the source code of [DIBgram](https://github.com/DIBgram/DIBgram) - an unofficial Telegram web application which looks like Telegram Desktop.
|
||||
|
||||
TDLib can be used also from NativeScript through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
See [nativescript-tglib](https://github.com/arpit2438735/nativescript-tglib) as an example of a NativeScript library for building Telegram clients.
|
||||
|
||||
<a name="go"></a>
|
||||
## Using TDLib in Go projects
|
||||
|
||||
TDLib can be used from the Go programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and Cgo, and can be linked either statically or dynamically.
|
||||
|
||||
Convenient Go wrappers already exist for our JSON interface.
|
||||
For example, take a look at [github.com/zelenin/go-tdlib](https://github.com/zelenin/go-tdlib) or [github.com/Arman92/go-tdlib](https://github.com/Arman92/go-tdlib), which provide a convenient TDLib client, a generator for TDLib API classes and contain many examples.
|
||||
|
||||
You can also see [github.com/aliforever/go-tdlib](https://github.com/aliforever/go-tdlib) for another examples of TDLib JSON interface integration with Go.
|
||||
|
||||
<a name="java"></a>
|
||||
## Using TDLib in Java projects
|
||||
|
||||
TDLib can be used from the Java programming language through native [JNI](https://github.com/tdlib/td#using-java) binding.
|
||||
|
||||
We provide a generator for JNI bridge methods and Java classes for all TDLib API methods and objects.
|
||||
See [example/java](https://github.com/tdlib/td/tree/master/example/java) for an example of using TDLib from desktop Java along with detailed building and usage instructions.
|
||||
See [example/android](https://github.com/tdlib/td/tree/master/example/android) for detailed build instructions for Android.
|
||||
|
||||
<a name="kotlin"></a>
|
||||
## Using TDLib in Kotlin projects
|
||||
|
||||
TDLib can be used from the Kotlin/JVM programming language through same way as in [Java](#java).
|
||||
|
||||
You can also use [ktd](https://github.com/whyoleg/ktd) library with Kotlin-specific bindings.
|
||||
|
||||
See also [td-ktx](https://github.com/tdlibx/td-ktx) - Kotlin coroutines wrapper for TDLib.
|
||||
|
||||
<a name="csharp"></a>
|
||||
## Using TDLib in C# projects
|
||||
|
||||
TDLib provides a native [.NET](https://github.com/tdlib/td#using-dotnet) interface through `C++/CLI` and `C++/CX`.
|
||||
See [tdlib-netcore](https://github.com/dantmnf/tdlib-netcore) for a SWIG-like binding with automatically generated classes for TDLib API.
|
||||
See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for an example of building TDLib SDK for the Universal Windows Platform and an example of its usage from C#.
|
||||
See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for an example of building TDLib with `C++/CLI` support and an example of TDLib usage from C# on Windows.
|
||||
|
||||
If you want to write a cross-platform C# application using .NET Core, see [tdsharp](https://github.com/egramtel/tdsharp). It uses our [JSON](https://github.com/tdlib/td#using-json) interface,
|
||||
provides an asynchronous interface for interaction with TDLib, automatically generated classes for TDLib API and has some examples.
|
||||
|
||||
Also, see [Unigram](https://github.com/UnigramDev/Unigram), which is a full-featured client rewritten from scratch in C# using TDLib SDK for Universal Windows Platform in less than 2 months,
|
||||
[egram.tel](https://github.com/egramtel/egram.tel) – a cross-platform Telegram client written in C#, .NET Core, ReactiveUI and Avalonia, or
|
||||
[telewear](https://github.com/telewear/telewear) - a Telegram client for Samsung watches.
|
||||
|
||||
<a name="cxx"></a>
|
||||
## Using TDLib in C++ projects
|
||||
|
||||
TDLib has a simple and convenient C++11-interface for sending and receiving requests and can be statically linked to your application.
|
||||
|
||||
See [example/cpp](https://github.com/tdlib/td/tree/master/example/cpp) for an example of TDLib usage from C++.
|
||||
[td_example.cpp](https://github.com/tdlib/td/blob/master/example/cpp/td_example.cpp) contains an example of authorization, processing new incoming messages, getting a list of chats and sending a text message.
|
||||
|
||||
See also the source code of [Fernschreiber](https://github.com/Wunderfitz/harbour-fernschreiber) and [Depecher](https://github.com/blacksailer/depecher) – Telegram apps for Sailfish OS,
|
||||
[TELEports](https://gitlab.com/ubports/development/apps/teleports) – a Qt-client for Ubuntu Touch, [tdlib-purple](https://github.com/ars3niy/tdlib-purple) - Telegram plugin for Pidgin,
|
||||
or [MeeGram](https://github.com/qtinsider/meegram2) - a Telegram client for Nokia N9,
|
||||
[TDLib Native Sciter Extension](https://github.com/EricKotato/TDLibNSE) - a Sciter native extension for TDLib's JSON interface, all of which are based on TDLib.
|
||||
|
||||
<a name="swift"></a>
|
||||
## Using TDLib in Swift projects
|
||||
|
||||
TDLib can be used from the Swift programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
|
||||
|
||||
See [example/ios](https://github.com/tdlib/td/tree/master/example/ios) for an example of building TDLib for iOS, watchOS, tvOS, visionOS, and macOS.
|
||||
|
||||
See [TDLibKit](https://github.com/Swiftgram/TDLibKit), [tdlib-swift](https://github.com/modestman/tdlib-swift), or [TDLib-iOS](https://github.com/leoMehlig/TDLib-iOS), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See also the source code of [Moc](https://github.com/mock-foundation/moc) - a native and powerful macOS and iPadOS Telegram client, optimized for moderating large communities and personal use.
|
||||
|
||||
See [example/swift](https://github.com/tdlib/td/tree/master/example/swift) for an example of a macOS Swift application.
|
||||
|
||||
<a name="objective-c"></a>
|
||||
## Using TDLib in Objective-C projects
|
||||
|
||||
TDLib can be used from the Objective-C programming language through [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
|
||||
|
||||
See [example/ios](https://github.com/tdlib/td/tree/master/example/ios) for an example of building TDLib for iOS, watchOS, tvOS, visionOS, and macOS.
|
||||
|
||||
<a name="object-pascal"></a>
|
||||
## Using TDLib in Object Pascal projects with Delphi and Lazarus
|
||||
|
||||
TDLib can be used from the Object Pascal programming language through the [JSON](https://github.com/tdlib/td#using-json).
|
||||
|
||||
See [tdlib-delphi](https://github.com/dieletro/tdlib-delphi) for an example of TDLib usage from Delphi.
|
||||
|
||||
See [tdlib-lazarus](https://github.com/dieletro/tdlib-lazarus) for an example of TDLib usage from Lazarus.
|
||||
|
||||
<a name="dart"></a>
|
||||
## Using TDLib in Dart projects
|
||||
|
||||
TDLib can be used from the Dart programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and a Dart Native Extension or Dart FFI.
|
||||
|
||||
See [tdlib-dart](https://github.com/ivk1800/tdlib-dart), which provide convenient TDLib client with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See also [dart_tdlib](https://github.com/periodicaidan/dart_tdlib), [flutter_libtdjson](https://github.com/up9cloud/flutter_libtdjson), [Dart wrapper for TDLib](https://github.com/tdlib/td/pull/708/commits/237060abd4c205768153180e9f814298d1aa9d49), or [tdlib_bindings](https://github.com/lesnitsky/tdlib_bindings) for an example of a TDLib Dart bindings through FFI.
|
||||
|
||||
See [Telegram Client library](https://github.com/azkadev/telegram_client), [project.scarlet](https://github.com/aaugmentum/project.scarlet), [tdlib](https://github.com/i-Naji/tdlib),
|
||||
[tdlib-dart](https://github.com/drewpayment/tdlib-dart), [FluGram](https://github.com/triedcatched/tdlib-dart), or [telegram-service](https://github.com/igorder-dev/telegram-service) for examples of using TDLib from Dart.
|
||||
|
||||
See also [telegram-flutter](https://github.com/ivk1800/telegram-flutter) - Telegram client written in Dart, and [f-Telegram](https://github.com/evgfilim1/ftg) - Flutter Telegram client.
|
||||
|
||||
<a name="rust"></a>
|
||||
## Using TDLib in Rust projects
|
||||
|
||||
TDLib can be used from the Rust programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [rust-tdlib](https://github.com/antonio-antuan/rust-tdlib), or [tdlib](https://github.com/paper-plane-developers/tdlib-rs), which provide convenient TDLib clients with automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
See [rtdlib](https://github.com/fewensa/rtdlib), [tdlib-rs](https://github.com/d653/tdlib-rs), [tdlib-futures](https://github.com/yuri91/tdlib-futures),
|
||||
[tdlib-sys](https://github.com/nuxeh/tdlib-sys), [tdjson-rs](https://github.com/mersinvald/tdjson-rs), [rust-tdlib](https://github.com/vhaoran/rust-tdlib), or [tdlib-json-sys](https://github.com/aykxt/tdlib-json-sys) for examples of TDLib Rust bindings.
|
||||
|
||||
Also, see [Paper Plane](https://github.com/paper-plane-developers/paper-plane) – a Telegram client written in Rust and GTK.
|
||||
|
||||
<a name="erlang"></a>
|
||||
## Using TDLib in Erlang projects
|
||||
|
||||
TDLib can be used from the Erlang programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [erl-tdlib](https://github.com/lattenwald/erl-tdlib) for an example of TDLib Erlang bindings.
|
||||
|
||||
<a name="php"></a>
|
||||
## Using TDLib in PHP projects
|
||||
|
||||
If you use modern PHP >= 7.4, you can use TDLib via a PHP FFI extension. For example, take a look at [ffi-tdlib](https://github.com/aurimasniekis/php-ffi-tdlib), or [tdlib-php-ffi](https://github.com/thisismzm/tdlib-php-ffi) - FFI-based TDLib wrappers.
|
||||
|
||||
See also [tdlib-schema](https://github.com/aurimasniekis/php-tdlib-schema) - a generator for TDLib API classes.
|
||||
|
||||
For older PHP versions you can use TDLib by wrapping its functionality in a PHP extension.
|
||||
|
||||
See [phptdlib](https://github.com/yaroslavche/phptdlib), [tdlib](https://github.com/aurimasniekis/php-ext-tdlib), or [PIF-TDPony](https://github.com/danog/pif-tdpony)
|
||||
for examples of such extensions which provide access to TDLib from PHP.
|
||||
|
||||
See [tdlib-bundle](https://github.com/yaroslavche/tdlib-bundle) – a Symfony bundle based on [phptdlib](https://github.com/yaroslavche/phptdlib).
|
||||
|
||||
<a name="lua"></a>
|
||||
## Using TDLib in Lua projects
|
||||
|
||||
TDLib can be used from the Lua programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [luajit-tdlib](https://github.com/Rami-Sabbagh/luajit-tdlib), [tdlua](https://github.com/giuseppeM99/tdlua), or
|
||||
[luajit-tdlib](https://github.com/Playermet/luajit-tdlib) for examples of TDLib Lua bindings and basic usage examples.
|
||||
|
||||
See also [tdbot](https://github.com/vysheng/tdbot), which makes all TDLib features available from Lua scripts.
|
||||
|
||||
<a name="d"></a>
|
||||
## Using TDLib in D projects
|
||||
|
||||
TDLib can be used from the D programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [d-tdlib-service](https://github.com/Lord-Evil/d-tdlib-service) for an example of TDLib D bindings.
|
||||
|
||||
<a name="ruby"></a>
|
||||
## Using TDLib in Ruby projects
|
||||
|
||||
TDLib can be used from the Ruby programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [tdlib-ruby](https://github.com/southbridgeio/tdlib-ruby) for examples of Ruby bindings and a client for TDLib.
|
||||
|
||||
<a name="Crystal"></a>
|
||||
## Using TDLib in Crystal projects
|
||||
|
||||
TDLib can be used from the Crystal programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [Proton](https://github.com/protoncr/proton) for examples of Crystal bindings with automatically generated types for all TDLib API methods and objects.
|
||||
|
||||
<a name="haskell"></a>
|
||||
## Using TDLib in Haskell projects
|
||||
|
||||
TDLib can be used from the Haskell programming language.
|
||||
|
||||
See [haskell-tdlib](https://github.com/mejgun/haskell-tdlib) or [tdlib](https://github.com/poscat0x04/tdlib) for examples of such usage and Haskell wrappers for TDLib.
|
||||
This library contains automatically generated Haskell types for all TDLib API methods and objects.
|
||||
|
||||
<a name="nim"></a>
|
||||
## Using TDLib in Nim projects
|
||||
|
||||
TDLib can be used from the Nim programming language.
|
||||
|
||||
See [telenim](https://github.com/Ethosa/telenim) for example of such usage and a Nim wrapper for TDLib.
|
||||
|
||||
<a name="clojure"></a>
|
||||
## Using TDLib in Clojure projects
|
||||
|
||||
TDLib can be used from the Clojure programming language through the [JSON](https://github.com/tdlib/td#using-json) interface.
|
||||
|
||||
See [clojure-tdlib-json-wrapper](https://github.com/MityaSaray/clojure-tdlib-json) for an example of TDLib Clojure bindings.
|
||||
|
||||
<a name="emacslisp"></a>
|
||||
## Using TDLib in Emacs Lisp projects
|
||||
|
||||
TDLib can be used from the Emacs Lisp programming language.
|
||||
|
||||
See [telega.el](https://github.com/zevlg/telega.el) for an example of a GNU Emacs Telegram client.
|
||||
|
||||
<a name="elixir"></a>
|
||||
## Using TDLib in Elixir projects
|
||||
|
||||
TDLib can be used from the Elixir programming language.
|
||||
|
||||
See [Elixir TDLib](https://github.com/QuantLayer/elixir-tdlib) for an example of such usage and an Elixir client for TDLib.
|
||||
The library contains automatically generated and fully-documented classes for all TDLib API methods and objects.
|
||||
|
||||
<a name="vala"></a>
|
||||
## Using TDLib in Vala projects
|
||||
|
||||
TDLib can be used from the Vala programming language.
|
||||
|
||||
See [TDLib Vala](https://github.com/AYMENJD/td-vala) for an example of such usage.
|
||||
|
||||
<a name="1s"></a>
|
||||
## Using TDLib from 1С:Enterprise
|
||||
|
||||
TDLib can be used from the 1С programming language.
|
||||
|
||||
See [TDLib bindings for 1С:Enterprise](https://github.com/Infactum/telegram-native) and [e1c.tAgents](https://github.com/fedbka/e1c.tAgents) for examples of such usage.
|
||||
|
||||
<a name="c"></a>
|
||||
## Using TDLib in C projects
|
||||
|
||||
TDLib can be used from the C programming language through the [JSON](https://github.com/tdlib/td#using-json) interface and can be linked statically or dynamically.
|
||||
|
||||
See [easy-tg](https://github.com/Trumeet/easy-tg) for an example of such usage.
|
||||
|
||||
You can also try to use our [C](https://github.com/tdlib/td/blob/master/td/telegram/td_c_client.h) client, which was used by the private TDLib-based version of [telegram-cli](https://github.com/vysheng/tg).
|
||||
|
||||
<a name="g"></a>
|
||||
## Using TDLib from G projects
|
||||
|
||||
TDLib can be used from the G graphical programming language in LabVIEW development environment.
|
||||
|
||||
See [TDLib bindings for LabVIEW](https://github.com/IvanLisRus/Telegram-Client_TDLib) for examples of such usage.
|
||||
|
||||
<a name="other"></a>
|
||||
## Using TDLib from other programming languages
|
||||
|
||||
You can use TDLib from any other programming language using [tdbot](https://github.com/vysheng/tdbot) or [TDLib JSON CLI](https://github.com/oott123/tdlib-json-cli),
|
||||
which provide a command line tool for interaction with TDLIb using the [JSON](https://github.com/tdlib/td#using-json) interface through stdin and stdout.
|
||||
You can use this method to use TDLib, for example, from Brainfuck (unfortunately, we haven't seen examples of sending a Telegram message through TDLib on Brainfuck yet).
|
||||
|
||||
Alternatively, you can use the TDLib [JSON](https://github.com/tdlib/td#using-json) interface directly from your programming language.
|
||||
|
||||
Feel free to create an issue, if you have created a valuable TDLib binding or a TDLib client in some programming language and want it to be added to this list of examples.
|
3
third-party/td/td/example/android/.gitignore
vendored
3
third-party/td/td/example/android/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
/SDK*
|
||||
/tdlib*
|
||||
/third-party*
|
51
third-party/td/td/example/android/AddIntDef.php
vendored
51
third-party/td/td/example/android/AddIntDef.php
vendored
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
if ($argc !== 2) {
|
||||
exit();
|
||||
}
|
||||
$file = file_get_contents($argv[1]);
|
||||
|
||||
if (strpos($file, 'androidx.annotation.IntDef') !== false) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$file = str_replace('import androidx.annotation.Nullable;', 'import androidx.annotation.IntDef;'.PHP_EOL.
|
||||
'import androidx.annotation.Nullable;'.PHP_EOL.
|
||||
PHP_EOL.
|
||||
'import java.lang.annotation.Retention;'.PHP_EOL.
|
||||
'import java.lang.annotation.RetentionPolicy;'.PHP_EOL, $file);
|
||||
|
||||
preg_match_all('/public static class ([A-Za-z0-9]+) extends ([A-Za-z0-9]+)/', $file, $matches, PREG_SET_ORDER);
|
||||
$children = [];
|
||||
foreach ($matches as $val) {
|
||||
if ($val[2] === 'Object') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$children[$val[2]][] = ' '.$val[1].'.CONSTRUCTOR';
|
||||
}
|
||||
|
||||
$file = preg_replace_callback('/public abstract static class ([A-Za-z0-9]+)(<R extends Object>)? extends Object [{]/',
|
||||
function ($val) use ($children) {
|
||||
$values = implode(','.PHP_EOL, $children[$val[1]]);
|
||||
return $val[0].<<<EOL
|
||||
|
||||
/**
|
||||
* Describes possible values returned by getConstructor().
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
$values
|
||||
})
|
||||
public @interface Constructors {}
|
||||
|
||||
/**
|
||||
* @return identifier uniquely determining type of the object.
|
||||
*/
|
||||
@Constructors
|
||||
@Override
|
||||
public abstract int getConstructor();
|
||||
EOL;
|
||||
},
|
||||
$file);
|
||||
|
||||
file_put_contents($argv[1], $file);
|
72
third-party/td/td/example/android/CMakeLists.txt
vendored
72
third-party/td/td/example/android/CMakeLists.txt
vendored
@ -1,72 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
|
||||
|
||||
project(TdAndroid VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
set(TD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
||||
|
||||
option(TD_ANDROID_JSON "Use \"ON\" to build JSON interface.")
|
||||
option(TD_ANDROID_JSON_JAVA "Use \"ON\" to build Java wrapper for JSON API.")
|
||||
|
||||
if (TD_ANDROID_JSON)
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -flto=thin -Oz")
|
||||
list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}")
|
||||
endif()
|
||||
add_subdirectory(${TD_DIR} td)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (NOT TD_ANDROID_JSON_JAVA)
|
||||
option(TD_ENABLE_JNI "Enable JNI-compatible TDLib API" ON)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_MODULE_PATH "${TD_DIR}/CMake")
|
||||
|
||||
include(TdSetUpCompiler)
|
||||
td_set_up_compiler()
|
||||
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -flto=thin -Oz")
|
||||
|
||||
list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}")
|
||||
add_subdirectory(${TD_DIR} td)
|
||||
|
||||
add_library(tdjni SHARED "${TD_DIR}/example/java/td_jni.cpp")
|
||||
|
||||
if (TD_ANDROID_JSON_JAVA)
|
||||
target_link_libraries(tdjni PRIVATE Td::TdJsonStatic)
|
||||
target_compile_definitions(tdjni PRIVATE TD_JSON_JAVA=1)
|
||||
set_target_properties(tdjni PROPERTIES OUTPUT_NAME "tdjsonjava")
|
||||
else()
|
||||
target_link_libraries(tdjni PRIVATE Td::TdStatic)
|
||||
endif()
|
||||
target_compile_definitions(tdjni PRIVATE PACKAGE_NAME="org/drinkless/tdlib")
|
||||
|
||||
add_custom_command(TARGET tdjni POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rename $<TARGET_FILE:tdjni> $<TARGET_FILE:tdjni>.debug
|
||||
COMMAND ${CMAKE_STRIP} --strip-debug --strip-unneeded $<TARGET_FILE:tdjni>.debug -o $<TARGET_FILE:tdjni>)
|
||||
else()
|
||||
add_subdirectory(${TD_DIR} td)
|
||||
|
||||
if (TD_ANDROID_JSON_JAVA)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib")
|
||||
set(TD_API_JAVA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${TD_API_JAVA_PACKAGE}/TdApi.java")
|
||||
set(TD_API_TLO_PATH "${TD_DIR}/td/generate/auto/tlo/td_api.tlo")
|
||||
set(TD_API_TL_PATH "${TD_DIR}/td/generate/scheme/td_api.tl")
|
||||
set(JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH "${TD_DIR}/td/generate/JavadocTlDocumentationGenerator.php")
|
||||
set(GENERATE_JAVA_CMD td_generate_java_api TdApi ${TD_API_TLO_PATH} ${CMAKE_CURRENT_SOURCE_DIR} ${TD_API_JAVA_PACKAGE})
|
||||
if (PHP_EXECUTABLE)
|
||||
set(GENERATE_JAVA_CMD ${GENERATE_JAVA_CMD} &&
|
||||
${PHP_EXECUTABLE} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH} ${TD_API_TL_PATH} ${TD_API_JAVA_PATH} androidx.annotation.Nullable @Nullable &&
|
||||
${PHP_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/AddIntDef.php ${TD_API_JAVA_PATH})
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${TD_API_JAVA_PACKAGE})
|
||||
add_custom_target(tl_generate_java
|
||||
COMMAND ${GENERATE_JAVA_CMD}
|
||||
COMMENT "Generate Java TL source files"
|
||||
DEPENDS td_generate_java_api tl_generate_tlo ${TD_API_TLO_PATH} ${TD_API_TL_PATH}
|
||||
)
|
||||
endif()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user