Remove embedded td

This commit is contained in:
Isaac 2025-04-11 20:08:41 +04:00
parent c6ff9717d4
commit 07037a82e7
1516 changed files with 0 additions and 712076 deletions

View File

@ -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
...

View File

@ -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

View File

@ -1,8 +0,0 @@
**/*build*/
**/.*.swp
**/.DS_Store
**/auto/
docs/
/tdlib/
vcpkg/
.clang-tidy

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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.

View File

@ -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);
}

View File

@ -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()

View File

@ -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()

View File

@ -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));
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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() {
}

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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());
}

View File

@ -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 &sum;
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
}

View File

@ -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
}

View File

@ -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());
}

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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() {
}

View File

@ -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";
}
}

View File

@ -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;
}

View File

@ -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();
}

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -1,3 +0,0 @@
/SDK*
/tdlib*
/third-party*

View File

@ -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);

View File

@ -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()

View File

@ -1,28 +0,0 @@
FROM --platform=linux/amd64 ubuntu:24.04 AS build
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yq default-jdk g++ git gperf make perl php-cli unzip wget && rm -rf /var/lib/apt/lists/*
WORKDIR /home
ARG ANDROID_NDK_VERSION=23.2.8568313
COPY ./check-environment.sh ./fetch-sdk.sh ./
RUN ./fetch-sdk.sh SDK "$ANDROID_NDK_VERSION"
ARG OPENSSL_VERSION=OpenSSL_1_1_1w
ARG BUILD_SHARED_OPENSSL_LIBS=
COPY ./build-openssl.sh ./
RUN ./build-openssl.sh SDK "$ANDROID_NDK_VERSION" openssl "$OPENSSL_VERSION" "$BUILD_SHARED_OPENSSL_LIBS"
ADD "https://api.github.com/repos/tdlib/td/git/refs/heads/master" version.json
ARG COMMIT_HASH=master
RUN git clone https://github.com/tdlib/td.git && cd td && git checkout "$COMMIT_HASH"
RUN cd td && git merge-base --is-ancestor 872d8ebd3ba9d922169839e6a24cee08b02b328a "$COMMIT_HASH"
ARG ANDROID_STL=c++_static
ARG TDLIB_INTERFACE=Java
RUN td/example/android/build-tdlib.sh SDK "$ANDROID_NDK_VERSION" openssl "$ANDROID_STL" "$TDLIB_INTERFACE" && rm -rf td/example/android/build-*
FROM scratch
COPY --from=build /home/td/example/android/tdlib/tdlib* /

View File

@ -1,32 +0,0 @@
# TDLib Android example
This is an example of building `TDLib` for Android.
You need a Bash shell on Linux, macOS, or Windows with some common tools, a C++ compiler, JDK, PHP, perl, and gperf pre-installed.
## Building TDLib for Android
* Run the script `./check-environment.sh` to check that you have all required Unix tools and Java utilities. If the script exits with an error message, install the missing tool.
* Run the script `./fetch-sdk.sh` to download Android SDK to a local directory.
* Run the script `./build-openssl.sh` to download and build OpenSSL for Android.
* Run the script `./build-tdlib.sh` to build TDLib for Android.
* The built libraries are now located in the `tdlib/libs` directory. If [Java](https://github.com/tdlib/td#using-java) interface was built, then corresponding Java code is located in the `tdlib/java` directory, and standalone Java documentation can be found in the `tdlib/javadoc` directory. You can also use archives `tdlib/tdlib.zip` and `tdlib/tdlib-debug.zip`, which contain all aforementioned data.
If you already have installed Android SDK and NDK, you can skip the second step and specify existing Android SDK root path and Android NDK version as the first and the second parameters to the subsequent scripts. Make sure that the SDK includes android-34 platform and CMake 3.22.1.
If you already have prebuilt OpenSSL, you can skip the third step and specify path to the prebuild OpenSSL as the third parameter to the script `./build-tdlib.sh`.
If you want to update TDLib to a newer version, you need to run only the script `./build-tdlib.sh`.
You can specify different OpenSSL version as the fourth parameter to the script `./build-openssl.sh`. By default OpenSSL 1.1.1 is used because of much smaller binary footprint and better performance than newer OpenSSL versions.
You can build shared OpenSSL libraries instead of static ones by passing any non-empty string as the fifth parameter to the script `./build-openssl.sh`. This can reduce total application size if you have a lot of other code that uses OpenSSL and want it to use the same shared library.
You can build TDLib against shared standard C++ library by specifying "c++_shared" as the fourth parameter to the script `./build-tdlib.sh`. This can reduce total application size if you have a lot of other C++ code and want it to use the same shared library.
You can also build TDLib with [JSON interface](https://github.com/tdlib/td#using-json) instead of [Java](https://github.com/tdlib/td#using-java) interface by passing "JSON" as the fifth parameter to the script `./build-tdlib.sh`.
You can also build TDLib with [JSON interface](https://github.com/tdlib/td#using-json) that can be called from Java by passing "JSONJava" as the fifth parameter to the script `./build-tdlib.sh`.
You can pass an empty string instead of any script parameter to use its default value. For example, you can use the command `./build-tdlib.sh '' '' '' '' 'JSON'` to build TDLib with [JSON interface](https://github.com/tdlib/td#using-json) using default values for other parameters.
Alternatively, you can use Docker to build TDLib for Android. Use `docker build --output tdlib .` to build the latest TDLib commit from Github, or `docker build --build-arg COMMIT_HASH=<commit-hash> --output tdlib .` to build specific commit. The output archives will be placed in the directory "tdlib" as specified. Additionally, you can specify build arguments "TDLIB_INTERFACE", "ANDROID_NDK_VERSION", "OPENSSL_VERSION", "BUILD_SHARED_OPENSSL_LIBS", and "ANDROID_STL" to the provided Dockerfile. For example, use `docker build --build-arg TDLIB_INTERFACE=JSON --output tdlib .` to build the latest TDLib with JSON interface.

View File

@ -1,93 +0,0 @@
#!/usr/bin/env bash
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
OPENSSL_INSTALL_DIR=${3:-third-party/openssl}
OPENSSL_VERSION=${4:-OpenSSL_1_1_1w} # openssl-3.3.0
BUILD_SHARED_LIBS=$5
if [ ! -d "$ANDROID_SDK_ROOT" ] ; then
echo "Error: directory \"$ANDROID_SDK_ROOT\" doesn't exist. Run ./fetch-sdk.sh first, or provide a valid path to Android SDK."
exit 1
fi
if [ -e "$OPENSSL_INSTALL_DIR" ] ; then
echo "Error: file or directory \"$OPENSSL_INSTALL_DIR\" already exists. Delete it manually to proceed."
exit 1
fi
source ./check-environment.sh || exit 1
if [[ "$OS_NAME" == "win" ]] && [[ "$BUILD_SHARED_LIBS" ]] ; then
echo "Error: OpenSSL shared libraries can't be built on Windows because of 'The command line is too long.' error during build. You can run the script in WSL instead."
exit 1
fi
mkdir -p $OPENSSL_INSTALL_DIR || exit 1
ANDROID_SDK_ROOT="$(cd "$(dirname -- "$ANDROID_SDK_ROOT")" >/dev/null; pwd -P)/$(basename -- "$ANDROID_SDK_ROOT")"
OPENSSL_INSTALL_DIR="$(cd "$(dirname -- "$OPENSSL_INSTALL_DIR")" >/dev/null; pwd -P)/$(basename -- "$OPENSSL_INSTALL_DIR")"
cd $(dirname $0)
echo "Downloading OpenSSL sources..."
rm -f $OPENSSL_VERSION.tar.gz || exit 1
$WGET https://github.com/openssl/openssl/archive/refs/tags/$OPENSSL_VERSION.tar.gz || exit 1
rm -rf ./openssl-$OPENSSL_VERSION || exit 1
tar xzf $OPENSSL_VERSION.tar.gz || exit 1
rm $OPENSSL_VERSION.tar.gz || exit 1
cd openssl-$OPENSSL_VERSION
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION # for OpenSSL 3.*.*
export ANDROID_NDK_HOME=$ANDROID_NDK_ROOT # for OpenSSL 1.1.1
PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin:$PATH
if ! clang --help >/dev/null 2>&1 ; then
echo "Error: failed to run clang from Android NDK."
if [[ "$OS_NAME" == "linux" ]] ; then
echo "Prebuilt Android NDK binaries are linked against glibc, so glibc must be installed."
fi
exit 1
fi
ANDROID_API32=16
ANDROID_API64=21
if [[ ${ANDROID_NDK_VERSION%%.*} -ge 24 ]] ; then
ANDROID_API32=19
fi
if [[ ${ANDROID_NDK_VERSION%%.*} -ge 26 ]] ; then
ANDROID_API32=21
fi
SHARED_BUILD_OPTION=$([ "$BUILD_SHARED_LIBS" ] && echo "shared" || echo "no-shared")
for ABI in arm64-v8a armeabi-v7a x86_64 x86 ; do
if [[ $ABI == "x86" ]] ; then
./Configure android-x86 ${SHARED_BUILD_OPTION} -U__ANDROID_API__ -D__ANDROID_API__=$ANDROID_API32 || exit 1
elif [[ $ABI == "x86_64" ]] ; then
./Configure android-x86_64 ${SHARED_BUILD_OPTION} -U__ANDROID_API__ -D__ANDROID_API__=$ANDROID_API64 || exit 1
elif [[ $ABI == "armeabi-v7a" ]] ; then
./Configure android-arm ${SHARED_BUILD_OPTION} -U__ANDROID_API__ -D__ANDROID_API__=$ANDROID_API32 -D__ARM_MAX_ARCH__=8 || exit 1
elif [[ $ABI == "arm64-v8a" ]] ; then
./Configure android-arm64 ${SHARED_BUILD_OPTION} -U__ANDROID_API__ -D__ANDROID_API__=$ANDROID_API64 || exit 1
fi
sed -i.bak 's/-O3/-O3 -ffunction-sections -fdata-sections/g' Makefile || exit 1
make depend -s || exit 1
make -j4 -s || exit 1
mkdir -p $OPENSSL_INSTALL_DIR/$ABI/lib/ || exit 1
if [ "$BUILD_SHARED_LIBS" ] ; then
cp libcrypto.so libssl.so $OPENSSL_INSTALL_DIR/$ABI/lib/ || exit 1
else
cp libcrypto.a libssl.a $OPENSSL_INSTALL_DIR/$ABI/lib/ || exit 1
fi
cp -r include $OPENSSL_INSTALL_DIR/$ABI/ || exit 1
make distclean || exit 1
done
cd ..
rm -rf ./openssl-$OPENSSL_VERSION || exit 1

View File

@ -1,117 +0,0 @@
#!/usr/bin/env bash
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
OPENSSL_INSTALL_DIR=${3:-third-party/openssl}
ANDROID_STL=${4:-c++_static}
TDLIB_INTERFACE=${5:-Java}
if [ "$ANDROID_STL" != "c++_static" ] && [ "$ANDROID_STL" != "c++_shared" ] ; then
echo 'Error: ANDROID_STL must be either "c++_static" or "c++_shared".'
exit 1
fi
if [ "$TDLIB_INTERFACE" != "Java" ] && [ "$TDLIB_INTERFACE" != "JSON" ] && [ "$TDLIB_INTERFACE" != "JSONJava" ] ; then
echo 'Error: TDLIB_INTERFACE must be either "Java", "JSON", or "JSONJava".'
exit 1
fi
source ./check-environment.sh || exit 1
if [ ! -d "$ANDROID_SDK_ROOT" ] ; then
echo "Error: directory \"$ANDROID_SDK_ROOT\" doesn't exist. Run ./fetch-sdk.sh first, or provide a valid path to Android SDK."
exit 1
fi
if [ ! -d "$OPENSSL_INSTALL_DIR" ] ; then
echo "Error: directory \"$OPENSSL_INSTALL_DIR\" doesn't exists. Run ./build-openssl.sh first."
exit 1
fi
ANDROID_SDK_ROOT="$(cd "$(dirname -- "$ANDROID_SDK_ROOT")" >/dev/null; pwd -P)/$(basename -- "$ANDROID_SDK_ROOT")"
ANDROID_NDK_ROOT="$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION"
OPENSSL_INSTALL_DIR="$(cd "$(dirname -- "$OPENSSL_INSTALL_DIR")" >/dev/null; pwd -P)/$(basename -- "$OPENSSL_INSTALL_DIR")"
PATH=$ANDROID_SDK_ROOT/cmake/3.22.1/bin:$PATH
TDLIB_INTERFACE_OPTION=$([ "$TDLIB_INTERFACE" == "JSON" ] && echo "-DTD_ANDROID_JSON=ON" || [ "$TDLIB_INTERFACE" == "JSONJava" ] && echo "-DTD_ANDROID_JSON_JAVA=ON" || echo "")
cd $(dirname $0)
echo "Generating TDLib source files..."
mkdir -p build-native-$TDLIB_INTERFACE || exit 1
cd build-native-$TDLIB_INTERFACE
cmake $TDLIB_INTERFACE_OPTION -DTD_GENERATE_SOURCE_FILES=ON .. || exit 1
cmake --build . || exit 1
cd ..
rm -rf tdlib || exit 1
if [ "$TDLIB_INTERFACE" == "Java" ] ; then
echo "Downloading annotation Java package..."
rm -f android.jar annotation-1.4.0.jar || exit 1
$WGET https://maven.google.com/androidx/annotation/annotation/1.4.0/annotation-1.4.0.jar || exit 1
echo "Generating Java source files..."
cmake --build build-native-$TDLIB_INTERFACE --target tl_generate_java || exit 1
php AddIntDef.php org/drinkless/tdlib/TdApi.java || exit 1
mkdir -p tdlib/java/org/drinkless/tdlib || exit 1
cp -p {..,tdlib}/java/org/drinkless/tdlib/Client.java || exit 1
mv {,tdlib/java/}org/drinkless/tdlib/TdApi.java || exit 1
rm -rf org || exit 1
echo "Generating Javadoc documentation..."
cp "$ANDROID_SDK_ROOT/platforms/android-34/android.jar" . || exit 1
JAVADOC_SEPARATOR=$([ "$OS_NAME" == "win" ] && echo ";" || echo ":")
javadoc -d tdlib/javadoc -encoding UTF-8 -charset UTF-8 -classpath "android.jar${JAVADOC_SEPARATOR}annotation-1.4.0.jar" -quiet -sourcepath tdlib/java org.drinkless.tdlib || exit 1
rm android.jar annotation-1.4.0.jar || exit 1
fi
if [ "$TDLIB_INTERFACE" == "JSONJava" ] ; then
mkdir -p tdlib/java/org/drinkless/tdlib || exit 1
cp -p {..,tdlib}/java/org/drinkless/tdlib/JsonClient.java || exit 1
fi
echo "Building TDLib..."
for ABI in arm64-v8a armeabi-v7a x86_64 x86 ; do
mkdir -p tdlib/libs/$ABI/ || exit 1
mkdir -p build-$ABI-$TDLIB_INTERFACE || exit 1
cd build-$ABI-$TDLIB_INTERFACE
cmake -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake" -DOPENSSL_ROOT_DIR="$OPENSSL_INSTALL_DIR/$ABI" -DCMAKE_BUILD_TYPE=RelWithDebInfo -GNinja -DANDROID_ABI=$ABI -DANDROID_STL=$ANDROID_STL -DANDROID_PLATFORM=android-16 $TDLIB_INTERFACE_OPTION .. || exit 1
if [ "$TDLIB_INTERFACE" == "Java" ] || [ "$TDLIB_INTERFACE" == "JSONJava" ] ; then
cmake --build . --target tdjni || exit 1
cp -p libtd*.so* ../tdlib/libs/$ABI/ || exit 1
fi
if [ "$TDLIB_INTERFACE" == "JSON" ] ; then
cmake --build . --target tdjson || exit 1
cp -p td/libtdjson.so ../tdlib/libs/$ABI/libtdjson.so.debug || exit 1
"$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin/llvm-strip" --strip-debug --strip-unneeded ../tdlib/libs/$ABI/libtdjson.so.debug -o ../tdlib/libs/$ABI/libtdjson.so || exit 1
fi
cd ..
if [[ "$ANDROID_STL" == "c++_shared" ]] ; then
if [[ "$ABI" == "arm64-v8a" ]] ; then
FULL_ABI="aarch64-linux-android"
elif [[ "$ABI" == "armeabi-v7a" ]] ; then
FULL_ABI="arm-linux-androideabi"
elif [[ "$ABI" == "x86_64" ]] ; then
FULL_ABI="x86_64-linux-android"
elif [[ "$ABI" == "x86" ]] ; then
FULL_ABI="i686-linux-android"
fi
cp "$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/sysroot/usr/lib/$FULL_ABI/libc++_shared.so" tdlib/libs/$ABI/ || exit 1
"$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin/llvm-strip" tdlib/libs/$ABI/libc++_shared.so || exit 1
fi
if [ -e "$OPENSSL_INSTALL_DIR/$ABI/lib/libcrypto.so" ] ; then
cp "$OPENSSL_INSTALL_DIR/$ABI/lib/libcrypto.so" "$OPENSSL_INSTALL_DIR/$ABI/lib/libssl.so" tdlib/libs/$ABI/ || exit 1
"$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin/llvm-strip" tdlib/libs/$ABI/libcrypto.so || exit 1
"$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_ARCH/bin/llvm-strip" tdlib/libs/$ABI/libssl.so || exit 1
fi
done
echo "Compressing..."
rm -f tdlib.zip tdlib-debug.zip || exit 1
jar -cMf tdlib-debug.zip tdlib || exit 1
rm tdlib/libs/*/*.debug || exit 1
jar -cMf tdlib.zip tdlib || exit 1
mv tdlib.zip tdlib-debug.zip tdlib || exit 1
echo "Done."

