Project import generated by Copybara.

GitOrigin-RevId: 318483224ad6164d9966f731d60cde37039bb2d4
This commit is contained in:
Arseny Smirnov 2018-12-31 22:04:05 +03:00
commit 71d03f39c3
622 changed files with 381353 additions and 0 deletions

98
.clang-format Normal file
View File

@ -0,0 +1,98 @@
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None # All
AllowShortIfStatementsOnASingleLine: false # true
AllowShortLoopsOnASingleLine: false # true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: true # false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true # false
BreakConstructorInitializers: BeforeComma # BeforeColon
# BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120 # 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
# ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# ObjCBlockIndentWidth: 2
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: false # true
SortIncludes: false # disabled, because we need case insensitive sort
SortUsingDeclarations: false # true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 100 # 8
UseTab: Never
...

17
.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
* 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
*.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
*.yml 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
*.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
*.tlo binary

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
**/*build*/
**/.*.swp
**/.DS_Store
**/auto/
auto/
db_backup
*.pyc
docs

60
.travis.yml Normal file
View File

@ -0,0 +1,60 @@
sudo: false
dist: trusty
addons_shortcuts:
addons_clang38: &clang38
apt:
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8' ]
packages: [ 'g++-5', 'clang-3.8','libc++-dev', 'libc++abi-dev', 'gperf']
addons_gcc5: &gcc5
apt:
sources: [ 'ubuntu-toolchain-r-test']
packages: [ 'gcc-5','g++-5', 'gperf']
branches:
only:
- travis
language: cpp
matrix:
include:
- os: linux
env: _CXX=g++-5 _CC=gcc-5 JOBS=1
addons: *gcc5
- os: linux
env: _CXX=clang++-3.8 _CC=clang-3.8 JOBS=4
addons: *clang38
- os: osx
env: JOBS=4
compiler: clang
before_install:
#- sudo apt-get -qq update
#- sudo apt-get install -y libxml2-dev
addons:
apt:
packages:
- gperf
install:
# /usr/bin/gcc is 4.6 always, but gcc-X.Y is available.
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then
export OPENSSL_ROOT_DIR=/usr/local/opt/openssl/;
brew link --force readline;
ulimit -n 1000;
fi
- false || [ -z "$_CXX" ] || export CXX=${_CXX}
- false || [ -z "$_CC" ] || export CC=${_CC}
- echo ${PATH}
- echo ${CXX}
- ${CXX} --version
- ${CXX} -v
script:
- mkdir build
- cd build
- cmake .. && make -j${JOBS} VERBOSE=1
- ./test/run_all_tests --filter -client

159
.ycm_extra_conf.py Normal file
View File

@ -0,0 +1,159 @@
# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# 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 AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org/>
import os
import ycm_core
# These are the compilation flags that will be used in case there's no
# compilation database set (by default, one is not set).
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
flags = [
"-stdlib=libc++",
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
"-Wno-deprecated-declarations",
"-std=c++14",
"-x",
"c++",
"-I",
".",
"-I", "tdutils",
"-I", "tdutils/generate",
"-I", "tdactor",
"-I", "tddb",
"-I", "tdnet",
"-I", "tdtl",
"-I", "td/generate",
"-I", "td/generate/auto",
"-I", "td",
"-isystem",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1",
"-isystem",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.3.0/include",
"-isystem",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"
]
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = 'build'
if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None
SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break
if new_flag:
new_flags.append( new_flag )
return new_flags
def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
return {
'flags': final_flags,
'do_cache': True
}

View File

@ -0,0 +1,74 @@
# - 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)
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})
if (DEFINED OLD_CMAKE_REQUIRED_FLAGS)
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
else()
unset(CMAKE_REQUIRED_FLAGS)
endif()
if(${MANGLED_FLAG})
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)
set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}")
check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG})
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
if (${MANGLED_FLAG})
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()

29
CMake/FindReadline.cmake Normal file
View File

@ -0,0 +1,29 @@
# from http://websvn.kde.org/trunk/KDE/kdeedu/cmake/modules/FindReadline.cmake
# http://websvn.kde.org/trunk/KDE/kdeedu/cmake/modules/COPYING-CMAKE-SCRIPTS
# --> BSD licensed
#
# GNU Readline library finder
if(READLINE_INCLUDE_DIR AND READLINE_LIBRARY)
set(READLINE_FOUND TRUE)
else(READLINE_INCLUDE_DIR AND READLINE_LIBRARY)
FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h
/usr/include/readline
)
# 2008-04-22 The next clause used to read like this:
#
# FIND_LIBRARY(READLINE_LIBRARY NAMES readline)
# FIND_LIBRARY(NCURSES_LIBRARY NAMES ncurses )
# include(FindPackageHandleStandardArgs)
# FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG NCURSES_LIBRARY READLINE_INCLUDE_DIR READLINE_LIBRARY )
#
# I was advised to modify it such that it will find an ncurses library if
# required, but not if one was explicitly given, that is, it allows the
# default to be overridden. PH
FIND_LIBRARY(READLINE_LIBRARY NAMES readline)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG READLINE_INCLUDE_DIR READLINE_LIBRARY )
MARK_AS_ADVANCED(READLINE_INCLUDE_DIR READLINE_LIBRARY)
endif(READLINE_INCLUDE_DIR AND READLINE_LIBRARY)

230
CMake/iOS.cmake Normal file
View File

