Project import generated by Copybara.
GitOrigin-RevId: 318483224ad6164d9966f731d60cde37039bb2d4
This commit is contained in:
commit
71d03f39c3
98
.clang-format
Normal file
98
.clang-format
Normal 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
17
.gitattributes
vendored
Normal 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
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
**/*build*/
|
||||
**/.*.swp
|
||||
**/.DS_Store
|
||||
**/auto/
|
||||
auto/
|
||||
db_backup
|
||||
*.pyc
|
||||
docs
|
60
.travis.yml
Normal file
60
.travis.yml
Normal 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
159
.ycm_extra_conf.py
Normal 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
|
||||
}
|
74
CMake/AddCXXCompilerFlag.cmake
Normal file
74
CMake/AddCXXCompilerFlag.cmake
Normal 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
29
CMake/FindReadline.cmake
Normal 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
230
CMake/iOS.cmake
Normal 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
671
CMakeLists.txt
Normal 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
|
||||
)
|
23
LICENSE_1_0.txt
Normal file
23
LICENSE_1_0.txt
Normal 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
124
README.md
Normal 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
3
TdConfig.cmake
Normal 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
61
benchmark/CMakeLists.txt
Normal 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
290
benchmark/bench_actor.cpp
Normal 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
213
benchmark/bench_crypto.cpp
Normal 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
237
benchmark/bench_db.cpp
Normal 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;
|
||||
}
|
9
benchmark/bench_empty.cpp
Normal file
9
benchmark/bench_empty.cpp
Normal 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;
|
||||
}
|
76
benchmark/bench_handshake.cpp
Normal file
76
benchmark/bench_handshake.cpp
Normal 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
78
benchmark/bench_http.cpp
Normal 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();
|
||||
}
|
118
benchmark/bench_http_reader.cpp
Normal file
118
benchmark/bench_http_reader.cpp
Normal 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());
|
||||
}
|
89
benchmark/bench_http_server.cpp
Normal file
89
benchmark/bench_http_server.cpp
Normal 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();
|
||||
}
|
138
benchmark/bench_http_server_cheat.cpp
Normal file
138
benchmark/bench_http_server_cheat.cpp
Normal 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();
|
||||
}
|
121
benchmark/bench_http_server_fast.cpp
Normal file
121
benchmark/bench_http_server_fast.cpp
Normal 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
163
benchmark/bench_log.cpp
Normal 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
392
benchmark/bench_misc.cpp
Normal 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 ∑
|
||||
|
||||
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
943
benchmark/bench_queue.cpp
Normal 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
108
benchmark/bench_tddb.cpp
Normal 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
28
benchmark/rmdir.cpp
Normal 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
39
benchmark/wget.cpp
Normal 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
16
bitbucket-pipelines.yml
Normal 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
|
13
example/cpp/CMakeLists.txt
Normal file
13
example/cpp/CMakeLists.txt
Normal 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
305
example/cpp/td_example.cpp
Normal 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();
|
||||
}
|
21
example/cpp/tdjson_example.cpp
Normal file
21
example/cpp/tdjson_example.cpp
Normal 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;
|
||||
}
|
72
example/python/tdjson_example.py
Normal file
72
example/python/tdjson_example.py
Normal 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
4
format.ps1
Normal file
@ -0,0 +1,4 @@
|
||||
./src.ps1 | ForEach-Object {
|
||||
echo $_
|
||||
clang-format -style=file -i $_
|
||||
}
|
2
format.sh
Executable file
2
format.sh
Executable 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
9
gen_git_commit_h.ps1
Normal 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
11
gen_git_commit_h.sh
Executable 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
314
memprof/memprof.cpp
Normal 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
27
memprof/memprof.h
Normal 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
47
sqlite/CMakeLists.txt
Normal 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
24
sqlite/sqlite/LICENSE
Normal 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
202634
sqlite/sqlite/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
10371
sqlite/sqlite/sqlite3.h
Normal file
10371
sqlite/sqlite/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
560
sqlite/sqlite/sqlite3ext.h
Normal file
560
sqlite/sqlite/sqlite3ext.h
Normal 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 */
|
1280
sqlite/sqlite/sqlite3session.h
Normal file
1280
sqlite/sqlite/sqlite3session.h
Normal file
File diff suppressed because it is too large
Load Diff
1
src.ps1
Normal file
1
src.ps1
Normal 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
2
src.sh
Executable 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
120
td/generate/CMakeLists.txt
Normal 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()
|
362
td/generate/DoxygenTlDocumentationGenerator.php
Normal file
362
td/generate/DoxygenTlDocumentationGenerator.php
Normal 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('*/', '*/', $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]);
|
262
td/generate/JavadocTlDocumentationGenerator.php
Normal file
262
td/generate/JavadocTlDocumentationGenerator.php
Normal 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('*/', '*/', $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]);
|
299
td/generate/TlDocumentationGenerator.php
Normal file
299
td/generate/TlDocumentationGenerator.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
20
td/generate/generate_c.cpp
Normal file
20
td/generate/generate_c.cpp
Normal 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"));
|
||||
}
|
55
td/generate/generate_common.cpp
Normal file
55
td/generate/generate_common.cpp
Normal 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
|
||||
}
|
29
td/generate/generate_java.cpp
Normal file
29
td/generate/generate_java.cpp
Normal 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));
|
||||
}
|
14
td/generate/generate_json.cpp
Normal file
14
td/generate/generate_json.cpp
Normal 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");
|
||||
}
|
91
td/generate/scheme/mtproto_api.tl
Normal file
91
td/generate/scheme/mtproto_api.tl
Normal 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;
|
BIN
td/generate/scheme/mtproto_api.tlo
Normal file
BIN
td/generate/scheme/mtproto_api.tlo
Normal file
Binary file not shown.
113
td/generate/scheme/secret_api.tl
Normal file
113
td/generate/scheme/secret_api.tl
Normal 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;
|
BIN
td/generate/scheme/secret_api.tlo
Normal file
BIN
td/generate/scheme/secret_api.tlo
Normal file
Binary file not shown.
2761
td/generate/scheme/td_api.tl
Normal file
2761
td/generate/scheme/td_api.tl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
td/generate/scheme/td_api.tlo
Normal file
BIN
td/generate/scheme/td_api.tlo
Normal file
Binary file not shown.
1082
td/generate/scheme/telegram_api.tl
Normal file
1082
td/generate/scheme/telegram_api.tl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
td/generate/scheme/telegram_api.tlo
Normal file
BIN
td/generate/scheme/telegram_api.tlo
Normal file
Binary file not shown.
6
td/generate/scheme/update-tlo.sh
Executable file
6
td/generate/scheme/update-tlo.sh
Executable 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
|
224
td/generate/tl_json_converter.cpp
Normal file
224
td/generate/tl_json_converter.cpp
Normal 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
|
17
td/generate/tl_json_converter.h
Normal file
17
td/generate/tl_json_converter.h
Normal 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
1355
td/generate/tl_writer_c.h
Normal file
File diff suppressed because it is too large
Load Diff
665
td/generate/tl_writer_cpp.cpp
Normal file
665
td/generate/tl_writer_cpp.cpp
Normal 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
104
td/generate/tl_writer_cpp.h
Normal 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
349
td/generate/tl_writer_h.cpp
Normal 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
90
td/generate/tl_writer_h.h
Normal 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
|
257
td/generate/tl_writer_hpp.cpp
Normal file
257
td/generate/tl_writer_hpp.cpp
Normal 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
105
td/generate/tl_writer_hpp.h
Normal 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
|
542
td/generate/tl_writer_java.cpp
Normal file
542
td/generate/tl_writer_java.cpp
Normal 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
|
116
td/generate/tl_writer_java.h
Normal file
116
td/generate/tl_writer_java.h
Normal 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
|
695
td/generate/tl_writer_jni_cpp.cpp
Normal file
695
td/generate/tl_writer_jni_cpp.cpp
Normal 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
|
114
td/generate/tl_writer_jni_cpp.h
Normal file
114
td/generate/tl_writer_jni_cpp.h
Normal 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
|
192
td/generate/tl_writer_jni_h.cpp
Normal file
192
td/generate/tl_writer_jni_h.cpp
Normal 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
|
57
td/generate/tl_writer_jni_h.h
Normal file
57
td/generate/tl_writer_jni_h.h
Normal 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
|
251
td/generate/tl_writer_td.cpp
Normal file
251
td/generate/tl_writer_td.cpp
Normal 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
|
61
td/generate/tl_writer_td.h
Normal file
61
td/generate/tl_writer_td.h
Normal 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
399
td/mtproto/AuthData.h
Normal 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
93
td/mtproto/AuthKey.h
Normal 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
333
td/mtproto/CryptoStorer.h
Normal 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
301
td/mtproto/Handshake.cpp
Normal 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
102
td/mtproto/Handshake.h
Normal 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
|
90
td/mtproto/HandshakeActor.cpp
Normal file
90
td/mtproto/HandshakeActor.cpp
Normal 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
|
58
td/mtproto/HandshakeActor.h
Normal file
58
td/mtproto/HandshakeActor.h
Normal 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
|
81
td/mtproto/HandshakeConnection.h
Normal file
81
td/mtproto/HandshakeConnection.h
Normal 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
|
81
td/mtproto/HttpTransport.cpp
Normal file
81
td/mtproto/HttpTransport.cpp
Normal 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
|
51
td/mtproto/HttpTransport.h
Normal file
51
td/mtproto/HttpTransport.h
Normal 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
|
28
td/mtproto/IStreamTransport.cpp
Normal file
28
td/mtproto/IStreamTransport.cpp
Normal 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
|
34
td/mtproto/IStreamTransport.h
Normal file
34
td/mtproto/IStreamTransport.h
Normal 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
|
28
td/mtproto/NoCryptoStorer.h
Normal file
28
td/mtproto/NoCryptoStorer.h
Normal 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
45
td/mtproto/PacketStorer.h
Normal 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
|
69
td/mtproto/PingConnection.h
Normal file
69
td/mtproto/PingConnection.h
Normal 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
|
126
td/mtproto/RawConnection.cpp
Normal file
126
td/mtproto/RawConnection.cpp
Normal 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
146
td/mtproto/RawConnection.h
Normal 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
Reference in New Issue
Block a user