View File

@ -1,51 +0,0 @@
#!/usr/bin/env bash
# The script checks that all needed tools are installed and sets OS_NAME, HOST_ARCH, and WGET variables
if [[ "$OSTYPE" == "linux"* ]] ; then
OS_NAME="linux"
HOST_ARCH="linux-x86_64"
elif [[ "$OSTYPE" == "darwin"* ]] ; then
OS_NAME="mac"
HOST_ARCH="darwin-x86_64"
elif [[ "$OSTYPE" == "msys" ]] ; then
OS_NAME="win"
HOST_ARCH="windows-x86_64"
else
echo "Error: this script supports only Bash shell on Linux, macOS, or Windows."
exit 1
fi
if which wget >/dev/null 2>&1 ; then
WGET="wget -q"
elif which curl >/dev/null 2>&1 ; then
WGET="curl -sfLO"
else
echo "Error: this script requires either curl or wget tool installed."
exit 1
fi
for TOOL_NAME in gperf jar java javadoc make perl php sed tar yes unzip ; do
if ! which "$TOOL_NAME" >/dev/null 2>&1 ; then
echo "Error: this script requires $TOOL_NAME tool installed."
exit 1
fi
done
if [[ $(which make) = *" "* ]] ; then
echo "Error: OpenSSL expects that full path to make tool doesn't contain spaces. Move it to some other place."
exit 1
fi
if ! perl -MExtUtils::MakeMaker -MLocale::Maketext::Simple -MPod::Usage -e '' >/dev/null 2>&1 ; then
echo "Error: Perl installation is broken."
if [[ "$OSTYPE" == "msys" ]] ; then
echo "For Git Bash you need to manually copy ExtUtils, Locale and Pod modules to /usr/share/perl5/core_perl from any compatible Perl installation."
fi
exit 1
fi
if ! java --help >/dev/null 2>&1 ; then
echo "Error: Java installation is broken. Install JDK from https://www.oracle.com/java/technologies/downloads/ or via the package manager."
exit 1
fi

View File