@ -0,0 +1,230 @@
# 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.
#
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
# By default this location is automatcially 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 automatcially 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)
exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
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 iphoneos)
# 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 iphonesimulator)
# 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 watchsimulator)
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchsimulator")
set (APPLE_WATCH 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")
if (NOT SIMULATOR_FLAG)
set (BITCODE "-fembed-bitcode")
endif()
set (CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE}")
# Hidden visibilty is required for cxx on iOS
set (CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} ${BITCODE} -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
exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR)
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 architecture for iOS
if (IOS_PLATFORM STREQUAL "OS")
set (IOS_ARCH "armv7;armv7s;arm64")
set (IOS_ARCH "armv7;arm64")
elseif (IOS_PLATFORM STREQUAL "SIMULATOR")
set (IOS_ARCH "i386;x86_64")
elseif (IOS_PLATFORM STREQUAL "WATCHOS")
set (IOS_ARCH "armv7k")
elseif (IOS_PLATFORM STREQUAL "WATCHSIMULATOR")
set (IOS_ARCH "i386")
else()
message (WARNING "Unknown IOS_PLATFORM=<${IOS_PLATFORM}>")
endif()
message (STATUS ${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)

671
CMakeLists.txt Normal file
View File

@ -0,0 +1,671 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(TDLib VERSION 1.0.0 LANGUAGES CXX C)
if (NOT DEFINED CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "")
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" "${CMAKE_MODULE_PATH}")
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
enable_testing()
if (POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
include(CheckIPOSupported)
check_ipo_supported(RESULT IPO_SUPPORTED)
if (IPO_SUPPORTED)
#set_property(DIRECTORY PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) do not work?
string(REPLACE ";" " " CXX_FLAGS_IPO "${CMAKE_CXX_COMPILE_OPTIONS_IPO}")
message(STATUS "Use link time optimizations: ${CXX_FLAGS_IPO}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CXX_FLAGS_IPO}")
endif()
endif()
# Configure CCache if available
find_program(CCACHE_FOUND ccache)
#set(CCACHE_FOUND 0)
if (CCACHE_FOUND)
message(STATUS "Found ccache")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
else()
message(STATUS "Could NOT find ccache")
endif()
set(MEMPROF "" CACHE STRING "Use one of \"ON\", \"FAST\" or \"SAFE\" to enable memory profiling. \
Works under Mac OS and Linux when compiled using glibc. \
In FAST mode stack is unwinded only using frame pointers, which may fail. \
In SAFE mode stack is unwinded using backtrace function from execinfo.h, which may be very slow. \
By default both methods are used to achieve maximum speed and accuracy")
# LIBRARIES
if (EMSCRIPTEN)
# use prebuilt zlib
set(ZLIB_FOUND 1)
set(ZLIB_LIBRARIES)
set(ZLIB_INCLUDE_DIR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s ALLOW_MEMORY_GROWTH=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS']\"")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -s ALLOW_MEMORY_GROWTH=1 -s USE_ZLIB=1 -s MODULARIZE=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['FS']\"")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1")
if (ASMJS)
set(TD_EMSCRIPTEN td_asmjs)
else()
set(TD_EMSCRIPTEN td_wasm)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s WASM=1")
endif()
endif()
if (NOT OPENSSL_FOUND)
find_package(OpenSSL)
endif()
if (OPENSSL_FOUND)
message(STATUS "Found OpenSSL: ${OPENSSL_INCLUDE_DIR} ${OPENSSL_LIBRARIES}")
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()
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()
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if (THREADS_HAVE_PTHREAD_ARG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
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} /GR- /W4 /wd4100 /wd4127 /wd4324 /wd4505 /wd4702")
elseif (CLANG OR GCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD14_FLAG} -fno-omit-frame-pointer -fno-exceptions -fno-rtti")
if (APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-dead_strip,-x,-S")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")
endif()
if (MEMPROF)
CHECK_CXX_COMPILER_FLAG(-no-pie CXX_NO_PIE_FLAG)
if (CXX_NO_PIE_FLAG)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
elseif (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_pie")
endif()
endif()
elseif (INTEL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STD14_FLAG}")
endif()
if (WIN32)
add_definitions(-DNTDDI_VERSION=0x06020000 -DWINVER=0x0602 -D_WIN32_WINNT=0x0602 -DNOMINMAX -DUNICODE -D_UNICODE)
endif()
if (CYGWIN)
add_definitions(-D_DEFAULT_SOURCE=1 -DFD_SETSIZE=4096)
endif()
if (NOT ANDROID) # _FILE_OFFSET_BITS is broken in ndk r15 and r15b and doesn't work prior to Android 7.0
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
include(AddCXXCompilerFlag)
if (NOT MSVC)
add_cxx_compiler_flag("-Wall")
endif()
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++14-compat-pedantic")
add_cxx_compiler_flag("-Qunused-arguments")
add_cxx_compiler_flag("-Wodr")
add_cxx_compiler_flag("-flto-odr-type-merging")
#add_cxx_compiler_flag("-Werror")
#add_cxx_compiler_flag("-Wcast-align")
# std::int32_t <-> int and off_t <-> std::size_t/std::int64_t
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wuseless-cast")
# external headers like openssl
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant")
#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")
add_subdirectory(tdtl)
add_subdirectory(tdutils)
add_subdirectory(td/generate)
if (NOT CMAKE_CROSSCOMPILING)
add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tdmime_auto)
endif()
if (NOT OPENSSL_FOUND)
message(WARNING "Not found OpenSSL: skip TDLib, tdactor, tdnet, tddb")
return()
endif()
if (NOT ZLIB_FOUND)
find_package(ZLIB)
endif()
if (NOT ZLIB_FOUND)
message(WARNING "Not found zlib: skip TDLib, tdactor, tdnet, tddb")
return()
endif()
add_subdirectory(tdactor)
add_subdirectory(tdnet)
add_subdirectory(sqlite)
add_subdirectory(tddb)
add_subdirectory(test)
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(benchmark)
endif()
get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if (HAS_PARENT)
set(TL_TD_AUTO_INCLUDES ${TL_TD_AUTO_INCLUDES} PARENT_SCOPE)
set(TL_TD_API_TLO ${TL_TD_API_TLO} PARENT_SCOPE)
set(TL_TD_JSON_AUTO ${TL_TD_JSON_AUTO} PARENT_SCOPE)
set(TD_TEST_SOURCE ${TD_TEST_SOURCE} PARENT_SCOPE)
endif()
#SOURCE SETS
set_source_files_properties(${TL_TD_AUTO} PROPERTIES GENERATED TRUE)
if (TD_API_JAVA_PACKAGE)
set(TL_JNI_OBJECT
td/tl/tl_jni_object.cpp
td/tl/tl_jni_object.h
)
else()
set(TL_JNI_OBJECT)
endif()
set(TL_TD_SCHEME_SOURCE
${TL_TD_AUTO}
${TL_JNI_OBJECT}
td/tl/TlObject.h
td/tl/tl_object_parse.h
td/tl/tl_object_store.h
)
set_source_files_properties(${TL_TD_JSON_AUTO} PROPERTIES GENERATED TRUE)
set(TL_TD_JSON
${TL_TD_JSON_AUTO}
td/tl/tl_json.h
)
set_source_files_properties(${TL_C_AUTO} PROPERTIES GENERATED TRUE)
set(TL_C_SCHEME_SOURCE
${TL_C_AUTO}
)
set(TDLIB_SOURCE
td/mtproto/crypto.cpp
td/mtproto/Handshake.cpp
td/mtproto/HandshakeActor.cpp
td/mtproto/HttpTransport.cpp
td/mtproto/IStreamTransport.cpp
td/mtproto/RawConnection.cpp
td/mtproto/SessionConnection.cpp
td/mtproto/TcpTransport.cpp
td/mtproto/Transport.cpp
td/mtproto/utils.cpp
td/telegram/AnimationsManager.cpp
td/telegram/AudiosManager.cpp
td/telegram/AuthManager.cpp
td/telegram/CallActor.cpp
td/telegram/CallDiscardReason.cpp
td/telegram/CallManager.cpp
td/telegram/CallbackQueriesManager.cpp
td/telegram/ClientActor.cpp
td/telegram/ConfigManager.cpp
td/telegram/ConfigShared.cpp
td/telegram/Contact.cpp
td/telegram/ContactsManager.cpp
td/telegram/DeviceTokenManager.cpp
td/telegram/DhCache.cpp
td/telegram/DialogDb.cpp
td/telegram/DialogId.cpp
td/telegram/DialogParticipant.cpp
td/telegram/DocumentsManager.cpp
td/telegram/files/FileDb.cpp
td/telegram/files/FileDownloader.cpp
td/telegram/files/FileFromBytes.cpp
td/telegram/files/FileGcParameters.cpp
td/telegram/files/FileGcWorker.cpp
td/telegram/files/FileGenerateManager.cpp
td/telegram/files/FileHashUploader.cpp
td/telegram/files/FileLoader.cpp
td/telegram/files/FileLoaderUtils.cpp
td/telegram/files/FileLoadManager.cpp
td/telegram/files/FileManager.cpp
td/telegram/files/FileStats.cpp
td/telegram/files/FileStatsWorker.cpp
td/telegram/files/FileUploader.cpp
td/telegram/files/PartsManager.cpp
td/telegram/files/ResourceManager.cpp
td/telegram/Game.cpp
td/telegram/Global.cpp
td/telegram/HashtagHints.cpp
td/telegram/InlineQueriesManager.cpp
td/telegram/Location.cpp
td/telegram/MessageEntity.cpp
td/telegram/MessagesDb.cpp
td/telegram/MessagesManager.cpp
td/telegram/misc.cpp
td/telegram/net/AuthDataShared.cpp
td/telegram/net/ConnectionCreator.cpp
td/telegram/net/DcAuthManager.cpp
td/telegram/net/DcOptionsSet.cpp
td/telegram/net/MtprotoHeader.cpp
td/telegram/net/NetActor.cpp
td/telegram/net/NetQuery.cpp
td/telegram/net/NetQueryCounter.cpp
td/telegram/net/NetQueryCreator.cpp
td/telegram/net/NetQueryDelayer.cpp
td/telegram/net/NetQueryDispatcher.cpp
td/telegram/net/NetStatsManager.cpp
td/telegram/net/PublicRsaKeyShared.cpp
td/telegram/net/PublicRsaKeyWatchdog.cpp
td/telegram/net/Session.cpp
td/telegram/net/SessionProxy.cpp
td/telegram/net/SessionMultiProxy.cpp
td/telegram/Payments.cpp
td/telegram/PasswordManager.cpp
td/telegram/PrivacyManager.cpp
td/telegram/Photo.cpp
td/telegram/ReplyMarkup.cpp
td/telegram/SecretChatActor.cpp
td/telegram/SecretChatDb.cpp
td/telegram/SecretChatsManager.cpp
td/telegram/SequenceDispatcher.cpp
td/telegram/StateManager.cpp
td/telegram/StickersManager.cpp
td/telegram/StorageManager.cpp
td/telegram/Td.cpp
td/telegram/TdDb.cpp
td/telegram/TopDialogManager.cpp
td/telegram/UpdatesManager.cpp
td/telegram/VideoNotesManager.cpp
td/telegram/VideosManager.cpp
td/telegram/VoiceNotesManager.cpp
td/telegram/WebPagesManager.cpp
td/mtproto/AuthData.h
td/mtproto/AuthKey.h
td/mtproto/crypto.h
td/mtproto/CryptoStorer.h
td/mtproto/Handshake.h
td/mtproto/HandshakeActor.h
td/mtproto/HandshakeConnection.h
td/mtproto/HttpTransport.h
td/mtproto/IStreamTransport.h
td/mtproto/NoCryptoStorer.h
td/mtproto/PacketStorer.h
td/mtproto/PingConnection.h
td/mtproto/RawConnection.h
td/mtproto/SessionConnection.h
td/mtproto/TcpTransport.h
td/mtproto/Transport.h
td/mtproto/utils.h
td/telegram/AccessRights.h
td/telegram/AnimationsManager.h
td/telegram/AudiosManager.h
td/telegram/AuthManager.h
td/telegram/CallActor.h
td/telegram/CallDiscardReason.h
td/telegram/CallId.h
td/telegram/CallManager.h
td/telegram/CallbackQueriesManager.h
td/telegram/ChannelId.h
td/telegram/ChatId.h
td/telegram/ClientActor.h
td/telegram/ConfigManager.h
td/telegram/ConfigShared.h
td/telegram/Contact.h
td/telegram/ContactsManager.h
td/telegram/DeviceTokenManager.h
td/telegram/DhCache.h
td/telegram/DhConfig.h
td/telegram/DialogDb.h
td/telegram/DialogId.h
td/telegram/DialogParticipant.h
td/telegram/DocumentsManager.h
td/telegram/files/FileDb.h
td/telegram/files/FileDownloader.h
td/telegram/files/FileFromBytes.h
td/telegram/files/FileGcParameters.h
td/telegram/files/FileGcWorker.h
td/telegram/files/FileGenerateManager.h
td/telegram/files/FileHashUploader.h
td/telegram/files/FileId.h
td/telegram/files/FileLoaderActor.h
td/telegram/files/FileLoader.h
td/telegram/files/FileLoaderUtils.h
td/telegram/files/FileLoadManager.h
td/telegram/files/FileLocation.h
td/telegram/files/FileManager.h
td/telegram/files/FileStats.h
td/telegram/files/FileStatsWorker.h
td/telegram/files/FileUploader.h
td/telegram/files/PartsManager.h
td/telegram/files/ResourceManager.h
td/telegram/files/ResourceState.h
td/telegram/Game.h
td/telegram/Global.h
td/telegram/HashtagHints.h
td/telegram/InlineQueriesManager.h
td/telegram/Location.h
td/telegram/logevent/LogEvent.h
td/telegram/logevent/SecretChatEvent.h
td/telegram/MessageEntity.h
td/telegram/MessageId.h
td/telegram/MessagesDb.h
td/telegram/MessagesManager.h
td/telegram/misc.h
td/telegram/net/AuthDataShared.h
td/telegram/net/ConnectionCreator.h
td/telegram/net/DcAuthManager.h
td/telegram/net/DcId.h
td/telegram/net/DcOptions.h
td/telegram/net/DcOptionsSet.h
td/telegram/net/MtprotoHeader.h
td/telegram/net/NetActor.h
td/telegram/net/NetQuery.h
td/telegram/net/NetQueryCounter.h
td/telegram/net/NetQueryCreator.h
td/telegram/net/NetQueryDelayer.h
td/telegram/net/NetQueryDispatcher.h
td/telegram/net/NetStatsManager.h
td/telegram/net/NetType.h
td/telegram/net/PublicRsaKeyShared.h
td/telegram/net/PublicRsaKeyWatchdog.h
td/telegram/net/Session.h
td/telegram/net/SessionProxy.h
td/telegram/net/SessionMultiProxy.h
td/telegram/net/TempAuthKeyWatchdog.h
td/telegram/PasswordManager.h
td/telegram/Payments.h
td/telegram/Photo.h
td/telegram/PrivacyManager.h
td/telegram/PtsManager.h
td/telegram/ReplyMarkup.h
td/telegram/SecretChatActor.h
td/telegram/SecretChatId.h
td/telegram/SecretChatDb.h
td/telegram/SecretChatsManager.h
td/telegram/SecretInputMedia.h
td/telegram/SequenceDispatcher.h
td/telegram/StateManager.h
td/telegram/StickersManager.h
td/telegram/StorageManager.h
td/telegram/Td.h
td/telegram/TdCallback.h
td/telegram/TdDb.h
td/telegram/TdParameters.h
td/telegram/TopDialogManager.h
td/telegram/UniqueId.h
td/telegram/UpdatesManager.h
td/telegram/UserId.h
td/telegram/Version.h
td/telegram/VideoNotesManager.h
td/telegram/VideosManager.h
td/telegram/VoiceNotesManager.h
td/telegram/WebPageId.h
td/telegram/WebPagesManager.h
td/telegram/AnimationsManager.hpp
td/telegram/AudiosManager.hpp
td/telegram/DocumentsManager.hpp
td/telegram/files/FileId.hpp
td/telegram/files/FileManager.hpp
td/telegram/Game.hpp
td/telegram/Payments.hpp
td/telegram/Photo.hpp
td/telegram/ReplyMarkup.hpp
td/telegram/StickersManager.hpp
td/telegram/VideoNotesManager.hpp
td/telegram/VideosManager.hpp
td/telegram/VoiceNotesManager.hpp
${TL_TD_SCHEME_SOURCE}
)
set(MEMPROF_SOURCE
memprof/memprof.cpp
memprof/memprof.h
)
#RULES
file(MAKE_DIRECTORY auto)
if (WIN32)
set(GIT_COMMIT_CMD powershell -ExecutionPolicy ByPass ./gen_git_commit_h.ps1)
else()
set(GIT_COMMIT_CMD ./gen_git_commit_h.sh)
endif()
add_custom_target(git_commit ALL
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GIT_COMMIT_CMD}
COMMENT "Generate git_commit.h"
)
#LIBRARIES
# memprof - simple library for memory usage profiling
add_library(memprof STATIC ${MEMPROF_SOURCE})
target_include_directories(memprof PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(memprof PRIVATE tdutils)
if (MEMPROF)
target_compile_definitions(memprof PRIVATE -DUSE_MEMPROF=1)
if (MEMPROF STREQUAL "SAFE")
target_compile_definitions(memprof PRIVATE -DUSE_MEMPROF_SAFE=1)
elseif (MEMPROF STREQUAL "FAST")
target_compile_definitions(memprof PRIVATE -DUSE_MEMPROF_FAST=1)
elseif (NOT ${MEMPROF})
message(FATAL_ERROR "Unsupported MEMPROF value \"${MEMPROF}\"")
endif()
endif()
# tdcore - mostly internal TDLib interface. One should use tdactor for interactions with it.
add_library(tdcore STATIC ${TDLIB_SOURCE})
target_include_directories(tdcore PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDES}>)
target_include_directories(tdcore SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(tdcore PUBLIC tdactor tdutils tdnet tddb PRIVATE ${OPENSSL_CRYPTO_LIBRARY})
if (NOT CMAKE_CROSSCOMPILING)
add_dependencies(tdcore tl_generate_common)
endif()
add_library(tdclient td/telegram/Client.cpp td/telegram/Client.h td/telegram/Log.cpp td/telegram/Log.h)
target_include_directories(tdclient PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDES}>
)
target_link_libraries(tdclient PRIVATE tdcore)
# tdc - TDLib interface in pure c.
add_library(tdc STATIC ${TL_C_SCHEME_SOURCE} td/telegram/td_c_client.cpp td/telegram/td_c_client.h)
target_include_directories(tdc PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDES}>)
target_link_libraries(tdc PRIVATE tdclient tdutils)
if (NOT CMAKE_CROSSCOMPILING)
add_dependencies(tdc tl_generate_c)
endif()
add_library(tdjson_private STATIC ${TL_TD_JSON} td/telegram/ClientJson.cpp td/telegram/ClientJson.h)
target_include_directories(tdjson_private PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${TL_TD_AUTO_INCLUDES}>)
target_link_libraries(tdjson_private PUBLIC tdclient tdutils)
if (NOT CMAKE_CROSSCOMPILING)
add_dependencies(tdjson_private tl_generate_common tl_generate_json)
endif()
set(TD_JSON_HEADERS td/telegram/td_json_client.h td/telegram/td_log.h)
set(TD_JSON_SOURCE td/telegram/td_json_client.cpp td/telegram/td_log.cpp)
include(GenerateExportHeader)
add_library(tdjson SHARED ${TD_JSON_SOURCE} ${TD_JSON_HEADERS})
target_link_libraries(tdjson PRIVATE tdjson_private)
generate_export_header(tdjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/td/telegram/tdjson_export.h)
target_include_directories(tdjson PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
if (APPLE)
set_target_properties(tdjson PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/tdclientjson_export_list")
endif()
add_library(tdjson_static STATIC ${TD_JSON_SOURCE} ${TD_JSON_HEADERS})
target_link_libraries(tdjson_static PRIVATE tdjson_private)
target_compile_definitions(tdjson_static PUBLIC TDJSON_STATIC_DEFINE)
target_include_directories(tdjson_static PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
if (WIN32 OR CYGWIN)
if (MSVC)
target_compile_options(tdcore PUBLIC "/bigobj")
elseif (GCC)
target_compile_options(tdcore PUBLIC "-Wa,-mbig-obj")
endif()
endif()
if (EMSCRIPTEN)
set(TD_EMSCRIPTEN_SRC td/telegram/td_emscripten.cpp)
add_executable(${TD_EMSCRIPTEN} ${TD_EMSCRIPTEN_SRC})
target_include_directories(${TD_EMSCRIPTEN} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(${TD_EMSCRIPTEN} PRIVATE tdjson_static)
endif()
#EXECUTABLES
if (NOT CMAKE_CROSSCOMPILING)
add_executable(tg_cli td/telegram/cli.cpp ${TL_TD_JSON})
if (NOT READLINE_FOUND)
find_package(Readline)
endif()
if (NOT READLINE_FOUND)
message(STATUS "Could NOT find Readline")
else()
message(STATUS "Found Readline: ${READLINE_INCLUDE_DIR} ${READLINE_LIBRARY}")
target_link_libraries(tg_cli PRIVATE ${READLINE_LIBRARY})
target_include_directories(tg_cli SYSTEM PRIVATE ${READLINE_INCLUDE_DIR})
target_compile_definitions(tg_cli PRIVATE -DUSE_READLINE=1)
endif()
target_link_libraries(tg_cli PRIVATE memprof tdcore tdtl)
add_dependencies(tg_cli tl_generate_json)
endif()
#Exported libraries
add_library(TdStatic INTERFACE)
target_link_libraries(TdStatic INTERFACE tdclient)
add_library(TdJson INTERFACE)
target_link_libraries(TdJson INTERFACE tdjson)
add_library(TdJsonStatic INTERFACE)
target_link_libraries(TdJsonStatic INTERFACE tdjson_static)
add_library(Td::TdStatic ALIAS TdStatic)
add_library(Td::TdJson ALIAS TdJson)
add_library(Td::TdJsonStatic ALIAS TdJsonStatic)
install(TARGETS tdjson TdJson tdjson_static tdjson_private tdclient tdcore TdJsonStatic TdStatic EXPORT TdTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(EXPORT TdTargets
FILE TdTargets.cmake
NAMESPACE Td::
DESTINATION lib/cmake/Td
)
install(FILES ${TD_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/td/telegram/tdjson_export.h DESTINATION include/td/telegram)
install(FILES td/telegram/Client.h td/telegram/Log.h DESTINATION include/td/telegram)
install(FILES td/tl/TlObject.h DESTINATION include/td/tl)
install(FILES ${TL_TD_AUTO_INCLUDES}/td/telegram/td_api.h ${TL_TD_AUTO_INCLUDES}/td/telegram/td_api.hpp DESTINATION include/td/telegram)
include(CMakePackageConfigHelpers)
write_basic_package_version_file("TdConfigVersion.cmake"
VERSION ${TDLib_VERSION}
COMPATIBILITY ExactVersion
)
install(FILES "TdConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/TdConfigVersion.cmake"
DESTINATION lib/cmake/Td
)

2473
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

23
LICENSE_1_0.txt Normal file
View File

@ -0,0 +1,23 @@
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.

124
README.md Normal file
View File

@ -0,0 +1,124 @@
# TDLib
This repository contains the code of the Telegram Database library (`TDLib`).
## Table of Content
- [About](#about)
- [Features](#features)
- [License](#license)
- [Dependencies](#dependencies)
- [Build with cmake](#build-cmake)
- [Using `TDLib` in CMake C++ projects](#using-cmake)
- [Using `TDLib` with other programming languages](#using-json)
- [Installing dependencies](#installing-dependencies)
- [Usage](#usage)
<a name="about"></a>
## About
`TDLib` is a cross-platform, fully functional Telegram client.
<a name="features"></a>
## Features
`TDLib` has many advantages. Notably `TDLib` is:
* **Cross-platform**: `TDLib` can be used on Android, iOS, Windows, macOS, Linux, Windows Phone, WebAssembly, watchOS, tvOS, 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 bindings to Java (using JNI) and C# (using C++/CLI).
* **Easy to use**: `TDLib` takes care of all network implementation details, encryption and local data storage.
* **High-performance**: in the Telegram Bot API, each `TDLib` instance handles more than 18000 active bots simultaneously.
* **Well-documented**: all `TDLib` API methods and public interfaces are fully documented.
* **Consistent**: `TDLib` guarantees that all updates will be delivered in the right order.
* **Reliable**: `TDLib` remains stable on slow and unstable 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 will be sent when they are available.
<a name="license"></a>
## License
The Telegram Database library is licensed under the terms of the
Boost Software License. See [LICENSE](http://www.boost.org/LICENSE_1_0.txt) for more information.
## Build
<a name="dependencies"></a>
### Dependencies
`TDLib` depends on:
* C++14 compatible compiler (clang 3.4+, GCC 4.9+, MSVC 19.0+ (Visual Studio 2015+), Intel C++ Compiler 17+)
* OpenSSL
* zlib
* gperf
* CMake (3.0.2+)
* php (optional, for docs generation)
* doxygen (options, for docs generation)
<a name="build-cmake"></a>
### Build with CMake
```
mkdir build
cd build
cmake ..
cmake --build .
cmake --build . --target install
```
<a name="using-cmake"></a>
### Using `TDLib` in CMake C++ projects
For C++ projects that use CMake, the best approach is to build `TDLib` as part of your project.
There are several libraries that you could use in your CMake project:
* Td::TdJson, Td::TdJsonStatic — dynamic and static version of a json interface. Has a simple C interface, so it can be easily used with any language that supports C bindings.
* Td::TdStatic — static library with C++ interface.
* Td::TdCoreStatic — static library with low-level C++ interface intended mostly for internal usage.
For example, part of your CMakeList.txt may look like this:
```
add_subdirectory(td)
target_link_library(YourLibrary Td::TdJson)
```
Or you could install `TDLib` and then reference it in your CMakeLists.txt like this:
```
find_package(Td 1.0)
target_link_library(YourLibrary Td::TdJson)
```
See [example/cpp/CMakeLists.txt](https://github.com/tdlib/td/tree/master/example/cpp/CMakeLists.txt).
<a name="using-json"></a>
### Using `TDLib` from other languages
`TDLib` provides efficient native C++, Java, and C# (will be released soon) interfaces.
But for most use cases we suggest to use the JSON interface. It can be easily used with any language that supports C binginds. See
[example/python/tdjson_example.py](https://github.com/tdlib/td/tree/master/example/python/tdjson_example.py) for an
example of such usage.
<a name="installing-dependencies"></a>
### Installing dependencies
#### OS X
* Install the latest XCode command line tools.
* Install other dependencies, for example, using [Homebrew](https://brew.sh):
```
homebrew install gperf cmake openssl
```
* After that you may need to manually specify path to the installed OpenSSL to CMake, e.g.,
```
cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/ ..
```
#### Windows
* Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf to the PATH variable.
* Install [vcpkg](https://github.com/Microsoft/vcpkg#quick-start).
* Run the following commands:
```
C:\src\vcpkg> .\vcpkg install openssl zlib
```
* Build `TDLib` with cmake as explained above, but instead of `cmake ..` use `cmake -DCMAKE_TOOLCHAIN_FILE=C:\src\vcpkg\scripts\buildsystems\vcpkg.cmake ..`.
#### Linux
Install all depenencies using your package manager.
<a name="usage"></a>
### `TDLib` usage
Take a look at our [examples](https://github.com/tdlib/td/tree/master/example) and [documentation](https://core.telegram.org/tdlib/docs/).

3
TdConfig.cmake Normal file
View File

@ -0,0 +1,3 @@
include(CMakeFindDependencyMacro)
#TODO: write all external dependencies
include("${CMAKE_CURRENT_LIST_DIR}/TdTargets.cmake")

61
benchmark/CMakeLists.txt Normal file
View File

@ -0,0 +1,61 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
#TODO: all benchmarks in one file
add_executable(bench_crypto bench_crypto.cpp)
target_link_libraries(bench_crypto PRIVATE tdcore tdutils ${OPENSSL_CRYPTO_LIBRARY})
target_include_directories(bench_crypto SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
if (NOT WIN32)
target_link_libraries(bench_crypto PRIVATE dl z) # for OpenSSL
endif()
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 tdcore 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(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()

290
benchmark/bench_actor.cpp Normal file
View File

@ -0,0 +1,290 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/logging.h"
#include <algorithm>
#if TD_MSVC
#pragma comment(linker, "/STACK:16777216")
#endif
template <int type>
class RingBench : public td::Benchmark {
public:
struct PassActor;
private:
int actor_n_;
int thread_n_;
std::vector<td::ActorId<PassActor>> actor_array_;
td::ConcurrentScheduler *scheduler_;
public:
std::string get_description() const override {
static const char *types[] = {"later", "immediate", "raw", "tail", "lambda"};
static_assert(0 <= type && type < 5, "");
return PSTRING("Ring (send_%s) (threads_n = %d)", types[type], thread_n_);
}
struct PassActor : public td::Actor {
int id;
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
// may be send event could be further optimized?
::td::Scheduler::instance()->hack(static_cast<td::ActorId<Actor>>(next_actor),
td::Event::raw(static_cast<td::uint32>(n - 1)));
}
} else if (type == 4) {
send_lambda(next_actor, [=, ptr = next_actor.get_actor_unsafe()] { ptr->pass(n - 1); });
}
}
}
void raw_event(const td::Event::Raw &raw) override {
pass(static_cast<int>(raw.u32));
}
void start_up() override {
yield();
}
void wakeup() override {
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() override {
scheduler_ = new td::ConcurrentScheduler();
scheduler_->init(thread_n_);
actor_array_ = std::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) override {
// first actor is on main_thread
actor_array_[0].get_actor_unsafe()->start_n = std::max(n, 100);
while (scheduler_->run_main(10)) {
// empty
}
}
void tear_down() override {
scheduler_->finish();
delete scheduler_;
}
};
template <int type>
class QueryBench : public td::Benchmark {
public:
std::string get_description() const override {
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 : 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(std::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:
std::unique_ptr<Callback> callback_;
};
class ServerActor : public td::Actor {
public:
class ClientCallback : public ClientActor::Callback {
public:
explicit ClientCallback(td::ActorId<ServerActor> server) : server_(server) {
}
void on_result(int x) override {
send_closure(server_, &ServerActor::on_result, x);
}
private:
td::ActorId<ServerActor> server_;
};
void start_up() override {
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() override {
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));
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_; });
} else if (type == 5) {
send_closure(client_, &ClientActor::f_promise,
td::PromiseCreator::lambda(
[id = actor_id(this), n = n_](td::Unit) { send_closure(id, &ServerActor::result, n * n); }));
return;
}
}
}
void run(int n) {
n_ = n;
wakeup();
}
void raw_event(const td::Event::Raw &event) override {
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_;
td::FutureActor<int> future_;
};
void start_up() override {
scheduler_ = new td::ConcurrentScheduler();
scheduler_->init(0);
server_ = scheduler_->create_actor_unsafe<ServerActor>(0, "Server");
scheduler_->start();
}
void run(int n) override {
// first actor is on main_thread
{
auto guard = scheduler_->get_current_guard();
send_closure(server_, &ServerActor::run, n);
}
while (scheduler_->run_main(10)) {
// empty
}
}
void tear_down() override {
server_.release();
scheduler_->finish();
delete scheduler_;
}
private:
td::ConcurrentScheduler *scheduler_;
td::ActorOwn<ServerActor> server_;
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
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<2>());
bench(QueryBench<3>());
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));
return 0;
}

213
benchmark/bench_crypto.cpp Normal file
View File

@ -0,0 +1,213 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/crypto.h"
#include "td/utils/int_types.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include <openssl/sha.h>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <random>
#include <string>
#include <vector>
static constexpr int DATA_SIZE = 8 << 10;
class SHA1Bench : public td::Benchmark {
public:
alignas(64) unsigned char data[DATA_SIZE];
std::string get_description() const override {
return PSTRING("SHA1 OpenSSL [%dKB]", DATA_SIZE >> 10);
}
void start_up() override {
for (int i = 0; i < DATA_SIZE; i++) {
data[i] = 123;
data[i] = 0;
}
}
void run(int n) override {
for (int i = 0; i < n; i++) {
unsigned char md[20];
SHA1(data, DATA_SIZE, md);
}
}
};
class AESBench : public td::Benchmark {
public:
alignas(64) unsigned char data[DATA_SIZE];
td::UInt256 key;
td::UInt256 iv;
std::string get_description() const override {
return PSTRING("AES OpenSSL [%dKB]", DATA_SIZE >> 10);
}
void start_up() override {
for (int i = 0; i < DATA_SIZE; i++) {
data[i] = 123;
}
td::Random::secure_bytes(key.raw, sizeof(key));
td::Random::secure_bytes(iv.raw, sizeof(iv));
}
void run(int n) override {
td::MutableSlice data_slice(data, DATA_SIZE);
for (int i = 0; i < n; i++) {
td::aes_ige_encrypt(key, &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;
for (int i = 0; i < 3; i++) {
v.push_back(td::thread([&] {
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 : public td::Benchmark {
public:
alignas(64) unsigned char data[DATA_SIZE];
std::string get_description() const override {
return PSTRING("Crc32 zlib [%dKB]", DATA_SIZE >> 10);
}
void start_up() override {
for (int i = 0; i < DATA_SIZE; i++) {
data[i] = 123;
data[i] = 0;
}
}
void run(int n) override {
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 : public td::Benchmark {
public:
alignas(64) unsigned char data[DATA_SIZE];
std::string get_description() const override {
return PSTRING("Crc64 Anton [%dKB]", DATA_SIZE >> 10);
}
void start_up() override {
for (int i = 0; i < DATA_SIZE; i++) {
data[i] = 123;
data[i] = 0;
}
}
void run(int n) override {
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::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());
td::bench(SHA1Bench());
td::bench(AESBench());
td::bench(Crc32Bench());
td::bench(Crc64Bench());
return 0;
}

237
benchmark/bench_db.cpp Normal file
View File

@ -0,0 +1,237 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/db/binlog/Binlog.h"
#include "td/db/BinlogKeyValue.h"
#include "td/db/SeqKeyValue.h"
#include "td/db/SqliteDb.h"
#include "td/db/SqliteKeyValueAsync.h"
#include "td/utils/benchmark.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include <memory>
template <class KeyValueT>
class TdKvBench : public td::Benchmark {
td::ConcurrentScheduler sched;
td::string name_;
public:
explicit TdKvBench(td::string name) {
name_ = std::move(name);
}
td::string get_description() const override {
return name_;
}
class Main : public td::Actor {
public:
explicit Main(int n) : n_(n) {
}
private:
void loop() override {
KeyValueT::destroy("test_tddb").ignore();
class Worker : public Actor {
public:
Worker(int n, td::string db_name) : n_(n) {
kv_.init(db_name).ensure();
}
private:
void loop() override {
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) override {
sched.init(1);
sched.create_actor_unsafe<Main>(1, "Main", n).release();
}
void run(int n) override {
sched.start();
while (sched.run_main(10)) {
// empty
}
sched.finish();
}
void tear_down() override {
}
};
template <bool is_encrypted = false>
class SqliteKVBench : public td::Benchmark {
td::SqliteDb db;
td::string get_description() const override {
return PSTRING() << "SqliteKV " << td::tag("is_encrypted", is_encrypted);
}
void start_up() override {
td::string path = "testdb.sqlite";
td::SqliteDb::destroy(path).ignore();
if (is_encrypted) {
td::SqliteDb::change_key(path, td::DbKey::password("cucumber"), td::DbKey::empty());
db = td::SqliteDb::open_with_key(path, td::DbKey::password("cucumber")).move_as_ok();
} else {
db = td::SqliteDb::open_with_key(path, 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) override {
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 : public td::Benchmark {
public:
td::string get_description() const override {
return "SqliteKeyValueAsync";
}
void start_up() override {
do_start_up().ensure();
scheduler_->start();
}
void run(int n) override {
auto guard = scheduler_->get_current_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() override {
scheduler_->run_main(0.1);
{
auto guard = scheduler_->get_current_guard();
sqlite_kv_async_.reset();
sqlite_kv_safe_.reset();
sql_connection_->close_and_destroy();
}
scheduler_->finish();
scheduler_.reset();
}
private:
std::unique_ptr<td::ConcurrentScheduler> scheduler_;
std::shared_ptr<td::SqliteConnectionSafe> sql_connection_;
std::shared_ptr<td::SqliteKeyValueSafe> sqlite_kv_safe_;
std::unique_ptr<td::SqliteKeyValueAsyncInterface> sqlite_kv_async_;
td::Status do_start_up() {
scheduler_ = std::make_unique<td::ConcurrentScheduler>();
scheduler_->init(1);
auto guard = scheduler_->get_current_guard();
td::string sql_db_name = "testdb.sqlite";
td::SqliteDb::destroy(sql_db_name).ignore();
sql_connection_ = std::make_shared<td::SqliteConnectionSafe>(sql_db_name);
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 : public td::Benchmark {
td::string get_description() const override {
return "SeqKvBench";
}
td::SeqKeyValue kv;
void run(int n) override {
for (int i = 0; i < n; i++) {
kv.set(td::to_string(i % 10), td::to_string(i));
}
}
};
template <bool is_encrypted = false>
class BinlogKeyValueBench : public td::Benchmark {
td::string get_description() const override {
return PSTRING() << "BinlogKeyValue " << td::tag("is_encrypted", is_encrypted);
}
td::BinlogKeyValue<td::Binlog> kv;
void start_up() override {
td::SqliteDb::destroy("test_binlog").ignore();
kv.init("test_binlog", is_encrypted ? td::DbKey::password("cucumber") : td::DbKey::empty()).ensure();
}
void run(int n) override {
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(BinlogKeyValueBench<true>());
bench(BinlogKeyValueBench<false>());
bench(SqliteKVBench<false>());
bench(SqliteKVBench<true>());
bench(SqliteKeyValueAsyncBench());
bench(TdKvBench<td::BinlogKeyValue<td::Binlog>>("BinlogKeyValue<Binlog>"));
bench(TdKvBench<td::BinlogKeyValue<td::ConcurrentBinlog>>("BinlogKeyValue<ConcurrentBinlog>"));
bench(SeqKvBench());
return 0;
}

View File

@ -0,0 +1,9 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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() {
return 0;
}

View File

@ -0,0 +1,76 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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" // for bench, do_not_optimize_away, etc
#include "td/mtproto/crypto.h"
#include "td/utils/base64.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include <map>
#if TD_LINUX || TD_ANDROID || TD_TIZEN
#include <semaphore.h>
#endif
namespace td {
static int32 g = 3;
static string prime_base64 =
"xxyuucaxyQSObFIvcPE_c5gNQCOOPiHBSTTQN1Y9kw9IGYoKp8FAWCKUk9IlMPTb-jNvbgrJJROVQ67UTM58NyD9UfaUWHBaxozU_mtrE6vcl0ZRKW"
"kyhFTxj6-MWV9kJHf-lrsqlB1bzR1KyMxJiAcI-ps3jjxPOpBgvuZ8-aSkppWBEFGQfhYnU7VrD2tBDbp02KhLKhSzFE4O8ShHVP0X7ZUNWWW0ud1G"
"WC2xF40WnGvEZbDW_5yjko_vW5rk5Bj8Feg-vqD4f6n_Xu1wBQ3tKEn0e_lZ2VaFDOkphR8NgRX2NbEF7i5OFdBLJFS_b0-t8DSxBAMRnNjjuS_MW"
"w";
class HandshakeBench : public Benchmark {
std::string get_description() const override {
return "Handshake";
}
class FakeDhCallback : public DhCallback {
public:
int is_good_prime(Slice prime_str) const override {
auto it = cache.find(prime_str.str());
if (it == cache.end()) {
return -1;
}
return it->second;
}
void add_good_prime(Slice prime_str) const override {
cache[prime_str.str()] = 1;
}
void add_bad_prime(Slice prime_str) const override {
cache[prime_str.str()] = 0;
}
mutable std::map<string, int> cache;
} dh_callback;
void run(int n) override {
DhHandshake a;
DhHandshake b;
auto prime = base64url_decode(prime_base64).move_as_ok();
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(&dh_callback).ensure();
b.run_checks(&dh_callback).ensure();
auto a_key = a.gen_key();
auto b_key = b.gen_key();
CHECK(a_key.first == b_key.first);
}
}
};
} // namespace td
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
td::bench(td::HandshakeBench());
return 0;
}

78
benchmark/bench_http.cpp Normal file
View File

@ -0,0 +1,78 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/net/HttpOutboundConnection.h"
#include "td/net/HttpQuery.h"
#include "td/utils/buffer.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>
namespace td {
std::atomic<int> counter;
class HttpClient : public HttpOutboundConnection::Callback {
void start_up() override {
IPAddress addr;
addr.init_ipv4_port("127.0.0.1", 8082).ensure();
auto fd = SocketFd::open(addr);
CHECK(fd.is_ok()) << fd.error();
connection_ =
create_actor<HttpOutboundConnection>("Connect", fd.move_as_ok(), std::numeric_limits<size_t>::max(), 0, 0,
ActorOwn<HttpOutboundConnection::Callback>(actor_id(this)));
yield();
cnt_ = 100000;
counter++;
}
void tear_down() override {
if (--counter == 0) {
Scheduler::instance()->finish();
}
}
void loop() override {
if (cnt_-- < 0) {
return stop();
}
send_closure(connection_, &HttpOutboundConnection::write_next, BufferSlice("GET / HTTP/1.1\r\n\r\n"));
send_closure(connection_, &HttpOutboundConnection::write_ok);
LOG(INFO) << "SEND";
}
void handle(HttpQueryPtr result) override {
loop();
}
void on_connection_error(Status error) override {
LOG(ERROR) << "ERROR: " << error;
}
ActorOwn<HttpOutboundConnection> connection_;
int cnt_;
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = make_unique<ConcurrentScheduler>();
scheduler->init(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();
return 0;
}
} // namespace td
int main() {
return td::main();
}

View File

@ -0,0 +1,118 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/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 : public td::Benchmark {
std::string get_description() const override {
return "HttpReaderBench";
}
void run(int n) override {
int 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() override {
writer_ = td::ChainBufferWriter::create_empty();
reader_ = writer_.extract_reader();
http_reader_.init(&reader_, 10000, 0);
}
};
class BufferBench : public td::Benchmark {
std::string get_description() const override {
return "BufferBench";
}
void run(int n) override {
int 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++) {
reader_.cut_head(http_query.size());
}
}
}
td::ChainBufferWriter writer_;
td::ChainBufferReader reader_;
td::HttpReader http_reader_;
void start_up() override {
writer_ = td::ChainBufferWriter::create_empty();
reader_ = writer_.extract_reader();
}
};
class FindBoundaryBench : public td::Benchmark {
std::string get_description() const override {
return "FindBoundaryBench";
}
void run(int n) override {
int 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());
reader_.cut_head(len + 2);
reader_.advance(2);
}
}
}
td::ChainBufferWriter writer_;
td::ChainBufferReader reader_;
td::HttpReader http_reader_;
void start_up() override {
writer_ = td::ChainBufferWriter::create_empty();
reader_ = writer_.extract_reader();
}
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
td::bench(BufferBench());
td::bench(FindBoundaryBench());
td::bench(HttpReaderBench());
}

View File

@ -0,0 +1,89 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/net/HttpHeaderCreator.h"
#include "td/net/HttpInboundConnection.h"
#include "td/net/HttpQuery.h"
#include "td/net/TcpListener.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
namespace td {
static int cnt = 0;
class HelloWorld : public HttpInboundConnection::Callback {
public:
void handle(HttpQueryPtr query, ActorOwn<HttpInboundConnection> connection) override {
// LOG(ERROR) << *query;
HttpHeaderCreator hc;
Slice content = "hello world";
//auto content = 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, &HttpInboundConnection::write_next, BufferSlice(res.ok()));
send_closure(connection.release(), &HttpInboundConnection::write_ok);
}
void hangup() override {
LOG(ERROR) << "CLOSE " << cnt--;
stop();
}
};
const int N = 0;
class Server : public TcpListener::Callback {
public:
void start_up() override {
listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
}
void accept(SocketFd fd) override {
LOG(ERROR) << "ACCEPT " << cnt++;
pos_++;
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
create_actor_on_scheduler<HttpInboundConnection>("HttpInboundConnection", scheduler_id, std::move(fd), 1024 * 1024,
0, 0,
create_actor_on_scheduler<HelloWorld>("HelloWorld", scheduler_id))
.release();
}
void hangup() override {
// may be it should be default?..
LOG(ERROR) << "hangup..";
stop();
}
private:
ActorOwn<TcpListener> listener_;
int pos_{0};
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = make_unique<ConcurrentScheduler>();
scheduler->init(N);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {
// empty
}
scheduler->finish();
return 0;
}
} // namespace td
int main() {
return td::main();
}

View File

@ -0,0 +1,138 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/net/HttpHeaderCreator.h"
#include "td/net/HttpInboundConnection.h"
#include "td/net/TcpListener.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/port/Fd.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <array>
namespace td {
// HttpInboundConnection header
static int cnt = 0;
class HelloWorld : public Actor {
public:
explicit HelloWorld(SocketFd socket_fd) : socket_fd_(std::move(socket_fd)) {
}
private:
SocketFd socket_fd_;
std::array<char, 1024> read_buf;
size_t read_new_lines{0};
std::string hello_;
std::string write_buf_;
size_t write_pos_{0};
void start_up() override {
socket_fd_.get_fd().set_observer(this);
subscribe(socket_fd_.get_fd());
HttpHeaderCreator hc;
Slice content = "hello world";
//auto content = 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() override {
auto status = do_loop();
if (status.is_error()) {
unsubscribe(socket_fd_.get_fd());
stop();
LOG(ERROR) << "CLOSE: " << status;
}
}
Status do_loop() {
TRY_STATUS(read_loop());
TRY_STATUS(write_loop());
if (can_close(socket_fd_)) {
return Status::Error("CLOSE");
}
return Status::OK();
}
Status write_loop() {
while (can_write(socket_fd_) && write_pos_ < write_buf_.size()) {
TRY_RESULT(written, socket_fd_.write(Slice(write_buf_).substr(write_pos_)));
write_pos_ += written;
if (write_pos_ == write_buf_.size()) {
write_pos_ = 0;
write_buf_.clear();
}
}
return Status::OK();
}
Status read_loop() {
while (can_read(socket_fd_)) {
TRY_RESULT(read_size, socket_fd_.read(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 Status::OK();
}
};
const int N = 0;
class Server : public TcpListener::Callback {
public:
void start_up() override {
listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
}
void accept(SocketFd fd) override {
LOG(ERROR) << "ACCEPT " << cnt++;
pos_++;
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
create_actor_on_scheduler<HelloWorld>("HttpInboundConnection", scheduler_id, std::move(fd)).release();
}
void hangup() override {
// may be it should be default?..
LOG(ERROR) << "hangup..";
stop();
}
private:
ActorOwn<TcpListener> listener_;
int pos_{0};
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = make_unique<ConcurrentScheduler>();
scheduler->init(N);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {
// empty
}
scheduler->finish();
return 0;
}
} // namespace td
int main() {
return td::main();
}

View File

@ -0,0 +1,121 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/net/HttpHeaderCreator.h"
#include "td/net/HttpQuery.h"
#include "td/net/HttpReader.h"
#include "td/net/TcpListener.h"
#include "td/utils/buffer.h"
#include "td/utils/BufferedFd.h"
#include "td/utils/logging.h"
#include "td/utils/port/Fd.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class HttpEchoConnection : public Actor {
public:
explicit HttpEchoConnection(SocketFd fd) : fd_(std::move(fd)) {
}
private:
BufferedFd<SocketFd> fd_;
HttpReader reader_;
HttpQuery query_;
void start_up() override {
fd_.get_fd().set_observer(this);
subscribe(fd_.get_fd());
reader_.init(&fd_.input_buffer(), 1024 * 1024, 0);
}
void handle_query() {
query_ = HttpQuery();
HttpHeaderCreator hc;
Slice content = "hello world";
//auto content = 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() override {
auto status = [&] {
TRY_STATUS(loop_read());
TRY_STATUS(loop_write());
return Status::OK();
}();
if (status.is_error() || can_close(fd_)) {
stop();
}
}
Status loop_read() {
if (can_read(fd_)) {
TRY_STATUS(fd_.flush_read());
}
while (true) {
TRY_RESULT(need, reader_.read_next(&query_));
if (need == 0) {
handle_query();
} else {
break;
}
}
return Status::OK();
}
Status loop_write() {
TRY_STATUS(fd_.flush_write());
return Status::OK();
}
};
const int N = 4;
class Server : public TcpListener::Callback {
public:
void start_up() override {
listener_ = create_actor<TcpListener>("Listener", 8082, ActorOwn<TcpListener::Callback>(actor_id(this)));
}
void accept(SocketFd fd) override {
pos_++;
auto scheduler_id = pos_ % (N != 0 ? N : 1) + (N != 0);
create_actor_on_scheduler<HttpEchoConnection>("HttpInboundConnection", scheduler_id, std::move(fd)).release();
}
void hangup() override {
LOG(ERROR) << "hangup..";
stop();
}
private:
ActorOwn<TcpListener> listener_;
int pos_{0};
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto scheduler = make_unique<ConcurrentScheduler>();
scheduler->init(N);
scheduler->create_actor_unsafe<Server>(0, "Server").release();
scheduler->start();
while (scheduler->run_main(10)) {
// empty
}
scheduler->finish();
return 0;
}
} // namespace td
int main() {
return td::main();
}

163
benchmark/bench_log.cpp Normal file
View File

@ -0,0 +1,163 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 <cstdio>
#include <fstream>
#include <iostream>
#include <mutex>
#include <ostream>
#include <streambuf>
#include <string>
#include <unistd.h>
#include "td/utils/benchmark.h"
#include "td/utils/logging.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 : public td::Benchmark {
protected:
std::string file_name_;
std::ofstream stream;
enum { buffer_size = 1 << 20 };
char buffer[buffer_size];
public:
std::string get_description() const override {
return "ostream (to file, no buf, no flush)";
}
void start_up() override {
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) override {
for (int i = 0; i < n; i++) {
stream << "This is just for test" << 987654321 << '\n';
}
}
void tear_down() override {
stream.close();
unlink(file_name_.c_str());
}
};
class FILEWriteBench : public td::Benchmark {
protected:
std::string file_name_;
FILE *file;
enum { buffer_size = 1 << 20 };
char buffer[buffer_size];
public:
std::string get_description() const override {
return "fprintf (to file, no buf, no flush)";
}
void start_up() override {
file_name_ = create_tmp_file();
file = fopen(file_name_.c_str(), "w");
// setvbuf(file, buffer, _IOFBF, buffer_size);
}
void run(int n) override {
for (int i = 0; i < n; i++) {
std::fprintf(file, "This is just for test%d\n", 987654321);
// std::fflush(file);
}
}
void tear_down() override {
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 : public td::Benchmark {
public:
std::string get_description() const override {
return "android_log";
}
void start_up() override {
}
void run(int n) override {
for (int i = 0; i < n; i++) {
ALOG("This is just for test%d\n", 987654321);
}
}
void tear_down() override {
}
};
#endif
class LogWriteBench : public td::Benchmark {
protected:
std::string file_name_;
std::ofstream stream;
std::streambuf *old_buf;
enum { buffer_size = 1 << 20 };
char buffer[buffer_size];
public:
std::string get_description() const override {
return "td_log (slow in debug mode)";
}
void start_up() override {
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) override {
for (int i = 0; i < n; i++) {
LOG(DEBUG) << "This is just for test" << 987654321;
}
}
void tear_down() override {
stream.close();
unlink(file_name_.c_str());
std::cerr.rdbuf(old_buf);
}
};
std::mutex mutex;
int main() {
td::bench(LogWriteBench());
#if TD_ANDROID
td::bench(ALogWriteBench());
#endif
td::bench(IostreamWriteBench());
td::bench(FILEWriteBench());
return 0;
}

392
benchmark/bench_misc.cpp Normal file
View File

@ -0,0 +1,392 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/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/Slice.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/telegram_api.hpp"
#if !TD_WINDOWS
#include <unistd.h>
#include <utime.h>
#endif
#if TD_LINUX || TD_ANDROID || TD_TIZEN
#include <semaphore.h>
#endif
#include <atomic>
#include <cstdint>
namespace td {
class F {
uint32 &sum;
public:
explicit F(uint32 &sum) : sum(sum) {
}
template <class T>
void operator()(const T &x) const {
sum += static_cast<uint32>(x.get_id());
}
};
BENCH(Call, "TL Call") {
tl_object_ptr<telegram_api::Function> x = make_tl_object<telegram_api::account_getWallPapers>();
uint32 res = 0;
F f(res);
for (int i = 0; i < n; i++) {
downcast_call(*x, f);
}
do_not_optimize_away(res);
}
#if !TD_EVENTFD_UNSUPPORTED
BENCH(EventFd, "EventFd") {
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;
}
do_not_optimize_away(res);
}
BENCH(NewObj, "new struct then delete") {
struct A {
int32 a = 0;
int32 b = 0;
int32 c = 0;
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;
do_not_optimize_away(res);
}
#if !TD_THREAD_UNSUPPORTED
BENCH(ThreadNew, "new struct then delete in several threads") {
td::NewObjBench a, b;
thread ta([&] { a.run(n / 2); });
thread tb([&] { b.run(n - n / 2); });
ta.join();
tb.join();
}
#endif
// Too hard for android clang (?)
BENCH(Time, "Clocks::monotonic") {
double res = 0;
for (int i = 0; i < n; i++) {
res += Clocks::monotonic();
}
do_not_optimize_away(res);
}
#if !TD_WINDOWS
class PipeBench : public Benchmark {
public:
int p[2];
PipeBench() {
pipe(p);
}
string get_description() const override {
return "pipe write + read int32";
}
void start_up() override {
pipe(p);
}
void run(int n) override {
int res = 0;
for (int i = 0; i < n; i++) {
int val = 1;
write(p[1], &val, sizeof(val));
read(p[0], &val, sizeof(val));
res += val;
}
do_not_optimize_away(res);
}
void tear_down() override {
close(p[0]);
close(p[1]);
}
};
#endif
#if TD_LINUX || TD_ANDROID || TD_TIZEN
class SemBench : public Benchmark {
sem_t sem;
public:
string get_description() const override {
return "sem post + wait";
}
void start_up() override {
int err = sem_init(&sem, 0, 0);
CHECK(err != -1);
}
void run(int n) override {
for (int i = 0; i < n; i++) {
sem_post(&sem);
sem_wait(&sem);
}
}
void tear_down() override {
sem_destroy(&sem);
}
};
#endif
#if !TD_WINDOWS
class UtimeBench : public Benchmark {
public:
void start_up() override {
FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok().close();
}
string get_description() const override {
return "utime";
}
void run(int n) override {
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 = FileFd::open("test", FileFd::Flags::Create | FileFd::Flags::Write).move_as_ok();
for (int i = 0; i < n; i++) {
fd.pwrite("a", 0).ok();
}
fd.close();
}
class CreateFileBench : public Benchmark {
string get_description() const override {
return "create_file";
}
void start_up() override {
mkdir("A").ensure();
}
void run(int n) override {
for (int i = 0; i < n; i++) {
FileFd::open("A/" + to_string(i), FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
}
}
void tear_down() override {
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
if (is_dir) {
rmdir(path).ignore();
} else {
unlink(path).ignore();
}
});
}
};
class WalkPathBench : public Benchmark {
string get_description() const override {
return "walk_path";
}
void start_up_n(int n) override {
mkdir("A").ensure();
for (int i = 0; i < n; i++) {
FileFd::open("A/" + to_string(i), FileFd::Flags::Write | FileFd::Flags::Create).move_as_ok().close();
}
}
void run(int n) override {
int cnt = 0;
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
stat(path).ok();
cnt++;
});
}
void tear_down() override {
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) {
if (is_dir) {
rmdir(path).ignore();
} else {
unlink(path).ignore();
}
});
}
};
#if !TD_THREAD_UNSUPPORTED
template <int ThreadN = 2>
class AtomicReleaseIncBench : public Benchmark {
string get_description() const override {
return PSTRING() << "AtomicReleaseInc" << ThreadN;
}
static std::atomic<uint64> a_;
void run(int n) override {
std::vector<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<uint64> AtomicReleaseIncBench<ThreadN>::a_;
template <int ThreadN = 2>
class AtomicReleaseCasIncBench : public Benchmark {
string get_description() const override {
return PSTRING() << "AtomicReleaseCasInc" << ThreadN;
}
static std::atomic<uint64> a_;
void run(int n) override {
std::vector<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<uint64> AtomicReleaseCasIncBench<ThreadN>::a_;
template <int ThreadN = 2>
class RwMutexReadBench : public Benchmark {
string get_description() const override {
return PSTRING() << "RwMutexRead" << ThreadN;
}
RwMutex mutex_;
void run(int n) override {
std::vector<thread> threads;
for (int i = 0; i < ThreadN; i++) {
threads.emplace_back([&] {
for (int i = 0; i < n / ThreadN; i++) {
mutex_.lock_read().ensure();
}
});
}
for (auto &thread : threads) {
thread.join();
}
}
};
template <int ThreadN = 2>
class RwMutexWriteBench : public Benchmark {
string get_description() const override {
return PSTRING() << "RwMutexWrite" << ThreadN;
}
RwMutex mutex_;
void run(int n) override {
std::vector<thread> threads;
for (int i = 0; i < ThreadN; i++) {
threads.emplace_back([&] {
for (int i = 0; i < n / ThreadN; i++) {
mutex_.lock_write().ensure();
}
});
}
for (auto &thread : threads) {
thread.join();
}
}
};
#endif
} // namespace td
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
#if !TD_THREAD_UNSUPPORTED
td::bench(td::AtomicReleaseIncBench<1>());
td::bench(td::AtomicReleaseIncBench<2>());
td::bench(td::AtomicReleaseCasIncBench<1>());
td::bench(td::AtomicReleaseCasIncBench<2>());
td::bench(td::RwMutexWriteBench<1>());
td::bench(td::RwMutexReadBench<1>());
td::bench(td::RwMutexWriteBench<>());
td::bench(td::RwMutexReadBench<>());
#endif
#if !TD_WINDOWS
td::bench(td::UtimeBench());
#endif
td::bench(td::WalkPathBench());
td::bench(td::CreateFileBench());
td::bench(td::PwriteBench());
td::bench(td::CallBench());
#if !TD_THREAD_UNSUPPORTED
td::bench(td::ThreadNewBench());
#endif
#if !TD_EVENTFD_UNSUPPORTED
td::bench(td::EventFdBench());
#endif
td::bench(td::NewObjBench());
td::bench(td::NewIntBench());
#if !TD_WINDOWS
td::bench(td::PipeBench());
#endif
#if TD_LINUX || TD_ANDROID || TD_TIZEN
td::bench(td::SemBench());
#endif
return 0;
}

943
benchmark/bench_queue.cpp Normal file
View File

@ -0,0 +1,943 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
// TODO: check system calls
// TODO: all return values must be checked
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <pthread.h>
#include <sched.h>
#include <semaphore.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "td/utils/benchmark.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/MpscPollableQueue.h"
#include "td/utils/queue.h"
#if TD_LINUX
#include <sys/eventfd.h>
#endif
using std::atomic;
using std::vector;
using td::int32;
using td::uint32;
#define MODE std::memory_order_relaxed
// void set_affinity(int mask) {
// int err, syscallres;
// pid_t pid = gettid();
// syscallres = syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
// if (syscallres) {
// err = errno;
// perror("oppa");
//}
//}
// TODO: warnings and asserts. There should be no warnings or debug output in production.
using qvalue_t = int;
// Just for testing, not production
class PipeQueue {
int input;
int output;
public:
void init() {
int new_pipe[2];
pipe(new_pipe);
output = new_pipe[0];
input = new_pipe[1];
}
void put(qvalue_t value) {
write(input, &value, sizeof(value));
}
qvalue_t get() {
qvalue_t res;
read(output, &res, sizeof(res));
return res;
}
void destroy() {
close(input);
close(output);
}
};
class Backoff {
int cnt;
public:
Backoff() : cnt(0) {
}
bool next() {
cnt++;
if (cnt < 50) {
return true;
} else {
sched_yield();
return cnt < 500;
}
}
};
class VarQueue {
atomic<qvalue_t> data;
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();
}
};
#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;
write(fd, &x, sizeof(x));
}
qvalue_t get() {
td::int64 x;
read(fd, &x, sizeof(x));
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(atomic<qvalue_t>)];
};
node q[queue_buf_size];
struct Position {
atomic<uint32> i;
char pad[64 - sizeof(atomic<uint32>)];
uint32 local_read_i;
uint32 local_write_i;
char pad2[64 - sizeof(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()) {
std::fprintf(stderr, "put strong failed\n");
std::exit(0);
}
put_unsafe(val);
}
void put(qvalue_t val) {
put_noflush(val);
flush_writer();
}
void destroy() {
}
};
#if TD_LINUX
class BufferedFdQueue {
int fd;
atomic<int> wait_flag;
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)) {
write(fd, &x, 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)) {
write(fd, &x, 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())) {
read(fd, &x, 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()) {
read(fd, &x, sizeof(x));
__sync_synchronize();
}
wait_flag.store(0, MODE);
return get_unsafe_flush();
}
void destroy() {
q.destroy();
close(fd);
}
};
class FdQueue {
int fd;
atomic<int> wait_flag;
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)) {
write(fd, &x, sizeof(x));
}
}
qvalue_t get() {
// td::int64 x;
// read(fd, &x, 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();
// std::fprintf(stderr, "!\n");
// while (res == -1 && read(fd, &x, sizeof(x))) {
// res = q.try_get();
//}
do {
__sync_synchronize();
res = q.try_get();
} while (res == -1 && read(fd, &x, sizeof(x)));
q.acquire();
wait_flag.store(0, MODE);
return res;
}
void destroy() {
q.destroy();
close(fd);
}
};
#endif
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 : public td::Benchmark {
QueueT client, server;
int connections_n, queries_n;
int server_active_connections;
int client_active_connections;
vector<td::int64> server_conn;
vector<td::int64> client_conn;
public:
explicit QueueBenchmark2(int connections_n = 1) : connections_n(connections_n) {
}
std::string get_description() const override {
return "QueueBenchmark2";
}
void start_up() override {
client.init();
server.init();
}
void tear_down() override {
client.destroy();
server.destroy();
}
void server_process(qvalue_t value) {
int no = value & 0x00FFFFFF;
int co = static_cast<int>(static_cast<unsigned int>(value) >> 24);
// std::fprintf(stderr, "-->%d %d\n", co, no);
if (co < 0 || co >= connections_n || no != server_conn[co]++) {
std::fprintf(stderr, "%d %d\n", co, no);
std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(server_conn[co] - 1));
std::fprintf(stderr, "Server BUG\n");
while (true) {
}
}
// std::fprintf(stderr, "no = %d/%d\n", no, queries_n);
// std::fprintf(stderr, "answer: %d %d\n", no, co);
client.writer_put(value);
client.writer_flush();
if (no + 1 >= queries_n) {
server_active_connections--;
}
}
void *server_run(void *) {
server_conn = vector<td::int64>(connections_n);
server_active_connections = connections_n;
while (server_active_connections > 0) {
int cnt = server.reader_wait();
if (cnt == 0) {
std::fprintf(stderr, "ERROR!\n");
std::exit(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;
int co = static_cast<int>(static_cast<unsigned int>(value) >> 24);
// std::fprintf(stderr, "<--%d %d\n", co, no);
if (co < 0 || co >= connections_n || no != client_conn[co]++) {
std::fprintf(stderr, "%d %d\n", co, no);
std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(client_conn[co] - 1));
std::fprintf(stderr, "BUG\n");
while (true) {
}
std::exit(0);
}
if (no + 1 < queries_n) {
// std::fprintf(stderr, "query: %d %d\n", no + 1, co);
server.writer_put(value + 1);
server.writer_flush();
} else {
client_active_connections--;
}
}
void *client_run(void *) {
client_conn = vector<td::int64>(connections_n);
client_active_connections = connections_n;
if (queries_n >= (1 << 24)) {
std::fprintf(stderr, "Too big queries_n\n");
std::exit(0);
}
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();
if (cnt == 0) {
std::fprintf(stderr, "ERROR!\n");
std::exit(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) override {
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 : public td::Benchmark {
QueueT client, server;
const int connections_n;
int queries_n;
public:
explicit QueueBenchmark(int connections_n = 1) : connections_n(connections_n) {
}
std::string get_description() const override {
return "QueueBenchmark";
}
void start_up() override {
client.init();
server.init();
}
void tear_down() override {
client.destroy();
server.destroy();
}
void *server_run(void *) {
vector<td::int64> conn(connections_n);
int active_connections = connections_n;
while (active_connections > 0) {
qvalue_t value = server.get();
int no = value & 0x00FFFFFF;
int co = static_cast<int>(value >> 24);
// std::fprintf(stderr, "-->%d %d\n", co, no);
if (co < 0 || co >= connections_n || no != conn[co]++) {
std::fprintf(stderr, "%d %d\n", co, no);
std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
std::fprintf(stderr, "Server BUG\n");
while (true) {
}
}
// std::fprintf(stderr, "no = %d/%d\n", no, queries_n);
client.put(value);
if (no + 1 >= queries_n) {
active_connections--;
}
}
return nullptr;
}
void *client_run(void *) {
vector<td::int64> conn(connections_n);
if (queries_n >= (1 << 24)) {
std::fprintf(stderr, "Too big queries_n\n");
std::exit(0);
}
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;
int co = static_cast<int>(value >> 24);
// std::fprintf(stderr, "<--%d %d\n", co, no);
if (co < 0 || co >= connections_n || no != conn[co]++) {
std::fprintf(stderr, "%d %d\n", co, no);
std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
std::fprintf(stderr, "BUG\n");
while (true) {
}
std::exit(0);
}
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 *) {
vector<td::int64> conn(connections_n);
if (queries_n >= (1 << 24)) {
std::fprintf(stderr, "Too big queries_n\n");
std::exit(0);
}
for (int it = 0; it < queries_n; it++) {
for (int i = 0; i < connections_n; i++) {
server.put((static_cast<td::int64>(i) << 24) + it);
}
for (int i = 0; i < connections_n; i++) {
qvalue_t value = client.get();
int no = value & 0x00FFFFFF;
int co = static_cast<int>(value >> 24);
// std::fprintf(stderr, "<--%d %d\n", co, no);
if (co < 0 || co >= connections_n || no != conn[co]++) {
std::fprintf(stderr, "%d %d\n", co, no);
std::fprintf(stderr, "expected %d %lld\n", co, static_cast<long long>(conn[co] - 1));
std::fprintf(stderr, "BUG\n");
while (true) {
}
std::exit(0);
}
}
}
// 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) override {
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 : public td::Benchmark {
enum { QN = 504 };
struct Thread {
int int_id;
pthread_t id;
QueueT queue;
Thread *next;
char pad[64];
void *run() {
qvalue_t value;
// std::fprintf(stderr, "start %d\n", int_id);
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() override {
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() override {
for (int i = 0; i < QN; i++) {
q[i].queue.destroy();
}
}
void run(int n) override {
for (int i = 0; i < QN; i++) {
pthread_create(&q[i].id, nullptr, run_gateway, &q[i]);
}
std::fprintf(stderr, "run %d\n", n);
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);
}
}
};
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG));
#define BENCH_Q2(Q, N) \
std::fprintf(stderr, "!%s %d:\t", #Q, N); \
td::bench(QueueBenchmark2<Q>(N));
#define BENCH_Q(Q, N) \
std::fprintf(stderr, "%s %d:\t", #Q, N); \
td::bench(QueueBenchmark<Q>(N));
#define BENCH_R(Q) \
std::fprintf(stderr, "%s:\t", #Q); \
td::bench(RingBenchmark<Q>());
// TODO: yield makes it extremely slow. Yet some backoff may be necessary.
// BENCH_R(SemQueue);
// BENCH_R(td::PollQueue<qvalue_t>);
BENCH_Q2(td::PollQueue<qvalue_t>, 1);
BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 1);
BENCH_Q2(td::PollQueue<qvalue_t>, 100);
BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 100);
BENCH_Q2(td::PollQueue<qvalue_t>, 10);
BENCH_Q2(td::MpscPollableQueue<qvalue_t>, 10);
BENCH_Q(VarQueue, 1);
// BENCH_Q(FdQueue, 1);
// BENCH_Q(BufferedFdQueue, 1);
BENCH_Q(PipeQueue, 1);
BENCH_Q(SemCheatQueue, 1);
BENCH_Q(SemQueue, 1);
// BENCH_Q2(td::PollQueue<qvalue_t>, 100);
// BENCH_Q2(td::PollQueue<qvalue_t>, 10);
// BENCH_Q2(td::PollQueue<qvalue_t>, 4);
// BENCH_Q2(td::InfBackoffQueue<qvalue_t>, 100);
// BENCH_Q2(td::InfBackoffQueue<qvalue_t>, 1);
// BENCH_Q(SemCheatQueue, 1);
// BENCH_Q(BufferedFdQueue, 100);
// BENCH_Q(BufferedFdQueue, 10);
// BENCH_Q(BufferQueue, 4);
// BENCH_Q(BufferQueue, 100);
// BENCH_Q(BufferQueue, 10);
// BENCH_Q(BufferQueue, 1);
return 0;
}

108
benchmark/bench_tddb.cpp Normal file
View File

@ -0,0 +1,108 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/MessageId.h"
#include "td/telegram/MessagesDb.h"
#include "td/telegram/UserId.h"
#include "td/utils/benchmark.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Random.h"
#include "td/utils/Status.h"
#include <memory>
namespace td {
static Status init_db(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 Status::OK();
}
class MessagesDbBench : public Benchmark {
public:
string get_description() const override {
return "MessagesDb";
}
void start_up() override {
LOG(ERROR) << "START UP";
do_start_up().ensure();
scheduler_->start();
}
void run(int n) override {
auto guard = scheduler_->get_current_guard();
for (int i = 0; i < n; i += 20) {
auto dialog_id = DialogId{UserId{Random::fast(1, 100)}};
auto message_id_raw = Random::fast(1, 100000);
for (int j = 0; j < 20; j++) {
auto message_id = MessageId{ServerMessageId{message_id_raw + j}};
auto unique_message_id = ServerMessageId{i + 1};
auto sender_user_id = UserId{Random::fast(1, 1000)};
auto random_id = i + 1;
auto ttl_expires_at = 0;
auto data = BufferSlice(Random::fast(100, 299));
// use async on same thread.
messages_db_async_->add_message({dialog_id, message_id}, unique_message_id, sender_user_id, random_id,
ttl_expires_at, 0, 0, "", std::move(data), Promise<>());
}
}
}
void tear_down() override {
scheduler_->run_main(0.1);
{
auto guard = scheduler_->get_current_guard();
sql_connection_.reset();
messages_db_sync_safe_.reset();
messages_db_async_.reset();
}
scheduler_->finish();
scheduler_.reset();
LOG(ERROR) << "TEAR DOWN";
}
private:
std::unique_ptr<td::ConcurrentScheduler> scheduler_;
std::shared_ptr<SqliteConnectionSafe> sql_connection_;
std::shared_ptr<MessagesDbSyncSafeInterface> messages_db_sync_safe_;
std::shared_ptr<MessagesDbAsyncInterface> messages_db_async_;
Status do_start_up() {
scheduler_ = std::make_unique<ConcurrentScheduler>();
scheduler_->init(1);
auto guard = scheduler_->get_current_guard();
string sql_db_name = "testdb.sqlite";
sql_connection_ = std::make_shared<SqliteConnectionSafe>(sql_db_name);
auto &db = sql_connection_->get();
TRY_STATUS(init_db(db));
db.exec("BEGIN TRANSACTION").ensure();
// version == 0 ==> db will be destroyed
TRY_STATUS(init_messages_db(db, 0));
db.exec("COMMIT TRANSACTION").ensure();
messages_db_sync_safe_ = create_messages_db_sync(sql_connection_);
messages_db_async_ = create_messages_db_async(messages_db_sync_safe_, 0);
return Status::OK();
}
};
} // namespace td
int main() {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING));
bench(td::MessagesDbBench());
return 0;
}

28
benchmark/rmdir.cpp Normal file
View File

@ -0,0 +1,28 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/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, bool is_dir) {
cnt++;
LOG(INFO) << path << " " << is_dir;
// if (is_dir) {
// td::rmdir(path);
//} else {
// td::unlink(path);
//}
});
LOG(INFO) << status << ": " << cnt;
return 0;
}

39
benchmark/wget.cpp Normal file
View File

@ -0,0 +1,39 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/PromiseFuture.h"
#include "td/net/HttpQuery.h"
#include "td/net/Wget.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <memory>
#include <string>
int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO));
td::VERBOSITY_NAME(fd) = VERBOSITY_NAME(INFO);
std::string url = (argc > 1 ? argv[1] : "https://telegram.org");
auto scheduler = std::make_unique<td::ConcurrentScheduler>();
scheduler->init(0);
scheduler
->create_actor_unsafe<td::Wget>(0, "Client", td::PromiseCreator::lambda([](td::Result<td::HttpQueryPtr> res) {
LOG(ERROR) << *res.ok();
td::Scheduler::instance()->finish();
}),
url)
.release();
scheduler->start();
while (scheduler->run_main(10)) {
// empty
}
scheduler->finish();
return 0;
}

16
bitbucket-pipelines.yml Normal file
View File

@ -0,0 +1,16 @@
image: gcc:5.4
pipelines:
default:
- step:
script:
- apt-get update
- apt-get install -y cmake
- apt-get install -y gperf
- g++ --version
- cmake --version
- mkdir build
- cd build
- cmake ..
- make -k run_all_tests
- ./test/run_all_tests

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(TdExample VERSION 1.0 LANGUAGES CXX)
find_package(Td 1.0.0)
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)

305
example/cpp/td_example.cpp Normal file
View File

@ -0,0 +1,305 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/Log.h>
#include <td/telegram/td_api.h>
#include <td/telegram/td_api.hpp>
#include <cstdint>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <string>
#include <vector>
// Simple single-threaded example of TDLib usage.
// Real world programs should use separate thread for the user input.
// 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>
, 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::Log::set_verbosity_level(1);
client_ = std::make_unique<td::Client>();
}
void loop() {
while (true) {
if (need_restart_) {
restart();
} else if (!are_authorized_) {
process_response(client_->receive(10));
} else {
std::cerr
<< "Enter action [q] quit [u] check for updates and request results [c] show chats [m <id> <text>] send message [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::cerr << "Checking for updates..." << std::endl;
while (true) {
auto response = client_->receive(0);
if (response.object) {
process_response(std::move(response));
} else {
break;
}
}
} else if (action == "l") {
std::cerr << "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::cerr << "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_ = std::move(text);
send_message->input_message_content_ = std::move(message_content);
send_query(std::move(send_message), {});
} else if (action == "c") {
std::cerr << "Loading chat list..." << std::endl;
send_query(td_api::make_object<td_api::getChats>(std::numeric_limits<std::int64_t>::max(), 0, 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::cerr << "[id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl;
}
});
}
}
}
}
private:
using Object = td_api::object_ptr<td_api::Object>;
std::unique_ptr<td::Client> client_;
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::int32_t, td_api::object_ptr<td_api::user>> users_;
std::map<std::int64_t, std::string> chat_title_;
void restart() {
client_.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_->send({query_id, std::move(f)});
}
void process_response(td::Client::Response response) {
if (!response.object) {
return;
}
//std::cerr << response.id << " " << to_string(response.object) << std::endl;
if (response.id == 0) {
return process_update(std::move(response.object));
}
auto it = handlers_.find(response.id);
if (it != handlers_.end()) {
it->second(std::move(response.object));
}
}
std::string get_user_name(std::int32_t user_id) {
auto it = users_.find(user_id);
if (it == users_.end()) {
return "unknown user";
}
return it->second->first_name_ + " " + it->second->last_name_;
}
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_;
auto sender_user_name = get_user_name(update_new_message.message_->sender_user_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_;
}
std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_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::cerr << "Got authorization" << std::endl;
},
[this](td_api::authorizationStateLoggingOut &) {
are_authorized_ = false;
std::cerr << "Logging out" << std::endl;
},
[this](td_api::authorizationStateClosing &) { std::cerr << "Closing" << std::endl; },
[this](td_api::authorizationStateClosed &) {
are_authorized_ = false;
need_restart_ = true;
std::cerr << "Terminated" << std::endl;
},
[this](td_api::authorizationStateWaitCode &wait_code) {
std::string first_name;
std::string last_name;
if (!wait_code.is_registered_) {
std::cerr << "Enter your first name: ";
std::cin >> first_name;
std::cerr << "Enter your last name: ";
std::cin >> last_name;
}
std::cerr << "Enter authentication code: ";
std::string code;
std::cin >> code;
send_query(td_api::make_object<td_api::checkAuthenticationCode>(code, first_name, last_name),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPassword &) {
std::cerr << "Enter authentication password: ";
std::string password;
std::cin >> password;
send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitPhoneNumber &) {
std::cerr << "Enter phone number: ";
std::string phone_number;
std::cin >> phone_number;
send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>(
phone_number, false /*allow_flash_calls*/, false /*is_current_phone_number*/),
create_authentication_query_handler());
},
[this](td_api::authorizationStateWaitEncryptionKey &) {
std::cerr << "Enter encryption key or DESTROY: ";
std::string key;
std::getline(std::cin, key);
if (key == "DESTROY") {
send_query(td_api::make_object<td_api::destroy>(), create_authentication_query_handler());
} else {
send_query(td_api::make_object<td_api::checkDatabaseEncryptionKey>(std::move(key)),
create_authentication_query_handler());
}
},
[this](td_api::authorizationStateWaitTdlibParameters &) {
auto parameters = td_api::make_object<td_api::tdlibParameters>();
parameters->use_message_database_ = true;
parameters->use_secret_chats_ = true;
parameters->api_id_ = 94575;
parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
parameters->system_language_code_ = "en";
parameters->device_model_ = "Desktop";
parameters->system_version_ = "Unknown";
parameters->application_version_ = "1.0";
parameters->enable_storage_optimizer_ = true;
send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)),
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::cerr << "Error: " << to_string(error);
on_authorization_state_update();
}
}
std::uint64_t next_query_id() {
return ++current_query_id_;
}
};
int main() {
TdExample example;
example.loop();
}

View File

@ -0,0 +1,21 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 <string>
#include <td/telegram/td_json_client.h>
int main(void) {
void *client = td_json_client_create();
while (true) {
std::string str = td_json_client_receive(client, 10);
if (!str.empty()) {
std::cout << str << std::endl;
break;
}
}
return 0;
}

View File

@ -0,0 +1,72 @@
from ctypes.util import find_library
from ctypes import *
import json
import sys
tdjson_path = find_library("tdjson") or "tdjson.dll"
if tdjson_path is None:
print('can\'t find tdjson library')
quit()
tdjson = CDLL(tdjson_path)
td_json_client_create = tdjson.td_json_client_create
td_json_client_create.restype = c_void_p
td_json_client_create.argtypes = []
td_json_client_receive = tdjson.td_json_client_receive
td_json_client_receive.restype = c_char_p
td_json_client_receive.argtypes = [c_void_p, c_double]
td_json_client_send = tdjson.td_json_client_send
td_json_client_send.restype = None
td_json_client_send.argtypes = [c_void_p, c_char_p]
td_json_client_execute = tdjson.td_json_client_execute
td_json_client_execute.restype = c_char_p
td_json_client_execute.argtypes = [c_void_p, c_char_p]
td_json_client_destroy = tdjson.td_json_client_destroy
td_json_client_destroy.restype = None
td_json_client_destroy.argtypes = [c_void_p]
td_set_log_file_path = tdjson.td_set_log_file_path
td_set_log_file_path.restype = None
td_set_log_file_path.argtypes = [c_char_p]
td_set_log_verbosity_level = tdjson.td_set_log_verbosity_level
td_set_log_verbosity_level.restype = None
td_set_log_verbosity_level.argtypes = [c_int]
td_set_log_verbosity_level(2)
client = td_json_client_create()
def td_send(query):
query = json.dumps(query).encode('utf-8')
td_json_client_send(client, query)
def td_receive():
result = td_json_client_receive(client, 10)
if result:
result = json.loads(result.decode('utf-8'))
return result
def td_execute(query):
query = json.dumps(query).encode('utf-8')
result = td_json_client_execute(client, query)
if result:
result = json.loads(result.decode('utf-8'))
return result
print(td_execute({'@type': 'getTextEntities', 'text': '@telegram /test_command https://telegram.org telegram.me', '@extra': ['5', 7.0]}))
td_send({'@type': 'getAuthorizationState', '@extra': 1.01234})
while True:
event = td_receive()
if event:
if event['@type'] is 'updateAuthorizationState' and event['authorization_state']['@type'] is 'authorizationStateClosed':
break
print(event)
sys.stdout.flush()
td_json_client_destroy(client)

4
format.ps1 Normal file
View File

@ -0,0 +1,4 @@
./src.ps1 | ForEach-Object {
echo $_
clang-format -style=file -i $_
}

2
format.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
./src.sh | grep -v CxCli.h | xargs -n 1 clang-format -style=file -i

9
gen_git_commit_h.ps1 Normal file
View File

@ -0,0 +1,9 @@
$commit = git rev-parse HEAD
git diff-index --quiet HEAD
$dirty = $LASTEXITCODE
echo "#pragma once`r`n#define GIT_COMMIT `"$commit`"`r`n#define GIT_DIRTY $dirty" | out-file -encoding ASCII auto/git_info.h.new
if (-not (Test-Path .\auto\git_info.h) -or (Compare-Object $(Get-Content .\auto\git_info.h.new) $(Get-Content .\auto\git_info.h))) {
mv -Force auto/git_info.h.new auto/git_info.h
} else {
rm auto/git_info.h.new
}

11
gen_git_commit_h.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
commit=$(git rev-parse HEAD)
git diff-index --quiet HEAD
dirty=$?
printf "#pragma once\n#define GIT_COMMIT \"$commit\"\n#define GIT_DIRTY $dirty\n" > auto/git_info.h.new
if cmp -s auto/git_info.h.new auto/git_info.h 2>&1 > /dev/null
then
rm -f auto/git_info.h.new
else
mv -f auto/git_info.h.new auto/git_info.h
fi

314
memprof/memprof.cpp Normal file
View File

@ -0,0 +1,314 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "memprof/memprof.h"
#include "td/utils/port/platform.h"
#if (TD_DARWIN || TD_LINUX) && defined(USE_MEMPROF)
#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <new>
#include <utility>
#include <vector>
#include <dlfcn.h>
#include <execinfo.h>
bool is_memprof_on() {
return true;
}
#if USE_MEMPROF_SAFE
double get_fast_backtrace_success_rate() {
return 0;
}
#else
#if TD_LINUX
extern void *__libc_stack_end;
#endif
static void *get_bp() {
void *bp;
#if defined(__i386__)
__asm__ volatile("movl %%ebp, %[r]" : [r] "=r"(bp));
#elif defined(__x86_64__)
__asm__ volatile("movq %%rbp, %[r]" : [r] "=r"(bp));
#endif
return bp;
}
static int fast_backtrace(void **buffer, int size) {
struct stack_frame {
stack_frame *bp;
void *ip;
};
stack_frame *bp = reinterpret_cast<stack_frame *>(get_bp());
int i = 0;
while (i < size &&
#if TD_LINUX
static_cast<void *>(bp) <= __libc_stack_end &&
#endif
!(reinterpret_cast<std::uintptr_t>(static_cast<void *>(bp)) & (sizeof(void *) - 1))) {
void *ip = bp->ip;
buffer[i++] = ip;
stack_frame *p = bp->bp;
if (p <= bp) {
break;
}
bp = p;
}
return i;
}
static std::atomic<std::size_t> fast_backtrace_failed_cnt, backtrace_total_cnt;
double get_fast_backtrace_success_rate() {
return 1 - static_cast<double>(fast_backtrace_failed_cnt.load(std::memory_order_relaxed)) /
static_cast<double>(std::max(std::size_t(1), backtrace_total_cnt.load(std::memory_order_relaxed)));
}
#endif
static Backtrace get_backtrace() {
static __thread bool in_backtrace; // static zero-initialized
Backtrace res{{nullptr}};
if (in_backtrace) {
return res;
}
in_backtrace = true;
std::array<void *, res.size() + BACKTRACE_SHIFT + 10> tmp{{nullptr}};
std::size_t n;
#if USE_MEMPROF_SAFE
n = backtrace(tmp.data(), static_cast<int>(tmp.size()));
#else
n = fast_backtrace(tmp.data(), static_cast<int>(tmp.size()));
auto from_shared = [](void *ptr) {
return reinterpret_cast<std::uintptr_t>(ptr) > static_cast<std::uintptr_t>(0x700000000000ull);
};
#if !USE_MEMPROF_FAST
auto end = tmp.begin() + std::min(res.size() + BACKTRACE_SHIFT, n);
if (std::find_if(tmp.begin(), end, from_shared) != end) {
fast_backtrace_failed_cnt.fetch_add(1, std::memory_order_relaxed);
n = backtrace(tmp.data(), static_cast<int>(tmp.size()));
}
backtrace_total_cnt.fetch_add(1, std::memory_order_relaxed);
#endif
n = std::remove_if(tmp.begin(), tmp.begin() + n, from_shared) - tmp.begin();
#endif
n = std::min(res.size() + BACKTRACE_SHIFT, n);
for (std::size_t i = BACKTRACE_SHIFT; i < n; i++) {
res[i - BACKTRACE_SHIFT] = tmp[i];
}
in_backtrace = false;
return res;
}
static constexpr std::size_t reserved = 16;
static constexpr std::int32_t malloc_info_magic = 0x27138373;
struct malloc_info {
std::int32_t magic;
std::int32_t size;
std::int32_t ht_pos;
};
static std::uint64_t get_hash(const Backtrace &bt) {
std::uint64_t h = 7;
for (std::size_t i = 0; i < bt.size() && i < BACKTRACE_HASHED_LENGTH; i++) {
h = h * 0x4372897893428797lu + reinterpret_cast<std::uintptr_t>(bt[i]);
}
return h;
}
struct HashtableNode {
std::atomic<std::uint64_t> hash;
Backtrace backtrace;
std::atomic<std::size_t> size;
};
static constexpr std::size_t ht_max_size = 1000000;
static std::atomic<std::size_t> ht_size{0};
static std::array<HashtableNode, ht_max_size> ht;
std::size_t get_ht_size() {
return ht_size.load();
}
std::int32_t get_ht_pos(const Backtrace &bt, bool force = false) {
auto hash = get_hash(bt);
std::int32_t pos = static_cast<std::int32_t>(hash % ht.size());
bool was_overflow = false;
while (true) {
auto pos_hash = ht[pos].hash.load();
if (pos_hash == 0) {
if (ht_size > ht_max_size / 2) {
if (force) {
assert(ht_size * 10 < ht_max_size * 7);
} else {
Backtrace unknown_bt{{nullptr}};
unknown_bt[0] = reinterpret_cast<void *>(1);
return get_ht_pos(unknown_bt, true);
}
}
std::uint64_t expected = 0;
if (ht[pos].hash.compare_exchange_strong(expected, hash)) {
ht[pos].backtrace = bt;
++ht_size;
return pos;
}
} else if (pos_hash == hash) {
return pos;
} else {
pos++;
if (pos == static_cast<std::int32_t>(ht.size())) {
pos = 0;
assert(!was_overflow);
was_overflow = true;
}
}
}
}
void dump_alloc(const std::function<void(const AllocInfo &)> &func) {
for (auto &node : ht) {
if (node.size == 0) {
continue;
}
func(AllocInfo{node.backtrace, node.size.load()});
}
}
void register_xalloc(malloc_info *info, std::int32_t diff) {
if (diff > 0) {
ht[info->ht_pos].size += info->size;
} else {
ht[info->ht_pos].size -= info->size;
}
}
extern "C" {
static void *malloc_with_frame(std::size_t size, const Backtrace &frame) {
static_assert(reserved % alignof(std::max_align_t) == 0, "fail");
static_assert(reserved >= sizeof(malloc_info), "fail");
#if TD_DARWIN
static void *malloc_void = dlsym(RTLD_NEXT, "malloc");
static auto malloc_old = *reinterpret_cast<decltype(malloc) **>(&malloc_void);
#else
extern decltype(malloc) __libc_malloc;
static auto malloc_old = __libc_malloc;
#endif
auto *info = static_cast<malloc_info *>(malloc_old(size + reserved));
auto *buf = reinterpret_cast<char *>(info);
info->magic = malloc_info_magic;
info->size = static_cast<std::int32_t>(size);
info->ht_pos = get_ht_pos(frame);
register_xalloc(info, +1);
void *data = buf + reserved;
return data;
}
static malloc_info *get_info(void *data_void) {
char *data = static_cast<char *>(data_void);
auto *buf = data - reserved;
auto *info = reinterpret_cast<malloc_info *>(buf);
assert(info->magic == malloc_info_magic);
return info;
}
void *malloc(std::size_t size) {
return malloc_with_frame(size, get_backtrace());
}
void free(void *data_void) {
if (data_void == nullptr) {
return;
}
auto *info = get_info(data_void);
register_xalloc(info, -1);
#if TD_DARWIN
static void *free_void = dlsym(RTLD_NEXT, "free");
static auto free_old = *reinterpret_cast<decltype(free) **>(&free_void);
#else
extern decltype(free) __libc_free;
static auto free_old = __libc_free;
#endif
return free_old(info);
}
void *calloc(std::size_t size_a, std::size_t size_b) {
auto size = size_a * size_b;
void *res = malloc_with_frame(size, get_backtrace());
std::memset(res, 0, size);
return res;
}
void *realloc(void *ptr, std::size_t size) {
if (ptr == nullptr) {
return malloc_with_frame(size, get_backtrace());
}
auto *info = get_info(ptr);
auto *new_ptr = malloc_with_frame(size, get_backtrace());
auto to_copy = std::min(static_cast<std::int32_t>(size), info->size);
std::memcpy(new_ptr, ptr, to_copy);
free(ptr);
return new_ptr;
}
void *memalign(std::size_t aligment, std::size_t size) {
assert(false && "Memalign is unsupported");
return nullptr;
}
}
// c++14 guarantees than it is enough to override this two operators.
void *operator new(std::size_t count) {
return malloc_with_frame(count, get_backtrace());
}
void operator delete(void *ptr) noexcept(true) {
free(ptr);
}
// because of gcc warning: the program should also define 'void operator delete(void*, std::size_t)'
void operator delete(void *ptr, std::size_t) noexcept(true) {
free(ptr);
}
// c++17
// void *operator new(std::size_t count, std::align_val_t al);
// void operator delete(void *ptr, std::align_val_t al);
#else
bool is_memprof_on() {
return false;
}
void dump_alloc(const std::function<void(const AllocInfo &)> &func) {
}
double get_fast_backtrace_success_rate() {
return 0;
}
std::size_t get_ht_size() {
return 0;
}
#endif
std::size_t get_used_memory_size() {
std::size_t res = 0;
dump_alloc([&](const auto info) { res += info.size; });
return res;
}

27
memprof/memprof.h Normal file
View File

@ -0,0 +1,27 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include <array>
#include <cstddef>
#include <functional>
constexpr std::size_t BACKTRACE_SHIFT = 2;
constexpr std::size_t BACKTRACE_HASHED_LENGTH = 6;
constexpr std::size_t BACKTRACE_LENGTH = 10;
using Backtrace = std::array<void *, BACKTRACE_LENGTH>;
struct AllocInfo {
Backtrace backtrace;
std::size_t size;
};
bool is_memprof_on();
std::size_t get_ht_size();
double get_fast_backtrace_success_rate();
void dump_alloc(const std::function<void(const AllocInfo &)> &func);
std::size_t get_used_memory_size();

47
sqlite/CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(SQLITE_SOURCE
sqlite/sqlite3.c
sqlite/sqlite3.h
sqlite/sqlite3ext.h
sqlite/sqlite3session.h
)
add_library(tdsqlite STATIC ${SQLITE_SOURCE})
target_include_directories(tdsqlite PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_include_directories(tdsqlite SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(tdsqlite PRIVATE ${OPENSSL_CRYPTO_LIBRARY})
target_compile_definitions(tdsqlite PRIVATE
-DSQLITE_DEFAULT_MEMSTATUS=0 #-DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE
)
target_compile_definitions(tdsqlite PRIVATE -DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2 -DSQLITE_ENABLE_FTS5 -DSQLITE_DISABLE_LFS)
if (NOT WIN32)
target_link_libraries(tdsqlite PRIVATE dl z)
target_compile_definitions(tdsqlite PRIVATE -DHAVE_USLEEP -DNDEBUG=1)
endif()
if (GCC OR CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations -Wno-unused-variable -Wno-unused-const-variable -Wno-unused-function")
if (CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-parentheses-equality -Wno-unused-value")
endif()
elseif (MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996")
endif()
if (UWP)
target_compile_options(tdsqlite PRIVATE /DSQLITE_OS_WINRT=1)
endif()
install(TARGETS tdsqlite EXPORT TdTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)

24
sqlite/sqlite/LICENSE Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2008, ZETETIC LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the ZETETIC LLC nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

202634
sqlite/sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

10371
sqlite/sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

560
sqlite/sqlite/sqlite3ext.h Normal file
View File

@ -0,0 +1,560 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */

File diff suppressed because it is too large Load Diff

1
src.ps1 Normal file
View File

@ -0,0 +1 @@
git ls-tree -r HEAD --name-only benchmark memprof td tdactor tddb tdnet tdtl tdutils test tg_http_client | Select-String "\.cpp$|\.h$|\.hpp$" | Select-String -NotMatch "third_party"

2
src.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
git ls-tree -r HEAD --name-only benchmark memprof td tdactor tddb tdnet tdtl tdutils test tg_http_client | grep -E "\.cpp$|\.h$|\.hpp$" | grep -v third_party

120
td/generate/CMakeLists.txt Normal file
View File

@ -0,0 +1,120 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
file(MAKE_DIRECTORY auto/td/telegram)
file(MAKE_DIRECTORY auto/td/mtproto)
set(TL_TD_AUTO_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/auto PARENT_SCOPE)
set(TL_TD_AUTO
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/mtproto/mtproto_api.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/mtproto/mtproto_api.h
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/mtproto/mtproto_api.hpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_api.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_api.h
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_api.hpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/telegram_api.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/telegram_api.h
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/telegram_api.hpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/secret_api.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/secret_api.h
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/secret_api.hpp
PARENT_SCOPE
)
set(TL_TD_JSON_AUTO
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_api_json.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_api_json.h
PARENT_SCOPE
)
set(TL_TD_API_TLO ${CMAKE_CURRENT_SOURCE_DIR}/scheme/td_api.tlo PARENT_SCOPE)
set(TL_C_AUTO
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_tdc_api.cpp
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_tdc_api.h
${CMAKE_CURRENT_SOURCE_DIR}/auto/td/telegram/td_tdc_api_inner.h
PARENT_SCOPE
)
set(TL_GENERATE_COMMON_SOURCE
generate_common.cpp
tl_writer_cpp.cpp
tl_writer_h.cpp
tl_writer_hpp.cpp
tl_writer_jni_cpp.cpp
tl_writer_jni_h.cpp
tl_writer_td.cpp
tl_writer_cpp.h
tl_writer_h.h
tl_writer_hpp.h
tl_writer_jni_cpp.h
tl_writer_jni_h.h
tl_writer_td.h
)
set(TL_GENERATE_C_SOURCE
generate_c.cpp
tl_writer_c.h
)
set(TL_GENERATE_JAVA_SOURCE
generate_java.cpp
tl_writer_java.cpp
tl_writer_java.h
)
set(TL_GENERATE_JSON_SOURCE
generate_json.cpp
tl_json_converter.cpp
tl_json_converter.h
)
if (NOT CMAKE_CROSSCOMPILING)
find_program(PHP_EXECUTABLE php)
if (PHP_EXECUTABLE AND NOT TD_API_JAVA_PACKAGE)
set(GENERATE_COMMON_CMD generate_common && ${PHP_EXECUTABLE} DoxygenTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/td_api.h)
else()
set(GENERATE_COMMON_CMD generate_common)
endif()
add_executable(generate_common ${TL_GENERATE_COMMON_SOURCE})
target_link_libraries(generate_common PRIVATE tdtl)
add_custom_target(tl_generate_common
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GENERATE_COMMON_CMD}
COMMENT "Generate common tl source files"
DEPENDS generate_common scheme/mtproto_api.tlo scheme/telegram_api.tlo scheme/secret_api.tlo scheme/td_api.tlo
)
if (TD_API_JAVA_PACKAGE)
target_compile_definitions(generate_common PRIVATE TD_API_JAVA_PACKAGE=\"${TD_API_JAVA_PACKAGE}\")
endif()
add_executable(generate_c ${TL_GENERATE_C_SOURCE})
target_link_libraries(generate_c PRIVATE tdtl)
add_custom_target(tl_generate_c
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND generate_c
COMMENT "Generate C tl source files"
DEPENDS generate_c scheme/td_api.tlo
)
add_executable(generate_java ${TL_GENERATE_JAVA_SOURCE})
target_link_libraries(generate_java PRIVATE tdtl)
add_executable(generate_json ${TL_GENERATE_JSON_SOURCE})
target_link_libraries(generate_json PRIVATE tdtl tdutils)
add_custom_target(tl_generate_json
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND generate_json
COMMENT "Generate JSON tl source files"
DEPENDS generate_json scheme/td_api.tlo
)
endif()

View File

@ -0,0 +1,362 @@
<?php
require_once 'TlDocumentationGenerator.php';
class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator
{
private function getParameterTypeName($type)
{
switch ($type) {
case 'Bool':
return 'bool ';
case 'int32':
return 'std::int32_t ';
case 'int53':
case 'int64':
return 'std::int64_t ';
case 'double':
return 'double ';
case 'string':
case 'bytes':
return 'std::string const &';
default:
if (substr($type, 0, 6) === 'vector') {
if ($type[6] !== '<' || $type[strlen($type) - 1] !== '>') {
return '';
}
return 'std::vector<'.$this->getTypeName(substr($type, 7, -1)).'> &&';
}
if (preg_match('/[^A-Za-z0-9.]/', $type)) {
return '';
}
return 'object_ptr<'.$this->getClassName($type).'> &&';
}
}
protected function escapeDocumentation($doc)
{
$doc = htmlspecialchars($doc);
$doc = str_replace('*/', '*&#47;', $doc);
$doc = str_replace('#', '\#', $doc);
return $doc;
}
protected function getFieldName($name)
{
if (substr($name, 0, 6) === 'param_') {
$name = substr($name, 6);
}
return $name.'_';
}
protected function getClassName($type)
{
return implode(explode('.', trim($type, "\n ;")));
}
protected function getTypeName($type)
{
switch ($type) {
case 'Bool':
return 'bool';
case 'int32':
return 'std::int32_t';
case 'int53':
case 'int64':
return 'std::int64_t';
case 'double':
return 'double';
case 'string':
case 'bytes':
return 'std::string';
case 'bool':
case 'int':
case 'long':
case 'Int':
case 'Long':
case 'Int32':
case 'Int53':
case 'Int64':
case 'Double':
case 'String':
case 'Bytes':
$this->printError("Wrong type $type");
return '';
default:
if (substr($type, 0, 6) === 'vector') {
if ($type[6] !== '<' || $type[strlen($type) - 1] !== '>') {
$this->printError("Wrong vector subtype in $type");
return '';
}
return 'std::vector<'.$this->getTypeName(substr($type, 7, -1)).'>';
}
if (preg_match('/[^A-Za-z0-9.]/', $type)) {
$this->printError("Wrong type $type");
return '';
}
return 'object_ptr<'.$this->getClassName($type).'>';
}
}
protected function getBaseClassName($is_function)
{
return $is_function ? 'Function' : 'Object';
}
protected function needRemoveLine($line)
{
$line = trim($line);
return strpos($line, '/**') === 0 || strpos($line, '*') === 0 || strpos($line, '///') === 0;
}
protected function needSkipLine($line)
{
$tline = trim($line);
return empty($tline) || $tline[0] == '}' || $tline == 'public:' || strpos($line, '#pragma ') === 0 ||
strpos($line, '#include <') === 0 || strpos($tline, 'return ') === 0 || strpos($tline, 'namespace') === 0 ||
preg_match('/class [A-Za-z0-9_]*;/', $line) || $tline === 'if (value == nullptr) {';
}
protected function isHeaderLine($line)
{
return strpos($line, 'template <') === 0;
}
protected function extractClassName($line)
{
if (strpos($line, 'class ') === 0) {
return explode(' ', trim($line))[1];
}
return '';
}
protected function fixLine($line)
{
if (strpos($line, 'ID = ') > 0 || strpos($line, 'ReturnType = ') > 0) {
return substr($line, 0, strpos($line, '='));
}
return $line;
}
protected function addGlobalDocumentation()
{
$this->addDocumentation('#include "td/tl/TlObject.h"', <<<EOT
/**
* \\file
* Contains declarations of all functions and types which represent a public TDLib interface.
*/
EOT
);
$this->addDocumentation('using BaseObject = ::td::TlObject;', <<<EOT
/**
* This class is a base class for all TDLib API classes and functions.
*/
EOT
);
$this->addDocumentation('using object_ptr = ::td::tl_object_ptr<Type>;', <<<EOT
/**
* A smart wrapper to store a pointer to a TDLib API object. Can be treated as an analogue of std::unique_ptr.
*/
EOT
);
$this->addDocumentation('object_ptr<Type> make_object(Args &&... args) {', <<<EOT
/**
* A function to create a dynamically allocated TDLib API object. Can be treated as an analogue of std::make_unique.
* Usage example:
* \\code
* auto get_authorization_state_request = td::td_api::make_object<td::td_api::getAuthorizationState>();
* auto send_message_request = td::td_api::make_object<td::td_api::sendMessage>(chat_id, 0, false, false, nullptr,
* td::td_api::make_object<td::td_api::inputMessageText>("Hello, world!!!", false, true, {}, nullptr));
* \\endcode
*
* \\tparam Type Type of an object to construct.
* \\param[in] args Arguments to pass to the object constructor.
* \\return Wrapped pointer to the created object.
*/
EOT
);
$this->addDocumentation('object_ptr<ToType> move_object_as(FromType &&from) {', <<<EOT
/**
* A function to cast a wrapped in td::td_api::object_ptr TDLib API object to its subclass or superclass.
* Casting an object to an incorrect type will lead to undefined bejaviour.
* Usage example:
* \\code
* td::tl_object_ptr<td::td_api::callState> call_state = ...;
* switch (call_state->get_id()) {
* case td::td_api::callStatePending::ID: {
* auto state = td::move_tl_object_as<td::td_api::callStatePending>(call_state);
* // use state
* break;
* }
* case td::td_api::callStateExchangingKeys::ID: {
* // no additional fields, no casting is needed
* break;
* }
* case td::td_api::callStateReady::ID: {
* auto state = td::move_tl_object_as<td::td_api::callStateReady>(call_state);
* // use state
* break;
* }
* case td::td_api::callStateHangingUp::ID: {
* // no additional fields, no casting is needed
* break;
* }
* case td::td_api::callStateDiscarded::ID: {
* auto state = td::move_tl_object_as<td::td_api::callStateDiscarded>(call_state);
* // use state
* break;
* }
* case td::td_api::callStateError::ID: {
* auto state = td::move_tl_object_as<td::td_api::callStateError>(call_state);
* // use state
* break;
* }
* default:
* assert(false);
* }
* \\endcode
*
* \\tparam ToType Type of a TDLib API object to move to.
* \\tparam FromType Type of a TDLib API object to move from, this is auto-deduced.
* \\param[in] from Wrapped in td::td_api::object_ptr pointer to a TDLib API object.
*/
EOT
);
$this->addDocumentation('std::string to_string(const BaseObject &value);', <<<EOT
/**
* Returns a string representation of the TDLib API object.
* \\param[in] value The object.
* \\return Object string representation.
*/
EOT
);
$this->addDocumentation('std::string to_string(const object_ptr<T> &value) {', <<<EOT
/**
* Returns a string representation of the TDLib API object.
* \\tparam T Object type, auto-deduced.
* \\param[in] value The object.
* \\return Object string representation.
*/
EOT
);
$this->addDocumentation(' void store(TlStorerToString &s, const char *field_name) const final;', <<<EOT
/**
* Helper function for to_string method. Appends string representation of the object to the storer.
* \\param[in] s Storer to which object string representation will be appended.
* \\param[in] field_name Object field_name if applicable.
*/
EOT
);
$this->addDocumentation('class Object: public TlObject {', <<<EOT
/**
* This class is a base class for all TDLib API classes.
*/
EOT
);
$this->addDocumentation('class Function: public TlObject {', <<<EOT
/**
* This class is a base class for all TDLib API functions.
*/
EOT
);
$this->addDocumentation(' static const std::int32_t ID', <<<EOT
/// Identifier uniquely determining a type of the object.
EOT
);
$this->addDocumentation(' std::int32_t get_id() const final {', <<<EOT
/**
* Returns identifier uniquely determining a type of the object.
* \\return this->ID.
*/
EOT
);
$this->addDocumentation(' using ReturnType', <<<EOT
/// Typedef for the type returned by the function.
EOT
);
}
protected function addAbstractClassDocumentation($class_name, $documentation)
{
$this->addDocumentation("class $class_name: public Object {", <<<EOT
/**
* This class is an abstract base class.
* $documentation
*/
EOT
);
}
protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type)
{
$return_type_description = $return_type ? "\n *\n * Returns $return_type." : '';
$this->addDocumentation("class $class_name final : public $base_class_name {", <<<EOT
/**
* $description$return_type_description
*/
EOT
);
}
protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null)
{
$this->addDocumentation($class_name." $type_name $field_name;", <<<EOT
/// $field_info
EOT
);
}
protected function addDefaultConstructorDocumentation($class_name)
{
$this->addDocumentation(" $class_name();", <<<EOT
/**
* Default constructor. All fields will be value-initilaized.
*/
EOT
);
}
protected function addFullConstructorDocumentation($class_name, $known_fields, $info)
{
$explicit = count($known_fields) == 1 ? 'explicit ' : '';
$full_constructor = " $explicit$class_name(";
$colon = '';
foreach ($known_fields as $name => $type) {
$full_constructor .= $colon.$this->getParameterTypeName($type).$this->getFieldName($name);
$colon = ', ';
}
$full_constructor .= ');';
$full_doc = <<<EOT
/**
* Constructor for initialization of all fields.
*
EOT;
foreach ($known_fields as $name => $type) {
$full_doc .= ' * \\param[in] '.$this->getFieldName($name).' '.$info[$name]."\n";
}
$full_doc .= ' */';
$this->addDocumentation($full_constructor, $full_doc);
}
}
$generator = new DoxygenTlDocumentationGenerator();
$generator->generate($argv[1], $argv[2]);

View File

@ -0,0 +1,262 @@
<?php
require_once 'TlDocumentationGenerator.php';
class JavadocTlDocumentationGenerator extends TlDocumentationGenerator
{
private $nullable_type;
private $nullable_annotation;
private $java_version;
protected function escapeDocumentation($doc)
{
$doc = htmlspecialchars($doc);
$doc = str_replace('*/', '*&#47;', $doc);
$doc = preg_replace_callback('/_([A-Za-z])/', function ($matches) {return strtoupper($matches[1]);}, $doc);
return $doc;
}
protected function getFieldName($name)
{
if (substr($name, 0, 6) === 'param_') {
$name = substr($name, 6);
}
return preg_replace_callback('/_([A-Za-z])/', function ($matches) {return strtoupper($matches[1]);}, trim($name));
}
protected function getClassName($type)
{
return implode(array_map(ucfirst, explode('.', trim($type, "\n ;"))));
}
protected function getTypeName($type)
{
switch ($type) {
case 'Bool':
return 'boolean';
case 'int32':
return 'int';
case 'int53':
case 'int64':
return 'long';
case 'double':
return $type;
case 'string':
return 'String';
case 'bytes':
return 'byte[]';
case 'bool':
case 'int':
case 'long':
case 'Int':
case 'Long':
case 'Int32':
case 'Int53':
case 'Int64':
case 'Double':
case 'String':
case 'Bytes':
$this->printError("Wrong type $type");
return '';
default:
if (substr($type, 0, 6) === 'vector') {
if ($type[6] !== '<' || $type[strlen($type) - 1] !== '>') {
$this->printError("Wrong vector subtype in $type");
return '';
}
return $this->getTypeName(substr($type, 7, -1)).'[]';
}
if (preg_match('/[^A-Za-z0-9.]/', $type)) {
$this->printError("Wrong type $type");
return '';
}
return $this->getClassName($type);
}
}
protected function getBaseClassName($is_function)
{
return $is_function ? 'Function' : 'Object';
}
protected function needRemoveLine($line)
{
return strpos(trim($line), '/**') === 0 || strpos(trim($line), '*') === 0 ||
($this->nullable_type && strpos($line, $this->nullable_type) > 0);
}
protected function needSkipLine($line)
{
$line = trim($line);
return strpos($line, 'public') !== 0 && !($this->nullable_type && $line == 'import java.util.Arrays;') &&
!$this->isHeaderLine($line);
}
protected function isHeaderLine($line)
{
return trim($line) === '@Override';
}
protected function extractClassName($line)
{
if (strpos($line, 'public static class ') > 0) {
return explode(' ', trim($line))[3];
}
return '';
}
protected function fixLine($line)
{
if (strpos($line, 'CONSTRUCTOR = ') > 0) {
return substr($line, 0, strpos($line, '='));
}
return $this->nullable_annotation ? str_replace($this->nullable_annotation.' ', '', $line) : $line;
}
protected function addGlobalDocumentation()
{
$this->addDocumentation('public class TdApi {', <<<EOT
/**
* This class contains as static nested classes all other TDLib interface
* type-classes and function-classes.
* <p>
* It has no inner classes, functions or public members.
*/
EOT
);
$this->addDocumentation(' public abstract static class Object {', <<<EOT
/**
* This class is a base class for all TDLib interface classes.
*/
EOT
);
$this->addDocumentation(' public abstract int getConstructor();', <<<EOT
/**
* @return identifier uniquely determining type of the object.
*/
EOT
);
$this->addDocumentation(' public String toString() {', <<<EOT
/**
* @return string representation of the object.
*/
EOT
);
$this->addDocumentation(' public abstract static class Function extends Object {', <<<EOT
/**
* This class is a base class for all TDLib interface function-classes.
*/
EOT
);
$this->addDocumentation(' public static final int CONSTRUCTOR', <<<EOT
/**
* Identifier uniquely determining type of the object.
*/
EOT
);
$this->addDocumentation(' public int getConstructor() {', <<<EOT
/**
* @return this.CONSTRUCTOR
*/
EOT
);
if ($this->nullable_type) {
$import = 'import java.util.Arrays;';
$this->addDocumentation($import, '');
$this->addLineReplacement($import, "import $this->nullable_type;\n$import\n");
}
}
protected function addAbstractClassDocumentation($class_name, $documentation)
{
$this->addDocumentation(" public abstract static class $class_name extends Object {", <<<EOT
/**
* This class is an abstract base class.
* $documentation
*/
EOT
);
}
protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type)
{
$return_type_description = $return_type ? "\n *\n * <p> Returns {@link $return_type $return_type} </p>" : '';
$this->addDocumentation(" public static class $class_name extends $base_class_name {", <<<EOT
/**
* $description$return_type_description
*/
EOT
);
}
protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null)
{
$full_line = $class_name." public $type_name $field_name;";
$this->addDocumentation($full_line, <<<EOT
/**
* $field_info
*/
EOT
);
if ($may_be_null && $this->nullable_annotation && ($this->java_version >= 8 || substr($type_name, -1) != ']')) {
$this->addLineReplacement($full_line, " public $this->nullable_annotation $type_name $field_name;\n");
}
}
protected function addDefaultConstructorDocumentation($class_name)
{
$this->addDocumentation(" public $class_name() {", <<<EOT
/**
* Default constructor.
*/
EOT
);
}
protected function addFullConstructorDocumentation($class_name, $known_fields, $info)
{
$explicit = count($known_fields) == 1 ? 'explicit ' : '';
$full_constructor = " public $class_name(";
$colon = '';
foreach ($known_fields as $name => $type) {
$full_constructor .= $colon.$this->getTypeName($type).' '.$this->getFieldName($name);
$colon = ', ';
}
$full_constructor .= ') {';
$full_doc = <<<EOT
/**
* Constructor for initialization of all fields.
*
EOT;
foreach ($known_fields as $name => $type) {
$full_doc .= ' * @param '.$this->getFieldName($name).' '.$info[$name]."\n";
}
$full_doc .= ' */';
$this->addDocumentation($full_constructor, $full_doc);
}
public function __construct($nullable_type, $nullable_annotation, $java_version) {
$this->nullable_type = trim($nullable_type);
$this->nullable_annotation = trim($nullable_annotation);
$this->java_version = intval($java_version);
}
}
$nullable_type = isset($argv[3]) ? $argv[3] : '';
$nullable_annotation = isset($argv[4]) ? $argv[4] : '';
$java_version = isset($argv[5]) ? intval($argv[5]) : 7;
$generator = new JavadocTlDocumentationGenerator($nullable_type, $nullable_annotation, $java_version);
$generator->generate($argv[1], $argv[2]);

View File

@ -0,0 +1,299 @@
<?php
abstract class TlDocumentationGenerator
{
private $current_line = '';
private $documentation = array();
private $line_replacement = array();
final protected function printError($error)
{
fwrite(STDERR, "$error near line \"".rtrim($this->current_line)."\"\n");
}
final protected function addDocumentation($code, $doc) {
if (isset($this->documentation[$code])) {
$this->printError("Duplicate documentation for \"$code\"");
}
$this->documentation[$code] = $doc;
}
final protected function addLineReplacement($line, $new_line) {
if (isset($this->line_replacement[$line])) {
$this->printError("Duplicate line replacement for \"$line\"");
}
$this->line_replacement[$line] = $new_line;
}
final protected function addDot($str) {
if (!$str) {
return '';
}
$len = strlen($str);
if ($str[$len - 1] === '.') {
return $str;
}
if ($str[$len - 1] === ')') {
// trying to place dot inside the brackets
$bracket_count = 1;
for ($pos = $len - 2; $pos >= 0; $pos--) {
if ($str[$pos] === ')') {
$bracket_count++;
}
if ($str[$pos] === '(') {
$bracket_count--;
if ($bracket_count === 0) {
break;
}
}
}
if ($bracket_count === 0) {
if (ctype_upper($str[$pos + 1])) {
return substr($str, 0, -1).'.)';
}
} else {
$this->printError("Unmatched bracket");
}
}
return $str.'.';
}
abstract protected function escapeDocumentation($doc);
abstract protected function getFieldName($name);
abstract protected function getClassName($name);
abstract protected function getTypeName($type);
abstract protected function getBaseClassName($is_function);
abstract protected function needRemoveLine($line);
abstract protected function needSkipLine($line);
abstract protected function isHeaderLine($line);
abstract protected function extractClassName($line);
abstract protected function fixLine($line);
abstract protected function addGlobalDocumentation();
abstract protected function addAbstractClassDocumentation($class_name, $value);
abstract protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type);
abstract protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null);
abstract protected function addDefaultConstructorDocumentation($class_name);
abstract protected function addFullConstructorDocumentation($class_name, $known_fields, $info);
public function generate($tl_scheme_file, $source_file)
{
$lines = array_filter(array_map('trim', file($tl_scheme_file)));
$description = '';
$current_class = '';
$is_function = false;
$need_class_description = false;
$this->addGlobalDocumentation();
foreach ($lines as $line) {
$this->current_line = $line;
if ($line === '---types---') {
$is_function = false;
} elseif ($line === '---functions---') {
$is_function = true;
$current_class = '';
$need_class_description = false;
} elseif ($line[0] === '/') {
if ($line[1] !== '/') {
$this->printError('Wrong comment');
continue;
}
if ($line[2] === '@' || $line[2] === '-') {
$description .= trim(substr($line, 2 + intval($line[2] === '-'))).' ';
} else {
$this->printError('Unexpected comment');
}
} elseif (strpos($line, '? =') || strpos($line, ' = Vector t;') || $line === 'boolFalse = Bool;' ||
$line === 'boolTrue = Bool;' || $line === 'bytes = Bytes;' || $line === 'int32 = Int32;' ||
$line === 'int53 = Int53;'|| $line === 'int64 = Int64;') {
// skip built-in types
continue;
} else {
$description = trim($description);
if ($description[0] !== '@') {
$this->printError('Wrong description begin');
}
$docs = explode('@', $description);
array_shift($docs);
$info = array();
foreach ($docs as $doc) {
list($key, $value) = explode(' ', $doc, 2);
$value = trim($value);
if ($need_class_description) {
if ($key === 'description') {
$need_class_description = false;
$value = $this->addDot($value);
$this->addAbstractClassDocumentation($current_class, $value);
continue;
} else {
$this->printError('Expected abstract class description');
}
}
if ($key === 'class') {
$current_class = $this->getClassName($value);
$need_class_description = true;
if ($is_function) {
$this->printError('Unexpected class definition');
}
} else {
if (isset($info[$key])) {
$this->printError("Duplicate info about `$key`");
}
$info[$key] = trim($value);
}
}
if (substr_count($line, '=') !== 1) {
$this->printError("Wrong '=' count");
continue;
}
list($fields, $type) = explode('=', $line);
$type = $this->getClassName($type);
$fields = explode(' ', trim($fields));
$class_name = $this->getClassName(array_shift($fields));
if ($type !== $current_class) {
$current_class = '';
$need_class_description = false;
}
if (!$is_function) {
$type_lower = strtolower($type);
$class_name_lower = strtolower($class_name);
if (empty($current_class) == ($type_lower !== $class_name_lower)) {
$this->printError('Wrong constructor name');
}
if (strpos($class_name_lower, $type_lower) !== 0) {
// $this->printError('Wrong constructor name');
}
}
$known_fields = array();
foreach ($fields as $field) {
list ($field_name, $field_type) = explode(':', $field);
if (isset($info['param_'.$field_name])) {
$known_fields['param_'.$field_name] = $field_type;
continue;
}
if (isset($info[$field_name])) {
$known_fields[$field_name] = $field_type;
continue;
}
$this->printError("Have no info about field `$field_name`");
}
foreach ($info as $name => $value) {
if (!$value) {
$this->printError("info[$name] for $class_name is empty");
} elseif ($value[0] < 'A' || $value[0] > 'Z') {
$this->printError("info[$name] for $class_name doesn't begins with capital letter");
}
}
foreach (array_diff_key($info, $known_fields) as $field_name => $field_info) {
if ($field_name !== 'description') {
$this->printError("Have info about unexisted field `$field_name`");
}
}
if (!$info['description']) {
$this->printError("Have no description for class `$class_name`");
}
foreach ($info as &$v) {
$v = $this->escapeDocumentation($this->addDot($v));
}
$base_class_name = $current_class ?: $this->getBaseClassName($is_function);
$this->addClassDocumentation($class_name, $base_class_name, $info['description'], $is_function ? $this->getTypeName($type) : '');
foreach ($known_fields as $name => $type) {
$may_be_null = stripos($info[$name], 'may be null') !== false;
$this->addFieldDocumentation($class_name, $this->getFieldName($name), $this->getTypeName($type), $info[$name], $may_be_null);
}
$this->addDefaultConstructorDocumentation($class_name);
if ($known_fields) {
$this->addFullConstructorDocumentation($class_name, $known_fields, $info);
}
$description = '';
}
}
$lines = file($source_file);
$result = '';
$current_class = '';
$current_headers = '';
foreach ($lines as $line) {
$this->current_line = $line;
if ($this->needRemoveLine($line)) {
continue;
}
if ($this->needSkipLine($line)) {
$result .= $current_headers.$line;
$current_headers = '';
continue;
}
if ($this->isHeaderLine($line)) {
$current_headers .= $line;
continue;
}
$current_class = $this->extractClassName($line) ?: $current_class;
$fixed_line = rtrim($this->fixLine($line));
$doc = '';
if (isset($this->documentation[$fixed_line])) {
$doc = $this->documentation[$fixed_line];
} elseif (isset($this->documentation[$current_class.$fixed_line])) {
$doc = $this->documentation[$current_class.$fixed_line];
} else {
$this->printError('Have no docs for "'.$fixed_line.'"');
}
if ($doc) {
$result .= $doc."\n";
}
if (isset($this->line_replacement[$fixed_line])) {
$line = $this->line_replacement[$fixed_line];
} elseif (isset($this->line_replacement[$current_class.$fixed_line])) {
$line = $this->line_replacement[$current_class.$fixed_line];
}
$result .= $current_headers.$line;
$current_headers = '';
}
if (file_get_contents($source_file) !== $result) {
file_put_contents($source_file, $result);
}
}
}

View File

@ -0,0 +1,20 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_c.h"
#include "td/tl/tl_config.h"
#include "td/tl/tl_generate.h"
int main() {
td::tl::tl_config config_td = td::tl::read_tl_config_from_file("scheme/td_api.tlo");
td::tl::write_tl_to_file(config_td, "auto/td/telegram/td_tdc_api.h",
td::TlWriterCCommon("TdApi", 1, "#include \"td/telegram/td_api.h\"\n"));
td::tl::write_tl_to_file(config_td, "auto/td/telegram/td_tdc_api_inner.h",
td::TlWriterCCommon("TdApi", -1, "#include \"td/telegram/td_api.h\"\n"));
td::tl::write_tl_to_file(config_td, "auto/td/telegram/td_tdc_api.cpp",
td::TlWriterCCommon("TdApi", 0, "#include \"td/telegram/td_api.h\"\n"));
}

View File

@ -0,0 +1,55 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_cpp.h"
#include "tl_writer_h.h"
#include "tl_writer_hpp.h"
#include "tl_writer_jni_cpp.h"
#include "tl_writer_jni_h.h"
#include "td/tl/tl_config.h"
#include "td/tl/tl_generate.h"
#include <algorithm>
#include <string>
#include <vector>
static void generate_cpp(const std::string &directory, const std::string &tl_name, const std::string &string_type,
const std::string &bytes_type, const std::vector<std::string> &ext_cpp_includes,
const std::vector<std::string> &ext_h_includes) {
std::string path = directory + "/" + tl_name;
td::tl::tl_config config = td::tl::read_tl_config_from_file("scheme/" + tl_name + ".tlo");
td::tl::write_tl_to_file(config, path + ".cpp",
td::TD_TL_writer_cpp(tl_name, string_type, bytes_type, ext_cpp_includes));
td::tl::write_tl_to_file(config, path + ".h", td::TD_TL_writer_h(tl_name, string_type, bytes_type, ext_h_includes));
td::tl::write_tl_to_file(config, path + ".hpp", td::TD_TL_writer_hpp(tl_name, string_type, bytes_type));
}
int main() {
generate_cpp("auto/td/telegram", "telegram_api", "std::string", "BufferSlice",
{"\"td/tl/tl_object_parse.h\"", "\"td/tl/tl_object_store.h\""}, {"\"td/utils/buffer.h\""});
generate_cpp("auto/td/telegram", "secret_api", "std::string", "BufferSlice",
{"\"td/tl/tl_object_parse.h\"", "\"td/tl/tl_object_store.h\""}, {"\"td/utils/buffer.h\""});
generate_cpp("auto/td/mtproto", "mtproto_api", "Slice", "Slice",
{"\"td/tl/tl_object_parse.h\"", "\"td/tl/tl_object_store.h\""}, {"\"td/utils/Slice.h\""});
#ifdef TD_API_JAVA_PACKAGE
td::tl::tl_config config = td::tl::read_tl_config_from_file("scheme/td_api.tlo");
std::string path = "auto/td/telegram/td_api";
std::string package = TD_API_JAVA_PACKAGE;
std::replace(package.begin(), package.end(), '/', '.');
td::tl::write_tl_to_file(
config, path + ".cpp",
td::TD_TL_writer_jni_cpp("td_api", "std::string", "std::string", {"\"td/tl/tl_jni_object.h\""}, package));
td::tl::write_tl_to_file(config, path + ".h",
td::TD_TL_writer_jni_h("td_api", "std::string", "std::string", {"<string>"}));
td::tl::write_tl_to_file(config, path + ".hpp", td::TD_TL_writer_hpp("td_api", "std::string", "std::string"));
#else
generate_cpp("auto/td/telegram", "td_api", "std::string", "std::string", {}, {"<string>"});
#endif
}

View File

@ -0,0 +1,29 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_java.h"
#include "td/tl/tl_config.h"
#include "td/tl/tl_generate.h"
#include <algorithm>
#include <string>
int main(int argc, char *argv[]) {
if (argc < 5) {
return 1;
}
std::string api_name = argv[1];
std::string source = argv[2];
std::string destination = argv[3];
std::string package = argv[4];
std::string package_name = package;
std::replace(package_name.begin(), package_name.end(), '/', '.');
destination += "/" + package + "/" + api_name + ".java";
td::tl::write_tl_to_file(td::tl::read_tl_config_from_file(source), destination,
td::TD_TL_writer_java(api_name, package_name));
}

View File

@ -0,0 +1,14 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_json_converter.h"
#include "td/tl/tl_config.h"
#include "td/tl/tl_generate.h"
int main() {
td::gen_json_converter(td::tl::read_tl_config_from_file("scheme/td_api.tlo"), "td/telegram/td_api_json");
}

View File

@ -0,0 +1,91 @@
int ? = Int;
long ? = Long;
double ? = Double;
string ? = String;
dummyHttpWait = HttpWait;
vector {t:Type} # [ t ] = Vector t;
int128 4*[ int ] = Int128;
int256 8*[ int ] = Int256;
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int
= BindAuthKeyInner;
//rpc_result#f35c6d01 req_msg_id:long result:string = RpcResult;
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
pong#347773c5 msg_id:long ping_id:long = Pong;
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
//msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
//message msg_id:long seqno:int bytes:int body:string = Message;
//msg_copy#e06046b2 orig_message:Message = MessageCopy;
gzip_packed#3072cfa1 packed_data:string = GzipPacked;
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
rsa_public_key n:string e:string = RSAPublicKey;
---functions---
req_pq#60469778 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts;
ping#7abe77ec ping_id:long = Pong;
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
destroy_session#e7512126 session_id:long = DestroySessionRes;
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
//test.useGzipPacked = GzipPacked;
//test.useServerDhInnerData = Server_DH_inner_data;
//test.useNewSessionCreated = NewSession;
//test.useMsgsAck = MsgsAck;
//test.useBadMsgNotification = BadMsgNotification;
//test.useOther key:rsa_public_key p_q_data:P_Q_inner_data dh_data:client_DH_inner_data = RpcError;

Binary file not shown.

View File

@ -0,0 +1,113 @@
int ? = Int;
long ? = Long;
double ? = Double;
string ? = String;
bytes = Bytes;
boolFalse = Bool;
boolTrue = Bool;
true#3fedd339 = True;
vector {t:Type} # [ t ] = Vector t;
decryptedMessage8#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService8#aa48327d random_id:long random_bytes:bytes action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaEmpty#89f5c4a = DecryptedMessageMedia;
decryptedMessageMediaPhoto23#32798a8c thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaVideo8#4cee6ef3 thumb:bytes thumb_w:int thumb_h:int duration:int w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaGeoPoint#35480a59 lat:double long:double = DecryptedMessageMedia;
decryptedMessageMediaContact#588a0a97 phone_number:string first_name:string last_name:string user_id:int = DecryptedMessageMedia;
decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction;
decryptedMessageMediaDocument23#b095434b thumb:bytes thumb_w:int thumb_h:int file_name:string mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio8#6080758f duration:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageActionReadMessages#c4f40be random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionDeleteMessages#65614304 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionScreenshotMessages#8ac1f475 random_ids:Vector<long> = DecryptedMessageAction;
decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction;
// layer 23
decryptedMessage23#204d3878 random_id:long ttl:int message:string media:DecryptedMessageMedia = DecryptedMessage;
decryptedMessageService#73164160 random_id:long action:DecryptedMessageAction = DecryptedMessage;
decryptedMessageMediaVideo23#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia;
decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer;
sendMessageTypingAction#16bf744e = SendMessageAction;
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
sendMessageRecordVideoAction#a187d66f = SendMessageAction;
sendMessageUploadVideoAction#92042ff7 = SendMessageAction;
sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction;
sendMessageUploadPhotoAction#990a3c1a = SendMessageAction;
sendMessageUploadDocumentAction#8faee98e = SendMessageAction;
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction;
decryptedMessageActionNotifyLayer#f3048883 layer:int = DecryptedMessageAction;
decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction;
decryptedMessageActionRequestKey#f3c9611b exchange_id:long g_a:bytes = DecryptedMessageAction;
decryptedMessageActionAcceptKey#6fe1735b exchange_id:long g_b:bytes key_fingerprint:long = DecryptedMessageAction;
decryptedMessageActionAbortKey#dd05ec6b exchange_id:long = DecryptedMessageAction;
decryptedMessageActionCommitKey#ec2e0b9b exchange_id:long key_fingerprint:long = DecryptedMessageAction;
decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker23#fb0a5727 = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio23#51448e5 duration:int = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia;
// layer 45
documentAttributeAudio45#ded218e0 duration:int title:string performer:string = DocumentAttribute;
// layer 46
decryptedMessage46#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage;
decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia;
decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
messageEntityMention#fa04579d offset:int length:int = MessageEntity;
messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity;
messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity;
messageEntityUrl#6ed02538 offset:int length:int = MessageEntity;
messageEntityEmail#64e475c2 offset:int length:int = MessageEntity;
messageEntityBold#bd610bc9 offset:int length:int = MessageEntity;
messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
messageEntityCode#28a20571 offset:int length:int = MessageEntity;
messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia;
decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia;
// layer 66
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
sendMessageUploadRoundAction#bb718624 = SendMessageAction;
documentAttributeVideo66#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
// layer 73
decryptedMessage#91cc4674 flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage;
---functions---
test.dummyFunction = Bool;

Binary file not shown.

2761
td/generate/scheme/td_api.tl Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,6 @@
#!/bin/sh
cd $(dirname $0)
tl-parser -e td_api.tlo td_api.tl
tl-parser -e telegram_api.tlo telegram_api.tl
tl-parser -e mtproto_api.tlo mtproto_api.tl
tl-parser -e secret_api.tlo secret_api.tl

View File

@ -0,0 +1,224 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_json_converter.h"
#include "td/tl/tl_simple.h"
#include "td/utils/buffer.h"
#include "td/utils/filesystem.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/StringBuilder.h"
#include <utility>
namespace td {
template <class T>
void gen_to_json_constructor(StringBuilder &sb, const T *constructor, bool is_header) {
sb << "void to_json(JsonValueScope &jv, "
<< "const td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &object)";
if (is_header) {
sb << ";\n";
return;
}
sb << " {\n";
sb << " auto jo = jv.enter_object();\n";
sb << " jo << ctie(\"@type\", \"" << tl::simple::gen_cpp_name(constructor->name) << "\");\n";
for (auto &arg : constructor->args) {
auto field = tl::simple::gen_cpp_field_name(arg.name);
// TODO: or as null
bool is_custom = arg.type->type == tl::simple::Type::Custom;
if (is_custom) {
sb << " if (object." << field << ") {\n ";
}
auto object = PSTRING() << "object." << tl::simple::gen_cpp_field_name(arg.name);
if (arg.type->type == tl::simple::Type::Bytes) {
object = PSTRING() << "base64_encode(" << object << ")";
} else if (arg.type->type == tl::simple::Type::Int64) {
object = PSTRING() << "JsonInt64{" << object << "}";
} else if (arg.type->type == tl::simple::Type::Vector &&
arg.type->vector_value_type->type == tl::simple::Type::Int64) {
object = PSTRING() << "JsonVectorInt64{" << object << "}";
}
sb << " jo << ctie(\"" << arg.name << "\", ToJson(" << object << "));\n";
if (is_custom) {
sb << " }\n";
}
}
sb << "}\n";
}
void gen_to_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header) {
for (auto *custom_type : schema.custom_types) {
if (custom_type->constructors.size() > 1) {
auto type_name = tl::simple::gen_cpp_name(custom_type->name);
sb << "void to_json(JsonValueScope &jv, const td_api::" << type_name << " &object)";
if (is_header) {
sb << ";\n";
} else {
sb << " {\n"
<< " td_api::downcast_call(const_cast<td_api::" << type_name
<< " &>(object), [&jv](const auto &object) { "
"to_json(jv, object); });\n"
<< "}\n";
}
}
for (auto *constructor : custom_type->constructors) {
gen_to_json_constructor(sb, constructor, is_header);
}
}
for (auto *function : schema.functions) {
gen_to_json_constructor(sb, function, is_header);
}
}
template <class T>
void gen_from_json_constructor(StringBuilder &sb, const T *constructor, bool is_header) {
sb << "Status from_json(td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &to, JsonObject &from)";
if (is_header) {
sb << ";\n";
} else {
sb << " {\n";
for (auto &arg : constructor->args) {
sb << " {\n";
sb << " TRY_RESULT(value, get_json_object_field(from, \"" << tl::simple::gen_cpp_name(arg.name)
<< "\", JsonValue::Type::Null, true));\n";
sb << " if (value.type() != JsonValue::Type::Null) {\n";
if (arg.type->type == tl::simple::Type::Bytes) {
sb << " TRY_STATUS(from_json_bytes(to." << tl::simple::gen_cpp_field_name(arg.name) << ", value));\n";
} else {
sb << " TRY_STATUS(from_json(to." << tl::simple::gen_cpp_field_name(arg.name) << ", value));\n";
}
sb << " }\n";
sb << " }\n";
}
sb << " return Status::OK();\n";
sb << "}\n";
}
}
void gen_from_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header) {
for (auto *custom_type : schema.custom_types) {
for (auto *constructor : custom_type->constructors) {
gen_from_json_constructor(sb, constructor, is_header);
}
}
for (auto *function : schema.functions) {
gen_from_json_constructor(sb, function, is_header);
}
}
using Vec = std::vector<std::pair<int32, std::string>>;
void gen_tl_constructor_from_string(StringBuilder &sb, Slice name, const Vec &vec, bool is_header) {
sb << "Result<int32> tl_constructor_from_string(td_api::" << name << " *object, const std::string &str)";
if (is_header) {
sb << ";\n";
return;
}
sb << " {\n";
sb << " static const std::unordered_map<Slice, int32, SliceHash> m = {\n";
bool is_first = true;
for (auto &p : vec) {
if (is_first) {
is_first = false;
} else {
sb << ",\n";
}
sb << " {\"" << p.second << "\", " << p.first << "}";
}
sb << "\n };\n";
sb << " auto it = m.find(str);\n";
sb << " if (it == m.end()) {\n"
<< " return Status::Error(\"Unknown class\");\n"
<< " }\n"
<< " return it->second;\n";
sb << "}\n";
}
void gen_tl_constructor_from_string(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header) {
Vec vec_for_nullary;
for (auto *custom_type : schema.custom_types) {
Vec vec;
for (auto *constructor : custom_type->constructors) {
vec.push_back(std::make_pair(constructor->id, constructor->name));
vec_for_nullary.push_back(vec.back());
}
if (vec.size() > 1) {
gen_tl_constructor_from_string(sb, tl::simple::gen_cpp_name(custom_type->name), vec, is_header);
}
}
gen_tl_constructor_from_string(sb, "Object", vec_for_nullary, is_header);
Vec vec_for_function;
for (auto *function : schema.functions) {
vec_for_function.push_back(std::make_pair(function->id, function->name));
}
gen_tl_constructor_from_string(sb, "Function", vec_for_function, is_header);
}
void gen_json_converter_file(const tl::simple::Schema &schema, const std::string &file_name_base, bool is_header) {
auto file_name = is_header ? file_name_base + ".h" : file_name_base + ".cpp";
file_name = "auto/" + file_name;
auto old_file_content = [&] {
auto r_content = read_file(file_name);
if (r_content.is_error()) {
return BufferSlice();
}
return r_content.move_as_ok();
}();
std::string buf(2000000, ' ');
StringBuilder sb(buf);
if (is_header) {
sb << "#pragma once\n\n";
sb << "#include \"td/telegram/td_api.h\"\n\n";
sb << "#include \"td/utils/JsonBuilder.h\"\n";
sb << "#include \"td/utils/Status.h\"\n\n";
} else {
sb << "#include \"" << file_name_base << ".h\"\n\n";
sb << "#include \"td/telegram/td_api.h\"\n";
sb << "#include \"td/telegram/td_api.hpp\"\n\n";
sb << "#include \"td/tl/tl_json.h\"\n\n";
sb << "#include \"td/utils/base64.h\"\n";
sb << "#include \"td/utils/common.h\"\n";
sb << "#include \"td/utils/Slice.h\"\n\n";
sb << "#include <unordered_map>\n\n";
}
sb << "namespace td {\n";
sb << "namespace td_api{\n";
gen_tl_constructor_from_string(sb, schema, is_header);
gen_from_json(sb, schema, is_header);
gen_to_json(sb, schema, is_header);
sb << "} // namespace td_api\n";
sb << "} // namespace td\n";
CHECK(!sb.is_error());
buf.resize(sb.as_cslice().size());
auto new_file_content = std::move(buf);
if (new_file_content != old_file_content.as_slice()) {
write_file(file_name, new_file_content).ensure();
}
}
void gen_json_converter(const tl::tl_config &config, const std::string &file_name) {
tl::simple::Schema schema(config);
gen_json_converter_file(schema, file_name, true);
gen_json_converter_file(schema, file_name, false);
}
} // namespace td

View File

@ -0,0 +1,17 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include <string>
#include "td/tl/tl_config.h"
namespace td {
void gen_json_converter(const tl::tl_config &config, const std::string &file_name);
} // namespace td

1355
td/generate/tl_writer_c.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_cpp.h"
#include <cassert>
namespace td {
std::string TD_TL_writer_cpp::gen_output_begin() const {
std::string ext_include_str;
for (auto &it : ext_include) {
ext_include_str += "#include " + it + "\n";
}
if (!ext_include_str.empty()) {
ext_include_str += "\n";
}
return "#include \"" + tl_name + ".h\"\n\n" + ext_include_str +
"#include \"td/utils/common.h\"\n"
"#include \"td/utils/format.h\"\n"
"#include \"td/utils/logging.h\"\n"
"#include \"td/utils/tl_parsers.h\"\n"
"#include \"td/utils/tl_storers.h\"\n\n"
"namespace td {\n"
"namespace " +
tl_name +
" {\n\n"
"std::string to_string(const BaseObject &value) {\n"
" TlStorerToString storer;\n"
" value.store(storer, \"\");\n"
" return storer.str();\n"
"}\n";
}
std::string TD_TL_writer_cpp::gen_output_end() const {
return "} // namespace " + tl_name +
"\n"
"} // namespace td\n";
}
std::string TD_TL_writer_cpp::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return "";
}
std::string TD_TL_writer_cpp::gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].index = static_cast<int>(i);
vars[i].is_stored = false;
vars[i].is_type = false;
vars[i].parameter_num = -1;
vars[i].function_arg_num = -1;
}
if (result_type != nullptr) {
assert(result_type->children.empty());
}
for (std::size_t i = 0; i < t->args.size(); i++) {
const tl::arg &a = t->args[i];
int arg_type = a.type->get_type();
if (arg_type == tl::NODE_TYPE_VAR_TYPE) {
const tl::tl_tree_var_type *var_type = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags & tl::FLAG_EXCL);
assert(var_type->var_num < static_cast<int>(vars.size()));
assert(var_type->var_num >= 0);
assert(!vars[var_type->var_num].is_type);
vars[var_type->var_num].is_type = true;
vars[var_type->var_num].function_arg_num = static_cast<int>(i);
}
}
std::string res;
for (std::size_t i = 0; i < vars.size(); i++) {
if (!vars[i].is_type) {
assert(vars[i].parameter_num == -1);
assert(vars[i].function_arg_num == -1);
assert(vars[i].is_stored == false);
res += " " + gen_class_name("#") + " " + gen_var_name(vars[i]) + ";\n";
}
}
return res;
}
std::string TD_TL_writer_cpp::gen_function_vars(const tl::tl_combinator *t,
std::vector<tl::var_description> &vars) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].index = static_cast<int>(i);
vars[i].is_stored = false;
vars[i].is_type = false;
vars[i].parameter_num = -1;
vars[i].function_arg_num = -1;
}
for (std::size_t i = 0; i < t->args.size(); i++) {
const tl::arg &a = t->args[i];
int arg_type = a.type->get_type();
if (arg_type == tl::NODE_TYPE_VAR_TYPE) {
const tl::tl_tree_var_type *var_type = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags & tl::FLAG_EXCL);
assert(var_type->var_num >= 0);
assert(!vars[var_type->var_num].is_type);
vars[var_type->var_num].is_type = true;
vars[var_type->var_num].function_arg_num = static_cast<int>(i);
}
}
return "";
}
std::string TD_TL_writer_cpp::gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const {
assert(result_type->children.empty());
return "";
}
std::string TD_TL_writer_cpp::gen_constructor_id_store_raw(const std::string &id) const {
return "s.store_binary(" + id + ");";
}
std::string TD_TL_writer_cpp::gen_constructor_id_store(std::int32_t id, int storer_type) const {
if (storer_type == 1) {
return "";
}
return " " + gen_constructor_id_store_raw(int_to_string(id)) + "\n";
}
std::string TD_TL_writer_cpp::gen_fetch_class_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
if (name == "#" || name == "Int32") {
return "TlFetchInt";
}
if (name == "Int53" || name == "Int64") {
return "TlFetchLong";
}
if (name == "True" || name == "Bool" || name == "Int" || name == "Long" || name == "Double" || name == "Int128" ||
name == "Int256") {
return "TlFetch" + name;
}
if (name == "String") {
return "TlFetchString<" + string_type + ">";
}
if (name == "Bytes") {
return "TlFetchBytes<" + bytes_type + ">";
}
if (name == "Vector") {
assert(t->arity == 1);
assert(tree_type->children.size() == 1);
assert(tree_type->children[0]->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return "TlFetchVector<" + gen_full_fetch_class_name(child) + ">";
}
assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name));
for (std::size_t i = 0; i < tree_type->children.size(); i++) {
assert(tree_type->children[i]->get_type() == tl::NODE_TYPE_NAT_CONST);
}
assert(tree_type->children.empty());
return "TlFetchObject<" + gen_main_class_name(t) + ">";
}
std::string TD_TL_writer_cpp::gen_full_fetch_class_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
assert(!(t->flags & tl::FLAG_DEFAULT_CONSTRUCTOR)); // Not supported yet
std::int32_t expected_constructor_id = 0;
if (tree_type->flags & tl::FLAG_BARE) {
assert(is_type_bare(t));
} else {
if (is_type_bare(t)) {
for (std::size_t i = 0; i < t->constructors_num; i++) {
if (is_built_in_complex_type(name) || is_combinator_supported(t->constructors[i])) {
assert(expected_constructor_id == 0);
expected_constructor_id = t->constructors[i]->id;
assert(expected_constructor_id != 0);
}
}
}
}
if (expected_constructor_id == 0) {
return gen_fetch_class_name(tree_type);
}
return "TlFetchBoxed<" + gen_fetch_class_name(tree_type) + ", " + int_to_string(expected_constructor_id) + ">";
}
std::string TD_TL_writer_cpp::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const {
return gen_full_fetch_class_name(tree_type) + "::parse(p)";
}
std::string TD_TL_writer_cpp::gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars,
bool flat, int parser_type) const {
assert(parser_type >= 0);
std::string field_name = (parser_type == 0 ? (field_num == 0 ? ": " : ", ") : "res->") + gen_field_name(a.name);
if (a.type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
assert(parser_type == 1);
const tl::tl_tree_var_type *t = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags == tl::FLAG_EXCL);
assert(a.var_num == -1);
assert(a.exist_var_num == -1);
assert(t->var_num >= 0);
assert(vars[t->var_num].is_type);
assert(!vars[t->var_num].is_stored);
vars[t->var_num].is_stored = true;
return " " + field_name + " = " + gen_base_function_class_name() + "::fetch(p);\n";
}
assert(!(a.flags & tl::FLAG_EXCL));
assert(!(a.flags & tl::FLAG_OPT_VAR));
std::string res = " ";
if (a.exist_var_num != -1) {
assert(0 <= a.exist_var_num && a.exist_var_num < static_cast<int>(vars.size()));
assert(vars[a.exist_var_num].is_stored);
res += "if (" + gen_var_name(vars[a.exist_var_num]) + " & " + int_to_string(1 << a.exist_var_bit) + ") { ";
}
if (flat) {
// TODO
// return gen_field_fetch(const tl::arg &a, std::vector<tl::var_description> &vars, int num, bool flat);
}
bool store_to_var_num = false;
if (a.var_num >= 0) {
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
assert(static_cast<const tl::tl_tree_type *>(a.type)->type->id == tl::ID_VAR_NUM);
assert(0 <= a.var_num && a.var_num < static_cast<int>(vars.size()));
if (!vars[a.var_num].is_stored) {
res += "if ((" + gen_var_name(vars[a.var_num]) + " = ";
store_to_var_num = true;
} else {
assert(false);
}
vars[a.var_num].is_stored = true;
}
res += field_name + (parser_type == 0 ? "(" : " = ");
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
res += gen_type_fetch(field_name, tree_type, vars, parser_type);
if (store_to_var_num) {
res += ") < 0) { FAIL(\"Variable of type # can't be negative\"); }";
} else {
res += (parser_type == 0 ? ")" : ";");
}
if (a.exist_var_num >= 0) {
res += " }";
if (store_to_var_num) {
res += " else { " + gen_var_name(vars[a.var_num]) + " = 0; }";
}
}
res += "\n";
return res;
}
std::string TD_TL_writer_cpp::gen_var_type_fetch(const tl::arg &a) const {
assert(false);
return "";
}
std::string TD_TL_writer_cpp::get_pretty_field_name(std::string field_name) const {
if (!field_name.empty() && field_name.back() == ']') {
return "";
}
auto equals_pos = field_name.find('=');
if (equals_pos != std::string::npos && equals_pos + 3 < field_name.size()) {
field_name = field_name.substr(equals_pos + 2);
if (field_name.back() == ')') {
field_name.pop_back();
}
}
while (!field_name.empty() && field_name.back() == '_') {
field_name.pop_back();
}
return field_name;
}
std::string TD_TL_writer_cpp::get_pretty_class_name(std::string class_name) const {
return class_name;
}
std::string TD_TL_writer_cpp::gen_vector_store(const std::string &field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars, int storer_type) const {
std::string num = field_name.back() == ']' ? "2" : "";
return "{ const std::vector<" + gen_type_name(t) + "> &v" + num + " = " + field_name +
"; const std::uint32_t multiplicity" + num + " = static_cast<std::uint32_t>(v" + num +
".size()); const auto vector_name" + num + " = \"" + get_pretty_class_name("vector") +
"[\" + td::to_string(multiplicity" + num + ")+ \"]\"; s.store_class_begin(\"" +
get_pretty_field_name(field_name) + "\", vector_name" + num +
".c_str()); "
"for (std::uint32_t i" +
num + " = 0; i" + num + " < multiplicity" + num + "; i" + num + "++) { " +
gen_type_store("v" + num + "[i" + num + "]", t, vars, storer_type) + " } s.store_class_end(); }";
}
std::string TD_TL_writer_cpp::gen_store_class_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
if (name == "#" || name == "Int" || name == "Long" || name == "Int32" || name == "Int53" || name == "Int64" ||
name == "Double" || name == "Int128" || name == "Int256") {
return "TlStoreBinary";
}
if (name == "Bool") {
return "TlStoreBool";
}
if (name == "True") {
return "TlStoreTrue";
}
if (name == "String" || name == "Bytes") {
return "TlStoreString";
}
if (name == "Vector") {
assert(t->arity == 1);
assert(tree_type->children.size() == 1);
assert(tree_type->children[0]->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return "TlStoreVector<" + gen_full_store_class_name(child) + ">";
}
assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name));
for (std::size_t i = 0; i < tree_type->children.size(); i++) {
assert(tree_type->children[i]->get_type() == tl::NODE_TYPE_NAT_CONST);
}
assert(tree_type->children.empty());
return "TlStoreObject";
}
std::string TD_TL_writer_cpp::gen_full_store_class_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
assert(!(t->flags & tl::FLAG_DEFAULT_CONSTRUCTOR)); // Not supported yet
if ((tree_type->flags & tl::FLAG_BARE) != 0 || t->name == "#" || t->name == "Bool") {
return gen_store_class_name(tree_type);
}
if (is_built_in_complex_type(t->name)) {
return "TlStoreBoxed<" + gen_store_class_name(tree_type) + ", " + int_to_string(t->constructors[0]->id) + ">";
}
if (!is_type_bare(t)) {
return "TlStoreBoxedUnknown<" + gen_store_class_name(tree_type) + ">";
}
for (std::size_t i = 0; i < t->constructors_num; i++) {
if (is_combinator_supported(t->constructors[i])) {
return "TlStoreBoxed<" + gen_store_class_name(tree_type) + ", " + int_to_string(t->constructors[i]->id) + ">";
}
}
assert(false);
return "";
}
std::string TD_TL_writer_cpp::gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const {
if (storer_type == 0) {
return gen_full_store_class_name(tree_type) + "::store(" + field_name + ", s);";
}
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
assert(!(t->flags & tl::FLAG_DEFAULT_CONSTRUCTOR)); // Not supported yet
if (name == "#" || name == "Int" || name == "Long" || name == "Int32" || name == "Int53" || name == "Int64" ||
name == "Double" || name == "Bool" || name == "String" || name == "Int128" || name == "Int256") {
return "s.store_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");";
} else if (name == "True") {
// currently nothing to do
return "";
} else if (name == "Bytes") {
return "s.store_bytes_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");";
} else if (name == "Vector") {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return gen_vector_store(field_name, child, vars, storer_type);
} else {
assert(tree_type->children.empty());
return "if (" + field_name + " == nullptr) { s.store_field(\"" + get_pretty_field_name(field_name) +
"\", \"null\"); } else { " + field_name + "->store(s, \"" + get_pretty_field_name(field_name) + "\"); }";
}
}
std::string TD_TL_writer_cpp::gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const {
std::string field_name = gen_field_name(a.name);
std::string res = storer_type == 1 ? " " : " ";
if (a.type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
const tl::tl_tree_var_type *t = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags == tl::FLAG_EXCL);
assert(a.var_num == -1);
assert(a.exist_var_num == -1);
assert(t->var_num >= 0);
assert(!vars[t->var_num].is_stored);
vars[t->var_num].is_stored = true;
assert(vars[t->var_num].is_type);
return res + field_name + "->store(s);\n";
}
assert(!(a.flags & tl::FLAG_EXCL));
if (a.flags & tl::FLAG_OPT_VAR) {
assert(false);
assert(a.exist_var_num == -1);
assert(0 <= a.var_num && a.var_num < static_cast<int>(vars.size()));
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
assert(static_cast<const tl::tl_tree_type *>(a.type)->type->id == tl::ID_VAR_NUM);
assert(vars[a.var_num].is_stored);
assert(!vars[a.var_num].is_type);
return "";
}
if (a.exist_var_num >= 0) {
assert(a.exist_var_num < static_cast<int>(vars.size()));
assert(vars[a.exist_var_num].is_stored);
res += "if (" + gen_var_name(vars[a.exist_var_num]) + " & " + int_to_string(1 << a.exist_var_bit) + ") { ";
}
if (flat) {
// TODO
// return gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat, int storer_type);
}
if (a.var_num >= 0) {
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
assert(static_cast<const tl::tl_tree_type *>(a.type)->type->id == tl::ID_VAR_NUM);
assert(a.var_num < static_cast<int>(vars.size()));
if (!vars[a.var_num].is_stored) {
field_name = "(" + gen_var_name(vars[a.var_num]) + " = " + field_name + ")";
vars[a.var_num].is_stored = true;
} else {
assert(false); // need to check value of stored var
field_name = gen_var_name(vars[a.var_num]);
}
}
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
res += gen_type_store(field_name, tree_type, vars, storer_type);
if (a.exist_var_num >= 0) {
res += " }";
}
res += "\n";
return res;
}
std::string TD_TL_writer_cpp::gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_cpp::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
return "";
}
std::string TD_TL_writer_cpp::gen_class_end() const {
return "";
}
std::string TD_TL_writer_cpp::gen_class_alias(const std::string &class_name, const std::string &alias_name) const {
return "";
}
std::string TD_TL_writer_cpp::gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const {
if (is_proxy) {
return "";
}
return "\nconst std::int32_t " + class_name + "::ID;\n";
}
std::string TD_TL_writer_cpp::gen_function_result_type(const tl::tl_tree *result) const {
return "";
}
std::string TD_TL_writer_cpp::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int parser_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored == false);
}
std::string fetched_type = "object_ptr<" + class_name + "> ";
assert(arity == 0);
if (parser_type == 0) {
return "\n" + class_name + "::" + class_name + "(" + parser_name +
" &p)\n"
"#define FAIL(error) p.set_error(error)\n";
}
return "\n" + fetched_type + class_name + "::fetch(" + parser_name +
" &p) {\n"
"#define FAIL(error) p.set_error(error); return nullptr;\n" +
(parser_type == -1 ? "" : " " + fetched_type + "res = make_tl_object<" + class_name + ">();\n");
}
std::string TD_TL_writer_cpp::gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored);
}
if (parser_type == 0) {
return "#undef FAIL\n"
"{" +
(field_num == 0 ? "\n (void)p;\n" : std::string()) + "}\n";
}
if (parser_type == -1) {
return "#undef FAIL\n"
"}\n";
}
return " if (p.get_error()) { FAIL(\"\"); }\n"
" return res;\n"
"#undef FAIL\n"
"}\n";
}
std::string TD_TL_writer_cpp::gen_fetch_function_result_begin(const std::string &parser_name,
const std::string &class_name,
const tl::tl_tree *result) const {
return "\n" + class_name + "::ReturnType " + class_name + "::fetch_result(" + parser_name +
" &p) {\n"
"#define FAIL(error) p.set_error(error); return ReturnType()\n"
" return ";
}
std::string TD_TL_writer_cpp::gen_fetch_function_result_end() const {
return ";\n"
"#undef FAIL\n"
"}\n";
}
std::string TD_TL_writer_cpp::gen_fetch_function_result_any_begin(const std::string &parser_name,
const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_cpp::gen_fetch_function_result_any_end(bool is_proxy) const {
return "";
}
std::string TD_TL_writer_cpp::gen_store_function_begin(const std::string &storer_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int storer_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].is_stored = false;
}
if (storer_type == -1) {
return "";
}
assert(arity == 0);
return "\n"
"void " +
class_name + "::store(" + storer_name + " &s" +
std::string(storer_type <= 0 ? "" : ", const char *field_name") + ") const {\n" +
(storer_type <= 0 ? " (void)sizeof(s);\n"
: " if (!LOG_IS_STRIPPED(ERROR)) {\n"
" s.store_class_begin(field_name, \"" +
get_pretty_class_name(class_name) + "\");\n");
}
std::string TD_TL_writer_cpp::gen_store_function_end(const std::vector<tl::var_description> &vars,
int storer_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored);
}
if (storer_type == -1) {
return "";
}
return (storer_type <= 0 ? std::string()
: " s.store_class_end();\n"
" }\n") +
"}\n";
}
std::string TD_TL_writer_cpp::gen_fetch_switch_begin() const {
return " int constructor = p.fetch_int();\n"
" switch (constructor) {\n";
}
std::string TD_TL_writer_cpp::gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const {
assert(arity == 0);
return " case " + gen_class_name(t->name) +
"::ID:\n"
" return " +
gen_class_name(t->name) + "::fetch(p);\n";
}
std::string TD_TL_writer_cpp::gen_fetch_switch_end() const {
return " default:\n"
" FAIL(PSTRING() << \"Unknown constructor found \" << format::as_hex(constructor));\n"
" }\n";
}
std::string TD_TL_writer_cpp::gen_constructor_begin(int fields_num, const std::string &class_name,
bool is_default) const {
return "\n" + class_name + "::" + class_name + "(";
}
std::string TD_TL_writer_cpp::gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const {
std::string field_type = gen_field_type(a);
if (field_type.empty()) {
return "";
}
std::string move_begin;
std::string move_end;
if ((field_type == bytes_type || field_type.compare(0, 11, "std::vector") == 0 ||
field_type.compare(0, 10, "object_ptr") == 0) &&
!is_default) {
move_begin = "std::move(";
move_end = ")";
}
return (field_num == 0 ? ")\n : " : " , ") + gen_field_name(a.name) + "(" + move_begin +
(is_default ? "" : gen_field_name(a.name)) + move_end + ")\n";
}
std::string TD_TL_writer_cpp::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const {
if (fields_num == 0) {
return ") {\n"
"}\n";
}
return "{}\n";
}
} // namespace td

104
td/generate/tl_writer_cpp.h Normal file
View File

@ -0,0 +1,104 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "tl_writer_td.h"
#include <cstdint>
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_cpp : public TD_TL_writer {
std::string gen_constructor_id_store_raw(const std::string &id) const;
std::string gen_fetch_class_name(const tl::tl_tree_type *tree_type) const;
std::string gen_full_fetch_class_name(const tl::tl_tree_type *tree_type) const;
std::string gen_store_class_name(const tl::tl_tree_type *tree_type) const;
std::string gen_full_store_class_name(const tl::tl_tree_type *tree_type) const;
std::vector<std::string> ext_include;
protected:
std::string gen_vector_store(const std::string &field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars, int storer_type) const;
virtual std::string get_pretty_field_name(std::string field_name) const;
virtual std::string get_pretty_class_name(std::string class_name) const;
public:
TD_TL_writer_cpp(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type,
const std::vector<std::string> &ext_include)
: TD_TL_writer(tl_name, string_type, bytes_type), ext_include(ext_include) {
}
std::string gen_output_begin() const override;
std::string gen_output_end() const override;
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const override;
std::string gen_function_vars(const tl::tl_combinator *t, std::vector<tl::var_description> &vars) const override;
std::string gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const override;
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const override;
std::string gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int parser_type) const override;
std::string gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const override;
std::string gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_var_type_fetch(const tl::arg &a) const override;
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override;
std::string gen_function_result_type(const tl::tl_tree *result) const override;
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const override;
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
const tl::tl_tree *result) const override;
std::string gen_fetch_function_result_end() const override;
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
bool is_proxy) const override;
std::string gen_fetch_function_result_any_end(bool is_proxy) const override;
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_store_function_end(const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_fetch_switch_begin() const override;
std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override;
std::string gen_fetch_switch_end() const override;
std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override;
std::string gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override;
};
} // namespace td

349
td/generate/tl_writer_h.cpp Normal file
View File

@ -0,0 +1,349 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_h.h"
#include <cassert>
#include <utility>
namespace td {
std::string TD_TL_writer_h::forward_declaration(std::string type) {
std::string prefix;
std::string suffix;
do {
std::size_t pos = type.find("::");
if (pos == std::string::npos) {
return prefix + "class " + type + ";\n" + suffix;
}
std::string namespace_name = type.substr(0, pos);
type = type.substr(pos + 2);
prefix += "namespace " + namespace_name + " {\n";
suffix += "} // namespace " + namespace_name + "\n";
} while (true);
assert(false);
return "";
}
std::string TD_TL_writer_h::gen_output_begin() const {
std::string ext_include_str;
for (auto &it : ext_include) {
ext_include_str += "#include " + it + "\n";
}
if (!ext_include_str.empty()) {
ext_include_str += "\n";
}
std::string ext_forward_declaration;
for (auto &storer_name : get_storers()) {
ext_forward_declaration += forward_declaration(storer_name);
}
for (auto &parser_name : get_parsers()) {
ext_forward_declaration += forward_declaration(parser_name);
}
if (!ext_forward_declaration.empty()) {
ext_forward_declaration += "\n";
}
return "#pragma once\n\n"
"#include \"td/tl/TlObject.h\"\n\n" +
ext_include_str +
"#include <cstdint>\n"
"#include <memory>\n"
"#include <utility>\n"
"#include <vector>\n\n"
"namespace td {\n" +
ext_forward_declaration + "namespace " + tl_name +
" {\n\n"
"using BaseObject = ::td::TlObject;\n\n"
"template <class Type>\n"
"using object_ptr = ::td::tl_object_ptr<Type>;\n\n"
"template <class Type, class... Args>\n"
"object_ptr<Type> make_object(Args &&... args) {\n"
" return object_ptr<Type>(new Type(std::forward<Args>(args)...));\n"
"}\n\n"
"template <class ToType, class FromType>\n"
"object_ptr<ToType> move_object_as(FromType &&from) {\n"
" return object_ptr<ToType>(static_cast<ToType *>(from.release()));\n"
"}\n\n"
"std::string to_string(const BaseObject &value);\n\n"
"template <class T>\n"
"std::string to_string(const object_ptr<T> &value) {\n"
" if (value == nullptr) {\n"
" return \"null\";\n"
" }\n"
"\n"
" return to_string(*value);\n"
"}\n\n";
}
std::string TD_TL_writer_h::gen_output_end() const {
return "} // namespace " + tl_name +
"\n"
"} // namespace td\n";
}
std::string TD_TL_writer_h::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return " " + type_name + (type_name.empty() || type_name[type_name.size() - 1] == ' ' ? "" : " ") + field_name +
";\n";
}
std::string TD_TL_writer_h::gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const {
return "";
}
std::string TD_TL_writer_h::gen_function_vars(const tl::tl_combinator *t,
std::vector<tl::var_description> &vars) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].index = static_cast<int>(i);
vars[i].is_stored = false;
vars[i].is_type = false;
vars[i].parameter_num = -1;
vars[i].function_arg_num = -1;
}
for (std::size_t i = 0; i < t->args.size(); i++) {
const tl::arg &a = t->args[i];
int arg_type = a.type->get_type();
if (arg_type == tl::NODE_TYPE_VAR_TYPE) {
const tl::tl_tree_var_type *var_type = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags & tl::FLAG_EXCL);
assert(var_type->var_num >= 0);
assert(!vars[var_type->var_num].is_type);
vars[var_type->var_num].is_type = true;
vars[var_type->var_num].function_arg_num = static_cast<int>(i);
}
}
std::string res;
for (std::size_t i = 0; i < vars.size(); i++) {
if (!vars[i].is_type) {
assert(vars[i].parameter_num == -1);
assert(vars[i].function_arg_num == -1);
assert(vars[i].is_stored == false);
res += " mutable " + gen_class_name("#") + " " + gen_var_name(vars[i]) + ";\n";
}
}
return res;
}
std::string TD_TL_writer_h::gen_flags_definitions(const tl::tl_combinator *t) const {
std::vector<std::pair<std::string, std::int32_t>> flags;
for (std::size_t i = 0; i < t->args.size(); i++) {
const tl::arg &a = t->args[i];
if (a.exist_var_num != -1) {
auto name = a.name;
for (auto &c : name) {
c = to_upper(c);
}
flags.push_back(std::make_pair(name, a.exist_var_bit));
}
}
std::string res;
if (!flags.empty()) {
res += " enum Flags : std::int32_t {";
bool first = true;
for (auto &p : flags) {
if (first) {
first = false;
} else {
res += ", ";
}
res += p.first + "_MASK = " + int_to_string(1 << p.second);
}
res += "};\n";
}
return res;
}
std::string TD_TL_writer_h::gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const {
return "";
}
std::string TD_TL_writer_h::gen_constructor_id_store(std::int32_t id, int storer_type) const {
return "";
}
std::string TD_TL_writer_h::gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars,
bool flat, int parser_type) const {
return "";
}
std::string TD_TL_writer_h::gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const {
return "";
}
std::string TD_TL_writer_h::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const {
return "";
}
std::string TD_TL_writer_h::gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const {
return "";
}
std::string TD_TL_writer_h::gen_var_type_fetch(const tl::arg &a) const {
assert(false);
return "";
}
std::string TD_TL_writer_h::gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const {
return "class " + class_name + ";\n\n";
}
std::string TD_TL_writer_h::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
return "class " + class_name + (!is_proxy ? " final " : "") + ": public " + base_class_name +
" {\n"
" public:\n";
}
std::string TD_TL_writer_h::gen_class_end() const {
return "};\n\n";
}
std::string TD_TL_writer_h::gen_class_alias(const std::string &class_name, const std::string &alias_name) const {
return "";
// return "typedef " + class_name + " " + alias_name + ";\n\n\n";
}
std::string TD_TL_writer_h::gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const {
if (is_proxy) {
if (class_name == gen_base_tl_class_name()) {
return "\n virtual std::int32_t get_id() const = 0;\n";
}
return "";
}
return "\n"
" static const std::int32_t ID = " +
int_to_string(id) +
";\n"
" std::int32_t get_id() const final {\n"
" return ID;\n"
" }\n";
}
std::string TD_TL_writer_h::gen_function_result_type(const tl::tl_tree *result) const {
assert(result->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *result_type = static_cast<const tl::tl_tree_type *>(result);
std::string fetched_type = gen_type_name(result_type);
if (!fetched_type.empty() && fetched_type[fetched_type.size() - 1] == ' ') {
fetched_type.pop_back();
}
return "\n"
" using ReturnType = " +
fetched_type + ";\n";
}
std::string TD_TL_writer_h::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int parser_type) const {
std::string fetched_type = "object_ptr<" + class_name + "> ";
if (parser_type == 0) {
return "\n"
" static " +
fetched_type + "fetch(" + parser_name +
" &p) {\n"
" return make_tl_object<" +
class_name +
">(p);\n"
" }\n\n" +
" explicit " + class_name + "(" + parser_name + " &p);\n";
}
assert(arity == 0);
return "\n"
" static " +
fetched_type + "fetch(" + parser_name + " &p);\n";
}
std::string TD_TL_writer_h::gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_function_result_begin(const std::string &parser_name,
const std::string &class_name,
const tl::tl_tree *result) const {
return "\n"
" static ReturnType fetch_result(" +
parser_name + " &p);\n";
}
std::string TD_TL_writer_h::gen_fetch_function_result_end() const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_function_result_any_begin(const std::string &parser_name,
const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_function_result_any_end(bool is_proxy) const {
return "";
}
std::string TD_TL_writer_h::gen_store_function_begin(const std::string &storer_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int storer_type) const {
assert(arity == 0);
if (storer_type == -1) {
return "";
}
return "\n"
" void store(" +
storer_name + " &s" + std::string(storer_type == 0 ? "" : ", const char *field_name") + ") const final;\n";
}
std::string TD_TL_writer_h::gen_store_function_end(const std::vector<tl::var_description> &vars,
int storer_type) const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_switch_begin() const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const {
return "";
}
std::string TD_TL_writer_h::gen_fetch_switch_end() const {
return "";
}
std::string TD_TL_writer_h::gen_constructor_begin(int fields_num, const std::string &class_name,
bool is_default) const {
return "\n"
" " +
std::string(fields_num == 1 ? "explicit " : "") + class_name + "(";
}
std::string TD_TL_writer_h::gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const {
return "";
}
std::string TD_TL_writer_h::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const {
return ");\n";
}
} // namespace td

90
td/generate/tl_writer_h.h Normal file
View File

@ -0,0 +1,90 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "tl_writer_td.h"
#include <cstdint>
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_h : public TD_TL_writer {
protected:
const std::vector<std::string> ext_include;
static std::string forward_declaration(std::string type);
public:
TD_TL_writer_h(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type,
const std::vector<std::string> &ext_include)
: TD_TL_writer(tl_name, string_type, bytes_type), ext_include(ext_include) {
}
std::string gen_output_begin() const override;
std::string gen_output_end() const override;
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_flags_definitions(const tl::tl_combinator *t) const override;
std::string gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const override;
std::string gen_function_vars(const tl::tl_combinator *t, std::vector<tl::var_description> &vars) const override;
std::string gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const override;
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const override;
std::string gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int parser_type) const override;
std::string gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const override;
std::string gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_var_type_fetch(const tl::arg &a) const override;
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override;
std::string gen_function_result_type(const tl::tl_tree *result) const override;
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const override;
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
const tl::tl_tree *result) const override;
std::string gen_fetch_function_result_end() const override;
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
bool is_proxy) const override;
std::string gen_fetch_function_result_any_end(bool is_proxy) const override;
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_store_function_end(const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_fetch_switch_begin() const override;
std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override;
std::string gen_fetch_switch_end() const override;
std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override;
std::string gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override;
};
} // namespace td

View File

@ -0,0 +1,257 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_hpp.h"
#include <cassert>
namespace td {
bool TD_TL_writer_hpp::is_documentation_generated() const {
return true;
}
int TD_TL_writer_hpp::get_additional_function_type(const std::string &additional_function_name) const {
assert(additional_function_name == "downcast_call");
return 2;
}
std::vector<std::string> TD_TL_writer_hpp::get_additional_functions() const {
std::vector<std::string> additional_functions;
additional_functions.push_back("downcast_call");
return additional_functions;
}
std::string TD_TL_writer_hpp::gen_base_type_class_name(int arity) const {
assert(arity == 0);
return "Object";
}
std::string TD_TL_writer_hpp::gen_base_tl_class_name() const {
return "BaseObject";
}
std::string TD_TL_writer_hpp::gen_output_begin() const {
return "#pragma once\n"
"\n"
"/**\n"
" * \\file\n"
" * Contains downcast_call methods for calling a function object on downcasted to\n"
" * the most derived class TDLib API object.\n"
" */\n"
"#include \"" +
tl_name +
".h\"\n"
"\n"
"namespace td {\n"
"namespace " +
tl_name + " {\n\n";
}
std::string TD_TL_writer_hpp::gen_output_end() const {
return "} // namespace " + tl_name +
"\n"
"} // namespace td\n";
}
std::string TD_TL_writer_hpp::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return "";
}
std::string TD_TL_writer_hpp::gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const {
return "";
}
std::string TD_TL_writer_hpp::gen_function_vars(const tl::tl_combinator *t,
std::vector<tl::var_description> &vars) const {
return "";
}
std::string TD_TL_writer_hpp::gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const {
return "";
}
std::string TD_TL_writer_hpp::gen_constructor_id_store(std::int32_t id, int storer_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars,
bool flat, int parser_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_var_type_fetch(const tl::arg &a) const {
assert(false);
return "";
}
std::string TD_TL_writer_hpp::gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_hpp::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
return "";
}
std::string TD_TL_writer_hpp::gen_class_end() const {
return "";
}
std::string TD_TL_writer_hpp::gen_class_alias(const std::string &class_name, const std::string &alias_name) const {
return "";
}
std::string TD_TL_writer_hpp::gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_hpp::gen_function_result_type(const tl::tl_tree *result) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int parser_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_result_begin(const std::string &parser_name,
const std::string &class_name,
const tl::tl_tree *result) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_result_end() const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_result_any_begin(const std::string &parser_name,
const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_function_result_any_end(bool is_proxy) const {
return "";
}
std::string TD_TL_writer_hpp::gen_store_function_begin(const std::string &storer_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int storer_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_store_function_end(const std::vector<tl::var_description> &vars,
int storer_type) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_switch_begin() const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const {
return "";
}
std::string TD_TL_writer_hpp::gen_fetch_switch_end() const {
return "";
}
std::string TD_TL_writer_hpp::gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const {
assert(function_name == "downcast_call");
return "";
}
std::string TD_TL_writer_hpp::gen_additional_proxy_function_begin(const std::string &function_name,
const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const {
assert(function_name == "downcast_call");
return "/**\n"
" * Calls the specified function object with the given object downcasted to its most derived type.\n"
" * \\param[in] obj Object to pass as an argument to the function object.\n"
" * \\param[in] func Function object to which the object will be passed.\n"
" * \\returns Whether function object call has happened. Should always return true for correct parameters.\n"
" */\n"
"template <class T>\n"
"bool downcast_call(" +
class_name +
" &obj, const T &func) {\n"
" switch (obj.get_id()) {\n";
}
std::string TD_TL_writer_hpp::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type, const std::string &class_name,
int arity) const {
assert(function_name == "downcast_call");
assert(false);
return "";
}
std::string TD_TL_writer_hpp::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type, const tl::tl_combinator *t,
int arity, bool is_function) const {
assert(function_name == "downcast_call");
return " case " + gen_class_name(t->name) +
"::ID:\n"
" func(static_cast<" +
gen_class_name(t->name) +
" &>(obj));\n"
" return true;\n";
}
std::string TD_TL_writer_hpp::gen_additional_proxy_function_end(const std::string &function_name,
const tl::tl_type *type, bool is_function) const {
assert(function_name == "downcast_call");
return " default:\n"
" return false;\n"
" }\n"
"}\n\n";
}
std::string TD_TL_writer_hpp::gen_constructor_begin(int fields_num, const std::string &class_name,
bool is_default) const {
return "";
}
std::string TD_TL_writer_hpp::gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const {
return "";
}
std::string TD_TL_writer_hpp::gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const {
return "";
}
std::string TD_TL_writer_hpp::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const {
return "";
}
} // namespace td

105
td/generate/tl_writer_hpp.h Normal file
View File

@ -0,0 +1,105 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "tl_writer_td.h"
#include <cstdint>
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_hpp : public TD_TL_writer {
public:
TD_TL_writer_hpp(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type)
: TD_TL_writer(tl_name, string_type, bytes_type) {
}
bool is_documentation_generated() const override;
int get_additional_function_type(const std::string &additional_function_name) const override;
std::vector<std::string> get_additional_functions() const override;
std::string gen_base_type_class_name(int arity) const override;
std::string gen_base_tl_class_name() const override;
std::string gen_output_begin() const override;
std::string gen_output_end() const override;
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const override;
std::string gen_function_vars(const tl::tl_combinator *t, std::vector<tl::var_description> &vars) const override;
std::string gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const override;
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const override;
std::string gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int parser_type) const override;
std::string gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const override;
std::string gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_var_type_fetch(const tl::arg &a) const override;
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override;
std::string gen_function_result_type(const tl::tl_tree *result) const override;
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const override;
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
const tl::tl_tree *result) const override;
std::string gen_fetch_function_result_end() const override;
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
bool is_proxy) const override;
std::string gen_fetch_function_result_any_end(bool is_proxy) const override;
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_store_function_end(const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_fetch_switch_begin() const override;
std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override;
std::string gen_fetch_switch_end() const override;
std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override;
std::string gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override;
std::string gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const override;
std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const tl::tl_combinator *t, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_end(const std::string &function_name, const tl::tl_type *type,
bool is_function) const override;
};
} // namespace td

View File

@ -0,0 +1,542 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_java.h"
#include <cassert>
namespace td {
const int TD_TL_writer_java::MAX_ARITY;
const std::string TD_TL_writer_java::base_type_class_names[MAX_ARITY + 1] = {"Object"};
const std::string TD_TL_writer_java::base_tl_class_name = "Object";
const std::string TD_TL_writer_java::base_function_class_name = "Function";
int TD_TL_writer_java::get_max_arity() const {
return MAX_ARITY;
}
bool TD_TL_writer_java::is_built_in_simple_type(const std::string &name) const {
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
name == "String" || name == "Bytes";
}
bool TD_TL_writer_java::is_built_in_complex_type(const std::string &name) const {
return name == "Vector";
}
bool TD_TL_writer_java::is_type_bare(const tl::tl_type *t) const {
return t->simple_constructors == 1 || (is_built_in_simple_type(t->name) && t->name != "Bool") ||
is_built_in_complex_type(t->name);
}
bool TD_TL_writer_java::is_combinator_supported(const tl::tl_combinator *constructor) const {
if (!TL_writer::is_combinator_supported(constructor)) {
return false;
}
for (std::size_t i = 0; i < constructor->args.size(); i++) {
if (constructor->args[i].type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
return false;
}
}
return true;
}
int TD_TL_writer_java::get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const {
return 0;
}
int TD_TL_writer_java::get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const {
return storer_name == "StringBuilder";
}
std::vector<std::string> TD_TL_writer_java::get_parsers() const {
std::vector<std::string> parsers;
parsers.push_back("<inlined>");
return parsers;
}
std::vector<std::string> TD_TL_writer_java::get_storers() const {
std::vector<std::string> storers;
storers.push_back("StringBuilder");
return storers;
}
std::string TD_TL_writer_java::gen_base_tl_class_name() const {
return base_tl_class_name;
}
std::string TD_TL_writer_java::gen_base_type_class_name(int arity) const {
assert(arity == 0);
return base_type_class_names[arity];
}
std::string TD_TL_writer_java::gen_base_function_class_name() const {
return base_function_class_name;
}
std::string TD_TL_writer_java::gen_class_name(std::string name) const {
if (name == "Object" || name == "#") {
assert(false);
}
bool next_to_upper = true;
std::string result;
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
next_to_upper = true;
continue;
}
if (next_to_upper) {
result += to_upper(name[i]);
next_to_upper = false;
} else {
result += name[i];
}
}
return result;
}
std::string TD_TL_writer_java::gen_field_name(std::string name) const {
assert(name.size() > 0);
assert(is_alnum(name.back()));
bool next_to_upper = false;
std::string result;
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
next_to_upper = true;
continue;
}
if (next_to_upper) {
result += to_upper(name[i]);
next_to_upper = false;
} else {
result += name[i];
}
}
return result;
}
std::string TD_TL_writer_java::gen_var_name(const tl::var_description &desc) const {
assert(false);
return std::string();
}
std::string TD_TL_writer_java::gen_parameter_name(int index) const {
assert(false);
return std::string();
}
std::string TD_TL_writer_java::gen_type_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
if (name == "#") {
assert(false);
}
if (name == "Bool") {
return "boolean";
}
if (name == "Int32") {
return "int";
}
if (name == "Int53" || name == "Int64") {
return "long";
}
if (name == "Double") {
return "double";
}
if (name == "String") {
return "String";
}
if (name == "Bytes") {
return "byte[]";
}
if (name == "Vector") {
assert(t->arity == 1);
assert(tree_type->children.size() == 1);
assert(tree_type->children[0]->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return gen_type_name(child) + "[]";
}
assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name));
for (std::size_t i = 0; i < tree_type->children.size(); i++) {
assert(tree_type->children[i]->get_type() == tl::NODE_TYPE_NAT_CONST);
}
return gen_main_class_name(t);
}
std::string TD_TL_writer_java::gen_array_type_name(const tl::tl_tree_array *arr, const std::string &field_name) const {
assert(false);
return std::string();
}
std::string TD_TL_writer_java::gen_var_type_name() const {
return gen_base_function_class_name();
}
std::string TD_TL_writer_java::gen_int_const(const tl::tl_tree *tree_c,
const std::vector<tl::var_description> &vars) const {
assert(false);
return std::string();
}
std::string TD_TL_writer_java::gen_output_begin() const {
return "package " + package_name +
";\n\n"
"import java.util.Arrays;\n\n"
"public class " +
tl_name +
" {\n"
" private static final char[] HEX_CHARACTERS = \"0123456789ABCDEF\".toCharArray();\n\n";
}
std::string TD_TL_writer_java::gen_output_end() const {
return "}\n";
}
std::string TD_TL_writer_java::gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_java::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
return " public " + std::string(is_proxy ? "abstract " : "") + "static class " + class_name +
(class_name == gen_base_tl_class_name() ? std::string() : " extends " + base_class_name) + " {\n";
}
std::string TD_TL_writer_java::gen_class_end() const {
return " }\n\n";
}
std::string TD_TL_writer_java::gen_class_alias(const std::string &class_name, const std::string &alias_name) const {
return "";
}
std::string TD_TL_writer_java::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return " public " + type_name + " " + field_name + ";\n";
}
std::string TD_TL_writer_java::gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].index = static_cast<int>(i);
vars[i].is_stored = false;
vars[i].is_type = false;
vars[i].parameter_num = -1;
vars[i].function_arg_num = -1;
}
if (result_type != nullptr) {
assert(result_type->children.empty());
}
for (std::size_t i = 0; i < t->args.size(); i++) {
assert(t->args[i].type->get_type() != tl::NODE_TYPE_VAR_TYPE);
}
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_type);
}
return "";
}
std::string TD_TL_writer_java::gen_function_vars(const tl::tl_combinator *t,
std::vector<tl::var_description> &vars) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].index = static_cast<int>(i);
vars[i].is_stored = false;
vars[i].is_type = false;
vars[i].parameter_num = -1;
vars[i].function_arg_num = -1;
}
for (std::size_t i = 0; i < t->args.size(); i++) {
assert(t->args[i].type->get_type() != tl::NODE_TYPE_VAR_TYPE);
}
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_type);
}
return "";
}
std::string TD_TL_writer_java::gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const {
assert(result_type->children.empty());
return "";
}
std::string TD_TL_writer_java::gen_constructor_id_store(std::int32_t id, int storer_type) const {
return "";
}
std::string TD_TL_writer_java::gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars,
bool flat, int parser_type) const {
assert(parser_type >= 0);
assert(a.exist_var_num == -1);
assert(a.type->get_type() != tl::NODE_TYPE_VAR_TYPE);
assert(!(a.flags & tl::FLAG_EXCL));
assert(!(a.flags & tl::FLAG_OPT_VAR));
if (flat) {
// TODO
// return gen_field_fetch(const tl::arg &a, std::vector<tl::var_description> &vars, int num, bool flat);
}
assert(a.var_num == -1);
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
return "";
}
std::string TD_TL_writer_java::gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const {
assert(a.exist_var_num == -1);
assert(a.type->get_type() != tl::NODE_TYPE_VAR_TYPE);
assert(!(a.flags & tl::FLAG_EXCL));
assert(!(a.flags & tl::FLAG_OPT_VAR));
if (flat) {
// TODO
// return gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat, int storer_type);
}
assert(a.var_num == -1);
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
return storer_type == 1 ? " " + gen_type_store(gen_field_name(a.name), tree_type, vars, storer_type) + "\n"
: "";
}
std::string TD_TL_writer_java::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const {
return "";
}
std::string TD_TL_writer_java::gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const {
if (storer_type == 1) {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
std::string res;
res = "appendLine(s, shift).append(\"" + field_name + " = \")";
if (name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "Bool" ||
name == "String") {
res += ".append(" + field_name + ");";
} else if (name == "Bytes") {
res += ".append(\"bytes { \"); ";
res += "{ for (byte k : " + field_name + ") { ";
res += "int b = (int)k & 255; s.append(HEX_CHARACTERS[b >> 4]).append(HEX_CHARACTERS[b & 15]).append(' '); ";
res += "} } s.append('}');";
} else if (name == "Vector") {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
std::string vector_type = child->type->name;
res +=
".append(Arrays." + std::string(vector_type == "Vector" ? "deepTo" : "to") + "String(" + field_name + "));";
} else {
res += "; if (" + field_name + " != null) { " + field_name +
".toStringBuilder(shift, s); } else { s.append(\"null\"); }";
assert(tree_type->children.empty());
}
return res;
}
return "";
}
std::string TD_TL_writer_java::gen_var_type_fetch(const tl::arg &a) const {
assert(false);
return "";
}
std::string TD_TL_writer_java::gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const {
if (is_proxy) {
return class_name == gen_base_tl_class_name() ? "\n public abstract int getConstructor();\n" : "";
}
return "\n"
" public static final int CONSTRUCTOR = " +
int_to_string(id) +
";\n\n"
" @Override\n"
" public int getConstructor() {\n"
" return " +
int_to_string(id) +
";\n"
" }\n";
}
std::string TD_TL_writer_java::gen_function_result_type(const tl::tl_tree *result) const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int parser_type) const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_result_begin(const std::string &parser_name,
const std::string &class_name,
const tl::tl_tree *result) const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_result_end() const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_result_any_begin(const std::string &parser_name,
const std::string &class_name, bool is_proxy) const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_function_result_any_end(bool is_proxy) const {
return "";
}
std::string TD_TL_writer_java::gen_store_function_begin(const std::string &storer_name, const std::string &class_name,
int arity, std::vector<tl::var_description> &vars,
int storer_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].is_stored = false;
}
assert(arity == 0);
if (storer_type == 1) {
return "\n"
" @Override\n"
" protected void toStringBuilder(int shift, " +
storer_name +
" s) {\n"
" s.append(\"" +
class_name +
"\").append(\" {\");\n"
" shift += 2;\n";
}
if (storer_type == -1) {
return class_name == gen_base_tl_class_name() ? "\n"
" public String toString() {\n"
" StringBuilder s = new StringBuilder();\n"
" toStringBuilder(0, s);\n"
" return s.toString();\n"
" }\n"
"\n"
" protected " +
storer_name + " appendLine(" + storer_name +
" s, int shift) {\n"
" s.append('\\n');\n"
" for (int i = 0; i < shift; i++) {\n"
" s.append(' ');\n"
" }\n"
" return s;\n"
" }\n"
"\n"
" protected abstract void toStringBuilder(int shift, " +
storer_name + " s);\n"
: "";
}
assert(false);
return "";
}
std::string TD_TL_writer_java::gen_store_function_end(const std::vector<tl::var_description> &vars,
int storer_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored);
}
if (storer_type == 1) {
return " shift -= 2;\n"
" appendLine(s, shift).append(\"}\");\n"
" }\n";
}
return "";
}
std::string TD_TL_writer_java::gen_fetch_switch_begin() const {
return "";
}
std::string TD_TL_writer_java::gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const {
assert(arity == 0);
return "";
}
std::string TD_TL_writer_java::gen_fetch_switch_end() const {
return "";
}
std::string TD_TL_writer_java::gen_constructor_begin(int fields_num, const std::string &class_name,
bool is_default) const {
return "\n"
" public " +
class_name + "(";
}
std::string TD_TL_writer_java::gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const {
if (is_default) {
return "";
}
std::string field_type = gen_field_type(a);
if (field_type.empty()) {
return "";
}
if (field_type[field_type.size() - 1] != ' ') {
field_type += ' ';
}
return (field_num == 0 ? "" : ", ") + field_type + gen_field_name(a.name);
}
std::string TD_TL_writer_java::gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const {
std::string field_type = gen_field_type(a);
if (field_type.empty()) {
return "";
}
if (is_default) {
return (field_num == 0 ? ") {\n" : "");
}
return std::string(field_num == 0 ? ") {\n" : "") + " this." + gen_field_name(a.name) + " = " +
gen_field_name(a.name) + ";\n";
}
std::string TD_TL_writer_java::gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const {
if (fields_num == 0) {
return ") {\n"
" }\n";
}
return " }\n";
}
} // namespace td

View File

@ -0,0 +1,116 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/tl/tl_writer.h"
#include <cstdint>
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_java : public tl::TL_writer {
static const int MAX_ARITY = 0;
static const std::string base_type_class_names[MAX_ARITY + 1];
static const std::string base_tl_class_name;
static const std::string base_function_class_name;
const std::string package_name;
public:
TD_TL_writer_java(const std::string &tl_name, const std::string &package_name)
: TL_writer(tl_name), package_name(package_name) {
}
int get_max_arity() const override;
bool is_built_in_simple_type(const std::string &name) const override;
bool is_built_in_complex_type(const std::string &name) const override;
bool is_type_bare(const tl::tl_type *t) const override;
bool is_combinator_supported(const tl::tl_combinator *constructor) const override;
int get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const override;
int get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const override;
std::vector<std::string> get_parsers() const override;
std::vector<std::string> get_storers() const override;
std::string gen_base_tl_class_name() const override;
std::string gen_base_type_class_name(int arity) const override;
std::string gen_base_function_class_name() const override;
std::string gen_class_name(std::string name) const override;
std::string gen_field_name(std::string name) const override;
std::string gen_var_name(const tl::var_description &desc) const override;
std::string gen_parameter_name(int index) const override;
std::string gen_type_name(const tl::tl_tree_type *tree_type) const override;
std::string gen_array_type_name(const tl::tl_tree_array *arr, const std::string &field_name) const override;
std::string gen_var_type_name() const override;
std::string gen_int_const(const tl::tl_tree *tree_c, const std::vector<tl::var_description> &vars) const override;
std::string gen_output_begin() const override;
std::string gen_output_end() const override;
std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_class_end() const override;
std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_vars(const tl::tl_combinator *t, const tl::tl_tree_type *result_type,
std::vector<tl::var_description> &vars) const override;
std::string gen_function_vars(const tl::tl_combinator *t, std::vector<tl::var_description> &vars) const override;
std::string gen_uni(const tl::tl_tree_type *result_type, std::vector<tl::var_description> &vars,
bool check_negative) const override;
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const override;
std::string gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int parser_type) const override;
std::string gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const override;
std::string gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_var_type_fetch(const tl::arg &a) const override;
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override;
std::string gen_function_result_type(const tl::tl_tree *result) const override;
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const override;
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
const tl::tl_tree *result) const override;
std::string gen_fetch_function_result_end() const override;
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
bool is_proxy) const override;
std::string gen_fetch_function_result_any_end(bool is_proxy) const override;
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_store_function_end(const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_fetch_switch_begin() const override;
std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override;
std::string gen_fetch_switch_end() const override;
std::string gen_constructor_begin(int fields_num, const std::string &class_name, bool is_default) const override;
std::string gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_field_init(int field_num, const tl::arg &a, bool is_default) const override;
std::string gen_constructor_end(const tl::tl_combinator *t, int fields_num, bool is_default) const override;
};
} // namespace td

View File

@ -0,0 +1,695 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_jni_cpp.h"
#include <cassert>
#include <cstdio>
namespace td {
bool TD_TL_writer_jni_cpp::is_built_in_simple_type(const std::string &name) const {
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
name == "String" || name == "Bytes";
}
bool TD_TL_writer_jni_cpp::is_built_in_complex_type(const std::string &name) const {
return name == "Vector";
}
int TD_TL_writer_jni_cpp::get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const {
return 1;
}
int TD_TL_writer_jni_cpp::get_additional_function_type(const std::string &additional_function_name) const {
return 1;
}
std::vector<std::string> TD_TL_writer_jni_cpp::get_parsers() const {
std::vector<std::string> parsers;
parsers.push_back("JNIEnv *env, jobject");
return parsers;
}
std::vector<std::string> TD_TL_writer_jni_cpp::get_storers() const {
std::vector<std::string> storers;
storers.push_back("JNIEnv *env, jobject");
storers.push_back("TlStorerToString");
return storers;
}
std::vector<std::string> TD_TL_writer_jni_cpp::get_additional_functions() const {
std::vector<std::string> additional_functions;
additional_functions.push_back("init_jni_vars");
return additional_functions;
}
std::string TD_TL_writer_jni_cpp::gen_base_type_class_name(int arity) const {
assert(arity == 0);
return "Object";
}
std::string TD_TL_writer_jni_cpp::gen_base_tl_class_name() const {
return "Object";
}
std::string TD_TL_writer_jni_cpp::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
return "\n"
"jclass " +
class_name + "::Class;\n";
}
std::string TD_TL_writer_jni_cpp::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return "jfieldID " + class_name + "::" + field_name + "fieldID;\n";
}
std::string TD_TL_writer_jni_cpp::gen_constructor_id_store(std::int32_t id, int storer_type) const {
return "";
}
std::string TD_TL_writer_jni_cpp::gen_vector_fetch(std::string field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars,
int parser_type) const {
std::string vector_type = gen_type_name(t);
std::string type;
std::string Type;
if (vector_type == "bool") {
assert(false); // TODO
}
if (vector_type == "std::int32_t") {
type = "int";
Type = "Int";
}
if (vector_type == "std::int64_t") {
type = "long";
Type = "Long";
}
if (vector_type == "double") {
type = "double";
Type = "Double";
}
std::string res_begin;
std::string res_end;
std::string fetch_object;
if (field_name.empty()) {
res_begin = "({ std::vector<" + vector_type + "> res_tmp_; ";
field_name = "res_tmp_";
res_end = " std::move(res_tmp_); })";
fetch_object = "p; ";
} else {
fetch_object = "jni::fetch_object(env, p, " + field_name + "fieldID); ";
}
std::string resize_vector;
if (!type.empty()) {
resize_vector = field_name + ".resize(length_tmp_); ";
} else {
resize_vector = field_name + ".reserve(length_tmp_); ";
}
std::string res;
if (!type.empty()) {
res =
"{ "
"j" +
type + "Array arr_tmp_ = (j" + type + "Array)" + fetch_object +
"if (arr_tmp_) { "
"jsize length_tmp_ = env->GetArrayLength(arr_tmp_); " +
resize_vector + "env->Get" + Type + "ArrayRegion(arr_tmp_, 0, length_tmp_, reinterpret_cast<j" + type +
" *>(&" + field_name +
"[0])); "
"env->DeleteLocalRef(arr_tmp_); "
"} }";
} else if (vector_type == string_type) {
res =
"{ "
"jobjectArray arr_tmp_ = (jobjectArray)" +
fetch_object +
"if (arr_tmp_) { "
"jsize length_tmp_ = env->GetArrayLength(arr_tmp_); " +
resize_vector +
"for (jsize i_tmp_ = 0; i_tmp_ < length_tmp_; i_tmp_++) { "
"jstring str_tmp_ = (jstring)env->GetObjectArrayElement(arr_tmp_, i_tmp_); " +
field_name +
".push_back(jni::from_jstring(env, str_tmp_)); "
"env->DeleteLocalRef(str_tmp_); "
"} "
"env->DeleteLocalRef(arr_tmp_); "
"} }";
} else if (vector_type.compare(0, 11, "std::vector") == 0) {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(t->children[0]);
res =
"{ "
"jobjectArray arr_tmp_ = (jobjectArray)" +
fetch_object +
"if (arr_tmp_) { "
"jsize length_tmp_ = env->GetArrayLength(arr_tmp_); " +
resize_vector +
"for (jsize i_tmp_ = 0; i_tmp_ < length_tmp_; i_tmp_++) { "
"jobject p = env->GetObjectArrayElement(arr_tmp_, i_tmp_); " +
field_name + ".push_back(" + gen_vector_fetch("", child, vars, parser_type) +
"); "
"if (p) { env->DeleteLocalRef(p); "
"} } "
"env->DeleteLocalRef(arr_tmp_); "
"} }";
} else if (vector_type == bytes_type) {
std::fprintf(stderr, "Vector of Bytes is not supported\n");
assert(false);
} else {
assert(vector_type.compare(0, 10, "object_ptr") == 0);
res =
"{ "
"jobjectArray arr_tmp_ = (jobjectArray)" +
fetch_object +
"if (arr_tmp_) { "
"jsize length_tmp_ = env->GetArrayLength(arr_tmp_); " +
resize_vector +
"for (jsize i_tmp_ = 0; i_tmp_ < length_tmp_; i_tmp_++) { "
"jobject o_ = env->GetObjectArrayElement(arr_tmp_, i_tmp_); " +
field_name + ".push_back(" + gen_main_class_name(t->type) +
"::fetch(env, o_)); "
"if (o_) { env->DeleteLocalRef(o_); "
"} } "
"env->DeleteLocalRef(arr_tmp_); "
"} }";
}
return res_begin + res + res_end;
}
std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
assert(!(t->flags & tl::FLAG_DEFAULT_CONSTRUCTOR));
assert(parser_type == 1);
if (!(tree_type->flags & tl::FLAG_BARE)) {
if (is_type_bare(t)) {
if (field_name != "") {
std::fprintf(stderr, "Do not use non-bare fields with bare type %s\n", name.c_str());
// assert(false);
}
}
} else {
assert(is_type_bare(t));
}
std::string res_begin;
if (!field_name.empty()) {
res_begin = field_name + " = ";
}
std::string res;
assert(name != "#");
if (field_name.empty()) {
if (name == "Bool") {
return "env->CallObjectMethod(p, jni::BooleanGetValueMethodID)";
} else if (name == "Int32") {
return "env->CallObjectMethod(p, jni::IntegerGetValueMethodID)";
} else if (name == "Int53" || name == "Int64") {
return "env->CallObjectMethod(p, jni::LongGetValueMethodID)";
} else if (name == "Double") {
return "env->CallObjectMethod(p, jni::DoubleGetValueMethodID)";
} else if (name == "String") {
return "jni::from_jstring(env, (jstring)p)";
} else if (name == "Bytes") {
return "jni::from_bytes(env, (jbyteArray)p)";
}
}
if (name == "Bool") {
res = "env->GetBooleanField(p, " + field_name + "fieldID)";
} else if (name == "Int32") {
res = "env->GetIntField(p, " + field_name + "fieldID)";
} else if (name == "Int53" || name == "Int64") {
res = "env->GetLongField(p, " + field_name + "fieldID)";
} else if (name == "Double") {
res = "env->GetDoubleField(p, " + field_name + "fieldID)";
} else if (name == "String") {
res = "jni::fetch_string(env, p, " + field_name + "fieldID)";
} else if (name == "Bytes") {
res = "jni::from_bytes(env, (jbyteArray)jni::fetch_object(env, p, " + field_name + "fieldID))";
} else if (name == "Vector") {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return gen_vector_fetch(field_name, child, vars, parser_type);
} else {
if (field_name == "") {
return gen_main_class_name(tree_type->type) + "::fetch(env, p)";
}
res = "({jobject jobject_tmp_ = jni::fetch_object(env, p, " + field_name + "fieldID); " +
gen_main_class_name(tree_type->type) + "::fetch(env, jobject_tmp_);})";
}
return res_begin + res;
}
std::string TD_TL_writer_jni_cpp::gen_field_fetch(int field_num, const tl::arg &a,
std::vector<tl::var_description> &vars, bool flat,
int parser_type) const {
assert(parser_type >= 0);
std::string field_name = (parser_type == 0 ? (field_num == 0 ? ": " : ", ") : "res->") + gen_field_name(a.name);
assert(a.exist_var_num == -1);
if (a.type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
assert(parser_type == 1);
const tl::tl_tree_var_type *t = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags == tl::FLAG_EXCL);
assert(a.var_num == -1);
assert(t->var_num >= 0);
assert(vars[t->var_num].is_type);
assert(!vars[t->var_num].is_stored);
vars[t->var_num].is_stored = true;
assert(false && "not supported");
return " " + field_name + " = " + gen_base_function_class_name() + "::fetch(env, p);\n";
}
assert(!(a.flags & tl::FLAG_EXCL));
assert(!(a.flags & tl::FLAG_OPT_VAR));
if (flat) {
// TODO
// return gen_field_fetch(const tl::arg &a, std::vector<tl::var_description> &vars, int num, bool flat);
}
assert(a.var_num == -1);
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
assert(parser_type != 0);
return " " + gen_type_fetch(field_name, tree_type, vars, parser_type) + ";\n";
}
std::string TD_TL_writer_jni_cpp::get_pretty_field_name(std::string field_name) const {
return gen_java_field_name(TD_TL_writer_cpp::get_pretty_field_name(field_name));
}
std::string TD_TL_writer_jni_cpp::get_pretty_class_name(std::string class_name) const {
if (class_name == "vector") {
return "Array";
}
return gen_basic_java_class_name(class_name);
}
std::string TD_TL_writer_jni_cpp::gen_vector_store(const std::string &field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars,
int storer_type) const {
if (storer_type == 1) {
return TD_TL_writer_cpp::gen_vector_store(field_name, t, vars, storer_type);
}
std::string vector_type = gen_type_name(t);
if (vector_type == "bool") {
assert(false); // TODO
}
if (vector_type == "std::int32_t" || vector_type == "std::int64_t" || vector_type == "double" ||
vector_type == string_type || vector_type.compare(0, 11, "std::vector") == 0 ||
vector_type.compare(0, 10, "object_ptr") == 0) {
return "{ "
"auto arr_tmp_ = jni::store_vector(env, " +
field_name +
"); "
"if (arr_tmp_) { "
"env->SetObjectField(s, " +
field_name +
"fieldID, arr_tmp_); "
"env->DeleteLocalRef(arr_tmp_); "
"} }";
}
if (vector_type == bytes_type) {
std::fprintf(stderr, "Vector of Bytes is not supported\n");
assert(false);
}
assert(false);
return "";
}
std::string TD_TL_writer_jni_cpp::gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
assert(!field_name.empty());
assert(!(t->flags & tl::FLAG_DEFAULT_CONSTRUCTOR));
if (!(tree_type->flags & tl::FLAG_BARE)) {
if (storer_type == 0) {
if (is_type_bare(t)) {
std::fprintf(stderr, "Do not use non-bare fields with bare type %s\n", name.c_str());
// assert(false);
}
}
} else {
assert(is_type_bare(t));
}
std::string res;
if (name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "Bool" || name == "String") {
if (storer_type == 1) {
res = "s.store_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");";
} else if (name == "Bool") {
res = "env->SetBooleanField(s, " + field_name + "fieldID, " + field_name + ");";
} else if (name == "Int32") {
res = "env->SetIntField(s, " + field_name + "fieldID, " + field_name + ");";
} else if (name == "Int53" || name == "Int64") {
res = "env->SetLongField(s, " + field_name + "fieldID, " + field_name + ");";
} else if (name == "Double") {
res = "env->SetDoubleField(s, " + field_name + "fieldID, " + field_name + ");";
} else if (name == "String") {
res = "{ jstring nextString = jni::to_jstring(env, " + field_name +
"); if (nextString) { env->SetObjectField(s, " + field_name +
"fieldID, nextString); env->DeleteLocalRef(nextString); } }";
} else {
assert(false);
}
} else if (name == "Bytes") {
if (storer_type == 1) {
res = "s.store_bytes_field(\"" + get_pretty_field_name(field_name) + "\", " + field_name + ");";
} else {
res = "{ jbyteArray nextBytes = jni::to_bytes(env, " + field_name +
"); if (nextBytes) { env->SetObjectField(s, " + field_name +
"fieldID, nextBytes); env->DeleteLocalRef(nextBytes); } }";
}
} else if (name == "Vector") {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
res = gen_vector_store(field_name, child, vars, storer_type);
} else {
if (storer_type == 1) {
res = "if (" + field_name + " == nullptr) { s.store_field(\"" + get_pretty_field_name(field_name) +
"\", \"null\"); } else { " + field_name + "->store(s, \"" + get_pretty_field_name(field_name) + "\"); }";
} else {
res = "if (" + field_name + " != nullptr) { jobject next; " + field_name +
"->store(env, next); if (next) { env->SetObjectField(s, " + field_name +
"fieldID, next); env->DeleteLocalRef(next); } }";
}
assert(tree_type->children.empty());
}
return res;
}
std::string TD_TL_writer_jni_cpp::gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const {
std::string field_name = gen_field_name(a.name);
std::string shift = storer_type == 1 ? " " : " ";
assert(a.exist_var_num == -1);
if (a.type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
const tl::tl_tree_var_type *t = static_cast<const tl::tl_tree_var_type *>(a.type);
assert(a.flags == tl::FLAG_EXCL);
assert(a.var_num == -1);
assert(t->var_num >= 0);
assert(!vars[t->var_num].is_stored);
vars[t->var_num].is_stored = true;
assert(vars[t->var_num].is_type);
assert(false && "not supported");
return shift + field_name + "->store(env, s);\n";
}
assert(!(a.flags & tl::FLAG_EXCL));
assert(!(a.flags & tl::FLAG_OPT_VAR));
if (flat) {
// TODO
// return gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat, int storer_type);
}
assert(a.var_num == -1);
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
return shift + gen_type_store(field_name, tree_type, vars, storer_type) + "\n";
}
std::string TD_TL_writer_jni_cpp::gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const {
if (is_proxy) {
return "";
}
return "\nconst std::int32_t " + class_name + "::ID;\n";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_begin(const std::string &parser_name,
const std::string &class_name, int arity,
std::vector<tl::var_description> &vars,
int parser_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored == false);
}
std::string fetched_type = "object_ptr<" + class_name + "> ";
assert(arity == 0);
assert(parser_type != 0);
return "\n" + fetched_type + class_name + "::fetch(" + parser_name + " &p) {\n" +
(parser_type == -1 ? ""
: " if (p == nullptr) return nullptr;\n"
" " +
fetched_type + "res = make_object<" + class_name + ">();\n");
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
assert(vars[i].is_stored);
}
assert(parser_type != 0);
if (parser_type == -1) {
return "}\n";
}
return " return res;\n"
"}\n";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_result_begin(const std::string &parser_name,
const std::string &class_name,
const tl::tl_tree *result) const {
return "\n" + class_name + "::ReturnType " + class_name + "::fetch_result(" + parser_name +
" &p) {\n"
" if (p == nullptr) return ReturnType();\n" +
" return ";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_result_end() const {
return ";\n"
"}\n";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_result_any_begin(const std::string &parser_name,
const std::string &class_name,
bool is_proxy) const {
return "";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_function_result_any_end(bool is_proxy) const {
return "";
}
std::string TD_TL_writer_jni_cpp::gen_store_function_begin(const std::string &storer_name,
const std::string &class_name, int arity,
std::vector<tl::var_description> &vars,
int storer_type) const {
for (std::size_t i = 0; i < vars.size(); i++) {
vars[i].is_stored = false;
}
if (storer_type == -1) {
return "";
}
assert(arity == 0);
return "\n"
"void " +
class_name + "::store(" + storer_name + " &s" +
std::string(storer_type <= 0 ? "" : ", const char *field_name") + ") const {\n" +
(storer_type <= 0 ? " if (!(s = env->AllocObject(Class))) { return; }\n"
: " if (!LOG_IS_STRIPPED(ERROR)) {\n"
" s.store_class_begin(field_name, \"" +
get_pretty_class_name(class_name) + "\");\n");
}
std::string TD_TL_writer_jni_cpp::gen_fetch_switch_begin() const {
return " if (p == nullptr) { return nullptr; }\n"
" switch (env->CallIntMethod(p, jni::GetConstructorID)) {\n";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const {
assert(arity == 0);
return " case " + gen_class_name(t->name) +
"::ID:\n"
" return " +
gen_class_name(t->name) + "::fetch(env, p);\n";
}
std::string TD_TL_writer_jni_cpp::gen_fetch_switch_end() const {
return " default:\n"
" UNREACHABLE();\n"
" return nullptr;\n"
" }\n";
}
std::string TD_TL_writer_jni_cpp::gen_java_field_name(std::string name) const {
std::string result;
bool next_to_upper = false;
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
next_to_upper = true;
continue;
}
if (next_to_upper) {
result += to_upper(name[i]);
next_to_upper = false;
} else {
result += name[i];
}
}
return result;
}
std::string TD_TL_writer_jni_cpp::gen_basic_java_class_name(std::string name) const {
std::string result;
bool next_to_upper = true;
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
next_to_upper = true;
continue;
}
if (next_to_upper) {
result += to_upper(name[i]);
next_to_upper = false;
} else {
result += name[i];
}
}
return result;
}
std::string TD_TL_writer_jni_cpp::gen_java_class_name(std::string name) const {
return package_name + "/" + "TdApi" + "$" + gen_basic_java_class_name(name);
}
std::string TD_TL_writer_jni_cpp::gen_type_signature(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
assert(name != "#");
assert(name != gen_base_tl_class_name());
if (name == "Bool") {
return "Z";
} else if (name == "Int32") {
return "I";
} else if (name == "Int53" || name == "Int64") {
return "J";
} else if (name == "Double") {
return "D";
} else if (name == "String") {
return "Ljava/lang/String;";
} else if (name == "Bytes") {
return "[B";
} else if (name == "Vector") {
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return "[" + gen_type_signature(child);
} else {
return "L" + gen_java_class_name(gen_main_class_name(t)) + ";";
}
assert(false);
return "";
}
std::string TD_TL_writer_jni_cpp::gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const {
assert(function_name == "init_jni_vars");
std::string class_name = gen_class_name(t->name);
std::string class_name_class = "Class";
std::string res =
"\n"
"void " +
class_name + "::" + function_name +
"(JNIEnv *env) {\n"
" " +
class_name_class + " = jni::get_jclass(env, \"" + gen_java_class_name(gen_class_name(t->name)) + "\");\n";
if (t->args.size()) {
res +=
"\n"
" if (" +
class_name_class + ") {\n";
for (std::size_t i = 0; i < t->args.size(); i++) {
const tl::arg &a = t->args[i];
assert(a.type->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *tree_type = static_cast<tl::tl_tree_type *>(a.type);
std::string field_name = gen_field_name(a.name);
assert(field_name.size());
std::string java_field_name = gen_java_field_name(std::string(field_name, 0, field_name.size() - 1));
res += " " + field_name + "fieldID = jni::get_field_id(env, " + class_name_class + ", \"" + java_field_name +
"\", \"" + gen_type_signature(tree_type) + "\");\n";
}
res += " }\n";
}
res += "}\n";
return res;
}
std::string TD_TL_writer_jni_cpp::gen_additional_proxy_function_begin(const std::string &function_name,
const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const {
assert(function_name == "init_jni_vars");
assert(arity == 0);
return "\n"
"void " +
class_name + "::" + function_name +
"(JNIEnv *env) {\n"
" Class = jni::get_jclass(env, \"" +
gen_java_class_name(class_name) + "\");\n";
}
std::string TD_TL_writer_jni_cpp::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type,
const std::string &class_name, int arity) const {
assert(function_name == "init_jni_vars");
assert(arity == 0);
return " " + class_name + "::" + function_name + "(env);\n";
}
std::string TD_TL_writer_jni_cpp::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type,
const tl::tl_combinator *t, int arity,
bool is_function) const {
assert(function_name == "init_jni_vars");
assert(arity == 0);
return " " + gen_class_name(t->name) + "::" + function_name + "(env);\n";
}
std::string TD_TL_writer_jni_cpp::gen_additional_proxy_function_end(const std::string &function_name,
const tl::tl_type *type, bool is_function) const {
assert(function_name == "init_jni_vars");
return "}\n";
}
} // namespace td

View File

@ -0,0 +1,114 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "tl_writer_cpp.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_jni_cpp : public TD_TL_writer_cpp {
std::string gen_vector_fetch(std::string field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars, int parser_type) const;
std::string gen_vector_store(const std::string &field_name, const tl::tl_tree_type *t,
const std::vector<tl::var_description> &vars, int storer_type) const;
std::string package_name;
std::string gen_java_field_name(std::string name) const;
std::string gen_basic_java_class_name(std::string name) const;
std::string gen_java_class_name(std::string name) const;
std::string gen_type_signature(const tl::tl_tree_type *tree_type) const;
std::string get_pretty_field_name(std::string field_name) const override;
std::string get_pretty_class_name(std::string class_name) const override;
public:
TD_TL_writer_jni_cpp(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type,
const std::vector<std::string> &ext_include, const std::string &package_name_)
: TD_TL_writer_cpp(tl_name, string_type, bytes_type, ext_include), package_name(package_name_) {
for (std::size_t i = 0; i < package_name.size(); i++) {
if (package_name[i] == '.') {
package_name[i] = '/';
}
}
}
bool is_built_in_simple_type(const std::string &name) const override;
bool is_built_in_complex_type(const std::string &name) const override;
int get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const override;
int get_additional_function_type(const std::string &additional_function_name) const override;
std::vector<std::string> get_parsers() const override;
std::vector<std::string> get_storers() const override;
std::vector<std::string> get_additional_functions() const override;
std::string gen_base_type_class_name(int arity) const override;
std::string gen_base_tl_class_name() const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_constructor_id_store(std::int32_t id, int storer_type) const override;
std::string gen_field_fetch(int field_num, const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int parser_type) const override;
std::string gen_field_store(const tl::arg &a, std::vector<tl::var_description> &vars, bool flat,
int storer_type) const override;
std::string gen_type_fetch(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_type_store(const std::string &field_name, const tl::tl_tree_type *tree_type,
const std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const override;
std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int parser_type) const override;
std::string gen_fetch_function_end(int field_num, const std::vector<tl::var_description> &vars,
int parser_type) const override;
std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name,
const tl::tl_tree *result) const override;
std::string gen_fetch_function_result_end() const override;
std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name,
bool is_proxy) const override;
std::string gen_fetch_function_result_any_end(bool is_proxy) const override;
std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity,
std::vector<tl::var_description> &vars, int storer_type) const override;
std::string gen_fetch_switch_begin() const override;
std::string gen_fetch_switch_case(const tl::tl_combinator *t, int arity) const override;
std::string gen_fetch_switch_end() const override;
std::string gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const override;
std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const tl::tl_combinator *t, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_end(const std::string &function_name, const tl::tl_type *type,
bool is_function) const override;
};
} // namespace td

View File

@ -0,0 +1,192 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_jni_h.h"
#include <cassert>
namespace td {
bool TD_TL_writer_jni_h::is_built_in_simple_type(const std::string &name) const {
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
name == "String" || name == "Bytes";
}
bool TD_TL_writer_jni_h::is_built_in_complex_type(const std::string &name) const {
return name == "Vector";
}
int TD_TL_writer_jni_h::get_additional_function_type(const std::string &additional_function_name) const {
if (additional_function_name == "init_jni_vars") {
return 1;
}
return TD_TL_writer_h::get_additional_function_type(additional_function_name);
}
int TD_TL_writer_jni_h::get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const {
return 1;
}
std::vector<std::string> TD_TL_writer_jni_h::get_parsers() const {
std::vector<std::string> parsers;
parsers.push_back("JNIEnv *env, jobject");
return parsers;
}
std::vector<std::string> TD_TL_writer_jni_h::get_storers() const {
std::vector<std::string> storers;
storers.push_back("JNIEnv *env, jobject");
storers.push_back("TlStorerToString");
return storers;
}
std::vector<std::string> TD_TL_writer_jni_h::get_additional_functions() const {
std::vector<std::string> additional_functions = TD_TL_writer_h::get_additional_functions();
additional_functions.push_back("init_jni_vars");
return additional_functions;
}
std::string TD_TL_writer_jni_h::gen_base_type_class_name(int arity) const {
assert(arity == 0);
return "Object";
}
std::string TD_TL_writer_jni_h::gen_base_tl_class_name() const {
return "Object";
}
std::string TD_TL_writer_jni_h::gen_output_begin() const {
std::string ext_include_str;
for (auto &it : ext_include) {
ext_include_str += "#include " + it + "\n";
}
return "#pragma once\n\n"
"#include \"td/tl/TlObject.h\"\n\n"
"#include <cstdint>\n"
"#include <memory>\n"
"#include <utility>\n"
"#include <vector>\n\n"
"#include <jni.h>\n\n" +
ext_include_str +
"\n"
"namespace td {\n" +
forward_declaration("TlStorerToString") +
"\n"
"namespace " +
tl_name +
" {\n\n"
"class " +
gen_base_tl_class_name() +
";\n"
"using BaseObject = " +
gen_base_tl_class_name() +
";\n\n"
"template <class Type>\n"
"using object_ptr = ::td::tl_object_ptr<Type>;\n\n"
"template <class Type, class... Args>\n"
"object_ptr<Type> make_object(Args &&... args) {\n"
" return object_ptr<Type>(new Type(std::forward<Args>(args)...));\n"
"}\n\n"
"template <class ToType, class FromType>\n"
"object_ptr<ToType> move_object_as(FromType &&from) {\n"
" return object_ptr<ToType>(static_cast<ToType *>(from.release()));\n"
"}\n\n"
"std::string to_string(const BaseObject &value);\n\n"
"template <class T>\n"
"std::string to_string(const object_ptr<T> &value) {\n"
" if (value == nullptr) {\n"
" return \"null\";\n"
" }\n"
"\n"
" return to_string(*value);\n"
"}\n\n";
}
std::string TD_TL_writer_jni_h::gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const {
if (class_name == gen_base_tl_class_name()) {
return "class " + class_name +
" {\n"
" public:\n"
" virtual ~" +
class_name + "() {}\n\n" +
" virtual void store(JNIEnv *env, jobject &s) const {\n"
" }\n\n"
" virtual void store(TlStorerToString &s, const char *field_name) const = 0;\n\n"
" static jclass Class;\n";
}
return "class " + class_name + (!is_proxy ? " final " : "") + ": public " + base_class_name +
" {\n"
" public:\n"
" static jclass Class;\n";
}
std::string TD_TL_writer_jni_h::gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const {
return TD_TL_writer_h::gen_field_definition(class_name, type_name, field_name) + " static jfieldID " + field_name +
"fieldID;\n";
}
std::string TD_TL_writer_jni_h::gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const {
if (function_name == "init_jni_vars") {
return "\n"
" static void " +
function_name + "(JNIEnv *env);\n";
}
return TD_TL_writer_h::gen_additional_function(function_name, t, is_function);
}
std::string TD_TL_writer_jni_h::gen_additional_proxy_function_begin(const std::string &function_name,
const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const {
if (function_name == "init_jni_vars") {
return "\n"
" static void " +
function_name + "(JNIEnv *env);\n";
}
return TD_TL_writer_h::gen_additional_proxy_function_begin(function_name, type, class_name, arity, is_function);
}
std::string TD_TL_writer_jni_h::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type,
const std::string &class_name, int arity) const {
if (function_name == "init_jni_vars") {
return "";
}
return TD_TL_writer_h::gen_additional_proxy_function_case(function_name, type, class_name, arity);
}
std::string TD_TL_writer_jni_h::gen_additional_proxy_function_case(const std::string &function_name,
const tl::tl_type *type, const tl::tl_combinator *t,
int arity, bool is_function) const {
if (function_name == "init_jni_vars") {
return "";
}
return TD_TL_writer_h::gen_additional_proxy_function_case(function_name, type, t, arity, is_function);
}
std::string TD_TL_writer_jni_h::gen_additional_proxy_function_end(const std::string &function_name,
const tl::tl_type *type, bool is_function) const {
if (function_name == "init_jni_vars") {
return "";
}
return TD_TL_writer_h::gen_additional_proxy_function_end(function_name, type, is_function);
}
} // namespace td

View File

@ -0,0 +1,57 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "tl_writer_h.h"
#include <string>
#include <vector>
namespace td {
class TD_TL_writer_jni_h : public TD_TL_writer_h {
public:
TD_TL_writer_jni_h(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type,
const std::vector<std::string> &ext_include)
: TD_TL_writer_h(tl_name, string_type, bytes_type, ext_include) {
}
bool is_built_in_simple_type(const std::string &name) const override;
bool is_built_in_complex_type(const std::string &name) const override;
int get_parser_type(const tl::tl_combinator *t, const std::string &parser_name) const override;
int get_additional_function_type(const std::string &additional_function_name) const override;
std::vector<std::string> get_parsers() const override;
std::vector<std::string> get_storers() const override;
std::vector<std::string> get_additional_functions() const override;
std::string gen_base_type_class_name(int arity) const override;
std::string gen_base_tl_class_name() const override;
std::string gen_output_begin() const override;
std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name,
bool is_proxy) const override;
std::string gen_field_definition(const std::string &class_name, const std::string &type_name,
const std::string &field_name) const override;
std::string gen_additional_function(const std::string &function_name, const tl::tl_combinator *t,
bool is_function) const override;
std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const std::string &class_name, int arity) const override;
std::string gen_additional_proxy_function_case(const std::string &function_name, const tl::tl_type *type,
const tl::tl_combinator *t, int arity,
bool is_function) const override;
std::string gen_additional_proxy_function_end(const std::string &function_name, const tl::tl_type *type,
bool is_function) const override;
};
} // namespace td

View File

@ -0,0 +1,251 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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 "tl_writer_td.h"
#include <cassert>
namespace td {
const int TD_TL_writer::MAX_ARITY;
const std::string TD_TL_writer::base_type_class_names[MAX_ARITY + 1] = {"Object"};
const std::string TD_TL_writer::base_tl_class_name = "TlObject";
const std::string TD_TL_writer::base_function_class_name = "Function";
int TD_TL_writer::get_max_arity() const {
return MAX_ARITY;
}
bool TD_TL_writer::is_built_in_simple_type(const std::string &name) const {
return name == "True" || name == "Bool" || name == "Int" || name == "Long" || name == "Double" || name == "String" ||
name == "Int32" || name == "Int53" || name == "Int64" || name == "Int128" || name == "Int256" ||
name == "Bytes";
}
bool TD_TL_writer::is_built_in_complex_type(const std::string &name) const {
return name == "Vector";
}
bool TD_TL_writer::is_type_bare(const tl::tl_type *t) const {
return t->simple_constructors <= 1 || (is_built_in_simple_type(t->name) && t->name != "Bool") ||
is_built_in_complex_type(t->name);
}
bool TD_TL_writer::is_combinator_supported(const tl::tl_combinator *constructor) const {
if (!TL_writer::is_combinator_supported(constructor)) {
return false;
}
for (std::size_t i = 0; i < constructor->args.size(); i++) {
if (constructor->args[i].type->get_type() == tl::NODE_TYPE_VAR_TYPE) {
return false;
}
}
return true;
}
int TD_TL_writer::get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const {
return storer_name == "TlStorerToString";
}
tl::TL_writer::Mode TD_TL_writer::get_parser_mode(int type) const {
if (tl_name == "td_api") {
return Server;
}
if (tl_name == "telegram_api") {
return Client;
}
return All;
}
tl::TL_writer::Mode TD_TL_writer::get_storer_mode(int type) const {
if (type == 1) {
return All;
}
if (tl_name == "td_api") {
return Server;
}
if (tl_name == "telegram_api") {
return Client;
}
return All;
}
std::vector<std::string> TD_TL_writer::get_parsers() const {
std::vector<std::string> parsers;
if (tl_name == "telegram_api") {
parsers.push_back("TlBufferParser");
} else if (tl_name == "mtproto_api" || tl_name == "secret_api") {
parsers.push_back("TlParser");
}
return parsers;
}
std::vector<std::string> TD_TL_writer::get_storers() const {
std::vector<std::string> storers;
if (tl_name == "telegram_api" || tl_name == "mtproto_api" || tl_name == "secret_api") {
storers.push_back("TlStorerCalcLength");
storers.push_back("TlStorerUnsafe");
}
storers.push_back("TlStorerToString");
return storers;
}
std::string TD_TL_writer::gen_base_tl_class_name() const {
return base_tl_class_name;
}
std::string TD_TL_writer::gen_base_type_class_name(int arity) const {
assert(arity == 0);
return base_type_class_names[arity];
}
std::string TD_TL_writer::gen_base_function_class_name() const {
return base_function_class_name;
}
std::string TD_TL_writer::gen_class_name(std::string name) const {
if (name == "Object") {
assert(false);
}
if (name == "#") {
return "std::int32_t";
}
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
name[i] = '_';
}
}
return name;
}
std::string TD_TL_writer::gen_field_name(std::string name) const {
for (std::size_t i = 0; i < name.size(); i++) {
if (!is_alnum(name[i])) {
name[i] = '_';
}
}
assert(name.size() > 0);
assert(name[name.size() - 1] != '_');
return name + "_";
}
std::string TD_TL_writer::gen_var_name(const tl::var_description &desc) const {
assert(!desc.is_type);
if (desc.parameter_num != -1) {
assert(false);
}
return "var" + int_to_string(desc.index);
}
std::string TD_TL_writer::gen_parameter_name(int index) const {
assert(false);
return std::string();
}
std::string TD_TL_writer::gen_type_name(const tl::tl_tree_type *tree_type) const {
const tl::tl_type *t = tree_type->type;
const std::string &name = t->name;
if (name == "#") {
return "std::int32_t";
}
if (name == "True") {
return "bool";
}
if (name == "Bool") {
return "bool";
}
if (name == "Int" || name == "Int32") {
return "std::int32_t";
}
if (name == "Long" || name == "Int53" || name == "Int64") {
return "std::int64_t";
}
if (name == "Double") {
return "double";
}
if (name == "String") {
return string_type;
}
if (name == "Int128") {
return "UInt128";
}
if (name == "Int256") {
return "UInt256";
}
if (name == "Bytes") {
return bytes_type;
}
if (name == "Vector") {
assert(t->arity == 1);
assert(tree_type->children.size() == 1);
assert(tree_type->children[0]->get_type() == tl::NODE_TYPE_TYPE);
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
return "std::vector<" + gen_type_name(child) + ">";
}
assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name));
for (std::size_t i = 0; i < tree_type->children.size(); i++) {
assert(tree_type->children[i]->get_type() == tl::NODE_TYPE_NAT_CONST);
}
return "object_ptr<" + gen_main_class_name(t) + ">";
}
std::string TD_TL_writer::gen_array_type_name(const tl::tl_tree_array *arr, const std::string &field_name) const {
assert(false);
return std::string();
}
std::string TD_TL_writer::gen_var_type_name() const {
return "object_ptr<" + gen_base_function_class_name() + ">";
}
std::string TD_TL_writer::gen_int_const(const tl::tl_tree *tree_c, const std::vector<tl::var_description> &vars) const {
assert(false);
return std::string();
}
std::string TD_TL_writer::gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const {
if (is_default) {
return "";
}
std::string field_type = gen_field_type(a);
if (field_type.empty()) {
return "";
}
if (field_type[field_type.size() - 1] != ' ') {
field_type += ' ';
}
std::string res = (field_num == 0 ? "" : ", ");
if (field_type == "bool " || field_type == "std::int32_t " || field_type == "std::int64_t " ||
field_type == "double ") {
res += field_type;
} else if (field_type == "UInt128 " || field_type == "UInt256 " || field_type == string_type + " ") {
res += field_type + "const &";
} else if (field_type.compare(0, 11, "std::vector") == 0 || field_type == bytes_type + " ") {
res += field_type + "&&";
} else if (field_type.compare(0, 10, "object_ptr") == 0) {
res += field_type + "&&";
} else {
assert(false && "unreachable");
}
return res + gen_field_name(a.name);
}
} // namespace td

View File

@ -0,0 +1,61 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/tl/tl_writer.h"
#include <string>
#include <vector>
namespace td {
class TD_TL_writer : public tl::TL_writer {
static const int MAX_ARITY = 0;
static const std::string base_type_class_names[MAX_ARITY + 1];
static const std::string base_tl_class_name;
static const std::string base_function_class_name;
protected:
const std::string string_type;
const std::string bytes_type;
public:
TD_TL_writer(const std::string &tl_name, const std::string &string_type, const std::string &bytes_type)
: TL_writer(tl_name), string_type(string_type), bytes_type(bytes_type) {
}
int get_max_arity() const override;
bool is_built_in_simple_type(const std::string &name) const override;
bool is_built_in_complex_type(const std::string &name) const override;
bool is_type_bare(const tl::tl_type *t) const override;
bool is_combinator_supported(const tl::tl_combinator *constructor) const override;
int get_storer_type(const tl::tl_combinator *t, const std::string &storer_name) const override;
Mode get_parser_mode(int type) const override;
Mode get_storer_mode(int type) const override;
std::vector<std::string> get_parsers() const override;
std::vector<std::string> get_storers() const override;
std::string gen_base_tl_class_name() const override;
std::string gen_base_type_class_name(int arity) const override;
std::string gen_base_function_class_name() const override;
std::string gen_class_name(std::string name) const override;
std::string gen_field_name(std::string name) const override;
std::string gen_var_name(const tl::var_description &desc) const override;
std::string gen_parameter_name(int index) const override;
std::string gen_type_name(const tl::tl_tree_type *tree_type) const override;
std::string gen_array_type_name(const tl::tl_tree_array *arr, const std::string &field_name) const override;
std::string gen_var_type_name() const override;
std::string gen_int_const(const tl::tl_tree *tree_c, const std::vector<tl::var_description> &vars) const override;
std::string gen_constructor_parameter(int field_num, const tl::arg &a, bool is_default) const override;
};
} // namespace td

399
td/mtproto/AuthData.h Normal file
View File

@ -0,0 +1,399 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/AuthKey.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/Time.h"
#include <algorithm>
#include <set>
namespace td {
namespace mtproto {
struct ServerSalt {
int64 salt;
double valid_since;
double valid_until;
};
template <class StorerT>
void store(const ServerSalt &salt, StorerT &storer) {
storer.template store_binary<int64>(salt.salt);
storer.template store_binary<double>(salt.valid_since);
storer.template store_binary<double>(salt.valid_until);
}
template <class ParserT>
void parse(ServerSalt &salt, ParserT &parser) {
salt.salt = parser.fetch_long();
salt.valid_since = parser.fetch_double();
salt.valid_until = parser.fetch_double();
}
class MessageIdDuplicateChecker {
public:
Status check(int64 message_id) {
// In addition, the identifiers (msg_id) of the last N messages received from the other side must be stored, and if
// a message comes in with msg_id lower than all or equal to any of the stored values, that message is to be
// ignored. Otherwise, the new message msg_id is added to the set, and, if the number of stored msg_id values is
// greater than N, the oldest (i. e. the lowest) is forgotten.
if (saved_message_ids_.size() == MAX_SAVED_MESSAGE_IDS) {
auto oldest_message_id = *saved_message_ids_.begin();
if (message_id < oldest_message_id) {
return Status::Error(1, PSLICE() << "Ignore very old message_id " << tag("oldest message_id", oldest_message_id)
<< tag("got message_id", message_id));
}
}
if (saved_message_ids_.count(message_id) != 0) {
return Status::Error(1, PSLICE() << "Ignore duplicated_message id " << tag("message_id", 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 Status::OK();
}
private:
static constexpr size_t MAX_SAVED_MESSAGE_IDS = 1000;
std::set<int64> saved_message_ids_;
};
class AuthData {
public:
AuthData() {
server_salt_.salt = Random::secure_int64();
server_salt_.valid_since = -1e10;
server_salt_.valid_until = -1e10;
}
AuthData(const AuthData &) = delete;
AuthData &operator=(const AuthData &) = delete;
virtual ~AuthData() = default;
bool is_ready(double now) {
if (!has_main_auth_key()) {
LOG(INFO) << "Need main auth key";
return false;
}
if (use_pfs() && !has_tmp_auth_key(now)) {
LOG(INFO) << "Need tmp auth key";
return false;
}
if (!has_salt(now)) {
LOG(INFO) << "no salt";
return false;
}
return true;
}
uint64 session_id_;
void set_main_auth_key(AuthKey auth_key) {
main_auth_key_ = std::move(auth_key);
}
const AuthKey &get_main_auth_key() const {
// CHECK(has_main_auth_key());
return main_auth_key_;
}
bool has_main_auth_key() const {
return !main_auth_key_.empty();
}
bool need_main_auth_key() const {
return !has_main_auth_key();
}
void set_tmp_auth_key(AuthKey auth_key) {
CHECK(!auth_key.empty());
tmp_auth_key_ = std::move(auth_key);
}
const AuthKey &get_tmp_auth_key() const {
// CHECK(has_tmp_auth_key());
return tmp_auth_key_;
}
bool was_tmp_auth_key() const {
return use_pfs() && !tmp_auth_key_.empty();
}
bool need_tmp_auth_key(double now) const {
if (!use_pfs()) {
return false;
}
if (tmp_auth_key_.empty()) {
return true;
}
if (now > tmp_auth_key_.expire_at() - 60 * 60 * 2 /*2 hours*/) {
return true;
}
if (!has_tmp_auth_key(now)) {
return true;
}
return false;
}
void drop_main_auth_key() {
main_auth_key_ = AuthKey();
}
void drop_tmp_auth_key() {
tmp_auth_key_ = AuthKey();
}
bool has_tmp_auth_key(double now) const {
if (!use_pfs()) {
return false;
}
if (tmp_auth_key_.empty()) {
return false;
}
if (now > tmp_auth_key_.expire_at() - 60 * 60 /*1 hour*/) {
return false;
}
return true;
}
const AuthKey &get_auth_key() const {
if (use_pfs()) {
return get_tmp_auth_key();
}
return get_main_auth_key();
}
bool has_auth_key(double now) const {
if (use_pfs()) {
return has_tmp_auth_key(now);
}
return has_main_auth_key();
}
bool get_auth_flag() const {
return main_auth_key_.auth_flag();
}
void set_auth_flag(bool auth_flag) {
main_auth_key_.set_auth_flag(auth_flag);
if (!auth_flag) {
tmp_auth_key_.set_auth_flag(auth_flag);
}
}
bool get_bind_flag() const {
return !use_pfs() || tmp_auth_key_.auth_flag();
}
void on_bind() {
CHECK(use_pfs());
tmp_auth_key_.set_auth_flag(true);
}
Slice header() {
if (use_pfs()) {
return tmp_auth_key_.need_header() ? Slice(header_) : Slice();
} else {
return main_auth_key_.need_header() ? Slice(header_) : Slice();
}
}
void set_header(std::string header) {
header_ = std::move(header);
}
void on_api_response() {
if (use_pfs()) {
if (tmp_auth_key_.auth_flag()) {
tmp_auth_key_.set_need_header(false);
}
} else {
if (main_auth_key_.auth_flag()) {
main_auth_key_.set_need_header(false);
}
}
}
uint64 get_session_id() const {
return session_id_;
}
double get_server_time(double now) const {
return server_time_difference_ + now;
}
double get_server_time_difference() const {
return server_time_difference_;
}
// diff == msg_id / 2^32 - now == old_server_now - now <= server_now - now
// server_time_difference >= max{diff}
bool update_server_time_difference(double diff) {
if (!server_time_difference_was_updated_) {
server_time_difference_was_updated_ = true;
LOG(DEBUG) << "UPDATE_SERVER_TIME_DIFFERENCE: " << server_time_difference_ << " -> " << diff;
server_time_difference_ = diff;
} else if (server_time_difference_ + 1e-4 < diff) {
LOG(DEBUG) << "UPDATE_SERVER_TIME_DIFFERENCE: " << server_time_difference_ << " -> " << diff;
server_time_difference_ = diff;
} else {
return false;
}
LOG(DEBUG) << "SERVER_TIME: " << format::as_hex(static_cast<int>(get_server_time(Time::now_cached())));
return true;
}
void set_server_time_difference(double diff) {
server_time_difference_was_updated_ = false;
server_time_difference_ = diff;
}
uint64 get_server_salt(double now) {
update_salt(now);
return server_salt_.salt;
}
void set_server_salt(uint64 salt, double now) {
server_salt_.salt = salt;
double server_time = get_server_time(now);
server_salt_.valid_since = server_time;
server_salt_.valid_until = server_time + 60 * 10;
future_salts_.clear();
}
bool is_server_salt_valid(double now) {
return server_salt_.valid_until > get_server_time(now) + 60;
}
bool has_salt(double now) {
update_salt(now);
return is_server_salt_valid(now);
}
bool need_future_salts(double now) {
update_salt(now);
return future_salts_.empty() || !is_server_salt_valid(now);
}
virtual void set_future_salts(const std::vector<ServerSalt> &salts, double now) {
if (salts.empty()) {
return;
}
future_salts_ = salts;
std::sort(future_salts_.begin(), future_salts_.end(),
[](const ServerSalt &a, const ServerSalt &b) { return a.valid_since > b.valid_since; });
update_salt(now);
}
std::vector<ServerSalt> get_future_salts() const {
auto res = future_salts_;
res.push_back(server_salt_);
return res;
}
int64 next_message_id(double now) {
double server_time = get_server_time(now);
int64 t = static_cast<int64>(server_time * (1ll << 32));
// randomize lower bits for clocks with low precision
// TODO(perf) do not do this for systems with good precision?..
auto rx = Random::secure_int32();
auto to_xor = rx & ((1 << 22) - 1);
auto to_mul = ((rx >> 22) & 1023) + 1;
t ^= to_xor;
auto result = t & -4;
if (last_message_id_ >= result) {
result = last_message_id_ + 8 * to_mul;
}
last_message_id_ = result;
return result;
}
bool is_valid_outbound_msg_id(int64 id, double now) {
double server_time = get_server_time(now);
auto id_time = static_cast<double>(id / (1ll << 32));
return server_time - 300 / 2 < id_time && id_time < server_time + 60 / 2;
}
bool is_valid_inbound_msg_id(int64 id, double now) {
double server_time = get_server_time(now);
auto id_time = static_cast<double>(id / (1ll << 32));
return server_time - 300 < id_time && id_time < server_time + 30;
}
Status check_packet(int64 session_id, int64 message_id, double now, bool &time_difference_was_updated) {
// Client is to check that the session_id field in the decrypted message indeed equals to that of an active session
// created by the client.
if (get_session_id() != static_cast<uint64>(session_id)) {
return Status::Error(PSLICE() << "Got packet from different session "
<< tag("current session_id", get_session_id())
<< tag("got session_id", session_id));
}
// Client must check that msg_id has even parity for messages from client to server, and odd parity for messages
// from server to client.
if ((message_id & 1) == 0) {
return Status::Error(PSLICE() << "Got invalid message_id " << tag("message_id", message_id));
}
TRY_STATUS(duplicate_checker_.check(message_id));
time_difference_was_updated = update_server_time_difference(static_cast<uint32>(message_id >> 32) - now);
// In addition, msg_id values that belong over 30 seconds in the future or over 300 seconds in the past are to be
// ignored (recall that msg_id approximately equals unixtime * 2^32). This is especially important for the server.
// The client would also find this useful (to protect from a replay attack), but only if it is certain of its time
// (for example, if its time has been synchronized with that of the server).
if (server_time_difference_was_updated_ && !is_valid_inbound_msg_id(message_id, now)) {
return Status::Error(PSLICE() << "Ignore message with too old or too new message_id "
<< tag("message_id", message_id));
}
return Status::OK();
}
Status check_update(int64 message_id) {
return updates_duplicate_checker_.check(message_id);
}
int32 next_seq_no(bool is_content_related) {
int32 res = seq_no_;
if (is_content_related) {
res |= 1;
seq_no_ += 2;
}
return res;
}
void clear_seq_no() {
seq_no_ = 0;
}
void set_use_pfs(bool use_pfs) {
use_pfs_ = use_pfs;
}
bool use_pfs() const {
return use_pfs_;
}
private:
bool use_pfs_ = true;
AuthKey main_auth_key_;
AuthKey tmp_auth_key_;
bool server_time_difference_was_updated_ = false;
double server_time_difference_ = 0;
ServerSalt server_salt_;
int64 last_message_id_ = 0;
int32 seq_no_ = 0;
std::string header_;
std::vector<ServerSalt> future_salts_;
MessageIdDuplicateChecker duplicate_checker_;
MessageIdDuplicateChecker updates_duplicate_checker_;
void update_salt(double now) {
double server_time = get_server_time(now);
while (!future_salts_.empty() && (future_salts_.back().valid_since < server_time)) {
server_salt_ = future_salts_.back();
future_salts_.pop_back();
}
}
};
} // namespace mtproto
} // namespace td

93
td/mtproto/AuthKey.h Normal file
View File

@ -0,0 +1,93 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/utils/common.h"
#include "td/utils/Time.h"
#include <memory>
namespace td {
namespace mtproto {
class AuthKey {
public:
AuthKey() = default;
AuthKey(uint64 auth_key_id, string &&auth_key) : auth_key_id_(auth_key_id), auth_key_(auth_key) {
}
bool empty() const {
return auth_key_.empty();
}
const string &key() const {
return auth_key_;
}
uint64 id() const {
return auth_key_id_;
}
bool auth_flag() const {
return auth_flag_;
}
bool was_auth_flag() const {
return was_auth_flag_;
}
void set_auth_flag(bool new_auth_flag) {
if (new_auth_flag == false) {
clear();
} else {
was_auth_flag_ = true;
}
auth_flag_ = new_auth_flag;
}
bool need_header() const {
return need_header_;
}
void set_need_header(bool need_header) {
need_header_ = need_header;
}
double expire_at() const {
return expire_at_;
}
void set_expire_at(double expire_at) {
expire_at_ = expire_at;
// expire_at_ = Time::now() + 60 * 60 + 10 * 60;
}
void clear() {
auth_key_.clear();
}
enum { AUTH_FLAG = 1, WAS_AUTH_FLAG = 2 };
template <class StorerT>
void store(StorerT &storer) const {
storer.store_binary(auth_key_id_);
storer.store_binary((auth_flag_ ? AUTH_FLAG : 0) | (was_auth_flag_ ? WAS_AUTH_FLAG : 0));
storer.store_string(auth_key_);
}
template <class ParserT>
void parse(ParserT &parser) {
auth_key_id_ = parser.fetch_long();
auto flags = parser.fetch_int();
auth_flag_ = (flags & AUTH_FLAG) != 0;
was_auth_flag_ = (flags & WAS_AUTH_FLAG) != 0 || auth_flag_;
auth_key_ = parser.template fetch_string<string>();
// just in case
need_header_ = true;
}
private:
uint64 auth_key_id_ = 0;
// TODO(perf): std::shared_ptr
string auth_key_;
bool auth_flag_ = false;
bool was_auth_flag_ = false;
bool need_header_ = true;
double expire_at_ = 0;
};
} // namespace mtproto
} // namespace td

333
td/mtproto/CryptoStorer.h Normal file
View File

@ -0,0 +1,333 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/AuthData.h"
#include "td/mtproto/PacketStorer.h"
#include "td/mtproto/utils.h"
#include "td/mtproto/mtproto_api.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Time.h"
namespace td {
namespace mtproto {
template <class Object, class ObjectStorer>
class ObjectImpl {
public:
ObjectImpl(bool not_empty, Object &&object, AuthData *auth_data, bool need_ack = false)
: not_empty_(not_empty), object_(std::move(object)), object_storer_(object_) {
if (empty()) {
return;
}
message_id_ = auth_data->next_message_id(Time::now_cached());
seq_no_ = auth_data->next_seq_no(need_ack);
}
template <class T>
void do_store(T &storer) const {
if (empty()) {
return;
}
storer.store_binary(message_id_);
storer.store_binary(seq_no_);
storer.store_binary(static_cast<int32>(object_storer_.size()));
storer.store_storer(object_storer_);
}
bool not_empty() const {
return not_empty_;
}
bool empty() const {
return !not_empty_;
}
uint64 get_message_id() const {
return message_id_;
}
private:
bool not_empty_;
Object object_;
ObjectStorer object_storer_;
uint64 message_id_;
int32 seq_no_;
};
using AckImpl = ObjectImpl<mtproto_api::msgs_ack, TLObjectStorer<mtproto_api::msgs_ack>>;
using PingImpl = ObjectImpl<mtproto_api::ping_delay_disconnect, TLStorer<mtproto_api::ping_delay_disconnect>>;
using HttpWaitImpl = ObjectImpl<mtproto_api::http_wait, TLStorer<mtproto_api::http_wait>>;
using GetFutureSaltsImpl = ObjectImpl<mtproto_api::get_future_salts, TLStorer<mtproto_api::get_future_salts>>;
using ResendImpl = ObjectImpl<mtproto_api::msg_resend_req, TLObjectStorer<mtproto_api::msg_resend_req>>;
using CancelImpl = ObjectImpl<mtproto_api::rpc_drop_answer, TLStorer<mtproto_api::rpc_drop_answer>>;
using GetInfoImpl = ObjectImpl<mtproto_api::msgs_state_req, TLObjectStorer<mtproto_api::msgs_state_req>>;
class CancelVectorImpl {
public:
CancelVectorImpl(bool not_empty, const vector<int64> &to_cancel, AuthData *auth_data, bool need_ack) {
storers_.reserve(to_cancel.size());
for (auto &request_id : to_cancel) {
storers_.emplace_back(true, mtproto_api::rpc_drop_answer(request_id), auth_data, true);
}
}
template <class T>
void do_store(T &storer) const {
for (auto &s : storers_) {
storer.store_storer(s);
}
}
bool not_empty() const {
return !storers_.empty();
}
uint64 get_message_id() const {
CHECK(storers_.size() == 1);
return storers_[0].get_message_id();
}
private:
vector<PacketStorer<CancelImpl>> storers_;
};
class QueryImpl {
public:
QueryImpl(const Query &query, Slice header) : query_(query), header_(header) {
}
template <class T>
void do_store(T &storer) const {
storer.store_binary(query_.message_id);
storer.store_binary(query_.seq_no);
Slice header = this->header_;
Slice invoke_header = Slice();
// TODO(refactor):
// invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
// This code makes me very sad.
// InvokeAfterMsg is not even in mtproto_api. It is in telegram_api.
#pragma pack(push, 4)
struct {
uint32 constructor_id;
uint64 invoke_after_id;
} invoke_data;
#pragma pack(pop)
if (query_.invoke_after_id != 0) {
invoke_data.constructor_id = 0xcb9f372d;
invoke_data.invoke_after_id = query_.invoke_after_id;
invoke_header = Slice(reinterpret_cast<const uint8 *>(&invoke_data), sizeof(invoke_data));
}
Slice data = query_.packet.as_slice();
mtproto_api::gzip_packed packed(data);
auto plain_storer = create_storer(data);
auto gzip_storer = create_storer(packed);
const Storer &data_storer =
query_.gzip_flag ? static_cast<const Storer &>(gzip_storer) : static_cast<const Storer &>(plain_storer);
auto invoke_header_storer = create_storer(invoke_header);
auto header_storer = create_storer(header);
auto suff_storer = create_storer(invoke_header_storer, data_storer);
auto all_storer = create_storer(header_storer, suff_storer);
storer.store_binary(static_cast<uint32>(all_storer.size()));
storer.store_storer(all_storer);
}
private:
const Query &query_;
Slice header_;
};
class QueryVectorImpl {
public:
QueryVectorImpl(const vector<Query> &to_send, Slice header) : to_send_(to_send), header_(header) {
}
template <class T>
void do_store(T &storer) const {
if (to_send_.empty()) {
return;
}
for (auto &query : to_send_) {
storer.store_storer(PacketStorer<QueryImpl>(query, header_));
}
}
private:
const vector<Query> &to_send_;
Slice header_;
};
class ContainerImpl {
public:
ContainerImpl(int32 cnt, Storer &storer) : cnt_(cnt), storer_(storer) {
}
template <class T>
void do_store(T &storer) const {
storer.store_binary(mtproto_api::msg_container::ID);
storer.store_binary(cnt_);
storer.store_storer(storer_);
}
private:
int32 cnt_;
Storer &storer_;
};
class CryptoImpl {
public:
CryptoImpl(const vector<Query> &to_send, Slice header, vector<int64> &&to_ack, int64 ping_id, int ping_timeout,
int max_delay, int max_after, int max_wait, int future_salt_n, vector<int64> get_info,
vector<int64> resend, vector<int64> cancel, AuthData *auth_data, uint64 *container_id, uint64 *get_info_id,
uint64 *resend_id, uint64 *ping_message_id, uint64 *parent_message_id)
: query_storer_(to_send, header)
, ack_empty_(to_ack.empty())
, ack_storer_(!ack_empty_, mtproto_api::msgs_ack(std::move(to_ack)), auth_data)
, ping_storer_(ping_id != 0, mtproto_api::ping_delay_disconnect(ping_id, ping_timeout), auth_data)
, http_wait_storer_(max_delay >= 0, mtproto_api::http_wait(max_delay, max_after, max_wait), auth_data)
, get_future_salts_storer_(future_salt_n > 0, mtproto_api::get_future_salts(future_salt_n), auth_data)
, get_info_not_empty_(!get_info.empty())
, get_info_storer_(get_info_not_empty_, mtproto_api::msgs_state_req(std::move(get_info)), auth_data, true)
, resend_not_empty_(!resend.empty())
, resend_storer_(resend_not_empty_, mtproto_api::msg_resend_req(std::move(resend)), auth_data, true)
, cancel_not_empty_(!cancel.empty())
, cancel_cnt_(static_cast<int32>(cancel.size()))
, cancel_storer_(cancel_not_empty_, std::move(cancel), auth_data, true)
, tmp_storer_(query_storer_, ack_storer_)
, tmp2_storer_(tmp_storer_, http_wait_storer_)
, tmp3_storer_(tmp2_storer_, get_future_salts_storer_)
, tmp4_storer_(tmp3_storer_, get_info_storer_)
, tmp5_storer_(tmp4_storer_, resend_storer_)
, tmp6_storer_(tmp5_storer_, cancel_storer_)
, concat_storer_(tmp6_storer_, ping_storer_)
, cnt_(static_cast<int32>(to_send.size()) + ack_storer_.not_empty() + ping_storer_.not_empty() +
http_wait_storer_.not_empty() + get_future_salts_storer_.not_empty() + get_info_storer_.not_empty() +
resend_storer_.not_empty() + cancel_cnt_)
, container_storer_(cnt_, concat_storer_) {
CHECK(cnt_ != 0);
if (get_info_storer_.not_empty() && get_info_id) {
*get_info_id = get_info_storer_.get_message_id();
}
if (resend_storer_.not_empty() && resend_id) {
*resend_id = resend_storer_.get_message_id();
}
if (ping_storer_.not_empty() && ping_message_id) {
*ping_message_id = ping_storer_.get_message_id();
}
if (cnt_ > 1 ||
(!to_send.empty() && !auth_data->is_valid_outbound_msg_id(to_send[0].message_id, Time::now_cached()))) {
type_ = Mixed;
message_id_ = auth_data->next_message_id(Time::now_cached());
seq_no_ = auth_data->next_seq_no(false);
*container_id = message_id_;
*parent_message_id = message_id_;
} else if (!to_send.empty()) {
CHECK(to_send.size() == 1u);
type_ = OnlyQuery;
*parent_message_id = to_send[0].message_id;
} else if (ack_storer_.not_empty()) {
type_ = OnlyAck;
*parent_message_id = ack_storer_.get_message_id();
} else if (ping_storer_.not_empty()) {
type_ = OnlyPing;
*parent_message_id = ping_storer_.get_message_id();
} else if (http_wait_storer_.not_empty()) {
type_ = OnlyHttpWait;
*parent_message_id = http_wait_storer_.get_message_id();
} else if (get_future_salts_storer_.not_empty()) {
type_ = OnlyGetFutureSalts;
*parent_message_id = get_future_salts_storer_.get_message_id();
} else if (get_info_storer_.not_empty()) {
type_ = OnlyGetInfo;
*parent_message_id = get_info_storer_.get_message_id();
} else if (resend_storer_.not_empty()) {
type_ = OnlyResend;
*parent_message_id = resend_storer_.get_message_id();
} else if (cancel_storer_.not_empty()) {
type_ = OnlyCancel;
*parent_message_id = cancel_storer_.get_message_id();
} else {
UNREACHABLE();
}
}
template <class T>
void do_store(T &storer) const {
switch (type_) {
case OnlyAck:
return storer.store_storer(ack_storer_);
case OnlyQuery:
return storer.store_storer(query_storer_);
case OnlyPing:
return storer.store_storer(ping_storer_);
case OnlyHttpWait:
return storer.store_storer(http_wait_storer_);
case OnlyGetFutureSalts:
return storer.store_storer(get_future_salts_storer_);
case OnlyResend:
return storer.store_storer(resend_storer_);
case OnlyCancel:
return storer.store_storer(cancel_storer_);
case OnlyGetInfo:
return storer.store_storer(get_info_storer_);
default:
storer.store_binary(message_id_);
storer.store_binary(seq_no_);
storer.store_binary(static_cast<int32>(container_storer_.size()));
storer.store_storer(container_storer_);
}
}
private:
PacketStorer<QueryVectorImpl> query_storer_;
bool ack_empty_;
PacketStorer<AckImpl> ack_storer_;
PacketStorer<PingImpl> ping_storer_;
PacketStorer<HttpWaitImpl> http_wait_storer_;
PacketStorer<GetFutureSaltsImpl> get_future_salts_storer_;
bool get_info_not_empty_;
PacketStorer<GetInfoImpl> get_info_storer_;
bool resend_not_empty_;
PacketStorer<ResendImpl> resend_storer_;
bool cancel_not_empty_;
int32 cancel_cnt_;
PacketStorer<CancelVectorImpl> cancel_storer_;
ConcatStorer tmp_storer_;
ConcatStorer tmp2_storer_;
ConcatStorer tmp3_storer_;
ConcatStorer tmp4_storer_;
ConcatStorer tmp5_storer_;
ConcatStorer tmp6_storer_;
ConcatStorer concat_storer_;
int32 cnt_;
PacketStorer<ContainerImpl> container_storer_;
enum Type {
OnlyQuery,
OnlyAck,
OnlyPing,
OnlyHttpWait,
OnlyGetFutureSalts,
OnlyResend,
OnlyCancel,
OnlyGetInfo,
Mixed
};
Type type_;
uint64 message_id_;
int32 seq_no_;
};
} // namespace mtproto
} // namespace td

301
td/mtproto/Handshake.cpp Normal file
View File

@ -0,0 +1,301 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/Handshake.h"
#include "td/mtproto/utils.h"
#include "td/mtproto/mtproto_api.h"
#include "td/utils/buffer.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Random.h"
#include "td/utils/Status.h"
#include "td/utils/Time.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/tl_storers.h"
namespace td {
namespace mtproto {
void AuthKeyHandshake::clear() {
last_query_ = BufferSlice();
state_ = Start;
}
bool AuthKeyHandshake::is_ready_for_start() {
return state_ == Start;
}
bool AuthKeyHandshake::is_ready_for_message(const UInt128 &message_nonce) {
return state_ != Finish && state_ != Start && nonce == message_nonce;
}
bool AuthKeyHandshake::is_ready_for_finish() {
return state_ == Finish;
}
void AuthKeyHandshake::on_finish() {
clear();
}
template <class DataT>
Result<size_t> AuthKeyHandshake::fill_data_with_hash(uint8 *data_with_hash, const DataT &data) {
// data_with_hash := SHA1(data) + data + (any random bytes); such that the length equal 255 bytes;
uint8 *data_ptr = data_with_hash + 20;
size_t data_size = tl_calc_length(data);
if (data_size + 20 + 4 > 255) {
return Status::Error("Too big data");
}
as<int32>(data_ptr) = data.get_id();
tl_store_unsafe(data, data_ptr + 4);
sha1(Slice(data_ptr, data_size + 4), data_with_hash);
return data_size + 20 + 4;
}
Status AuthKeyHandshake::on_res_pq(Slice message, Callback *connection, PublicRsaKeyInterface *public_rsa_key) {
TRY_RESULT(res_pq, fetch_result<mtproto_api::req_pq>(message));
if (res_pq->nonce_ != nonce) {
return Status::Error("Nonce mismatch");
}
server_nonce = res_pq->server_nonce_;
auto r_rsa = public_rsa_key->get_rsa(res_pq->server_public_key_fingerprints_);
if (r_rsa.is_error()) {
public_rsa_key->drop_keys();
return r_rsa.move_as_error();
}
int64 rsa_fingerprint = r_rsa.ok().second;
RSA rsa = std::move(r_rsa.ok_ref().first);
string p, q;
if (pq_factorize(res_pq->pq_, &p, &q) == -1) {
return Status::Error("Failed to factorize");
}
Random::secure_bytes(new_nonce.raw, sizeof(new_nonce));
alignas(8) uint8 data_with_hash[255];
Result<size_t> r_data_size = 0;
switch (mode_) {
case Mode::Main:
r_data_size = fill_data_with_hash(data_with_hash,
mtproto_api::p_q_inner_data(res_pq->pq_, p, q, nonce, server_nonce, new_nonce));
break;
case Mode::Temp:
r_data_size = fill_data_with_hash(
data_with_hash,
mtproto_api::p_q_inner_data_temp(res_pq->pq_, p, q, nonce, server_nonce, new_nonce, expire_in_));
expire_at_ = Time::now() + expire_in_;
break;
case Mode::Unknown:
default:
UNREACHABLE();
r_data_size = Status::Error(500, "Unreachable");
}
if (r_data_size.is_error()) {
return r_data_size.move_as_error();
}
size_t size = r_data_size.ok();
// encrypted_data := RSA (data_with_hash, server_public_key); a 255-byte long number (big endian)
// is raised to the requisite power over the requisite modulus, and the result is stored as a 256-byte number.
string encrypted_data(256, 0);
rsa.encrypt(data_with_hash, size, reinterpret_cast<unsigned char *>(&encrypted_data[0]));
// req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long
// encrypted_data:string = Server_DH_Params
mtproto_api::req_DH_params req_dh_params(nonce, server_nonce, p, q, rsa_fingerprint, std::move(encrypted_data));
send(connection, create_storer(req_dh_params));
state_ = ServerDHParams;
return Status::OK();
}
Status AuthKeyHandshake::on_server_dh_params(Slice message, Callback *connection, DhCallback *dh_callback) {
TRY_RESULT(server_dh_params, fetch_result<mtproto_api::req_DH_params>(message));
switch (server_dh_params->get_id()) {
case mtproto_api::server_DH_params_ok::ID:
break;
case mtproto_api::server_DH_params_fail::ID:
return Status::Error("Server dh params fail");
default:
return Status::Error("Unknown result");
}
auto dh_params = move_tl_object_as<mtproto_api::server_DH_params_ok>(server_dh_params);
// server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
if (dh_params->nonce_ != nonce) {
return Status::Error("Nonce mismatch");
}
if (dh_params->server_nonce_ != server_nonce) {
return Status::Error("Server nonce mismatch");
}
if (dh_params->encrypted_answer_.size() & 15) {
return Status::Error("Bad padding for encrypted part");
}
tmp_KDF(server_nonce, new_nonce, &tmp_aes_key, &tmp_aes_iv);
auto save_tmp_aes_iv = tmp_aes_iv;
// encrypted_answer := AES256_ige_encrypt (answer_with_hash, tmp_aes_key, tmp_aes_iv);
MutableSlice answer(const_cast<char *>(dh_params->encrypted_answer_.begin()), dh_params->encrypted_answer_.size());
aes_ige_decrypt(tmp_aes_key, &tmp_aes_iv, answer, answer);
tmp_aes_iv = save_tmp_aes_iv;
// answer_with_hash := SHA1(answer) + answer + (0-15 random bytes)
TlParser answer_parser(answer);
UInt<160> answer_sha1 = answer_parser.fetch_binary<UInt<160>>();
int32 id = answer_parser.fetch_int();
if (id != mtproto_api::server_DH_inner_data::ID) {
return Status::Error("Failed to fetch server_DH_inner_data");
}
mtproto_api::server_DH_inner_data dh_inner_data(answer_parser);
if (answer_parser.get_error() != nullptr) {
return Status::Error("Failed to fetch server_DH_inner_data");
}
size_t pad = answer_parser.get_left_len();
if (pad >= 16) {
return Status::Error("Too much pad");
}
size_t dh_inner_data_size = answer.size() - pad - 20;
UInt<160> answer_real_sha1;
sha1(Slice(answer.ubegin() + 20, dh_inner_data_size), answer_real_sha1.raw);
if (answer_sha1 != answer_real_sha1) {
return Status::Error("SHA1 mismatch");
}
// server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int =
// Server_DH_inner_data;
if (dh_inner_data.nonce_ != nonce) {
return Status::Error("Nonce mismatch");
}
if (dh_inner_data.server_nonce_ != server_nonce) {
return Status::Error("Server nonce mismatch");
}
server_time_diff = dh_inner_data.server_time_ - Time::now();
string g_b;
string auth_key_str;
TRY_STATUS(
dh_handshake(dh_inner_data.g_, dh_inner_data.dh_prime_, dh_inner_data.g_a_, &g_b, &auth_key_str, dh_callback));
mtproto_api::client_DH_inner_data data(nonce, server_nonce, 0, g_b);
size_t data_size = 4 + tl_calc_length(data);
size_t encrypted_data_size = 20 + data_size;
size_t encrypted_data_size_with_pad = (encrypted_data_size + 15) & -16;
string encrypted_data_str(encrypted_data_size_with_pad, 0);
MutableSlice encrypted_data = encrypted_data_str;
as<int32>(encrypted_data.begin() + 20) = data.get_id();
tl_store_unsafe(data, encrypted_data.begin() + 20 + 4);
sha1(Slice(encrypted_data.ubegin() + 20, data_size), encrypted_data.ubegin());
Random::secure_bytes(encrypted_data.ubegin() + encrypted_data_size,
encrypted_data_size_with_pad - encrypted_data_size);
tmp_KDF(server_nonce, new_nonce, &tmp_aes_key, &tmp_aes_iv);
aes_ige_encrypt(tmp_aes_key, &tmp_aes_iv, encrypted_data, encrypted_data);
mtproto_api::set_client_DH_params set_client_dh_params(nonce, server_nonce, std::move(encrypted_data_str));
send(connection, create_storer(set_client_dh_params));
auth_key = AuthKey(dh_auth_key_id(auth_key_str), std::move(auth_key_str));
if (mode_ == Mode::Temp) {
auth_key.set_expire_at(expire_at_);
}
server_salt = as<int64>(new_nonce.raw) ^ as<int64>(server_nonce.raw);
state_ = DHGenResponse;
return Status::OK();
}
Status AuthKeyHandshake::on_dh_gen_response(Slice message, Callback *connection) {
TRY_RESULT(answer, fetch_result<mtproto_api::set_client_DH_params>(message));
switch (answer->get_id()) {
case mtproto_api::dh_gen_ok::ID:
state_ = Finish;
break;
case mtproto_api::dh_gen_fail::ID:
return Status::Error("DhGenFail");
case mtproto_api::dh_gen_retry::ID:
return Status::Error("DhGenRetry");
default:
return Status::Error("Unknown set_client_DH_params response");
}
return Status::OK();
}
void AuthKeyHandshake::send(Callback *connection, const Storer &storer) {
auto writer = BufferWriter{storer.size(), 0, 0};
storer.store(writer.as_slice().ubegin());
last_query_ = writer.as_buffer_slice();
return do_send(connection, create_storer(last_query_.as_slice()));
}
void AuthKeyHandshake::do_send(Callback *connection, const Storer &storer) {
return connection->send_no_crypto(storer);
}
Status AuthKeyHandshake::start_main(Callback *connection) {
mode_ = Mode::Main;
return on_start(connection);
}
Status AuthKeyHandshake::start_tmp(Callback *connection, int32 expire_in) {
mode_ = Mode::Temp;
expire_in_ = expire_in;
return on_start(connection);
}
void AuthKeyHandshake::resume(Callback *connection) {
if (state_ == Start) {
return on_start(connection).ignore();
}
if (state_ == Finish) {
LOG(ERROR) << "State is Finish during resume. UNREACHABLE";
return clear();
}
if (last_query_.empty()) {
LOG(ERROR) << "Last query empty! UNREACHABLE " << state_;
return clear();
}
LOG(INFO) << "RESUME";
do_send(connection, create_storer(last_query_.as_slice()));
}
Status AuthKeyHandshake::on_start(Callback *connection) {
if (state_ != Start) {
clear();
return Status::Error(PSLICE() << "on_start called after start " << tag("state", state_));
}
Random::secure_bytes(nonce.raw, sizeof(nonce));
send(connection, create_storer(mtproto_api::req_pq(nonce)));
state_ = ResPQ;
return Status::OK();
}
Status AuthKeyHandshake::on_message(Slice message, Callback *connection, Context *context) {
Status status = [&] {
switch (state_) {
case ResPQ:
return on_res_pq(message, connection, context->get_public_rsa_key_interface());
case ServerDHParams:
return on_server_dh_params(message, connection, context->get_dh_callback());
case DHGenResponse:
return on_dh_gen_response(message, connection);
default:
UNREACHABLE();
}
}();
if (status.is_error()) {
clear();
}
return status;
}
} // namespace mtproto
} // namespace td

102
td/mtproto/Handshake.h Normal file
View File

@ -0,0 +1,102 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/AuthKey.h"
#include "td/mtproto/crypto.h"
#include "td/utils/buffer.h"
#include "td/utils/int_types.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class Storer;
namespace mtproto {
class AuthKeyHandshakeContext {
public:
virtual ~AuthKeyHandshakeContext() = default;
virtual DhCallback *get_dh_callback() = 0;
virtual PublicRsaKeyInterface *get_public_rsa_key_interface() = 0;
};
class AuthKeyHandshake {
public:
class Callback {
public:
Callback() = default;
Callback(const Callback &) = delete;
Callback &operator=(const Callback &) = delete;
virtual ~Callback() = default;
virtual void send_no_crypto(const Storer &storer) = 0;
};
using Context = AuthKeyHandshakeContext;
enum class Mode { Unknown, Main, Temp };
AuthKey auth_key;
double server_time_diff = 0;
uint64 server_salt = 0;
bool is_ready_for_start();
Status start_main(Callback *connection) TD_WARN_UNUSED_RESULT;
Status start_tmp(Callback *connection, int32 expire_in) TD_WARN_UNUSED_RESULT;
bool is_ready_for_message(const UInt128 &message_nonce);
bool is_ready_for_finish();
void on_finish();
explicit AuthKeyHandshake(int32 expire_in = 0) {
if (expire_in == 0) {
mode_ = Mode::Main;
} else {
mode_ = Mode::Temp;
expire_in_ = expire_in;
}
}
void init_main() {
clear();
mode_ = Mode::Main;
}
void init_temp(int32 expire_in) {
clear();
mode_ = Mode::Temp;
expire_in_ = expire_in;
}
void resume(Callback *connection);
Status on_message(Slice message, Callback *connection, Context *context) TD_WARN_UNUSED_RESULT;
bool is_ready() {
return is_ready_for_finish();
}
void clear();
private:
using State = enum { Start, ResPQ, ServerDHParams, DHGenResponse, Finish };
State state_ = Start;
Mode mode_ = Mode::Unknown;
int32 expire_in_;
double expire_at_ = 0;
UInt128 nonce;
UInt128 server_nonce;
UInt256 new_nonce;
UInt256 tmp_aes_key;
UInt256 tmp_aes_iv;
BufferSlice last_query_;
template <class DataT>
Result<size_t> fill_data_with_hash(uint8 *data_with_hash, const DataT &data) TD_WARN_UNUSED_RESULT;
void send(Callback *connection, const Storer &storer);
void do_send(Callback *connection, const Storer &storer);
Status on_start(Callback *connection) TD_WARN_UNUSED_RESULT;
Status on_res_pq(Slice message, Callback *connection, PublicRsaKeyInterface *public_rsa_key) TD_WARN_UNUSED_RESULT;
Status on_server_dh_params(Slice message, Callback *connection, DhCallback *dh_callback) TD_WARN_UNUSED_RESULT;
Status on_dh_gen_response(Slice message, Callback *connection) TD_WARN_UNUSED_RESULT;
};
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,90 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/HandshakeActor.h"
#include "td/mtproto/HandshakeConnection.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Status.h"
namespace td {
namespace mtproto {
HandshakeActor::HandshakeActor(std::unique_ptr<AuthKeyHandshake> handshake,
std::unique_ptr<RawConnection> raw_connection,
std::unique_ptr<AuthKeyHandshakeContext> context, double timeout,
Promise<std::unique_ptr<RawConnection>> raw_connection_promise,
Promise<std::unique_ptr<AuthKeyHandshake>> handshake_promise)
: handshake_(std::move(handshake))
, connection_(
std::make_unique<HandshakeConnection>(std::move(raw_connection), handshake_.get(), std::move(context)))
, timeout_(timeout)
, raw_connection_promise_(std::move(raw_connection_promise))
, handshake_promise_(std::move(handshake_promise)) {
}
void HandshakeActor::close() {
finish(Status::Error("Cancelled"));
stop();
}
void HandshakeActor::start_up() {
connection_->get_pollable().set_observer(this);
subscribe(connection_->get_pollable());
set_timeout_in(timeout_);
yield();
}
void HandshakeActor::loop() {
auto status = connection_->flush();
if (status.is_error()) {
finish(std::move(status));
return stop();
}
if (handshake_->is_ready_for_finish()) {
finish(Status::OK());
return stop();
}
}
void HandshakeActor::return_connection(Status status) {
auto raw_connection = connection_->move_as_raw_connection();
if (!raw_connection) {
CHECK(!raw_connection_promise_);
return;
}
unsubscribe(raw_connection->get_pollable());
raw_connection->get_pollable().set_observer(nullptr);
if (raw_connection_promise_) {
if (status.is_error()) {
if (raw_connection->stats_callback()) {
raw_connection->stats_callback()->on_error();
}
raw_connection->close();
raw_connection_promise_.set_error(std::move(status));
} else {
if (raw_connection->stats_callback()) {
raw_connection->stats_callback()->on_pong();
}
raw_connection_promise_.set_value(std::move(raw_connection));
}
} else {
if (raw_connection->stats_callback()) {
raw_connection->stats_callback()->on_error();
}
raw_connection->close();
}
}
void HandshakeActor::return_handshake() {
if (!handshake_promise_) {
CHECK(!handshake_);
return;
}
handshake_promise_.set_value(std::move(handshake_));
}
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,58 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/Status.h"
namespace td {
class DhCallback;
namespace mtproto {
class AuthKeyHandshake;
class AuthKeyHandshakeContext;
class RawConnection;
class HandshakeConnection;
// Has Raw connection. Generates new auth key. And returns it and raw_connection. Or error...
class HandshakeActor : public Actor {
public:
HandshakeActor(std::unique_ptr<AuthKeyHandshake> handshake, std::unique_ptr<RawConnection> raw_connection,
std::unique_ptr<AuthKeyHandshakeContext> context, double timeout,
Promise<std::unique_ptr<RawConnection>> raw_connection_promise,
Promise<std::unique_ptr<AuthKeyHandshake>> handshake_promise);
void close();
private:
std::unique_ptr<AuthKeyHandshake> handshake_;
std::unique_ptr<HandshakeConnection> connection_;
double timeout_;
Promise<std::unique_ptr<RawConnection>> raw_connection_promise_;
Promise<std::unique_ptr<AuthKeyHandshake>> handshake_promise_;
void start_up() override;
void tear_down() override {
finish(Status::OK());
}
void timeout_expired() override {
finish(Status::Error("Timeout expired"));
stop();
}
void loop() override;
void finish(Status status) {
// NB: order may be important for parent
return_connection(std::move(status));
return_handshake();
}
void return_connection(Status status);
void return_handshake();
};
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,81 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/AuthKey.h"
#include "td/mtproto/Handshake.h"
#include "td/mtproto/NoCryptoStorer.h"
#include "td/mtproto/RawConnection.h"
#include "td/mtproto/Transport.h"
#include "td/mtproto/utils.h"
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/Fd.h"
#include "td/utils/Status.h"
namespace td {
namespace mtproto {
class HandshakeConnection
: private RawConnection::Callback
, private AuthKeyHandshake::Callback {
public:
HandshakeConnection(std::unique_ptr<RawConnection> raw_connection, AuthKeyHandshake *handshake,
std::unique_ptr<AuthKeyHandshakeContext> context)
: raw_connection_(std::move(raw_connection)), handshake_(handshake), context_(std::move(context)) {
handshake_->resume(this);
}
Fd &get_pollable() {
return raw_connection_->get_pollable();
}
std::unique_ptr<RawConnection> move_as_raw_connection() {
return std::move(raw_connection_);
}
void close() {
raw_connection_->close();
}
Status flush() {
auto status = raw_connection_->flush(AuthKey(), *this);
if (status.code() == -404) {
LOG(WARNING) << "Clear handshake " << tag("error", status);
handshake_->clear();
}
return status;
}
private:
std::unique_ptr<RawConnection> raw_connection_;
AuthKeyHandshake *handshake_;
std::unique_ptr<AuthKeyHandshakeContext> context_;
void send_no_crypto(const Storer &storer) override {
raw_connection_->send_no_crypto(PacketStorer<NoCryptoImpl>(0, storer));
}
Status on_raw_packet(const PacketInfo &packet_info, BufferSlice packet) override {
if (packet_info.no_crypto_flag == false) {
return Status::Error("Expected not encrypted packet");
}
// skip header
if (packet.size() < 12) {
return Status::Error("Result is too small");
}
packet.confirm_read(12);
TRY_STATUS(handshake_->on_message(packet.as_slice(), this, context_.get()));
return Status::OK();
}
};
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,81 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/HttpTransport.h"
#include "td/net/HttpHeaderCreator.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
// TODO: do I need \r\n as delimiter?
#include <cstring>
namespace td {
namespace mtproto {
namespace http {
Result<size_t> Transport::read_next(BufferSlice *message, uint32 *quick_ack) {
CHECK(can_read());
auto r_size = reader_.read_next(&http_query_);
if (r_size.is_error() || r_size.ok() != 0) {
return r_size;
}
if (http_query_.type_ != HttpQuery::Type::RESPONSE) {
return Status::Error("Unexpected http query type");
}
if (http_query_.container_.size() != 2u) {
return Status::Error("Wrong response");
}
*message = std::move(http_query_.container_[1]);
turn_ = Write;
return 0;
}
void Transport::write(BufferWriter &&message, bool quick_ack) {
CHECK(can_write());
CHECK(!quick_ack);
/*
* POST /api HTTP/1.1
* Content-Length: [message->size()]
* Host: url
*/
HttpHeaderCreator hc;
hc.init_post("/api");
hc.add_header("Host", "");
hc.set_keep_alive();
hc.set_content_size(message.size());
auto r_head = hc.finish();
if (r_head.is_error()) {
UNREACHABLE();
}
Slice src = r_head.ok();
MutableSlice dst = message.prepare_prepend();
CHECK(dst.size() >= src.size()) << dst.size() << " >= " << src.size();
std::memcpy(dst.end() - src.size(), src.begin(), src.size());
message.confirm_prepend(src.size());
output_->append(message.as_buffer_slice());
turn_ = Read;
}
bool Transport::can_read() const {
return turn_ == Read;
}
bool Transport::can_write() const {
return turn_ == Write;
}
size_t Transport::max_prepend_size() const {
return MAX_PREPEND_SIZE;
}
} // namespace http
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,51 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/IStreamTransport.h"
#include "td/net/HttpQuery.h"
#include "td/net/HttpReader.h"
#include "td/utils/buffer.h"
#include "td/utils/port/Fd.h"
#include "td/utils/Status.h"
namespace td {
namespace mtproto {
namespace http {
class Transport : public IStreamTransport {
public:
Result<size_t> read_next(BufferSlice *message, uint32 *quick_ack) override TD_WARN_UNUSED_RESULT;
bool support_quick_ack() const override {
return false;
}
void write(BufferWriter &&message, bool quick_ack) override;
bool can_read() const override;
bool can_write() const override;
void init(ChainBufferReader *input, ChainBufferWriter *output) override {
reader_.init(input);
output_ = output;
}
size_t max_prepend_size() const override;
TransportType get_type() const override {
return TransportType::Http;
}
private:
HttpReader reader_;
HttpQuery http_query_;
ChainBufferWriter *output_;
enum { Write, Read } turn_ = Write;
enum { MAX_PREPEND_SIZE = 96 };
};
} // namespace http
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,28 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/IStreamTransport.h"
#include "td/mtproto/HttpTransport.h"
#include "td/mtproto/TcpTransport.h"
#include "td/utils/logging.h"
namespace td {
namespace mtproto {
std::unique_ptr<IStreamTransport> create_transport(TransportType type) {
switch (type) {
case TransportType::ObfuscatedTcp:
return std::make_unique<tcp::ObfuscatedTransport>();
case TransportType::Tcp:
return std::make_unique<tcp::OldTransport>();
case TransportType::Http:
return std::make_unique<http::Transport>();
}
UNREACHABLE();
}
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,34 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/port/Fd.h"
#include "td/utils/Status.h"
namespace td {
namespace mtproto {
enum class TransportType { Tcp, ObfuscatedTcp, Http };
class IStreamTransport {
public:
IStreamTransport() = default;
IStreamTransport(const IStreamTransport &) = delete;
IStreamTransport &operator=(const IStreamTransport &) = delete;
virtual ~IStreamTransport() = default;
virtual Result<size_t> read_next(BufferSlice *message, uint32 *quick_ack) = 0;
virtual bool support_quick_ack() const = 0;
virtual void write(BufferWriter &&message, bool quick_ack) = 0;
virtual bool can_read() const = 0;
virtual bool can_write() const = 0;
virtual void init(ChainBufferReader *input, ChainBufferWriter *output) = 0;
virtual size_t max_prepend_size() const = 0;
virtual TransportType get_type() const = 0;
};
std::unique_ptr<IStreamTransport> create_transport(TransportType type);
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,28 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/PacketStorer.h"
namespace td {
namespace mtproto {
class NoCryptoImpl {
public:
NoCryptoImpl(uint64 message_id, const Storer &data) : message_id(message_id), data(data) {
}
template <class T>
void do_store(T &storer) const {
storer.store_binary(message_id);
storer.store_binary(static_cast<int32>(data.size()));
storer.store_storer(data);
}
private:
uint64 message_id;
const Storer &data;
};
} // namespace mtproto
} // namespace td

45
td/mtproto/PacketStorer.h Normal file
View File

@ -0,0 +1,45 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/utils/Storer.h"
#include "td/utils/tl_storers.h"
#include <limits>
namespace td {
namespace mtproto {
template <class Impl>
class PacketStorer
: public Storer
, public Impl {
public:
size_t size() const override {
if (size_ != std::numeric_limits<size_t>::max()) {
return size_;
}
TlStorerCalcLength storer;
this->do_store(storer);
return size_ = storer.get_length();
}
size_t store(uint8 *ptr) const override {
char *start = reinterpret_cast<char *>(ptr);
TlStorerUnsafe storer(start);
this->do_store(storer);
return storer.get_buf() - start;
}
using Impl::Impl;
private:
mutable size_t size_ = std::numeric_limits<size_t>::max();
};
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,69 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/AuthKey.h"
#include "td/mtproto/NoCryptoStorer.h"
#include "td/mtproto/RawConnection.h"
#include "td/mtproto/utils.h"
#include "td/utils/buffer.h"
#include "td/utils/port/Fd.h"
#include "td/utils/Random.h"
#include "td/utils/Status.h"
#include "td/mtproto/mtproto_api.h"
namespace td {
namespace mtproto {
class PingConnection : private RawConnection::Callback {
public:
explicit PingConnection(std::unique_ptr<RawConnection> raw_connection) : raw_connection_(std::move(raw_connection)) {
}
Fd &get_pollable() {
return raw_connection_->get_pollable();
}
std::unique_ptr<RawConnection> move_as_raw_connection() {
return std::move(raw_connection_);
}
void close() {
raw_connection_->close();
}
Status flush() {
if (!was_ping_) {
UInt128 nonce;
Random::secure_bytes(nonce.raw, sizeof(nonce));
raw_connection_->send_no_crypto(PacketStorer<NoCryptoImpl>(1, create_storer(mtproto_api::req_pq(nonce))));
was_ping_ = true;
}
return raw_connection_->flush(AuthKey(), *this);
}
bool was_pong() const {
return was_pong_;
}
Status on_raw_packet(const PacketInfo &packet_info, BufferSlice packet) override {
if (packet.size() < 12) {
return Status::Error("Result is too small");
}
packet.confirm_read(12);
// TODO: fetch_result
was_pong_ = true;
return Status::OK();
}
private:
std::unique_ptr<RawConnection> raw_connection_;
bool was_ping_ = false;
bool was_pong_ = false;
};
} // namespace mtproto
} // namespace td

View File

@ -0,0 +1,126 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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/RawConnection.h"
#include "td/mtproto/AuthKey.h"
#include "td/mtproto/Transport.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <utility>
namespace td {
namespace mtproto {
void RawConnection::send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token) {
mtproto::PacketInfo info;
info.version = 2;
info.no_crypto_flag = false;
info.salt = salt;
info.session_id = session_id;
auto packet = BufferWriter{mtproto::Transport::write(storer, auth_key, &info), transport_->max_prepend_size(), 0};
mtproto::Transport::write(storer, auth_key, &info, packet.as_slice());
bool use_quick_ack = false;
if (quick_ack_token != 0 && transport_->support_quick_ack()) {
auto tmp = quick_ack_to_token_.insert(std::make_pair(info.message_ack, quick_ack_token));
if (tmp.second) {
use_quick_ack = true;
} else {
LOG(ERROR) << "quick_ack collision " << tag("quick_ack", info.message_ack);
}
}
transport_->write(std::move(packet), use_quick_ack);
}
uint64 RawConnection::send_no_crypto(const Storer &storer) {
mtproto::PacketInfo info;
info.no_crypto_flag = true;
auto packet =
BufferWriter{mtproto::Transport::write(storer, mtproto::AuthKey(), &info), transport_->max_prepend_size(), 0};
mtproto::Transport::write(storer, mtproto::AuthKey(), &info, packet.as_slice());
LOG(INFO) << "Send handshake packet: " << format::as_hex_dump<4>(packet.as_slice());
transport_->write(std::move(packet), false);
return info.message_id;
}
Status RawConnection::flush_read(const AuthKey &auth_key, Callback &callback) {
auto r = socket_fd_.flush_read();
if (r.is_ok() && stats_callback_) {
stats_callback_->on_read(r.ok());
}
while (transport_->can_read()) {
BufferSlice packet;
uint32 quick_ack = 0;
TRY_RESULT(wait_size, transport_->read_next(&packet, &quick_ack));
if (wait_size != 0) {
break;
}
if (quick_ack != 0) {
auto it = quick_ack_to_token_.find(quick_ack);
if (it == quick_ack_to_token_.end()) {
LOG(WARNING) << Status::Error(PSLICE() << "Unknown " << tag("quick_ack", quick_ack));
continue;
// TODO: return Status::Error(PSLICE() << "Unknown " << tag("quick_ack", quick_ack));
}
auto token = it->second;
quick_ack_to_token_.erase(it);
callback.on_quick_ack(token);
continue;
}
MutableSlice data = packet.as_slice();
PacketInfo info;
info.version = 2;
int32 error_code = 0;
TRY_STATUS(mtproto::Transport::read(data, auth_key, &info, &data, &error_code));
if (error_code) {
if (error_code == -429) {
if (stats_callback_) {
stats_callback_->on_mtproto_error();
}
return Status::Error(500, PSLICE() << "Mtproto error: " << error_code);
}
if (error_code == -404) {
return Status::Error(-404, PSLICE() << "Mtproto error: " << error_code);
}
return Status::Error(PSLICE() << "Mtproto error: " << error_code);
}
// If a packet was successfully decrypted, then it is ok to assume that the connection is alive
if (!auth_key.empty()) {
if (stats_callback_) {
stats_callback_->on_pong();
}
}
TRY_STATUS(callback.on_raw_packet(info, packet.from_slice(data)));
}
TRY_STATUS(std::move(r));
return Status::OK();
}
Status RawConnection::flush_write() {
TRY_RESULT(size, socket_fd_.flush_write());
if (size > 0 && stats_callback_) {
stats_callback_->on_write(size);
}
return Status::OK();
}
} // namespace mtproto
} // namespace td

146
td/mtproto/RawConnection.h Normal file
View File

@ -0,0 +1,146 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
//
// 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)
//
#pragma once
#include "td/mtproto/IStreamTransport.h"
#include "td/utils/buffer.h"
#include "td/utils/BufferedFd.h"
#include "td/utils/common.h"
#include "td/utils/port/Fd.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Status.h"
#include "td/telegram/StateManager.h"
#include <map>
namespace td {
class Storer;
namespace mtproto {
class AuthKey;
struct PacketInfo;
} // namespace mtproto
} // namespace td
namespace td {
namespace mtproto {
class RawConnection {
public:
class StatsCallback {
public:
virtual ~StatsCallback() = default;
virtual void on_read(uint64 bytes) = 0;
virtual void on_write(uint64 bytes) = 0;
virtual void on_pong() = 0; // called when we know that connection is alive
virtual void on_error() = 0; // called on RawConnectin error. Such error should be very rare on good connections.
virtual void on_mtproto_error() = 0;
};
RawConnection() = default;
RawConnection(SocketFd socket_fd, TransportType transport_type, std::unique_ptr<StatsCallback> stats_callback)
: socket_fd_(std::move(socket_fd))
, transport_(create_transport(transport_type))
, stats_callback_(std::move(stats_callback)) {
transport_->init(&socket_fd_.input_buffer(), &socket_fd_.output_buffer());
}
void set_connection_token(StateManager::ConnectionToken connection_token) {
connection_token_ = std::move(connection_token);
}
bool can_send() const {
return transport_->can_write();
}
TransportType get_transport_type() const {
return transport_->get_type();
}
void send_crypto(const Storer &storer, int64 session_id, int64 salt, const AuthKey &auth_key,
uint64 quick_ack_token = 0);
uint64 send_no_crypto(const Storer &storer);
Fd &get_pollable() {
return socket_fd_.get_fd();
}
StatsCallback *stats_callback() {
return stats_callback_.get();
}
class Callback {
public:
Callback() = default;
Callback(const Callback &) = delete;
Callback &operator=(const Callback &) = delete;
virtual ~Callback() = default;
virtual Status on_raw_packet(const PacketInfo &info, BufferSlice packet) = 0;
virtual Status on_quick_ack(uint64 quick_ack_token) {
return Status::Error("quick acks unsupported fully, but still used");
}
virtual Status before_write() {
return Status::OK();
}
};
// NB: After first returned error, all subsequent calls will return error too.
Status flush(const AuthKey &auth_key, Callback &callback) TD_WARN_UNUSED_RESULT {
auto status = do_flush(auth_key, callback);
if (status.is_error()) {
if (stats_callback_ && status.code() != 2) {
stats_callback_->on_error();
}
has_error_ = true;
}
return status;
}
bool has_error() const {
return has_error_;
}
void close() {
transport_.reset();
socket_fd_.close();
}
uint32 extra_{0};
string debug_str_;
double rtt_{0};
private:
BufferedFd<SocketFd> socket_fd_;
unique_ptr<IStreamTransport> transport_;
std::map<uint32, uint64> quick_ack_to_token_;
bool has_error_{false};
std::unique_ptr<StatsCallback> stats_callback_;
StateManager::ConnectionToken connection_token_;
Status flush_read(const AuthKey &auth_key, Callback &callback);
Status flush_write();
Status do_flush(const AuthKey &auth_key, Callback &callback) TD_WARN_UNUSED_RESULT {
if (has_error_) {
return Status::Error("Connection already failed");
}
// read/write
// EINVAL may be returned in linux kernel < 2.6.28. And on some new kernels too.
// just close connection and hope that read or write will not return this error too.
TRY_STATUS(socket_fd_.get_pending_error());
TRY_STATUS(flush_read(auth_key, callback));
TRY_STATUS(callback.before_write());
TRY_STATUS(flush_write());
if (can_close(socket_fd_)) {
return Status::Error("Connection closed");
}
return Status::OK();
}
};
} // namespace mtproto
} // namespace td

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