@ -1,30 +0,0 @@
#!/usr/bin/env bash
ANDROID_SDK_ROOT=${1:-SDK}
ANDROID_NDK_VERSION=${2:-23.2.8568313}
if [ -e "$ANDROID_SDK_ROOT" ] ; then
echo "Error: file or directory \"$ANDROID_SDK_ROOT\" already exists. Delete it manually to proceed."
exit 1
fi
source ./check-environment.sh || exit 1
SDKMANAGER="./sdkmanager"
if [[ "$OS_NAME" == "win" ]] ; then
SDKMANAGER="./sdkmanager.bat"
fi
echo "Downloading SDK Manager..."
mkdir -p "$ANDROID_SDK_ROOT" || exit 1
cd "$ANDROID_SDK_ROOT" || exit 1
$WGET "https://dl.google.com/android/repository/commandlinetools-$OS_NAME-11076708_latest.zip" || exit 1
mkdir -p cmdline-tools || exit 1
unzip -qq "commandlinetools-$OS_NAME-11076708_latest.zip" -d cmdline-tools || exit 1
rm "commandlinetools-$OS_NAME-11076708_latest.zip" || exit 1
mv cmdline-tools/* cmdline-tools/latest/ || exit 1
echo "Installing required SDK tools..."
cd cmdline-tools/latest/bin/ || exit 1
yes | $SDKMANAGER --licenses >/dev/null || exit 1
$SDKMANAGER --install "ndk;$ANDROID_NDK_VERSION" "cmake;3.22.1" "build-tools;34.0.0" "platforms;android-34" > /dev/null || exit 1

View File

@ -1 +0,0 @@
/td/

View File

@ -1,13 +0,0 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TdExample VERSION 1.0 LANGUAGES CXX)
find_package(Td 1.8.46 REQUIRED)
add_executable(tdjson_example tdjson_example.cpp)
target_link_libraries(tdjson_example PRIVATE Td::TdJson)
set_property(TARGET tdjson_example PROPERTY CXX_STANDARD 11)
add_executable(td_example td_example.cpp)
target_link_libraries(td_example PRIVATE Td::TdStatic)
set_property(TARGET td_example PROPERTY CXX_STANDARD 14)

View File

@ -1,24 +0,0 @@
# TDLib C++ basic usage examples
TDLib should be prebuilt and installed to local subdirectory `td/`:
```
cd <path to TDLib sources>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=../example/cpp/td ..
cmake --build . --target install
```
Also, see [building](https://github.com/tdlib/td#building) for additional details on TDLib building.
After this you can build the examples:
```
cd <path to TDLib sources>/example/cpp
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DTd_DIR=<full path to TDLib sources>/example/cpp/td/lib/cmake/Td ..
cmake --build .
```
Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs.
To run the examples you may need to manually copy needed shared libraries from `td/bin` to a directory containing built binaries.

View File

@ -1,337 +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/telegram/td_api.hpp>
#include <cstdint>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
// Simple single-threaded example of TDLib usage.
// Real world programs should use separate thread for the user input.
// Example includes user authentication, receiving updates, getting chat list and sending text messages.
// overloaded
namespace detail {
template <class... Fs>
struct overload;
template <class F>
struct overload<F> : public F {
explicit overload(F f) : F(f) {
}
};
template <class F, class... Fs>
struct overload<F, Fs...>
: public overload<F>
, public overload<Fs...> {
overload(F f, Fs... fs) : overload<F>(f), overload<Fs...>(fs...) {
}
using overload<F>::operator();
using overload<Fs...>::operator();
};
} // namespace detail
template <class... F>
auto overloaded(F... f) {
return detail::overload<F...>(f...);
}
namespace td_api = td::td_api;
class TdExample {
public:
TdExample() {
td::ClientManager::execute(td_api::make_object<td_api::setLogVerbosityLevel>(1));
client_manager_ = std::make_unique<td::ClientManager>();
client_id_ = client_manager_->create_client_id();
send_query(td_api::make_object<td_api::getOption>("version"), {});
}
void loop() {
while (true) {
if (need_restart_) {
restart();
} else if (!are_authorized_) {
process_response(client_manager_->receive(10));
} else {
std::cout << "Enter action [q] quit [u] check for updates and request results [c] show chats [m <chat_id> "
"<text>] send message [me] show self [l] logout: "
<< std::endl;
std::string line;
std::getline(std::cin, line);
std::istringstream ss(line);
std::string action;
if (!(ss >> action)) {
continue;
}
if (action == "q") {
return;
}
if (action == "u") {
std::cout << "Checking for updates..." << std::endl;
while (true) {
auto response = client_manager_->receive(0);
if (response.object) {
process_response(std::move(response));
} else {
break;
}
}
} else if (action == "close") {
std::cout << "Closing..." << std::endl;
send_query(td_api::make_object<td_api::close>(), {});
} else if (action == "me") {
send_query(td_api::make_object<td_api::getMe>(),
[this](Object object) { std::cout << to_string(object) << std::endl; });
} else if (action == "l") {
std::cout << "Logging out..." << std::endl;
send_query(td_api::make_object<td_api::logOut>(), {});
} else if (action == "m") {
std::int64_t chat_id;
ss >> chat_id;
ss.get();
std::string text;
std::getline(ss, text);
std::cout << "Sending message to chat " << chat_id << "..." << std::endl;
auto send_message = td_api::make_object<td_api::sendMessage>();
send_message->chat_id_ = chat_id;
auto message_content = td_api::make_object<td_api::inputMessageText>();
message_content->text_ = td_api::make_object<td_api::formattedText>();
message_content->text_->text_ = std::move(text);
send_message->input_message_content_ = std::move(message_content);
send_query(std::move(send_message), {});
} else if (action == "c") {
std::cout << "Loading chat list..." << std::endl;
send_query(td_api::make_object<td_api::getChats>(nullptr, 20), [this](Object object) {
if (object->get_id() == td_api::error::ID) {
return;
}
auto chats = td::move_tl_object_as<td_api::chats>(object);
for (auto chat_id : chats->chat_ids_) {
std::cout << "[chat_id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl;
}
});
}
}
}
}
private:
using Object = td_api::object_ptr<td_api::Object>;
std::unique_ptr<td::ClientManager> client_manager_;
std::int32_t client_id_{0};
td_api::object_ptr<td_api::AuthorizationState> authorization_state_;
bool are_authorized_{false};
bool need_restart_{false};
std::uint64_t current_query_id_{0};
std::uint64_t authentication_query_id_{0};
std::map<std::uint64_t, std::function<void(Object)>> handlers_;
std::map<std::int64_t, td_api::object_ptr<td_api::user>> users_;
std::map<std::int64_t, std::string> chat_title_;
void restart() {
client_manager_.reset();
*this = TdExample();
}
void send_query(td_api::object_ptr<td_api::Function> f, std::function<void(Object)> handler) {
auto query_id = next_query_id();
if (handler) {
handlers_.emplace(query_id, std::move(handler));
}
client_manager_->send(client_id_, query_id, std::move(f));
}
void process_response(td::ClientManager::Response response) {
if (!response.object) {
return;
}
//std::cout << response.request_id << " " << to_string(response.object) << std::endl;
if (response.request_id == 0) {
return process_update(std::move(response.object));
}
auto it = handlers_.find(response.request_id);
if (it != handlers_.end()) {
it->second(std::move(response.object));
handlers_.erase(it);
}
}
std::string get_user_name(std::int64_t user_id) const {
auto it = users_.find(user_id);
if (it == users_.end()) {
return "unknown user";
}
return it->second->first_name_ + " " + it->second->last_name_;
}
std::string get_chat_title(std::int64_t chat_id) const {
auto it = chat_title_.find(chat_id);
if (it == chat_title_.end()) {
return "unknown chat";
}
return it->second;
}
void process_update(td_api::object_ptr<td_api::Object> update) {
td_api::downcast_call(
*update, overloaded(
[this](td_api::updateAuthorizationState &update_authorization_state) {
authorization_state_ = std::move(update_authorization_state.authorization_state_);
on_authorization_state_update();
},
[this](td_api::updateNewChat &update_new_chat) {
chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_;
},
[this](td_api::updateChatTitle &update_chat_title) {
chat_title_[update_chat_title.chat_id_] = update_chat_title.title_;
},
[this](td_api::updateUser &update_user) {
auto user_id = update_user.user_->id_;
users_[user_id] = std::move(update_user.user_);
},
[this](td_api::updateNewMessage &update_new_message) {
auto chat_id = update_new_message.message_->chat_id_;
std::string sender_name;
td_api::downcast_call(*update_new_message.message_->sender_id_,
overloaded(
[this, &sender_name](td_api::messageSenderUser &user) {
sender_name = get_user_name(user.user_id_);
},
[this, &sender_name](td_api::messageSenderChat &chat) {
sender_name = get_chat_title(chat.chat_id_);
}));
std::string text;
if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) {
text = static_cast<td_api::messageText &>(*update_new_message.message_->content_).text_->text_;
}
std::cout << "Receive message: [chat_id:" << chat_id << "] [from:" << sender_name << "] ["
<< text << "]" << std::endl;
},
[](auto &update) {}));
}
auto create_authentication_query_handler() {
return [this, id = authentication_query_id_](Object object) {
if (id == authentication_query_id_) {
check_authentication_error(std::move(object));
}
};
}
void on_authorization_state_update() {
authentication_query_id_++;
td_api::downcast_call(*authorization_state_,
overloaded(
[this](td_api::authorizationStateReady &) {
are_authorized_ = true;
std::cout << "Authorization is completed" << std::endl;
},
[this](td_api::authorizationStateLoggingOut &) {
are_authorized_ = false;
std::cout << "Logging out" << std::endl;
},
[this](td_api::authorizationStateClosing &) { std::cout << "Closing" << std::endl; },
[this](td_api::authorizationStateClosed &) {
are_authorized_ = false;
need_restart_ = true;
std::cout << "Terminated" << std::endl;
},
[this](td_api::authorizationStateWaitPhoneNumber &) {
std::cout << "Enter phone number: " << std::flush;
std::string phone_number;
std::cin >> phone_number;
send_query(
td_api::make_object<td_api::setAuthenticationPhoneNumber>(phone_number, nullptr),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailAddress &) {
std::cout << "Enter email address: " << std::flush;
std::string email_address;
std::cin >> email_address;
send_query(td_api::make_object<td_api::setAuthenticationEmailAddress>(email_address),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEmailCode &) {
std::cout << "Enter email authentication code: " << std::flush;
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationEmailCode>(
td_api::make_object<td_api::emailAddressAuthenticationCode>(code)),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitCode &) {
std::cout << "Enter authentication code: " << std::flush;
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationCode>(code),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitRegistration &) {
std::string first_name;
std::string last_name;
std::cout << "Enter your first name: " << std::flush;
std::cin >> first_name;
std::cout << "Enter your last name: " << std::flush;
std::cin >> last_name;
send_query(td_api::make_object<td_api::registerUser>(first_name, last_name, false),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPassword &) {
std::cout << "Enter authentication password: " << std::flush;
std::string password;
std::getline(std::cin, password);
send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitOtherDeviceConfirmation &state) {
std::cout << "Confirm this login link on another device: " << state.link_ << std::endl;
},
[this](td_api::authorizationStateWaitTdlibParameters &) {
auto request = td_api::make_object<td_api::setTdlibParameters>();
request->database_directory_ = "tdlib";
request->use_message_database_ = true;
request->use_secret_chats_ = true;
request->api_id_ = 94575;
request->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
request->system_language_code_ = "en";
request->device_model_ = "Desktop";
request->application_version_ = "1.0";
send_query(std::move(request), create_authentication_query_handler());
}));
}
void check_authentication_error(Object object) {
if (object->get_id() == td_api::error::ID) {
auto error = td::move_tl_object_as<td_api::error>(object);
std::cout << "Error: " << to_string(error) << std::flush;
on_authorization_state_update();
}
}
std::uint64_t next_query_id() {
return ++current_query_id_;
}
};
int main() {
TdExample example;
example.loop();
}

View File

@ -1,55 +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 <iostream>
#include <td/telegram/td_json_client.h>
// Basic example of TDLib JSON interface usage.
// Native interface should be preferred instead in C++, so here is only an example of
// the main event cycle, which should be essentially the same for all languages.
int main() {
// disable TDLib logging
td_execute("{\"@type\":\"setLogVerbosityLevel\", \"new_verbosity_level\":0}");
int client_id = td_create_client_id();
// somehow share the client_id with other threads, which will be able to send requests via td_send
// start the client by sending request to it
td_send(client_id, "{\"@type\":\"getOption\", \"name\":\"version\"}");
const bool test_incorrect_queries = false;
if (test_incorrect_queries) {
td_execute("{\"@type\":\"setLogVerbosityLevel\", \"new_verbosity_level\":1}");
td_execute("");
td_execute("test");
td_execute("\"test\"");
td_execute("{\"@type\":\"test\", \"@extra\":1}");
td_send(client_id, "{\"@type\":\"getFileMimeType\"}");
td_send(client_id, "{\"@type\":\"getFileMimeType\", \"@extra\":1}");
td_send(client_id, "{\"@type\":\"getFileMimeType\", \"@extra\":null}");
td_send(client_id, "{\"@type\":\"test\"}");
td_send(client_id, "[]");
td_send(client_id, "{\"@type\":\"test\", \"@extra\":1}");
td_send(client_id, "{\"@type\":\"sendMessage\", \"chat_id\":true, \"@extra\":1}");
td_send(client_id, "test");
}
const double WAIT_TIMEOUT = 10.0; // seconds
while (true) {
const char *result = td_receive(WAIT_TIMEOUT);
if (result != nullptr) {
// parse the result as a JSON object and process it as an incoming update or an answer to a previously sent request
// if (result is UpdateAuthorizationState with authorizationStateClosed) {
// break;
// }
std::cout << result << std::endl;
}
}
}

View File

@ -1,5 +0,0 @@
/.vs/
/bin/
/obj/
/project.lock.json
/TdExample.csproj.user

View File

@ -1,40 +0,0 @@
# TDLib C# example
This is an example of building TDLib with `C++/CLI` support and an example of TDLib usage from C#.
## Building TDLib
* Download and install Microsoft Visual Studio 2015 or later.
* Download and install [CMake](https://cmake.org/download/); choose "Add CMake to the system PATH" option while installing.
* Install `gperf`, `zlib`, and `openssl` using [vcpkg](https://github.com/Microsoft/vcpkg#quick-start):
```
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
git checkout 07b30b49e5136a36100a2ce644476e60d7f3ddc1
.\bootstrap-vcpkg.bat
.\vcpkg.exe install gperf:x64-windows gperf:x86-windows openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows
```
* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download). Add the path to php.exe to the PATH environment variable.
* Build `TDLib` with CMake enabling `.NET` support and specifying correct path to `vcpkg` toolchain file:
```
cd <path to TDLib sources>/example/csharp
mkdir build
cd build
cmake -A Win32 -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=<path to vcpkg>/scripts/buildsystems/vcpkg.cmake ../../..
cmake --build . --config Release
cmake --build . --config Debug
cd ..
mkdir build64
cd build64
cmake -A x64 -DTD_ENABLE_DOTNET=ON -DCMAKE_TOOLCHAIN_FILE=<path to vcpkg>/scripts/buildsystems/vcpkg.cmake ../../..
cmake --build . --config Release
cmake --build . --config Debug
```
## Example of usage
After `TDLib` is built you can open and run TdExample project.
It contains a simple console C# application with implementation of authorization and message sending.
Just open it with Visual Studio 2015 or later and run.
Also, see TdExample.csproj for example of including TDLib in C# project with all native shared library dependencies.

View File

@ -1,293 +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)
//
using Td = Telegram.Td;
using TdApi = Telegram.Td.Api;
using System;
using System.Threading;
namespace TdExample
{
/// <summary>
/// Example class for TDLib usage from C#.
/// </summary>
class Example
{
private static Td.Client _client = null;
private readonly static Td.ClientResultHandler _defaultHandler = new DefaultHandler();
private static TdApi.AuthorizationState _authorizationState = null;
private static volatile bool _haveAuthorization = false;
private static volatile bool _needQuit = false;
private static volatile bool _canQuit = false;
private static volatile AutoResetEvent _gotAuthorization = new AutoResetEvent(false);
private static readonly string _newLine = Environment.NewLine;
private static readonly string _commandsLine = "Enter command (gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, r - Restart, q - Quit): ";
private static volatile string _currentPrompt = null;
private static Td.Client CreateTdClient()
{
return Td.Client.Create(new UpdateHandler());
}
private static void Print(string str)
{
if (_currentPrompt != null)
{
Console.WriteLine();
}
Console.WriteLine(str);
if (_currentPrompt != null)
{
Console.Write(_currentPrompt);
}
}
private static string ReadLine(string str)
{
Console.Write(str);
_currentPrompt = str;
var result = Console.ReadLine();
_currentPrompt = null;
return result;
}
private static void OnAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState)
{
if (authorizationState != null)
{
_authorizationState = authorizationState;
}
if (_authorizationState is TdApi.AuthorizationStateWaitTdlibParameters)
{
TdApi.SetTdlibParameters request = new TdApi.SetTdlibParameters();
request.DatabaseDirectory = "tdlib";
request.UseMessageDatabase = true;
request.UseSecretChats = true;
request.ApiId = 94575;
request.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.SystemLanguageCode = "en";
request.DeviceModel = "Desktop";
request.ApplicationVersion = "1.0";
_client.Send(request, new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitPhoneNumber)
{
string phoneNumber = ReadLine("Please enter phone number: ");
_client.Send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitEmailAddress)
{
string emailAddress = ReadLine("Please enter email address: ");
_client.Send(new TdApi.SetAuthenticationEmailAddress(emailAddress), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitEmailCode)
{
string code = ReadLine("Please enter email authentication code: ");
_client.Send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(code)), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitOtherDeviceConfirmation state)
{
Console.WriteLine("Please confirm this login link on another device: " + state.Link);
}
else if (_authorizationState is TdApi.AuthorizationStateWaitCode)
{
string code = ReadLine("Please enter authentication code: ");
_client.Send(new TdApi.CheckAuthenticationCode(code), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitRegistration)
{
string firstName = ReadLine("Please enter your first name: ");
string lastName = ReadLine("Please enter your last name: ");
_client.Send(new TdApi.RegisterUser(firstName, lastName, false), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateWaitPassword)
{
string password = ReadLine("Please enter password: ");
_client.Send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler());
}
else if (_authorizationState is TdApi.AuthorizationStateReady)
{
_haveAuthorization = true;
_gotAuthorization.Set();
}
else if (_authorizationState is TdApi.AuthorizationStateLoggingOut)
{
_haveAuthorization = false;
Print("Logging out");
}
else if (_authorizationState is TdApi.AuthorizationStateClosing)
{
_haveAuthorization = false;
Print("Closing");
}
else if (_authorizationState is TdApi.AuthorizationStateClosed)
{
Print("Closed");
if (!_needQuit)
{
_client = CreateTdClient(); // recreate _client after previous has closed
} else {
_canQuit = true;
}
}
else
{
Print("Unsupported authorization state:" + _newLine + _authorizationState);
}
}
private static long GetChatId(string arg)
{
long chatId = 0;
try
{
chatId = Convert.ToInt64(arg);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
return chatId;
}
private static void GetCommand()
{
string command = ReadLine(_commandsLine);
string[] commands = command.Split(new char[] { ' ' }, 2);
try
{
switch (commands[0])
{
case "gc":
_client.Send(new TdApi.GetChat(GetChatId(commands[1])), _defaultHandler);
break;
case "me":
_client.Send(new TdApi.GetMe(), _defaultHandler);
break;
case "sm":
string[] args = commands[1].Split(new char[] { ' ' }, 2);
sendMessage(GetChatId(args[0]), args[1]);
break;
case "lo":
_haveAuthorization = false;
_client.Send(new TdApi.LogOut(), _defaultHandler);
break;
case "r":
_haveAuthorization = false;
_client.Send(new TdApi.Close(), _defaultHandler);
break;
case "q":
_needQuit = true;
_haveAuthorization = false;
_client.Send(new TdApi.Close(), _defaultHandler);
break;
default:
Print("Unsupported command: " + command);
break;
}
}
catch (IndexOutOfRangeException)
{
Print("Not enough arguments");
}
}
private static void sendMessage(long chatId, string message)
{
// initialize reply markup just for testing
TdApi.InlineKeyboardButton[] row = { new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl()) };
TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][] { row, row, row });
TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), null, true);
_client.Send(new TdApi.SendMessage(chatId, 0, null, null, replyMarkup, content), _defaultHandler);
}
static void Main()
{
// disable TDLib log
Td.Client.Execute(new TdApi.SetLogVerbosityLevel(0));
if (Td.Client.Execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))) is TdApi.Error)
{
throw new System.IO.IOException("Write access to the current directory is required");
}
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
Td.Client.Run();
}).Start();
// create Td.Client
_client = CreateTdClient();
// test Client.Execute
_defaultHandler.OnResult(Td.Client.Execute(new TdApi.GetTextEntities("@telegram /test_command https://telegram.org telegram.me @gif @test")));
// main loop
while (!_needQuit)
{
// await authorization
_gotAuthorization.Reset();
_gotAuthorization.WaitOne();
_client.Send(new TdApi.LoadChats(null, 100), _defaultHandler); // preload main chat list
while (_haveAuthorization)
{
GetCommand();
}
}
while (!_canQuit) {
Thread.Sleep(1);
}
}
private class DefaultHandler : Td.ClientResultHandler
{
void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
{
Print(@object.ToString());
}
}
private class UpdateHandler : Td.ClientResultHandler
{
void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
{
if (@object is TdApi.UpdateAuthorizationState)
{
OnAuthorizationStateUpdated((@object as TdApi.UpdateAuthorizationState).AuthorizationState);
}
else
{
// Print("Unsupported update: " + @object);
}
}
}
private class AuthorizationRequestHandler : Td.ClientResultHandler
{
void Td.ClientResultHandler.OnResult(TdApi.BaseObject @object)
{
if (@object is TdApi.Error)
{
Print("Receive an error:" + _newLine + @object);
OnAuthorizationStateUpdated(null); // repeat last action
}
else
{
// result is already received through UpdateAuthorizationState, nothing to do
}
}
}
}
}

View File

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}</ProjectGuid>
<OutputType>Exe</OutputType>
<NoStandardLibraries>false</NoStandardLibraries>
<AssemblyName>ConsoleApplication</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>TdExample</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Telegram.Td, Version=0.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath Condition=" '$(Platform)' == 'x86' ">build\$(Configuration)\Telegram.Td.dll</HintPath>
<HintPath Condition=" '$(Platform)' == 'x64' ">build64\$(Configuration)\Telegram.Td.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="TdExample.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<Content Include="build\Debug\zlibd1.dll">
<Link>zlibd1.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<Content Include="build\Release\zlib1.dll">
<Link>zlib1.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<Content Include="build64\Debug\zlibd1.dll">
<Link>zlibd1.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<Content Include="build64\Release\zlib1.dll">
<Link>zlib1.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Platform)' == 'x86' ">
<Content Include="build\$(Configuration)\libcrypto-3.dll">
<Link>libcrypto-3.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="build\$(Configuration)\libssl-3.dll">
<Link>libssl-3.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
<Content Include="build64\$(Configuration)\libcrypto-3-x64.dll">
<Link>libcrypto-3-x64.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="build64\$(Configuration)\libssl-3-x64.dll">
<Link>libssl-3-x64.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSHARP.Targets" />
<ProjectExtensions>
<VisualStudio AllowExistingFolder="true" />
</ProjectExtensions>
</Project>

View File

@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2046
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TdExample", "TdExample.csproj", "{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x64.ActiveCfg = Debug|x64
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x64.Build.0 = Debug|x64
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.ActiveCfg = Debug|x86
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Debug|x86.Build.0 = Debug|x86
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x64.ActiveCfg = Release|x64
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x64.Build.0 = Release|x64
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.ActiveCfg = Release|x86
{3F9A93EA-DC26-4F8B-ACE0-131B33B00CA7}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {865856BA-F733-45DF-B35F-12607605B023}
EndGlobalSection
EndGlobal

View File

@ -1,118 +0,0 @@
diff --git a/Makefile b/Makefile
index a1d13e9..8efcf20 100644
--- a/Makefile
+++ b/Makefile
@@ -18,8 +18,13 @@
# - OpenSSL - build OpenSSL for all platforms
# - OpenSSL-macOS - build OpenSSL for macOS
# - OpenSSL-iOS - build OpenSSL for iOS
+# - OpenSSL-iOS-simulator - build OpenSSL for iOS-simulator
# - OpenSSL-tvOS - build OpenSSL for tvOS
+# - OpenSSL-tvOS-simulator - build OpenSSL for tvOS-simulator
# - OpenSSL-watchOS - build OpenSSL for watchOS
+# - OpenSSL-watchOS-simulator - build OpenSSL for watchOS-simulator
+# - OpenSSL-visionOS - build OpenSSL for visionOS
+# - OpenSSL-visionOS-simulator - build OpenSSL for visionOS-simulator
# - libFFI - build libFFI for all platforms (except macOS)
# - libFFI-iOS - build libFFI for iOS
# - libFFI-tvOS - build libFFI for tvOS
@@ -50,7 +55,7 @@ XZ_VERSION=5.4.2
# Preference is to use OpenSSL 3; however, Cryptography 3.4.8 (and
# probably some other packages as well) only works with 1.1.1, so
# we need to preserve the ability to build the older OpenSSL (for now...)
-OPENSSL_VERSION=3.1.0
+OPENSSL_VERSION=3.1.5
# OPENSSL_VERSION_NUMBER=1.1.1
# OPENSSL_REVISION=q
# OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION)
@@ -59,7 +64,7 @@ LIBFFI_VERSION=3.4.2
# Supported OS and dependencies
DEPENDENCIES=BZip2 XZ OpenSSL libFFI
-OS_LIST=macOS iOS tvOS watchOS
+OS_LIST=macOS iOS iOS-simulator tvOS tvOS-simulator watchOS watchOS-simulator visionOS visionOS-simulator
CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar
@@ -69,22 +74,41 @@ VERSION_MIN-macOS=10.15
CFLAGS-macOS=-mmacosx-version-min=$(VERSION_MIN-macOS)
# iOS targets
-TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64
+TARGETS-iOS=iphoneos.arm64
VERSION_MIN-iOS=12.0
CFLAGS-iOS=-mios-version-min=$(VERSION_MIN-iOS)
+# iOS-simulator targets
+TARGETS-iOS-simulator=iphonesimulator.x86_64 iphonesimulator.arm64
+CFLAGS-iOS-simulator=-mios-simulator-version-min=$(VERSION_MIN-iOS)
+
# tvOS targets
-TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64
+TARGETS-tvOS=appletvos.arm64
VERSION_MIN-tvOS=9.0
CFLAGS-tvOS=-mtvos-version-min=$(VERSION_MIN-tvOS)
PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no
+# tvOS-simulator targets
+TARGETS-tvOS-simulator=appletvsimulator.x86_64 appletvsimulator.arm64
+CFLAGS-tvOS-simulator=-mtvos-simulator-version-min=$(VERSION_MIN-tvOS)
+
# watchOS targets
-TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32
+TARGETS-watchOS=watchos.armv7k watchos.arm64_32 watchos.arm64
VERSION_MIN-watchOS=4.0
CFLAGS-watchOS=-mwatchos-version-min=$(VERSION_MIN-watchOS)
PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no
+# watchOS-simulator targets
+TARGETS-watchOS-simulator=watchsimulator.i386 watchsimulator.x86_64 watchsimulator.arm64
+CFLAGS-watchOS-simulator=-mwatchos-simulator-version-min=$(VERSION_MIN-watchOS)
+
+# visionOS targets
+TARGETS-visionOS=xros.arm64
+PYTHON_CONFIGURE-visionOS=ac_cv_func_sigaltstack=no
+
+# visionOS-simulator targets
+TARGETS-visionOS-simulator=xrsimulator.x86_64 xrsimulator.arm64
+
# The architecture of the machine doing the build
HOST_ARCH=$(shell uname -m)
HOST_PYTHON=install/macOS/macosx/python-$(PYTHON_VERSION)
@@ -212,6 +236,10 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target)))
ifeq ($(os),macOS)
TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-darwin
+else ifeq ($(os),visionOS)
+TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-xros
+else ifeq ($(os),visionOS-simulator)
+TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-xros-simulator
else
ifeq ($$(findstring simulator,$$(SDK-$(target))),)
TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))
@@ -662,7 +690,7 @@ BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbz2.a
XZ_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/xz-$(XZ_VERSION)
XZ_FATLIB-$(sdk)=$$(XZ_MERGE-$(sdk))/lib/liblzma.a
-OPENSSL_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION)
+OPENSSL_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/openssl
OPENSSL_FATINCLUDE-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/include
OPENSSL_SSL_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libssl.a
OPENSSL_CRYPTO_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libcrypto.a
@@ -716,14 +744,14 @@ $$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENS
mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib
lipo -create -output $$@ \
$$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \
- 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.lipo.log
+ 2>&1 | tee -a merge/$(os)/openssl-$(OPENSSL_VERSION).ssl.lipo.log
$$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target)))
@echo ">>> Build OpenSSL crypto fat library for $(sdk)"
mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib
lipo -create -output $$@ \
$$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \
- 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.lipo.log
+ 2>&1 | tee -a merge/$(os)/openssl-$(OPENSSL_VERSION).crypto.lipo.log
###########################################################################
# SDK: libFFI

View File

@ -1,46 +0,0 @@
# Universal XCFramework build example
Below are instructions for building TDLib for iOS, watchOS, tvOS, visionOS, and also macOS.
If you need only a macOS build for the current architecture, take a look at [TDLib build instructions generator](https://tdlib.github.io/td/build.html).
For example of usage take a look at our [Swift example](https://github.com/tdlib/td/tree/master/example/swift).
To compile `TDLib` you will need to:
* Install the latest Xcode via `xcode-select --install` or downloading it from [Xcode website](https://developer.apple.com/xcode/).
It is not enough to install only command line developer tools to build `TDLib` for iOS. Xcode >= 14.0 is required to build TDLib for watchOS.
* Install other build dependencies using [Homebrew](https://brew.sh):
```
brew install gperf cmake coreutils
```
* If you don't want to build `TDLib` for macOS first, you **must** pregenerate required source code files in the following way:
```
cd <path to TDLib sources>
mkdir native-build
cd native-build
cmake -DTD_GENERATE_SOURCE_FILES=ON ..
cmake --build .
```
* Build OpenSSL for iOS, watchOS, tvOS, visionOS, and macOS:
```
cd <path to TDLib sources>/example/ios
./build-openssl.sh
```
Here we use scripts from [Python Apple support](https://github.com/beeware/Python-Apple-support), but any other OpenSSL build should work too.
[Python Apple support](https://github.com/beeware/Python-Apple-support) has known problems with spaces in the path to the current directory, so
you need to ensure that there are no spaces in the path.
Built OpenSSL libraries should be stored in the directory `third_party/openssl/<platform>`, because the next script will rely on this location.
* Build TDLib for iOS, watchOS, tvOS, visionOS, and macOS:
```
cd <path to TDLib sources>/example/ios
./build.sh
```
This may take a while, because TDLib will be built about 16 times.
Resulting XCFramework will work on any architecture and even on a simulator.
We use [CMake/iOS.cmake](https://github.com/tdlib/td/blob/master/CMake/iOS.cmake) toolchain, other toolchains may work too.
Built libraries and XCFramework will be stored in `tdjson` directory.
Documentation for all available classes and methods can be found at https://core.telegram.org/tdlib/docs.
If you receive an "error: SDK "appletvsimulator" cannot be located", you need to run the command "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" before running ./build.sh.

View File

@ -1,37 +0,0 @@
#!/bin/sh
cd $(dirname $0)
git clone https://github.com/beeware/Python-Apple-support
cd Python-Apple-support
git checkout 6f43aba0ddd5a9f52f39775d0141bd4363614020 || exit 1
git reset --hard || exit 1
git apply ../Python-Apple-support.patch || exit 1
cd ..
platforms="macOS iOS watchOS tvOS visionOS"
for platform in $platforms;
do
if [[ $platform = "macOS" ]]; then
simulators="0"
else
simulators="0 1"
fi
for simulator in $simulators;
do
if [[ $simulator = "1" ]]; then
platform="${platform}-simulator"
fi
echo $platform
cd Python-Apple-support
#NB: -j will fail
make OpenSSL-$platform || exit 1
cd ..
rm -rf third_party/openssl/$platform || exit 1
mkdir -p third_party/openssl/$platform/lib || exit 1
cp ./Python-Apple-support/merge/$platform/openssl/lib/libcrypto.a third_party/openssl/$platform/lib/ || exit 1
cp ./Python-Apple-support/merge/$platform/openssl/lib/libssl.a third_party/openssl/$platform/lib/ || exit 1
cp -r ./Python-Apple-support/merge/$platform/openssl/include/ third_party/openssl/$platform/include || exit 1
done
done

View File

@ -1,92 +0,0 @@
#!/bin/sh
cd $(dirname $0)
td_path=$(grealpath ../..)
rm -rf build
mkdir -p build
cd build
set_cmake_options () {
# Set CMAKE options depending on platform passed $1
openssl_path=$(grealpath ../third_party/openssl/$1)
echo "OpenSSL path = ${openssl_path}"
openssl_crypto_library="${openssl_path}/lib/libcrypto.a"
openssl_ssl_library="${openssl_path}/lib/libssl.a"
options=""
options="$options -DOPENSSL_FOUND=1"
options="$options -DOPENSSL_CRYPTO_LIBRARY=${openssl_crypto_library}"
options="$options -DOPENSSL_SSL_LIBRARY=${openssl_ssl_library}"
options="$options -DOPENSSL_INCLUDE_DIR=${openssl_path}/include"
options="$options -DOPENSSL_LIBRARIES=${openssl_crypto_library};${openssl_ssl_library}"
options="$options -DCMAKE_BUILD_TYPE=Release"
}
platforms="macOS iOS watchOS tvOS visionOS"
#platforms="watchOS"
for platform in $platforms;
do
echo "Platform = ${platform}"
if [[ $platform = "macOS" ]]; then
simulators="0"
else
simulators="0 1"
fi
for simulator in $simulators;
do
if [[ $platform = "macOS" ]]; then
other_options="-DCMAKE_OSX_ARCHITECTURES='x86_64;arm64'"
else
if [[ $platform = "watchOS" ]]; then
ios_platform="WATCH"
elif [[ $platform = "tvOS" ]]; then
ios_platform="TV"
elif [[ $platform = "visionOS" ]]; then
ios_platform="VISION"
else
ios_platform=""
fi
if [[ $simulator = "1" ]]; then
platform="${platform}-simulator"
ios_platform="${ios_platform}SIMULATOR"
else
ios_platform="${ios_platform}OS"
fi
echo "iOS platform = ${ios_platform}"
other_options="-DIOS_PLATFORM=${ios_platform} -DCMAKE_TOOLCHAIN_FILE=${td_path}/CMake/iOS.cmake"
fi
set_cmake_options $platform
build="build-${platform}"
install="install-${platform}"
rm -rf $build
mkdir -p $build
mkdir -p $install
cd $build
cmake $td_path $options $other_options -DCMAKE_INSTALL_PREFIX=../${install}
make -j3 install || exit
cd ..
install_name_tool -id @rpath/libtdjson.dylib ${install}/lib/libtdjson.dylib
mkdir -p ../tdjson/${platform}/include
rsync --recursive ${install}/include/ ../tdjson/${platform}/include/
mkdir -p ../tdjson/${platform}/lib
cp ${install}/lib/libtdjson.dylib ../tdjson/${platform}/lib/
done
done
produced_dylibs=(install-*/lib/libtdjson.dylib)
xcodebuild_frameworks=()
for dylib in "${produced_dylibs[@]}";
do
xcodebuild_frameworks+=(-library $(grealpath "${dylib}"))
done
# Make xcframework
xcodebuild -create-xcframework \
"${xcodebuild_frameworks[@]}" \
-output "libtdjson.xcframework"
rsync --recursive libtdjson.xcframework ../tdjson/

View File

@ -1,5 +0,0 @@
**/*build/
/bin/
/docs/
/org/drinkless/tdlib/TdApi.java
/td/

View File

@ -1,141 +0,0 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TdJavaExample VERSION 1.0 LANGUAGES CXX)
option(TD_JSON_JAVA "Use \"ON\" to build Java wrapper for JSON API.")
if (POLICY CMP0074)
# use environment variables to find libraries
cmake_policy(SET CMP0074 NEW)
endif()
find_package(Td REQUIRED)
if (NOT JNI_FOUND)
find_package(JNI REQUIRED COMPONENTS JVM)
endif()
message(STATUS "Found JNI: ${JNI_INCLUDE_DIRS} ${JNI_LIBRARIES}")
if (NOT Java_FOUND)
find_package(Java REQUIRED)
endif()
message(STATUS "Found Java: ${Java_JAVAC_EXECUTABLE} ${Java_JAVADOC_EXECUTABLE}")
# Generating TdApi.java
set(TD_API_JAVA_PACKAGE "org/drinkless/tdlib")
set(TD_API_JAVA_PATH ${CMAKE_CURRENT_SOURCE_DIR})
set(JAVA_SOURCE_PATH "${TD_API_JAVA_PATH}/${TD_API_JAVA_PACKAGE}")
if (TD_JSON_JAVA)
add_custom_target(td_generate_java_api
COMMAND cmake -E echo ""
COMMENT "Skip generation of Java TDLib API source files"
)
set(JAVA_EXAMPLE_FILES "${JAVA_SOURCE_PATH}/example/JsonExample.java")
set(JAVA_SOURCE_FILES "${JAVA_SOURCE_PATH}/JsonClient.java")
else()
find_program(PHP_EXECUTABLE php)
if ((CMAKE_SYSTEM_NAME MATCHES "FreeBSD") AND (CMAKE_SYSTEM_VERSION MATCHES "HBSD"))
set(PHP_EXECUTABLE "PHP_EXECUTABLE-NOTFOUND")
endif()
set(TD_API_TLO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tlo)
set(TD_API_TL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/scheme/td_api.tl)
set(JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td/generate/JavadocTlDocumentationGenerator.php)
set(GENERATE_JAVA_API_CMD ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api TdApi "${TD_API_TLO_PATH}" "${TD_API_JAVA_PATH}" "${TD_API_JAVA_PACKAGE}")
if (PHP_EXECUTABLE)
set(GENERATE_JAVA_API_CMD ${GENERATE_JAVA_API_CMD} && ${PHP_EXECUTABLE} "${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH}" "${TD_API_TL_PATH}" "${JAVA_SOURCE_PATH}/TdApi.java")
endif()
add_custom_target(td_generate_java_api
COMMAND ${GENERATE_JAVA_API_CMD}
COMMENT "Generating Java TDLib API source files"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/td/bin/td_generate_java_api ${TD_API_TLO_PATH} ${TD_API_TL_PATH} ${JAVADOC_TL_DOCUMENTATION_GENERATOR_PATH}
)
set(JAVA_EXAMPLE_FILES "${JAVA_SOURCE_PATH}/example/Example.java")
set(JAVA_SOURCE_FILES "${JAVA_SOURCE_PATH}/Client.java" "${JAVA_SOURCE_PATH}/TdApi.java")
endif()
if (CMAKE_VERSION VERSION_LESS "3.17")
set(CMAKE_RM_COMMAND remove_directory)
else()
set(CMAKE_RM_COMMAND rm -rf --)
endif()
get_filename_component(JAVA_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin REALPATH BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIRECTORY})
add_custom_target(build_java
COMMAND ${CMAKE_COMMAND} -E ${CMAKE_RM_COMMAND} "${JAVA_OUTPUT_DIRECTORY}/${TD_API_JAVA_PACKAGE}"
COMMAND ${Java_JAVAC_EXECUTABLE} -encoding UTF-8 -d "${JAVA_OUTPUT_DIRECTORY}" ${JAVA_EXAMPLE_FILES} ${JAVA_SOURCE_FILES}
COMMENT "Building Java code"
DEPENDS td_generate_java_api
)
add_custom_target(generate_javadoc
COMMAND ${CMAKE_COMMAND} -E ${CMAKE_RM_COMMAND} "${JAVA_OUTPUT_DIRECTORY}/../docs"
COMMAND ${Java_JAVADOC_EXECUTABLE} -encoding UTF-8 -charset UTF-8 -d "${JAVA_OUTPUT_DIRECTORY}/../docs" ${JAVA_SOURCE_FILES}
WORKING_DIRECTORY ${TD_API_JAVA_PATH}
COMMENT "Generating Javadoc documentation"
DEPENDS td_generate_java_api
)
# Building shared library
add_library(tdjni SHARED
td_jni.cpp
)
target_include_directories(tdjni PRIVATE ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
target_link_libraries(tdjni PRIVATE ${JAVA_JVM_LIBRARY})
target_compile_definitions(tdjni PRIVATE PACKAGE_NAME="${TD_API_JAVA_PACKAGE}")
if (TD_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()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(GCC 1)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CLANG 1)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
set(INTEL 1)
elseif (NOT MSVC)
message(FATAL_ERROR "Compiler isn't supported")
endif()
include(CheckCXXCompilerFlag)
if (GCC OR CLANG OR INTEL)
if (WIN32 AND INTEL)
set(STD14_FLAG /Qstd=c++14)
else()
set(STD14_FLAG -std=c++14)
endif()
check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD14)
if (NOT HAVE_STD14)
string(REPLACE "c++14" "c++1y" STD14_FLAG "${STD14_FLAG}")
check_cxx_compiler_flag(${STD14_FLAG} HAVE_STD1Y)
set(HAVE_STD14 ${HAVE_STD1Y})
endif()
target_compile_options(tdjni PRIVATE "${STD14_FLAG}")
elseif (MSVC)
set(HAVE_STD14 MSVC_VERSION>=1900)
endif()
if (NOT HAVE_STD14)
message(FATAL_ERROR "No C++14 support in the compiler. Please upgrade the compiler.")
endif()
add_dependencies(tdjni td_generate_java_api build_java generate_javadoc)
install(TARGETS tdjni
LIBRARY DESTINATION bin
RUNTIME DESTINATION bin
)
if (MSVC AND VCPKG_TOOLCHAIN)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/" DESTINATION bin FILES_MATCHING PATTERN "*.dll" PATTERN "*.pdb")
endif()

View File

@ -1,47 +0,0 @@
# TDLib Java example
To run this example, you will need installed JDK >= 1.6.
For Javadoc documentation generation PHP is needed.
You can find complete build instructions for your operating system at https://tdlib.github.io/td/build.html?language=Java.
In general, the build process looks as follows.
TDLib should be prebuilt with JNI bindings and installed to local subdirectory `td/` as follows:
```
cd <path to TDLib sources>
mkdir jnibuild
cd jnibuild
cmake -DCMAKE_BUILD_TYPE=Release -DTD_ENABLE_JNI=ON -DCMAKE_INSTALL_PREFIX:PATH=../example/java/td ..
cmake --build . --target install
```
If you want to compile TDLib for 32-bit/64-bit Java on Windows using MSVC, you will also need to add `-A Win32`/`-A x64` option to CMake.
In Windows, use vcpkg toolchain file by adding parameter -DCMAKE_TOOLCHAIN_FILE=<VCPKG_DIR>/scripts/buildsystems/vcpkg.cmake
If you want to build JsonClient.java wrapper for JSON interface instead of the native JNI interface, add `-DTD_JSON_JAVA=ON` option to CMake.
After this you can compile the example source code:
```
cd <path to TDLib sources>/example/java
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DTd_DIR=<full path to TDLib sources>/example/java/td/lib/cmake/Td -DCMAKE_INSTALL_PREFIX:PATH=.. ..
cmake --build . --target install
```
Compiled TDLib shared library and Java example after that will be placed in `bin/` and Javadoc documentation in `docs/`.
After this you can run the Java example:
```
cd <path to TDLib sources>/example/java/bin
java '-Djava.library.path=.' org/drinkless/tdlib/example/Example
```
If you built JSON interface example using `-DTD_JSON_JAVA=ON` option, then use the command `java '-Djava.library.path=.' org/drinkless/tdlib/example/JsonExample` instead.
If you receive "Could NOT find JNI ..." error from CMake, you need to specify to CMake path to the installed JDK, for example, "-DJAVA_HOME=/usr/lib/jvm/java-8-oracle/".
If you receive java.lang.UnsatisfiedLinkError with "Can't find dependent libraries", you may also need to copy some dependent shared OpenSSL and zlib libraries to `bin/`.
Make sure that you compiled the example for the same architecture as your JVM.

View File

@ -1,259 +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)
//
package org.drinkless.tdlib;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Main class for interaction with the TDLib.
*/
public final class Client {
static {
try {
System.loadLibrary("tdjni");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
/**
* Interface for handler for results of queries to TDLib and incoming updates from TDLib.
*/
public interface ResultHandler {
/**
* Callback called on result of query to TDLib or incoming update from TDLib.
*
* @param object Result of query or update of type TdApi.Update about new events.
*/
void onResult(TdApi.Object object);
}
/**
* Interface for handler of exceptions thrown while invoking ResultHandler.
* By default, all such exceptions are ignored.
* All exceptions thrown from ExceptionHandler are ignored.
*/
public interface ExceptionHandler {
/**
* Callback called on exceptions thrown while invoking ResultHandler.
*
* @param e Exception thrown by ResultHandler.
*/
void onException(Throwable e);
}
/**
* Interface for handler of messages that are added to the internal TDLib log.
*/
public interface LogMessageHandler {
/**
* Callback called on messages that are added to the internal TDLib log.
*
* @param verbosityLevel Log verbosity level with which the message was added from -1 up to 1024.
* If 0, then TDLib will crash as soon as the callback returns.
* None of the TDLib methods can be called from the callback.
* @param message The message added to the internal TDLib log.
*/
void onLogMessage(int verbosityLevel, String message);
}
/**
* Exception class thrown when TDLib error occurred while performing {@link #execute(TdApi.Function)}.
*/
public static class ExecutionException extends Exception {
/**
* Original TDLib error occurred when performing one of the synchronous functions.
*/
public final TdApi.Error error;
/**
* @param error TDLib error occurred while performing {@link #execute(TdApi.Function)}.
*/
ExecutionException (TdApi.Error error) {
super(error.code + ": " + error.message);
this.error = error;
}
}
/**
* Sends a request to the TDLib.
*
* @param query Object representing a query to the TDLib.
* @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TdApi.Error as parameter. If it is null, nothing
* will be called.
* @param exceptionHandler Exception handler with onException method which will be called on
* exception thrown from resultHandler. If it is null, then
* defaultExceptionHandler will be called.
*/
public void send(TdApi.Function query, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
long queryId = currentQueryId.incrementAndGet();
if (resultHandler != null) {
handlers.put(queryId, new Handler(resultHandler, exceptionHandler));
}
nativeClientSend(nativeClientId, queryId, query);
}
/**
* Sends a request to the TDLib with an empty ExceptionHandler.
*
* @param query Object representing a query to the TDLib.
* @param resultHandler Result handler with onResult method which will be called with result
* of the query or with TdApi.Error as parameter. If it is null, then
* defaultExceptionHandler will be called.
*/
public void send(TdApi.Function query, ResultHandler resultHandler) {
send(query, resultHandler, null);
}
/**
* Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously.
*
* @param query Object representing a query to the TDLib.
* @param <T> Automatically deduced return type of the query.
* @return request result.
* @throws ExecutionException if query execution fails.
*/
@SuppressWarnings("unchecked")
public static <T extends TdApi.Object> T execute(TdApi.Function<T> query) throws ExecutionException {
TdApi.Object object = nativeClientExecute(query);
if (object instanceof TdApi.Error) {
throw new ExecutionException((TdApi.Error) object);
}
return (T) object;
}
/**
* Creates new Client.
*
* @param updateHandler Handler for incoming updates.
* @param updateExceptionHandler Handler for exceptions thrown from updateHandler. If it is null, exceptions will be ignored.
* @param defaultExceptionHandler Default handler for exceptions thrown from all ResultHandler. If it is null, exceptions will be ignored.
* @return created Client
*/
public static Client create(ResultHandler updateHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) {
Client client = new Client(updateHandler, updateExceptionHandler, defaultExceptionHandler);
synchronized (responseReceiver) {
if (!responseReceiver.isRun) {
responseReceiver.isRun = true;
Thread receiverThread = new Thread(responseReceiver, "TDLib thread");
receiverThread.setDaemon(true);
receiverThread.start();
}
}
return client;
}
/**
* Sets the handler for messages that are added to the internal TDLib log.
* None of the TDLib methods can be called from the callback.
*
* @param maxVerbosityLevel The maximum verbosity level of messages for which the callback will be called.
* @param logMessageHandler Handler for messages that are added to the internal TDLib log. Pass null to remove the handler.
*/
public static void setLogMessageHandler(int maxVerbosityLevel, Client.LogMessageHandler logMessageHandler) {
nativeClientSetLogMessageHandler(maxVerbosityLevel, logMessageHandler);
}
private static class ResponseReceiver implements Runnable {
public boolean isRun = false;
@Override
public void run() {
while (true) {
int resultN = nativeClientReceive(clientIds, eventIds, events, 100000.0 /*seconds*/);
for (int i = 0; i < resultN; i++) {
processResult(clientIds[i], eventIds[i], events[i]);
events[i] = null;
}
}
}
private void processResult(int clientId, long id, TdApi.Object object) {
boolean isClosed = false;
if (id == 0 && object instanceof TdApi.UpdateAuthorizationState) {
TdApi.AuthorizationState authorizationState = ((TdApi.UpdateAuthorizationState) object).authorizationState;
if (authorizationState instanceof TdApi.AuthorizationStateClosed) {
isClosed = true;
}
}
Handler handler = id == 0 ? updateHandlers.get(clientId) : handlers.remove(id);
if (handler != null) {
try {
handler.resultHandler.onResult(object);
} catch (Throwable cause) {
ExceptionHandler exceptionHandler = handler.exceptionHandler;
if (exceptionHandler == null) {
exceptionHandler = defaultExceptionHandlers.get(clientId);
}
if (exceptionHandler != null) {
try {
exceptionHandler.onException(cause);
} catch (Throwable ignored) {
}
}
}
}
if (isClosed) {
updateHandlers.remove(clientId); // there will be no more updates
defaultExceptionHandlers.remove(clientId); // ignore further exceptions
clientCount.decrementAndGet();
}
}
private static final int MAX_EVENTS = 1000;
private final int[] clientIds = new int[MAX_EVENTS];
private final long[] eventIds = new long[MAX_EVENTS];
private final TdApi.Object[] events = new TdApi.Object[MAX_EVENTS];
}
private final int nativeClientId;
private static final ConcurrentHashMap<Integer, ExceptionHandler> defaultExceptionHandlers = new ConcurrentHashMap<Integer, ExceptionHandler>();
private static final ConcurrentHashMap<Integer, Handler> updateHandlers = new ConcurrentHashMap<Integer, Handler>();
private static final ConcurrentHashMap<Long, Handler> handlers = new ConcurrentHashMap<Long, Handler>();
private static final AtomicLong currentQueryId = new AtomicLong();
private static final AtomicLong clientCount = new AtomicLong();
private static final ResponseReceiver responseReceiver = new ResponseReceiver();
private static class Handler {
final ResultHandler resultHandler;
final ExceptionHandler exceptionHandler;
Handler(ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
this.resultHandler = resultHandler;
this.exceptionHandler = exceptionHandler;
}
}
private Client(ResultHandler updateHandler, ExceptionHandler updateExceptionHandler, ExceptionHandler defaultExceptionHandler) {
clientCount.incrementAndGet();
nativeClientId = createNativeClient();
if (updateHandler != null) {
updateHandlers.put(nativeClientId, new Handler(updateHandler, updateExceptionHandler));
}
if (defaultExceptionHandler != null) {
defaultExceptionHandlers.put(nativeClientId, defaultExceptionHandler);
}
send(new TdApi.GetOption("version"), null, null);
}
private static native int createNativeClient();
private static native void nativeClientSend(int nativeClientId, long eventId, TdApi.Function function);
private static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
private static native TdApi.Object nativeClientExecute(TdApi.Function function);
private static native void nativeClientSetLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler);
}

View File

@ -1,79 +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)
//
package org.drinkless.tdlib;
/**
* Main class for interaction with the TDLib using JSON interface.
*/
public final class JsonClient {
static {
try {
System.loadLibrary("tdjsonjava");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
/**
* Returns an opaque identifier of a new TDLib instance.
* The TDLib instance will not send updates until the first request is sent to it.
* @return Opaque identifier of a new TDLib instance.
*/
public static native int createClientId();
/**
* Sends request to the TDLib client. May be called from any thread.
* @param clientId TDLib client identifier.
* @param request JSON-serialized request.
*/
public static native void send(int clientId, String request);
/**
* Receives incoming updates and request responses. Must not be called simultaneously from two different threads.
* @param timeout The maximum number of seconds allowed for this function to wait for new data.
* @return JSON-serialized incoming update or request response. May be null if the timeout expired before new data received.
*/
public static native String receive(double timeout);
/**
* Synchronously executes a TDLib request.
* A request can be executed synchronously, only if it is documented with "Can be called synchronously".
* @param request JSON-serialized request.
* @return JSON-serialized request response. May be null if the request is invalid.
*/
public static native String execute(String request);
/**
* Interface for handler of messages that are added to the internal TDLib log.
*/
public interface LogMessageHandler {
/**
* Callback called on messages that are added to the internal TDLib log.
*
* @param verbosityLevel Log verbosity level with which the message was added from -1 up to 1024.
* If 0, then TDLib will crash as soon as the callback returns.
* None of the TDLib methods can be called from the callback.
* @param message The message added to the internal TDLib log.
*/
void onLogMessage(int verbosityLevel, String message);
}
/**
* Sets the handler for messages that are added to the internal TDLib log.
* None of the TDLib methods can be called from the callback.
*
* @param maxVerbosityLevel The maximum verbosity level of messages for which the callback will be called.
* @param logMessageHandler Handler for messages that are added to the internal TDLib log. Pass null to remove the handler.
*/
public static native void setLogMessageHandler(int maxVerbosityLevel, JsonClient.LogMessageHandler logMessageHandler);
/**
* The class can't be instantiated.
*/
private JsonClient() {
}
}

View File

@ -1,780 +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)
//
package org.drinkless.tdlib.example;
import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
import java.io.BufferedReader;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.NavigableSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Example class for TDLib usage from Java.
*/
public final class Example {
private static Client client = null;
private static TdApi.AuthorizationState authorizationState = null;
private static volatile boolean haveAuthorization = false;
private static volatile boolean needQuit = false;
private static volatile boolean canQuit = false;
private static final Client.ResultHandler defaultHandler = new DefaultHandler();
private static final Lock authorizationLock = new ReentrantLock();
private static final Condition gotAuthorization = authorizationLock.newCondition();
private static final ConcurrentMap<Long, TdApi.User> users = new ConcurrentHashMap<Long, TdApi.User>();
private static final ConcurrentMap<Long, TdApi.BasicGroup> basicGroups = new ConcurrentHashMap<Long, TdApi.BasicGroup>();
private static final ConcurrentMap<Long, TdApi.Supergroup> supergroups = new ConcurrentHashMap<Long, TdApi.Supergroup>();
private static final ConcurrentMap<Integer, TdApi.SecretChat> secretChats = new ConcurrentHashMap<Integer, TdApi.SecretChat>();
private static final ConcurrentMap<Long, TdApi.Chat> chats = new ConcurrentHashMap<Long, TdApi.Chat>();
private static final NavigableSet<OrderedChat> mainChatList = new TreeSet<OrderedChat>();
private static boolean haveFullMainChatList = false;
private static final ConcurrentMap<Long, TdApi.UserFullInfo> usersFullInfo = new ConcurrentHashMap<Long, TdApi.UserFullInfo>();
private static final ConcurrentMap<Long, TdApi.BasicGroupFullInfo> basicGroupsFullInfo = new ConcurrentHashMap<Long, TdApi.BasicGroupFullInfo>();
private static final ConcurrentMap<Long, TdApi.SupergroupFullInfo> supergroupsFullInfo = new ConcurrentHashMap<Long, TdApi.SupergroupFullInfo>();
private static final String newLine = System.getProperty("line.separator");
private static final String commandsLine = "Enter command (gcs - GetChats, gc <chatId> - GetChat, me - GetMe, sm <chatId> <message> - SendMessage, lo - LogOut, q - Quit): ";
private static volatile String currentPrompt = null;
private static void print(String str) {
if (currentPrompt != null) {
System.out.println("");
}
System.out.println(str);
if (currentPrompt != null) {
System.out.print(currentPrompt);
}
}
private static void setChatPositions(TdApi.Chat chat, TdApi.ChatPosition[] positions) {
synchronized (mainChatList) {
synchronized (chat) {
for (TdApi.ChatPosition position : chat.positions) {
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
boolean isRemoved = mainChatList.remove(new OrderedChat(chat.id, position));
assert isRemoved;
}
}
chat.positions = positions;
for (TdApi.ChatPosition position : chat.positions) {
if (position.list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
boolean isAdded = mainChatList.add(new OrderedChat(chat.id, position));
assert isAdded;
}
}
}
}
}
private static void onAuthorizationStateUpdated(TdApi.AuthorizationState authorizationState) {
if (authorizationState != null) {
Example.authorizationState = authorizationState;
}
switch (Example.authorizationState.getConstructor()) {
case TdApi.AuthorizationStateWaitTdlibParameters.CONSTRUCTOR:
TdApi.SetTdlibParameters request = new TdApi.SetTdlibParameters();
request.databaseDirectory = "tdlib";
request.useMessageDatabase = true;
request.useSecretChats = true;
request.apiId = 94575;
request.apiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.systemLanguageCode = "en";
request.deviceModel = "Desktop";
request.applicationVersion = "1.0";
client.send(request, new AuthorizationRequestHandler());
break;
case TdApi.AuthorizationStateWaitPhoneNumber.CONSTRUCTOR: {
String phoneNumber = promptString("Please enter phone number: ");
client.send(new TdApi.SetAuthenticationPhoneNumber(phoneNumber, null), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitOtherDeviceConfirmation.CONSTRUCTOR: {
String link = ((TdApi.AuthorizationStateWaitOtherDeviceConfirmation) Example.authorizationState).link;
System.out.println("Please confirm this login link on another device: " + link);
break;
}
case TdApi.AuthorizationStateWaitEmailAddress.CONSTRUCTOR: {
String emailAddress = promptString("Please enter email address: ");
client.send(new TdApi.SetAuthenticationEmailAddress(emailAddress), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitEmailCode.CONSTRUCTOR: {
String code = promptString("Please enter email authentication code: ");
client.send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(code)), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitCode.CONSTRUCTOR: {
String code = promptString("Please enter authentication code: ");
client.send(new TdApi.CheckAuthenticationCode(code), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitRegistration.CONSTRUCTOR: {
String firstName = promptString("Please enter your first name: ");
String lastName = promptString("Please enter your last name: ");
client.send(new TdApi.RegisterUser(firstName, lastName, false), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateWaitPassword.CONSTRUCTOR: {
String password = promptString("Please enter password: ");
client.send(new TdApi.CheckAuthenticationPassword(password), new AuthorizationRequestHandler());
break;
}
case TdApi.AuthorizationStateReady.CONSTRUCTOR:
haveAuthorization = true;
authorizationLock.lock();
try {
gotAuthorization.signal();
} finally {
authorizationLock.unlock();
}
break;
case TdApi.AuthorizationStateLoggingOut.CONSTRUCTOR:
haveAuthorization = false;
print("Logging out");
break;
case TdApi.AuthorizationStateClosing.CONSTRUCTOR:
haveAuthorization = false;
print("Closing");
break;
case TdApi.AuthorizationStateClosed.CONSTRUCTOR:
print("Closed");
if (!needQuit) {
client = Client.create(new UpdateHandler(), null, null); // recreate client after previous has closed
} else {
canQuit = true;
}
break;
default:
System.err.println("Unsupported authorization state:" + newLine + Example.authorizationState);
}
}
private static int toInt(String arg) {
int result = 0;
try {
result = Integer.parseInt(arg);
} catch (NumberFormatException ignored) {
}
return result;
}
private static long getChatId(String arg) {
long chatId = 0;
try {
chatId = Long.parseLong(arg);
} catch (NumberFormatException ignored) {
}
return chatId;
}
private static String promptString(String prompt) {
System.out.print(prompt);
currentPrompt = prompt;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str = "";
try {
str = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
currentPrompt = null;
return str;
}
private static void getCommand() {
String command = promptString(commandsLine);
String[] commands = command.split(" ", 2);
try {
switch (commands[0]) {
case "gcs": {
int limit = 20;
if (commands.length > 1) {
limit = toInt(commands[1]);
}
getMainChatList(limit);
break;
}
case "gc":
client.send(new TdApi.GetChat(getChatId(commands[1])), defaultHandler);
break;
case "me":
client.send(new TdApi.GetMe(), defaultHandler);
break;
case "sm": {
String[] args = commands[1].split(" ", 2);
sendMessage(getChatId(args[0]), args[1]);
break;
}
case "lo":
haveAuthorization = false;
client.send(new TdApi.LogOut(), defaultHandler);
break;
case "q":
needQuit = true;
haveAuthorization = false;
client.send(new TdApi.Close(), defaultHandler);
break;
default:
System.err.println("Unsupported command: " + command);
}
} catch (ArrayIndexOutOfBoundsException e) {
print("Not enough arguments");
}
}
private static void getMainChatList(final int limit) {
synchronized (mainChatList) {
if (!haveFullMainChatList && limit > mainChatList.size()) {
// send LoadChats request if there are some unknown chats and have not enough known chats
client.send(new TdApi.LoadChats(new TdApi.ChatListMain(), limit - mainChatList.size()), new Client.ResultHandler() {
@Override
public void onResult(TdApi.Object object) {
switch (object.getConstructor()) {
case TdApi.Error.CONSTRUCTOR:
if (((TdApi.Error) object).code == 404) {
synchronized (mainChatList) {
haveFullMainChatList = true;
}
} else {
System.err.println("Receive an error for LoadChats:" + newLine + object);
}
break;
case TdApi.Ok.CONSTRUCTOR:
// chats had already been received through updates, let's retry request
getMainChatList(limit);
break;
default:
System.err.println("Receive wrong response from TDLib:" + newLine + object);
}
}
});
return;
}
java.util.Iterator<OrderedChat> iter = mainChatList.iterator();
System.out.println();
System.out.println("First " + limit + " chat(s) out of " + mainChatList.size() + " known chat(s):");
for (int i = 0; i < limit && i < mainChatList.size(); i++) {
long chatId = iter.next().chatId;
TdApi.Chat chat = chats.get(chatId);
synchronized (chat) {
System.out.println(chatId + ": " + chat.title);
}
}
print("");
}
}
private static void sendMessage(long chatId, String message) {
// initialize reply markup just for testing
TdApi.InlineKeyboardButton[] row = {new TdApi.InlineKeyboardButton("https://telegram.org?1", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?2", new TdApi.InlineKeyboardButtonTypeUrl()), new TdApi.InlineKeyboardButton("https://telegram.org?3", new TdApi.InlineKeyboardButtonTypeUrl())};
TdApi.ReplyMarkup replyMarkup = new TdApi.ReplyMarkupInlineKeyboard(new TdApi.InlineKeyboardButton[][]{row, row, row});
TdApi.InputMessageContent content = new TdApi.InputMessageText(new TdApi.FormattedText(message, null), null, true);
client.send(new TdApi.SendMessage(chatId, 0, null, null, replyMarkup, content), defaultHandler);
}
public static void main(String[] args) throws InterruptedException {
// set log message handler to handle only fatal errors (0) and plain log messages (-1)
Client.setLogMessageHandler(0, new LogMessageHandler());
// disable TDLib log and redirect fatal errors and plain log messages to a file
try {
Client.execute(new TdApi.SetLogVerbosityLevel(0));
Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false)));
} catch (Client.ExecutionException error) {
throw new IOError(new IOException("Write access to the current directory is required"));
}
// create client
client = Client.create(new UpdateHandler(), null, null);
// main loop
while (!needQuit) {
// await authorization
authorizationLock.lock();
try {
while (!haveAuthorization) {
gotAuthorization.await();
}
} finally {
authorizationLock.unlock();
}
while (haveAuthorization) {
getCommand();
}
}
while (!canQuit) {
Thread.sleep(1);
}
}
private static class OrderedChat implements Comparable<OrderedChat> {
final long chatId;
final TdApi.ChatPosition position;
OrderedChat(long chatId, TdApi.ChatPosition position) {
this.chatId = chatId;
this.position = position;
}
@Override
public int compareTo(OrderedChat o) {
if (this.position.order != o.position.order) {
return o.position.order < this.position.order ? -1 : 1;
}
if (this.chatId != o.chatId) {
return o.chatId < this.chatId ? -1 : 1;
}
return 0;
}
@Override
public boolean equals(Object obj) {
OrderedChat o = (OrderedChat) obj;
return this.chatId == o.chatId && this.position.order == o.position.order;
}
}
private static class DefaultHandler implements Client.ResultHandler {
@Override
public void onResult(TdApi.Object object) {
print(object.toString());
}
}
private static class UpdateHandler implements Client.ResultHandler {
@Override
public void onResult(TdApi.Object object) {
switch (object.getConstructor()) {
case TdApi.UpdateAuthorizationState.CONSTRUCTOR:
onAuthorizationStateUpdated(((TdApi.UpdateAuthorizationState) object).authorizationState);
break;
case TdApi.UpdateUser.CONSTRUCTOR:
TdApi.UpdateUser updateUser = (TdApi.UpdateUser) object;
users.put(updateUser.user.id, updateUser.user);
break;
case TdApi.UpdateUserStatus.CONSTRUCTOR: {
TdApi.UpdateUserStatus updateUserStatus = (TdApi.UpdateUserStatus) object;
TdApi.User user = users.get(updateUserStatus.userId);
synchronized (user) {
user.status = updateUserStatus.status;
}
break;
}
case TdApi.UpdateBasicGroup.CONSTRUCTOR:
TdApi.UpdateBasicGroup updateBasicGroup = (TdApi.UpdateBasicGroup) object;
basicGroups.put(updateBasicGroup.basicGroup.id, updateBasicGroup.basicGroup);
break;
case TdApi.UpdateSupergroup.CONSTRUCTOR:
TdApi.UpdateSupergroup updateSupergroup = (TdApi.UpdateSupergroup) object;
supergroups.put(updateSupergroup.supergroup.id, updateSupergroup.supergroup);
break;
case TdApi.UpdateSecretChat.CONSTRUCTOR:
TdApi.UpdateSecretChat updateSecretChat = (TdApi.UpdateSecretChat) object;
secretChats.put(updateSecretChat.secretChat.id, updateSecretChat.secretChat);
break;
case TdApi.UpdateNewChat.CONSTRUCTOR: {
TdApi.UpdateNewChat updateNewChat = (TdApi.UpdateNewChat) object;
TdApi.Chat chat = updateNewChat.chat;
synchronized (chat) {
chats.put(chat.id, chat);
TdApi.ChatPosition[] positions = chat.positions;
chat.positions = new TdApi.ChatPosition[0];
setChatPositions(chat, positions);
}
break;
}
case TdApi.UpdateChatTitle.CONSTRUCTOR: {
TdApi.UpdateChatTitle updateChat = (TdApi.UpdateChatTitle) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.title = updateChat.title;
}
break;
}
case TdApi.UpdateChatPhoto.CONSTRUCTOR: {
TdApi.UpdateChatPhoto updateChat = (TdApi.UpdateChatPhoto) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.photo = updateChat.photo;
}
break;
}
case TdApi.UpdateChatPermissions.CONSTRUCTOR: {
TdApi.UpdateChatPermissions update = (TdApi.UpdateChatPermissions) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.permissions = update.permissions;
}
break;
}
case TdApi.UpdateChatLastMessage.CONSTRUCTOR: {
TdApi.UpdateChatLastMessage updateChat = (TdApi.UpdateChatLastMessage) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.lastMessage = updateChat.lastMessage;
setChatPositions(chat, updateChat.positions);
}
break;
}
case TdApi.UpdateChatPosition.CONSTRUCTOR: {
TdApi.UpdateChatPosition updateChat = (TdApi.UpdateChatPosition) object;
if (updateChat.position.list.getConstructor() != TdApi.ChatListMain.CONSTRUCTOR) {
break;
}
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
int i;
for (i = 0; i < chat.positions.length; i++) {
if (chat.positions[i].list.getConstructor() == TdApi.ChatListMain.CONSTRUCTOR) {
break;
}
}
TdApi.ChatPosition[] new_positions = new TdApi.ChatPosition[chat.positions.length + (updateChat.position.order == 0 ? 0 : 1) - (i < chat.positions.length ? 1 : 0)];
int pos = 0;
if (updateChat.position.order != 0) {
new_positions[pos++] = updateChat.position;
}
for (int j = 0; j < chat.positions.length; j++) {
if (j != i) {
new_positions[pos++] = chat.positions[j];
}
}
assert pos == new_positions.length;
setChatPositions(chat, new_positions);
}
break;
}
case TdApi.UpdateChatReadInbox.CONSTRUCTOR: {
TdApi.UpdateChatReadInbox updateChat = (TdApi.UpdateChatReadInbox) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.lastReadInboxMessageId = updateChat.lastReadInboxMessageId;
chat.unreadCount = updateChat.unreadCount;
}
break;
}
case TdApi.UpdateChatReadOutbox.CONSTRUCTOR: {
TdApi.UpdateChatReadOutbox updateChat = (TdApi.UpdateChatReadOutbox) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.lastReadOutboxMessageId = updateChat.lastReadOutboxMessageId;
}
break;
}
case TdApi.UpdateChatActionBar.CONSTRUCTOR: {
TdApi.UpdateChatActionBar updateChat = (TdApi.UpdateChatActionBar) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.actionBar = updateChat.actionBar;
}
break;
}
case TdApi.UpdateChatAvailableReactions.CONSTRUCTOR: {
TdApi.UpdateChatAvailableReactions updateChat = (TdApi.UpdateChatAvailableReactions) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.availableReactions = updateChat.availableReactions;
}
break;
}
case TdApi.UpdateChatDraftMessage.CONSTRUCTOR: {
TdApi.UpdateChatDraftMessage updateChat = (TdApi.UpdateChatDraftMessage) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.draftMessage = updateChat.draftMessage;
setChatPositions(chat, updateChat.positions);
}
break;
}
case TdApi.UpdateChatMessageSender.CONSTRUCTOR: {
TdApi.UpdateChatMessageSender updateChat = (TdApi.UpdateChatMessageSender) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.messageSenderId = updateChat.messageSenderId;
}
break;
}
case TdApi.UpdateChatMessageAutoDeleteTime.CONSTRUCTOR: {
TdApi.UpdateChatMessageAutoDeleteTime updateChat = (TdApi.UpdateChatMessageAutoDeleteTime) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.messageAutoDeleteTime = updateChat.messageAutoDeleteTime;
}
break;
}
case TdApi.UpdateChatNotificationSettings.CONSTRUCTOR: {
TdApi.UpdateChatNotificationSettings update = (TdApi.UpdateChatNotificationSettings) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.notificationSettings = update.notificationSettings;
}
break;
}
case TdApi.UpdateChatPendingJoinRequests.CONSTRUCTOR: {
TdApi.UpdateChatPendingJoinRequests update = (TdApi.UpdateChatPendingJoinRequests) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.pendingJoinRequests = update.pendingJoinRequests;
}
break;
}
case TdApi.UpdateChatReplyMarkup.CONSTRUCTOR: {
TdApi.UpdateChatReplyMarkup updateChat = (TdApi.UpdateChatReplyMarkup) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.replyMarkupMessageId = updateChat.replyMarkupMessageId;
}
break;
}
case TdApi.UpdateChatBackground.CONSTRUCTOR: {
TdApi.UpdateChatBackground updateChat = (TdApi.UpdateChatBackground) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.background = updateChat.background;
}
break;
}
case TdApi.UpdateChatTheme.CONSTRUCTOR: {
TdApi.UpdateChatTheme updateChat = (TdApi.UpdateChatTheme) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.themeName = updateChat.themeName;
}
break;
}
case TdApi.UpdateChatUnreadMentionCount.CONSTRUCTOR: {
TdApi.UpdateChatUnreadMentionCount updateChat = (TdApi.UpdateChatUnreadMentionCount) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.unreadMentionCount = updateChat.unreadMentionCount;
}
break;
}
case TdApi.UpdateChatUnreadReactionCount.CONSTRUCTOR: {
TdApi.UpdateChatUnreadReactionCount updateChat = (TdApi.UpdateChatUnreadReactionCount) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.unreadReactionCount = updateChat.unreadReactionCount;
}
break;
}
case TdApi.UpdateChatVideoChat.CONSTRUCTOR: {
TdApi.UpdateChatVideoChat updateChat = (TdApi.UpdateChatVideoChat) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.videoChat = updateChat.videoChat;
}
break;
}
case TdApi.UpdateChatDefaultDisableNotification.CONSTRUCTOR: {
TdApi.UpdateChatDefaultDisableNotification update = (TdApi.UpdateChatDefaultDisableNotification) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.defaultDisableNotification = update.defaultDisableNotification;
}
break;
}
case TdApi.UpdateChatHasProtectedContent.CONSTRUCTOR: {
TdApi.UpdateChatHasProtectedContent updateChat = (TdApi.UpdateChatHasProtectedContent) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.hasProtectedContent = updateChat.hasProtectedContent;
}
break;
}
case TdApi.UpdateChatIsTranslatable.CONSTRUCTOR: {
TdApi.UpdateChatIsTranslatable update = (TdApi.UpdateChatIsTranslatable) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.isTranslatable = update.isTranslatable;
}
break;
}
case TdApi.UpdateChatIsMarkedAsUnread.CONSTRUCTOR: {
TdApi.UpdateChatIsMarkedAsUnread update = (TdApi.UpdateChatIsMarkedAsUnread) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.isMarkedAsUnread = update.isMarkedAsUnread;
}
break;
}
case TdApi.UpdateChatBlockList.CONSTRUCTOR: {
TdApi.UpdateChatBlockList update = (TdApi.UpdateChatBlockList) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.blockList = update.blockList;
}
break;
}
case TdApi.UpdateChatHasScheduledMessages.CONSTRUCTOR: {
TdApi.UpdateChatHasScheduledMessages update = (TdApi.UpdateChatHasScheduledMessages) object;
TdApi.Chat chat = chats.get(update.chatId);
synchronized (chat) {
chat.hasScheduledMessages = update.hasScheduledMessages;
}
break;
}
case TdApi.UpdateMessageMentionRead.CONSTRUCTOR: {
TdApi.UpdateMessageMentionRead updateChat = (TdApi.UpdateMessageMentionRead) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.unreadMentionCount = updateChat.unreadMentionCount;
}
break;
}
case TdApi.UpdateMessageUnreadReactions.CONSTRUCTOR: {
TdApi.UpdateMessageUnreadReactions updateChat = (TdApi.UpdateMessageUnreadReactions) object;
TdApi.Chat chat = chats.get(updateChat.chatId);
synchronized (chat) {
chat.unreadReactionCount = updateChat.unreadReactionCount;
}
break;
}
case TdApi.UpdateUserFullInfo.CONSTRUCTOR:
TdApi.UpdateUserFullInfo updateUserFullInfo = (TdApi.UpdateUserFullInfo) object;
usersFullInfo.put(updateUserFullInfo.userId, updateUserFullInfo.userFullInfo);
break;
case TdApi.UpdateBasicGroupFullInfo.CONSTRUCTOR:
TdApi.UpdateBasicGroupFullInfo updateBasicGroupFullInfo = (TdApi.UpdateBasicGroupFullInfo) object;
basicGroupsFullInfo.put(updateBasicGroupFullInfo.basicGroupId, updateBasicGroupFullInfo.basicGroupFullInfo);
break;
case TdApi.UpdateSupergroupFullInfo.CONSTRUCTOR:
TdApi.UpdateSupergroupFullInfo updateSupergroupFullInfo = (TdApi.UpdateSupergroupFullInfo) object;
supergroupsFullInfo.put(updateSupergroupFullInfo.supergroupId, updateSupergroupFullInfo.supergroupFullInfo);
break;
default:
// print("Unsupported update:" + newLine + object);
}
}
}
private static class AuthorizationRequestHandler implements Client.ResultHandler {
@Override
public void onResult(TdApi.Object object) {
switch (object.getConstructor()) {
case TdApi.Error.CONSTRUCTOR:
System.err.println("Receive an error:" + newLine + object);
onAuthorizationStateUpdated(null); // repeat last action
break;
case TdApi.Ok.CONSTRUCTOR:
// result is already received through UpdateAuthorizationState, nothing to do
break;
default:
System.err.println("Receive wrong response from TDLib:" + newLine + object);
}
}
}
private static class LogMessageHandler implements Client.LogMessageHandler {
@Override
public void onLogMessage(int verbosityLevel, String message) {
if (verbosityLevel == 0) {
onFatalError(message);
return;
}
System.err.println(message);
}
}
private static void onFatalError(String errorMessage) {
final class ThrowError implements Runnable {
private final String errorMessage;
private final AtomicLong errorThrowTime;
private ThrowError(String errorMessage, AtomicLong errorThrowTime) {
this.errorMessage = errorMessage;
this.errorThrowTime = errorThrowTime;
}
@Override
public void run() {
if (isDatabaseBrokenError(errorMessage) || isDiskFullError(errorMessage) || isDiskError(errorMessage)) {
processExternalError();
return;
}
errorThrowTime.set(System.currentTimeMillis());
throw new ClientError("TDLib fatal error: " + errorMessage);
}
private void processExternalError() {
errorThrowTime.set(System.currentTimeMillis());
throw new ExternalClientError("Fatal error: " + errorMessage);
}
final class ClientError extends Error {
private ClientError(String message) {
super(message);
}
}
final class ExternalClientError extends Error {
public ExternalClientError(String message) {
super(message);
}
}
private boolean isDatabaseBrokenError(String message) {
return message.contains("Wrong key or database is corrupted") ||
message.contains("SQL logic error or missing database") ||
message.contains("database disk image is malformed") ||
message.contains("file is encrypted or is not a database") ||
message.contains("unsupported file format") ||
message.contains("Database was corrupted and deleted during execution and can't be recreated");
}
private boolean isDiskFullError(String message) {
return message.contains("PosixError : No space left on device") ||
message.contains("database or disk is full");
}
private boolean isDiskError(String message) {
return message.contains("I/O error") || message.contains("Structure needs cleaning");
}
}
final AtomicLong errorThrowTime = new AtomicLong(Long.MAX_VALUE);
new Thread(new ThrowError(errorMessage, errorThrowTime), "TDLib fatal error thread").start();
// wait at least 10 seconds after the error is thrown
while (errorThrowTime.get() >= System.currentTimeMillis() - 10000) {
try {
Thread.sleep(1000 /* milliseconds */);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
}
}
}
}

View File

@ -1,47 +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)
//
package org.drinkless.tdlib.example;
import org.drinkless.tdlib.JsonClient;
/**
* Example class for TDLib usage from Java using JSON interface.
*/
public final class JsonExample {
public static void main(String[] args) throws InterruptedException {
// set log message handler to handle only fatal errors (0) and plain log messages (-1)
JsonClient.setLogMessageHandler(0, new LogMessageHandler());
// disable TDLib log and redirect fatal errors and plain log messages to a file
JsonClient.execute("{\"@type\":\"setLogVerbosityLevel\",\"new_verbosity_level\":0}");
JsonClient.execute("{\"@type\":\"setLogStream\",\"log_stream\":{\"@type\":\"logStreamFile\",\"path\":\"tdlib.log\",\"max_file_size\":128000000}}");
// create client identifier
int clientId = JsonClient.createClientId();
// send first request to activate the client
JsonClient.send(clientId, "{\"@type\":\"getOption\",\"name\":\"version\"}");
// main loop
while (true) {
String result = JsonClient.receive(100.0);
if (result != null) {
System.out.println(result);
}
}
}
private static class LogMessageHandler implements JsonClient.LogMessageHandler {
@Override
public void onLogMessage(int verbosityLevel, String message) {
System.err.print(message);
if (verbosityLevel == 0) {
System.err.println("Receive fatal error; the process will crash now");
}
}
}
}

View File

@ -1,241 +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)
//
#ifdef TD_JSON_JAVA
#include <td/telegram/td_json_client.h>
#else
#include <td/telegram/Client.h>
#include <td/telegram/td_api.h>
#endif
#include <td/tl/tl_jni_object.h>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <utility>
namespace td_jni {
#ifdef TD_JSON_JAVA
static jint JsonClient_createClientId(JNIEnv *env, jclass clazz) {
return static_cast<jint>(td_create_client_id());
}
static void JsonClient_send(JNIEnv *env, jclass clazz, jint client_id, jstring request) {
td_send(static_cast<int>(client_id), td::jni::from_jstring(env, request).c_str());
}
static jstring JsonClient_receive(JNIEnv *env, jclass clazz, jdouble timeout) {
auto result = td_receive(timeout);
if (result == nullptr) {
return nullptr;
}
return td::jni::to_jstring(env, result);
}
static jstring JsonClient_execute(JNIEnv *env, jclass clazz, jstring request) {
auto result = td_execute(td::jni::from_jstring(env, request).c_str());
if (result == nullptr) {
return nullptr;
}
return td::jni::to_jstring(env, result);
}
#else
static td::td_api::object_ptr<td::td_api::Function> fetch_function(JNIEnv *env, jobject function) {
td::jni::reset_parse_error();
auto result = td::td_api::Function::fetch(env, function);
if (td::jni::have_parse_error()) {
std::abort();
}
return result;
}
static td::ClientManager *get_manager() {
return td::ClientManager::get_manager_singleton();
}
static jint Client_createNativeClient(JNIEnv *env, jclass clazz) {
return static_cast<jint>(get_manager()->create_client_id());
}
static void Client_nativeClientSend(JNIEnv *env, jclass clazz, jint client_id, jlong id, jobject function) {
get_manager()->send(static_cast<std::int32_t>(client_id), static_cast<std::uint64_t>(id),
fetch_function(env, function));
}
static jint Client_nativeClientReceive(JNIEnv *env, jclass clazz, jintArray client_ids, jlongArray ids,
jobjectArray events, jdouble timeout) {
jsize events_size = env->GetArrayLength(ids); // client_ids, ids and events must be of equal size
if (events_size == 0) {
return 0;
}
jsize result_size = 0;
auto *manager = get_manager();
auto response = manager->receive(timeout);
while (response.object) {
auto client_id = static_cast<jint>(response.client_id);
env->SetIntArrayRegion(client_ids, result_size, 1, &client_id);
auto request_id = static_cast<jlong>(response.request_id);
env->SetLongArrayRegion(ids, result_size, 1, &request_id);
jobject object;
response.object->store(env, object);
env->SetObjectArrayElement(events, result_size, object);
env->DeleteLocalRef(object);
result_size++;
if (result_size == events_size) {
break;
}
response = manager->receive(0);
}
return result_size;
}
static jobject Client_nativeClientExecute(JNIEnv *env, jclass clazz, jobject function) {
jobject result;
td::ClientManager::execute(fetch_function(env, function))->store(env, result);
return result;
}
static jstring Object_toString(JNIEnv *env, jobject object) {
return td::jni::to_jstring(env, to_string(td::td_api::Object::fetch(env, object)));
}
static jstring Function_toString(JNIEnv *env, jobject object) {
return td::jni::to_jstring(env, to_string(td::td_api::Function::fetch(env, object)));
}
#endif
static constexpr jint JAVA_VERSION = JNI_VERSION_1_6;
static JavaVM *java_vm;
static jobject log_message_handler;
static void on_log_message(int verbosity_level, const char *log_message) {
auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION);
if (env == nullptr) {
return;
}
jobject handler = env->NewLocalRef(log_message_handler);
if (!handler) {
return;
}
jclass handler_class = env->GetObjectClass(handler);
if (handler_class) {
jmethodID on_log_message_method = env->GetMethodID(handler_class, "onLogMessage", "(ILjava/lang/String;)V");
if (on_log_message_method) {
jstring log_message_str = td::jni::to_jstring(env.get(), log_message);
if (log_message_str) {
env->CallVoidMethod(handler, on_log_message_method, static_cast<jint>(verbosity_level), log_message_str);
env->DeleteLocalRef((jobject)log_message_str);
}
}
env->DeleteLocalRef((jobject)handler_class);
}
env->DeleteLocalRef(handler);
}
static void Client_nativeClientSetLogMessageHandler(JNIEnv *env, jclass clazz, jint max_verbosity_level,
jobject new_log_message_handler) {
if (log_message_handler) {
#ifdef TD_JSON_JAVA
td_set_log_message_callback(0, nullptr);
#else
td::ClientManager::set_log_message_callback(0, nullptr);
#endif
jobject old_log_message_handler = log_message_handler;
log_message_handler = jobject();
env->DeleteGlobalRef(old_log_message_handler);
}
if (new_log_message_handler) {
log_message_handler = env->NewGlobalRef(new_log_message_handler);
if (!log_message_handler) {
// out of memory
return;
}
#ifdef TD_JSON_JAVA
td_set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
#else
td::ClientManager::set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
#endif
}
}
static jint register_native(JavaVM *vm) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JAVA_VERSION) != JNI_OK) {
return -1;
}
java_vm = vm;
auto register_method = [env](jclass clazz, std::string name, std::string signature, auto function_ptr) {
td::jni::register_native_method(env, clazz, std::move(name), std::move(signature),
reinterpret_cast<void *>(function_ptr));
};
#ifdef TD_JSON_JAVA
auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/JsonClient");
register_method(client_class, "createClientId", "()I", JsonClient_createClientId);
register_method(client_class, "send", "(ILjava/lang/String;)V", JsonClient_send);
register_method(client_class, "receive", "(D)Ljava/lang/String;", JsonClient_receive);
register_method(client_class, "execute", "(Ljava/lang/String;)Ljava/lang/String;", JsonClient_execute);
register_method(client_class, "setLogMessageHandler", "(IL" PACKAGE_NAME "/JsonClient$LogMessageHandler;)V",
Client_nativeClientSetLogMessageHandler);
#else
auto td_api_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi");
jfieldID commit_hash_field_id =
td::jni::get_static_field_id(env, td_api_class, "GIT_COMMIT_HASH", "Ljava/lang/String;");
std::string td_api_version = td::jni::fetch_static_string(env, td_api_class, commit_hash_field_id);
std::string tdjni_version = td::td_api::get_git_commit_hash();
if (tdjni_version != td_api_version) {
td::jni::set_fatal_error(env, "Mismatched TdApi.java (" + td_api_version + ") and tdjni shared library (" +
tdjni_version + ") versions");
return JAVA_VERSION;
}
auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client");
auto object_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Object");
auto function_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Function");
#define TD_OBJECT "L" PACKAGE_NAME "/TdApi$Object;"
#define TD_FUNCTION "L" PACKAGE_NAME "/TdApi$Function;"
register_method(client_class, "createNativeClient", "()I", Client_createNativeClient);
register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend);
register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive);
register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute);
register_method(client_class, "nativeClientSetLogMessageHandler", "(IL" PACKAGE_NAME "/Client$LogMessageHandler;)V",
Client_nativeClientSetLogMessageHandler);
register_method(object_class, "toString", "()Ljava/lang/String;", Object_toString);
register_method(function_class, "toString", "()Ljava/lang/String;", Function_toString);
#undef TD_FUNCTION
#undef TD_OBJECT
td::jni::init_vars(env, PACKAGE_NAME);
td::td_api::get_package_name_ref() = PACKAGE_NAME;
#endif
return JAVA_VERSION;
}
} // namespace td_jni
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
static jint jni_version = td_jni::register_native(vm); // call_once
return jni_version;
}

View File

@ -1,3 +0,0 @@
/td/
/tdlib/
/*.dll

View File

@ -1,11 +0,0 @@
# TDLib Python example
To run this example you need to [build](https://github.com/tdlib/td#building) TDLib and copy built tdjson shared library to this directory.
After this you can run the example:
```
python tdjson_example.py
```
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.

View File

@ -1,145 +0,0 @@
#
# Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com),
# Pellegrino Prevete (pellegrinoprevete@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)
#
from ctypes.util import find_library
from ctypes import *
import json
import os
import sys
# load shared library
tdjson_path = find_library('tdjson')
if tdjson_path is None:
if os.name == 'nt':
tdjson_path = os.path.join(os.path.dirname(__file__), 'tdjson.dll')
else:
sys.exit("Can't find 'tdjson' library")
tdjson = CDLL(tdjson_path)
# load TDLib functions from shared library
_td_create_client_id = tdjson.td_create_client_id
_td_create_client_id.restype = c_int
_td_create_client_id.argtypes = []
_td_receive = tdjson.td_receive
_td_receive.restype = c_char_p
_td_receive.argtypes = [c_double]
_td_send = tdjson.td_send
_td_send.restype = None
_td_send.argtypes = [c_int, c_char_p]
_td_execute = tdjson.td_execute
_td_execute.restype = c_char_p
_td_execute.argtypes = [c_char_p]
log_message_callback_type = CFUNCTYPE(None, c_int, c_char_p)
_td_set_log_message_callback = tdjson.td_set_log_message_callback
_td_set_log_message_callback.restype = None
_td_set_log_message_callback.argtypes = [c_int, log_message_callback_type]
# initialize TDLib log with desired parameters
@log_message_callback_type
def on_log_message_callback(verbosity_level, message):
if verbosity_level == 0:
sys.exit('TDLib fatal error: %r' % message)
def td_execute(query):
query = json.dumps(query).encode('utf-8')
result = _td_execute(query)
if result:
result = json.loads(result.decode('utf-8'))
return result
_td_set_log_message_callback(2, on_log_message_callback)
# setting TDLib log verbosity level to 1 (errors)
print(str(td_execute({'@type': 'setLogVerbosityLevel', 'new_verbosity_level': 1, '@extra': 1.01234})).encode('utf-8'))
# create client
client_id = _td_create_client_id()
# simple wrappers for client usage
def td_send(query):
query = json.dumps(query).encode('utf-8')
_td_send(client_id, query)
def td_receive():
result = _td_receive(1.0)
if result:
result = json.loads(result.decode('utf-8'))
return result
# another test for TDLib execute method
print(str(td_execute({'@type': 'getTextEntities', 'text': '@telegram /test_command https://telegram.org telegram.me', '@extra': ['5', 7.0, 'a']})).encode('utf-8'))
# start the client by sending a request to it
td_send({'@type': 'getOption', 'name': 'version', '@extra': 1.01234})
# main events cycle
while True:
event = td_receive()
if event:
# process authorization states
if event['@type'] == 'updateAuthorizationState':
auth_state = event['authorization_state']
# if client is closed, we need to destroy it and create new client
if auth_state['@type'] == 'authorizationStateClosed':
break
# set TDLib parameters
# you MUST obtain your own api_id and api_hash at https://my.telegram.org
# and use them in the setTdlibParameters call
if auth_state['@type'] == 'authorizationStateWaitTdlibParameters':
td_send({'@type': 'setTdlibParameters',
'database_directory': 'tdlib',
'use_message_database': True,
'use_secret_chats': True,
'api_id': 94575,
'api_hash': 'a3406de8d171bb422bb6ddf3bbd800e2',
'system_language_code': 'en',
'device_model': 'Desktop',
'application_version': '1.0'})
# enter phone number to log in
if auth_state['@type'] == 'authorizationStateWaitPhoneNumber':
phone_number = input('Please enter your phone number: ')
td_send({'@type': 'setAuthenticationPhoneNumber', 'phone_number': phone_number})
# enter email address to log in
if auth_state['@type'] == 'authorizationStateWaitEmailAddress':
email_address = input('Please enter your email address: ')
td_send({'@type': 'setAuthenticationEmailAddress', 'email_address': email_address})
# wait for email authorization code
if auth_state['@type'] == 'authorizationStateWaitEmailCode':
code = input('Please enter the email authentication code you received: ')
td_send({'@type': 'checkAuthenticationEmailCode',
'code': {'@type': 'emailAddressAuthenticationCode', 'code' : code}})
# wait for authorization code
if auth_state['@type'] == 'authorizationStateWaitCode':
code = input('Please enter the authentication code you received: ')
td_send({'@type': 'checkAuthenticationCode', 'code': code})
# wait for first and last name for new users
if auth_state['@type'] == 'authorizationStateWaitRegistration':
first_name = input('Please enter your first name: ')
last_name = input('Please enter your last name: ')
td_send({'@type': 'registerUser', 'first_name': first_name, 'last_name': last_name})
# wait for password if present
if auth_state['@type'] == 'authorizationStateWaitPassword':
password = input('Please enter your password: ')
td_send({'@type': 'checkAuthenticationPassword', 'password': password})
# handle an incoming update or an answer to a previously sent request
print(str(event).encode('utf-8'))
sys.stdout.flush()

View File

@ -1,3 +0,0 @@
/xcuserdata/
/*workspace/
/td/

View File

@ -1,15 +0,0 @@
# TDLib swift macOS example
TDLib should be prebuilt and installed to local subdirectory `td/`:
```
cd <path to TDLib sources>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=../example/swift/td ..
cmake --build . --target install
```
After this you can open and build the example with the latest Xcode.
Description of all available classes and methods can be found at [td_json_client](https://core.telegram.org/tdlib/docs/td__json__client_8h.html)
and [td_api](https://core.telegram.org/tdlib/docs/td__api_8h.html) documentation.

View File

@ -1,189 +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)
//
import Foundation
// TDLib Client Swift binding
class TdClient {
var client_id = td_create_client_id()
let tdlibMainLoop = DispatchQueue(label: "TDLib")
let tdlibQueryQueue = DispatchQueue(label: "TDLibQuery")
var queryF = Dictionary<Int64, (Dictionary<String,Any>)->()>()
var updateF: ((Dictionary<String,Any>)->())?
var queryId: Int64 = 0
func queryAsync(query: [String: Any], f: ((Dictionary<String,Any>)->())? = nil) {
tdlibQueryQueue.async {
var newQuery = query
if f != nil {
let nextQueryId = self.queryId + 1
newQuery["@extra"] = nextQueryId
self.queryF[nextQueryId] = f
self.queryId = nextQueryId
}
td_send(self.client_id, to_json(newQuery))
}
}
func querySync(query: [String: Any]) -> Dictionary<String,Any> {
let semaphore = DispatchSemaphore(value:0)
var result = Dictionary<String,Any>()
queryAsync(query: query) {
result = $0
semaphore.signal()
}
semaphore.wait()
return result
}
init() {
}
deinit {
}
func run(updateHandler: @escaping (Dictionary<String,Any>)->()) {
updateF = updateHandler
tdlibMainLoop.async { [weak self] in
while (true) {
if let s = self {
if let res = td_receive(10) {
let event = String(cString: res)
s.queryResultAsync(event)
}
} else {
break
}
}
}
}
private func queryResultAsync(_ result: String) {
tdlibQueryQueue.async {
let json = try? JSONSerialization.jsonObject(with: result.data(using: .utf8)!, options:[])
if let dictionary = json as? [String:Any] {
if let extra = dictionary["@extra"] as? Int64 {
let index = self.queryF.index(forKey: extra)!
self.queryF[index].value(dictionary)
self.queryF.remove(at: index)
} else {
self.updateF!(dictionary)
}
}
}
}
}
func to_json(_ obj: Any) -> String {
do {
let obj = try JSONSerialization.data(withJSONObject: obj)
return String(data: obj, encoding: .utf8)!
} catch {
return ""
}
}
var client = TdClient()
// start the client by sending request to it
client.queryAsync(query: ["@type":"getOption", "name":"version"])
func myReadLine() -> String {
while (true) {
if let line = readLine() {
return line
}
}
}
func updateAuthorizationState(authorizationState: Dictionary<String, Any>) {
switch(authorizationState["@type"] as! String) {
case "authorizationStateWaitTdlibParameters":
client.queryAsync(query:[
"@type":"setTdlibParameters",
"database_directory":"tdlib",
"use_message_database":true,
"use_secret_chats":true,
"api_id":94575,
"api_hash":"a3406de8d171bb422bb6ddf3bbd800e2",
"system_language_code":"en",
"device_model":"Desktop",
"application_version":"1.0"
]);
case "authorizationStateWaitPhoneNumber":
print("Enter your phone number: ")
let phone_number = myReadLine()
client.queryAsync(query:["@type":"setAuthenticationPhoneNumber", "phone_number":phone_number], f:checkAuthenticationError)
case "authorizationStateWaitEmailAddress":
print("Enter your email address: ")
let email_address = myReadLine()
client.queryAsync(query:["@type":"setAuthenticationEmailAddress", "email_address":email_address], f:checkAuthenticationError)
case "authorizationStateWaitEmailCode":
var code: String = ""
print("Enter email code: ")
code = myReadLine()
client.queryAsync(query:["@type":"checkAuthenticationEmailCode", "code":["@type":"emailAddressAuthenticationCode", "code":code]], f:checkAuthenticationError)
case "authorizationStateWaitCode":
var code: String = ""
print("Enter (SMS) code: ")
code = myReadLine()
client.queryAsync(query:["@type":"checkAuthenticationCode", "code":code], f:checkAuthenticationError)
case "authorizationStateWaitRegistration":
var first_name: String = ""
var last_name: String = ""
print("Enter your first name: ")
first_name = myReadLine()
print("Enter your last name: ")
last_name = myReadLine()
client.queryAsync(query:["@type":"registerUser", "first_name":first_name, "last_name":last_name], f:checkAuthenticationError)
case "authorizationStateWaitPassword":
print("Enter password: ")
let password = myReadLine()
client.queryAsync(query:["@type":"checkAuthenticationPassword", "password":password], f:checkAuthenticationError)
case "authorizationStateReady":
()
case "authorizationStateLoggingOut":
print("Logging out...")
case "authorizationStateClosing":
print("Closing...")
case "authorizationStateClosed":
print("Closed.")
default:
assert(false, "TODO: Unexpected authorization state");
}
}
func checkAuthenticationError(error: Dictionary<String, Any>) {
if (error["@type"] as! String == "error") {
client.queryAsync(query:["@type":"getAuthorizationState"], f:updateAuthorizationState)
}
}
client.run {
let update = $0
print(update)
if update["@type"] as! String == "updateAuthorizationState" {
updateAuthorizationState(authorizationState: update["authorization_state"] as! Dictionary<String, Any>)
}
}
while true {
sleep(1)
}

View File

@ -1,9 +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_json_client.h"
#include "td/telegram/td_log.h"

View File

@ -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.

View File

@ -1,33 +0,0 @@
# TDLib Universal Windows Platform example
This is an example of building TDLib SDK for Universal Windows Platform and an example of its usage from C#.
## Building SDK
* Download and install Microsoft Visual Studio 2017+ with Windows 10 SDK. We recommend to use the latest available versions of Microsoft Visual Studio and Windows 10 SDK.
* Download and install [CMake](https://cmake.org/download/). CMake 3.13 or newer is required.
* Install `zlib` and `openssl` for all UWP architectures and `gperf` for x86 using [vcpkg](https://github.com/Microsoft/vcpkg#quick-start):
```
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
git checkout 07b30b49e5136a36100a2ce644476e60d7f3ddc1
.\bootstrap-vcpkg.bat
.\vcpkg.exe install gperf:x86-windows openssl:arm-uwp openssl:arm64-uwp openssl:x64-uwp openssl:x86-uwp zlib:arm-uwp zlib:arm64-uwp zlib:x64-uwp zlib:x86-uwp
```
* (Optional. For XML documentation generation.) Download [PHP](https://windows.php.net/download). Add the path to php.exe to the PATH environment variable.
* Download and install [7-Zip](http://www.7-zip.org/download.html) archiver, which is used by the `build.ps1` script to create a Telegram.Td.UWP Visual Studio Extension. Add the path to 7z.exe to the PATH environment variable.
Alternatively `build.ps1` supports compressing using [WinRAR](https://en.wikipedia.org/wiki/WinRAR) with option `-compress winrar` and compressing using [zip](http://gnuwin32.sourceforge.net/packages/zip.htm) with `-compress zip`.
* Build `TDLib` using provided `build.ps1` script (TDLib should be built 6 times for multiple platforms in Debug and Release configurations, so it make take few hours). Pass path to vcpkg.exe as `-vcpkg-root` argument, for example:
```
powershell -ExecutionPolicy ByPass .\build.ps1 -vcpkg_root C:\vcpkg
```
If you need to restart the build from scratch, call `.\build.ps1 -vcpkg_root C:\vcpkg -mode clean` first.
* Install Visual Studio Extension "TDLib for Universal Windows Platform" located at `build-uwp\vsix\tdlib.vsix`, which was created on the previous step by `build.ps1` script.
After this `TDLib` can be used from any UWP project, built in Visual Studio.
Alternatively, you can build `TDLib` as a NuGet package, adding the option `-nupkg` to the `.\build.ps1` script invocation. The resulting package will be placed in the directory `build-uwp\nupkg`.
## Example of usage
The `app/` directory contains a simple example of a C# application for Universal Windows Platform. Just open it with Visual Studio 2015 or later and run.

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<FileList
DisplayName="TDLib for Universal Windows Platform"
ProductFamilyName="Telegram.Td.UWP"
MoreInfo="https://core.telegram.org/tdlib"
MinVSVersion="14.0"
AppliesTo="WindowsAppContainer"
DependsOn="Microsoft.VCLibs, version=14.0"
SupportsMultipleVersions="Error"
SupportedArchitectures="x86;x64;ARM;ARM64">
<File Reference="Telegram.Td.winmd" Implementation="Telegram.Td.dll" />
</FileList>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/01/nuspec.xsd">
<metadata>
<id>Telegram.Td.UWP</id>
<version>1.8.46</version>
<title>TDLib for Universal Windows Platform</title>
<authors>Telegram</authors>
<owners>Telegram</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="file">LICENSE_1_0.txt</license>
<projectUrl>https://core.telegram.org/tdlib</projectUrl>
<releaseNotes>https://github.com/tdlib/td/blob/master/CHANGELOG.md</releaseNotes>
<description>TDLib for Universal Windows Platform</description>
<copyright>© Telegram FZ-LLC. All rights reserved.</copyright>
<dependencies>
<group targetFramework="UAP10.0" />
</dependencies>
</metadata>
</package>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TelegramTdPlatform Condition="'$(Platform)' == 'Win32'">x86</TelegramTdPlatform>
<TelegramTdPlatform Condition="'$(Platform)' != 'Win32'">$(Platform)</TelegramTdPlatform>
<OpenSSLPlatform Condition="'$(Platform)' == 'Win32'"></OpenSSLPlatform>
<OpenSSLPlatform Condition="'$(Platform)' != 'Win32'">-$(Platform)</OpenSSLPlatform>
</PropertyGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'UAP'">
<Reference Include="$(MSBuildThisFileDirectory)..\..\lib\uap10.0\Telegram.Td.winmd">
<Implementation>Telegram.Td.dll</Implementation>
</Reference>
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(TelegramTdPlatform)\native\Telegram.Td.dll" />
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(TelegramTdPlatform)\native\libcrypto-3$(OpenSSLPlatform).dll" />
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(TelegramTdPlatform)\native\libssl-3$(OpenSSLPlatform).dll" />
<ReferenceCopyLocalPaths Include="$(MSBuildThisFileDirectory)..\..\runtimes\win10-$(TelegramTdPlatform)\native\zlib1.dll" />
</ItemGroup>
</Project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="winmd" ContentType="application/octet-stream" />
<Default Extension="pri" ContentType="application/octet-stream" />
<Default Extension="dll" ContentType="application/octet-stream" />
<Default Extension="h" ContentType="application/octet-stream" />
<Default Extension="lib" ContentType="application/octet-stream" />
<Default Extension="pdb" ContentType="application/octet-stream" />
<Default Extension="png" ContentType="application/octet-stream" />
<Default Extension="props" ContentType="application/octet-stream" />
<Default Extension="txt" ContentType="text/plain" />
<Default Extension="vsixmanifest" ContentType="text/xml" />
<Default Extension="xml" ContentType="text/xml" />
</Types>

View File

@ -1,5 +0,0 @@
/.vs/
/bin/
/obj/
/project.lock.json
/TdApp.csproj.user

View File

@ -1,7 +0,0 @@
<Application
x:Class="TdApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TdApp"
RequestedTheme="Light">
</Application>

View File

@ -1,104 +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)
//
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace TdApp
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
Microsoft.ApplicationInsights.WindowsCollectors.Session);
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns = "http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
</ApplicationInsights>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,29 +0,0 @@
<Page
x:Class="TdApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TdApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="Self">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="Input"/>
<Button Grid.Column="1" x:Name="Send" Content="send" Click="Button_Click"/>
</Grid>
<ListBox Grid.Row="1" x:Name="ItemsControl" ItemsSource="{Binding Items, ElementName=Self}">
</ListBox>
<!--<Button Content="Test" Click="Button_Click"/>-->
</Grid>
</Page>

View File

@ -1,186 +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)
//
using System;
using System.IO;
using Td = Telegram.Td;
using TdApi = Telegram.Td.Api;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace TdApp
{
public sealed partial class MainPage : Page
{
public System.Collections.ObjectModel.ObservableCollection<string> Items { get; set; }
private MyClientResultHandler _handler;
public MainPage()
{
InitializeComponent();
Items = new System.Collections.ObjectModel.ObservableCollection<string>();
_handler = new MyClientResultHandler(this);
Td.Client.Execute(new TdApi.SetLogVerbosityLevel(0));
Td.Client.Execute(new TdApi.SetLogStream(new TdApi.LogStreamFile(Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "log"), 1 << 27, false)));
Td.Client.SetLogMessageCallback(100, LogMessageCallback);
System.Threading.Tasks.Task.Run(() =>
{
Td.Client.Run();
});
_client = Td.Client.Create(_handler);
var request = new TdApi.SetTdlibParameters();
request.DatabaseDirectory = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
request.UseSecretChats = true;
request.UseMessageDatabase = true;
request.ApiId = 94575;
request.ApiHash = "a3406de8d171bb422bb6ddf3bbd800e2";
request.SystemLanguageCode = "en";
request.DeviceModel = "Desktop";
request.ApplicationVersion = "1.0.0";
_client.Send(request, null);
}
public void Print(String str)
{
var delayTask = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Items.Insert(0, str.Substring(0, Math.Min(1024, str.Length)));
});
}
private void LogMessageCallback(int verbosity_level, String str)
{
if (verbosity_level < 0) {
return;
}
Print(verbosity_level + ": " + str);
}
private Td.Client _client;
private void AcceptCommand(String command)
{
Input.Text = string.Empty;
Items.Insert(0, string.Format(">>{0}", command));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var command = Input.Text;
if (command.StartsWith("DESTROY"))
{
AcceptCommand("Destroy");
_client.Send(new TdApi.Destroy(), _handler);
}
else if (command.StartsWith("lo"))
{
AcceptCommand("LogOut");
_client.Send(new TdApi.LogOut(), _handler);
}
else if (command.StartsWith("sap"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.SetAuthenticationPhoneNumber(args[1], null), _handler);
}
else if (command.StartsWith("sae"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.SetAuthenticationEmailAddress(args[1]), _handler);
}
else if (command.StartsWith("caec"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.CheckAuthenticationEmailCode(new TdApi.EmailAddressAuthenticationCode(args[1])), _handler);
}
else if (command.StartsWith("cac"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.CheckAuthenticationCode(args[1]), _handler);
}
else if (command.StartsWith("cap"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.CheckAuthenticationPassword(args[1]), _handler);
}
else if (command.StartsWith("alm"))
{
var args = command.Split(" ".ToCharArray(), 3);
AcceptCommand(command);
_client.Send(new TdApi.AddLogMessage(Int32.Parse(args[1]), args[2]), _handler);
}
else if (command.StartsWith("gco"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.SearchContacts(), _handler);
}
else if (command.StartsWith("df"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1, 0, 0, false), _handler);
}
else if (command.StartsWith("bench"))
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
var cnt = Int32.Parse(args[1]);
var handler = new BenchSimpleHandler(this, cnt);
for (int i = 0; i < cnt; i++)
{
_client.Send(new TdApi.TestSquareInt(123), handler);
}
}
}
}
class MyClientResultHandler : Td.ClientResultHandler
{
private MainPage _page;
public MyClientResultHandler(MainPage page)
{
_page = page;
}
public void OnResult(TdApi.BaseObject obj)
{
var str = obj.ToString();
_page.Print(str);
}
}
class BenchSimpleHandler : Td.ClientResultHandler
{
private MainPage _page;
private int _cnt;
public BenchSimpleHandler(MainPage page, int cnt)
{
_page = page;
_cnt = cnt;
}
public void OnResult(TdApi.BaseObject obj)
{
_cnt--;
if (_cnt == 0)
{
_page.Print("DONE");
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More