diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..4edbaa62765 --- /dev/null +++ b/.clang-format @@ -0,0 +1,111 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +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: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +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: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..9f00b755a6e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# https://editorconfig.org/ + +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +charset = utf-8 +indent_style = space +tab_width = 4 + +[{*.{awk,sh},Makefile*}] +indent_size = 4 +indent_style = table +max_line_length = 80 + +[*.{c,cc,cpp,h,html,inc,php,phpt}] +indent_size = 4 +indent_style = space +max_line_length = 120 + +[*.md] +indent_style = space +max_line_length = 120 + +[COMMIT_EDITMSG] +indent_size = 4 +indent_style = space +max_line_length = 80 + diff --git a/.gitattributes b/.gitattributes index 5e42ccdfc11..a2047fd34cc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ /.github/ export-ignore /benchmark/ export-ignore /core-tests/ export-ignore -/.travis.yml export-ignore +*.h linguist-language=cpp diff --git a/.github/ISSUE-CN.md b/.github/ISSUE-CN.md index d2f5cf39b9f..c08b7485d95 100644 --- a/.github/ISSUE-CN.md +++ b/.github/ISSUE-CN.md @@ -5,11 +5,11 @@ # 需知 -当你觉得发现了一个Swoole内核的bug时, 请提出报告. +当你觉得发现了一个Swoole内核的BUG时, 请提出报告. Swoole的内核开发者们或许还不知道问题的存在, 除非你主动提出报告, 否则BUG也许将很难被发现并修复, 你可以在[Github的issue区](https://github.com/swoole/swoole-src/issues)提出错误报告(即点击右上角绿色的`New issue`按钮), 这里的错误报告将会被最优先解决. -请不要在邮件列表或私人信件中发送错误报告, Github的issue区同样可以提出对于Swoole的任何要求与建议. +请不要在邮件列表或私人信件中发送错误报告, GitHub的issue区同样可以提出对于Swoole的任何要求与建议. 在你提交错误报告之前, 请先阅读以下的**如何提交错误报告**. @@ -21,7 +21,7 @@ Swoole的内核开发者们或许还不知道问题的存在, Please answer these questions before submitting your issue. Thanks! > 在提交Issue前请回答以下问题: - + 1. What did you do? If possible, provide a simple script for reproducing the error. > 请详细描述问题的产生过程,贴出相关的代码,最好能提供一份可稳定重现的简单脚本代码。 @@ -32,16 +32,26 @@ Please answer these questions before submitting your issue. Thanks! > 实际运行的结果是什么? 4. What version of Swoole are you using (`php --ri swoole`)? -> 你的版本? 贴出 `php --ri swoole` 所打印的内容 +> 你的版本? 贴出 `php --ri swoole` 所打印的内容 5. What is your machine environment used (including the version of kernel & php & gcc)? -> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? +> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? > 可以使用`uname -a`, `php -v`, `gcc -v` 命令打印 ``` 其中, 最为关键的是提供**可稳定重现的简单脚本代码**, 否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 +## 内存分析 (强烈推荐) + +更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. + ## 关于段错误(核心转储) 此外, 在一种特殊情况下你可以使用调试工具来帮助开发者定位问题 @@ -59,8 +69,7 @@ WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 ulimit -c unlimited ``` -触发BUG, 核心转储文件会生成在 程序目录 或 系统根目录 或 /cores目录下 (取决于你的系统配置) -键入以下命令进入gdb调试程序 +触发BUG, 核心转储文件会生成在 程序目录 或 系统根目录 或 `/cores` 目录下 (取决于你的系统配置), 键入以下命令进入gdb调试程序 ``` gdb php core gdb php /tmp/core.4596 @@ -78,13 +87,3 @@ gdb php /tmp/core.4596 ``` 将以上信息都贴在issue中 - -#### 内存分析 (强烈推荐) - -更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG - -```shell -USE_ZEND_ALLOC=0 valgrind --log-file= php xxx.php -``` - -* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. diff --git a/.github/ISSUE.md b/.github/ISSUE.md index 58dc8a20e7d..3cbe4908678 100644 --- a/.github/ISSUE.md +++ b/.github/ISSUE.md @@ -4,7 +4,7 @@ English | [中文](./ISSUE-CN.md) ## Instruction -If you think you have found a bug in Swoole, please report it. +If you think you have found a bug in Swoole, please report it. The Swoole developers probably don't know about it, and unless you report it, chances are it won't be fixed. You can report bugs at https://github.com/swoole/swoole-src/issues. @@ -16,7 +16,6 @@ Please read the **How to report a bug document** before submitting any bug repor ## New issue First, while creating an issue, the system will give the following template: -> 首先在创建issue的同时, 系统将会给出如下模板, 请你认真填写它, 否则issue由于缺乏信息可能会被忽略: ```markdown Please answer these questions before submitting your issue. Thanks! @@ -28,6 +27,16 @@ Please answer these questions before submitting your issue. Thanks! ``` The most important thing is to provide a simple script for reproducing the error, otherwise, you must provide as much information as possible. +## Memory detection (recommended) + +In addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly. + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file. + ## CoreDump Besides, In a special case, you can use debugging tools to help developers locate problems @@ -36,7 +45,7 @@ Besides, In a special case, you can use debugging tools to help developers locat WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 ``` -When a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command. +When a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command. > Using `gdb` to track the core file need to add the `--enable-debug` parameter when compiling `swoole`. Enable core dump @@ -61,16 +70,7 @@ Use the f command in gdb to view the code segment corresponding to the ID. ``` If there is no function call stack information, it may be that the compiler has removed the debug information. Please manually modify the `Makefile` file in the swoole source directory and modify CFLAGS to -```shell -CFLAGS = -Wall -pthread -g -O0 -``` - -#### Memory detection (recommended) - -In addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly. ```shell -USE_ZEND_ALLOC=0 valgrind --log-file= php xxx.php +CFLAGS = -Wall -pthread -g -O0 ``` - -* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file. diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 1dca2b04559..bf6908e537a 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,4 +1,4 @@ -Please answer these questions before submitting your issue. Thanks! +Please answer these questions before submitting your issue. 1. What did you do? If possible, provide a simple script for reproducing the error. @@ -16,6 +16,6 @@ Please answer these questions before submitting your issue. Thanks! -5. What is your machine environment used (including version of kernel & php & gcc) ? +5. What is your machine environment used (show your `uname -a` & `php -v` & `gcc -v`) ? diff --git a/.github/WORKFLOW-PARAMETERS.md b/.github/WORKFLOW-PARAMETERS.md new file mode 100644 index 00000000000..91289839d12 --- /dev/null +++ b/.github/WORKFLOW-PARAMETERS.md @@ -0,0 +1,17 @@ +# Workflow Parameters +Adding parameters in the Git commit log can control the workflow. + +## --filter +This parameter specifies which workflows to run, instead of running all of them. +The command format is: `--filter=[flow1][flow2][flow...]` , +and it supports setting multiple workflows, with names matching the filename of the yml file. + +## --valgrind +Setting this parameter will cause the test program to be run with `valgrind`. + +```shell +git commit -m "commit message --filter=[core][unit] --valgrind" +``` + +## --asan +Setting this parameter will cause the test program to be run with `ASAN`. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..5ace4600a1f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/alpine.Dockerfile b/.github/workflows/alpine.Dockerfile new file mode 100644 index 00000000000..494bb252deb --- /dev/null +++ b/.github/workflows/alpine.Dockerfile @@ -0,0 +1,23 @@ +ARG PHP_VERSION +ARG ALPINE_VERSION + +FROM phpswoole/php:${PHP_VERSION}-alpine + +LABEL maintainer="Swoole Team " version="1.0" license="Apache2" + +ARG PHP_VERSION + +COPY . /swoole + +WORKDIR /swoole + +RUN set -ex \ + && phpize \ + && ./configure --enable-openssl --enable-swoole-curl \ + && make -s -j$(nproc) && make install + +RUN echo "extension=swoole.so" > "/usr/local/etc/php/conf.d/swoole.ini" +RUN php -v +RUN php -m +RUN php --ri swoole +RUN echo -e "\033[42;37m Build Completed :).\033[0m\n" diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml new file mode 100644 index 00000000000..1c228f05ab3 --- /dev/null +++ b/.github/workflows/core.yml @@ -0,0 +1,74 @@ +name: Core Tests + +on: [ push, pull_request ] + +env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + +jobs: + build: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')" + timeout-minutes: 10 + services: + tinyproxy: + image: "vimagick/tinyproxy" + ports: + - 8888:8888 + nginx: + image: "nginx" + ports: + - "80:80" + env: + NGINX_PORT: "[::]:80" + socks5: + image: "xkuma/socks5" + ports: + - 8080:1080 + env: + PROXY_USER: user + PROXY_PASSWORD: password + PROXY_SERVER: 0.0.0.0:1080 + + steps: + - uses: actions/checkout@v4 + + - name: install dependencies + run: sudo apt update -y && sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev libbrotli-dev + + - name: configure + run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-openssl + + - name: make + run: | + cmake . -D CODE_COVERAGE=ON -D enable_thread=1 + make VERBOSE=1 -j $(nproc) lib-swoole + + - name: make test with valgrind + if: "contains(github.event.head_commit.message, '--valgrind')" + run: | + sudo apt install -y valgrind + cd core-tests && SWOOLE_VALGRIND=1 ./run.sh + + - name: make test with asan + if: "contains(github.event.head_commit.message, '--asan')" + run: | + cd core-tests && SWOOLE_ENABLE_ASAN=1 ./run.sh + + - name: make test + run: + cd core-tests && ./run.sh + + - name: run coverage + shell: bash + run: sudo apt-get install lcov && + sudo lcov --directory . --capture --output-file coverage.info && + sudo lcov --remove coverage.info "${{runner.workspace}}/swoole-src/include/*" '/usr/*' --output-file coverage.info && + sudo lcov --list coverage.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.info + fail_ci_if_error: true diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 00000000000..c223a1333b7 --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,58 @@ +name: coverity-scan + +on: + push: + branches: + - coverity_scan + +env: + COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COVERITY_SCAN_EMAIL: team@swoole.com + COVERITY_PROJECT: ${{ github.repository }} + COV_TOOLS_DIR: ${{ github.workspace }}/cov-analysis-linux64 + COV_BUILD_DIR: ${{ github.workspace }} + COV_RESULTS_DIR: cov-int + COV_RESULTS_FILE: analysis-results.tgz + +jobs: + coverity-scan: + if: github.repository_owner == 'swoole' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.0" + + - name: Download and install Coverity Build Tool + run: | + wget -q https://scan.coverity.com/download/linux64 \ + --post-data "token=${COVERITY_SCAN_TOKEN}&project=${COVERITY_PROJECT}" \ + -O cov-analysis-linux64.tar.gz + mkdir ${COV_TOOLS_DIR} + tar xzf cov-analysis-linux64.tar.gz --strip 1 -C ${COV_TOOLS_DIR} + ls -la ${COV_TOOLS_DIR}/bin + + - name: Run build steps + run: | + sudo phpize && ./configure + + - name: Run Coverity Scan Analysis Tool + run: | + export PATH=${COV_TOOLS_DIR}/bin:$PATH + cd ${COV_BUILD_DIR} + cov-build --dir ${COV_RESULTS_DIR} make -j 4 + + - name: Upload Coverity Scan Analysis results + run: | + cd ${COV_BUILD_DIR} + tar czf ${COV_RESULTS_FILE} ${COV_RESULTS_DIR} + curl \ + --form project=${COVERITY_PROJECT} \ + --form token=${COVERITY_SCAN_TOKEN} \ + --form email=${COVERITY_SCAN_EMAIL} \ + --form file=@${COV_RESULTS_FILE} \ + --form version=${GITHUB_SHA} \ + --form description="GitHub Actions" \ + https://scan.coverity.com/builds?project=${COVERITY_PROJECT} diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml new file mode 100644 index 00000000000..b9232561366 --- /dev/null +++ b/.github/workflows/ext.yml @@ -0,0 +1,85 @@ +name: Compile Tests + +on: [ push, pull_request ] + +env: + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" + +jobs: + build-ubuntu-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[ext]')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install-deps + run: sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev + - name: phpize + run: phpize + - name: build1 + run: ./configure && + make clean && make -j$(nproc) + - name: build2 + run: ./configure --enable-sockets && + make clean && make -j$(nproc) + - name: build3 + run: ./configure --enable-sockets --enable-mysqlnd && + make clean && make -j$(nproc) + - name: build5 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(nproc) + - name: build6 + run: ./configure --enable-sockets --enable-mysqlnd --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build7 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-debug-log && + make clean && make -j$(nproc) + - name: build8 + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log && + make clean && make -j$(nproc) + - name: build with thread context + run: ./configure --enable-sockets --enable-mysqlnd --enable-swoole-curl --enable-openssl --enable-cares --enable-debug-log --enable-thread-context && + make clean && make -j$(nproc) + + build-macos-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[macos]')" + runs-on: macos-latest + steps: + - name: install dependencies + run: brew reinstall php + - uses: actions/checkout@v4 + - name: phpize + run: phpize + - name: build1 + run: ./configure CPPFLAGS="${CPPFLAGS}" && make clean && make -j$(sysctl -n hw.ncpu) + - name: build2 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build3 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build5 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build6 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl + --enable-swoole-curl --enable-debug-log && + make clean && make -j$(sysctl -n hw.ncpu) + - name: build7 + run: ./configure CPPFLAGS="${CPPFLAGS}" --enable-sockets --enable-mysqlnd --enable-openssl --enable-swoole-curl + --enable-debug-log --enable-cares && + make clean && make -j$(sysctl -n hw.ncpu) + + build-alpine-latest: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[alpine]')" + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [ '8.1', '8.2', '8.3', '8.4' ] + max-parallel: 8 + fail-fast: false + steps: + - uses: actions/checkout@v4 + - name: build + run: | + cp .github/workflows/alpine.Dockerfile alpine.Dockerfile + docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml new file mode 100644 index 00000000000..12c6523bea5 --- /dev/null +++ b/.github/workflows/framework.yml @@ -0,0 +1,122 @@ +name: Framework Tests + +on: + push: + pull_request: + +env: + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" + +jobs: + linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev valgrind + phpize + ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql + make -j$(nproc) + sudo make install + php -v + php -m + php --ini + php --ri swoole + + - name: Laravel Octane Tests + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.3' + run: | + git clone https://github.com/laravel/octane.git --depth=1 + cd octane/ + composer update --prefer-dist --no-interaction --no-progress + vendor/bin/phpunit + + - name: Hyperf Tests + if: matrix.framework == 'Hyperf' && matrix.php-version != '8.3' + env: + SW_VERSION: 'master' + MYSQL_VERSION: '5.7' + PGSQL_VERSION: '14' + run: | + git clone https://github.com/hyperf/hyperf.git --depth=1 + cd hyperf/ + composer update -o + ./.travis/requirement.install.sh + ./.travis/setup.services.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh + cp .travis/.env.example .env + export SWOOLE_BRANCH=${GITHUB_REF##*/} + if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine + else + .travis/run.test.sh + fi + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + + macos: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + phpize + ./configure CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares + make -j$(sysctl -n hw.ncpu) + sudo make install + php --ri swoole + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml new file mode 100644 index 00000000000..047bfdfca05 --- /dev/null +++ b/.github/workflows/iouring.yml @@ -0,0 +1,40 @@ +name: Linux io_uring Tests + +on: [push, pull_request] + +jobs: + test-linux: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')" + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_USE_IOURING=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh + diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml new file mode 100644 index 00000000000..749616aaa71 --- /dev/null +++ b/.github/workflows/thread.yml @@ -0,0 +1,42 @@ +name: Thread Support Tests + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[thread]')" + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + php: ['8.1-zts', '8.2-zts', '8.3-zts', '8.4-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + env: + phpts: ts + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run tests + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_THREAD=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 00000000000..a6d1ae06e89 --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,41 @@ +name: Unit Tests + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[unit]')" + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.4'] + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run pecl-install.sh + run: | + sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh + - name: Run unit tests + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/.github/workflows/xdebug.yml b/.github/workflows/xdebug.yml new file mode 100644 index 00000000000..8933683dccb --- /dev/null +++ b/.github/workflows/xdebug.yml @@ -0,0 +1,37 @@ +name: Xdebug Support Tests + +on: [push, pull_request] + +jobs: + tests: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[xdebug]')" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3'] + + name: PHP ${{ matrix.php }} - Swoole + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: phpize + ini-values: extension=swoole + coverage: xdebug + - name: Build Swoole + run: | + phpize + ./configure + make -j$(nproc) + sudo make install + - name: Check extension conflict + run: | + php -v + php -m + php --ri swoole diff --git a/.gitignore b/.gitignore index 2fb57a27bcc..9068deb313d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,21 +7,21 @@ *.so *.loT *.pid -*.gcno -*.gcov -/Debug/* +*.dep modules/* /.deps /.libs/ /core /examples/core /Debug +/Release /CMakeFiles /cmake_install.cmake /CMakeCache.txt /Makefile -/lib /bin +/lib +/library /install_manifest.txt /config.guess /config.h @@ -80,11 +80,25 @@ cmake-build-debug/ /.vs /configure.in /configure.ac + +# core-tests /core-tests/CMakeCache.txt /core-tests/CMakeFiles/ /core-tests/Makefile /core-tests/bin/ /core-tests/cmake_install.cmake +/core-tests/samples/Makefile +/core-tests/samples/cmake_install\.cmake +/core-tests/samples/CMakeCache\.txt +/core-tests/samples/bin/ +/core-tests/samples/CMakeFiles/ +/core-tests/.project +/core-tests/.cproject +/core-tests/compile_commands.json +/core-tests/samples/.project +/core-tests/fuzz/fuzz_results/ +/core-tests/fuzz/bin/ + /tools/vendor /tools/composer.lock /examples/wrapper/CMakeFiles @@ -92,8 +106,15 @@ cmake-build-debug/ /examples/wrapper/Makefile /examples/wrapper/server /examples/wrapper/cmake_install.cmake -core-tests/samples/Makefile -core-tests/samples/cmake_install\.cmake -core-tests/samples/CMakeCache\.txt -core-tests/samples/bin/ -core-tests/samples/CMakeFiles/ +/out +/gcov_result + +# coverage +*.gcno +*.gcov +*.gcda +*.info +/html +/tests/include/lib/vendor/ +/tests/include/lib/composer.lock +/scripts/data diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b9a6d68584e..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "thirdparty/jemalloc"] - path = thirdparty/jemalloc - url = https://github.com/jemalloc/jemalloc.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1b6685703fd..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -language: php - -compiler: - - gcc - - clang - -os: - - linux - -php: - - 7.1 - - 7.2 - - 7.3 - - nightly - -matrix: - fast_finish: true - allow_failures: - - php: nightly - -notifications: - email: team@swoole.com - -sudo: required - -env: - global: - - secure: "Mqg9ifSV0BLv2TIBw/x64aEuB8Y5aPXwXg7xAOq08oPlHBYSBgSYxroJL5PXs0fe7PszZF20bUBinwmEXYQzqgP/40Y1kmq/kfTsDXwTOc9qbAbA1pWvH+Sk1kDP5MLJRPWBCkqctyFd0I0u0SdzT1fgSqirqEz2bMnbAUVpSvo=" - - CODECOV_TOKEN=:50513381-198e-43d3-a8b1-e36b95038927 - -services: - - docker - -before_install: - - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - -addons: - coverity_scan: - project: - name: "swoole/swoole-src" - description: "Build submitted via Travis CI" - notification_email: team@swoole.com - build_command_prepend: "./configure; make clean" - build_command: "make -j 4" - branch_pattern: coverity_scan - -# compile -before_script: - - date - - env - - uname -a - - ulimit -a - - php -v - - ls -al - - pwd - - echo "`git log -20 --pretty --oneline`" - - echo "`git log -10 --stat --pretty --oneline`" - - ./travis/pecl-install.sh - - ./travis/simple-compile.sh - - php --ri swoole - -script: - - ./travis/route.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f1b843440b2..008a4901608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,58 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -SET(SWOOLE_VERSION 4.4.8) -SET(SWOOLE_CLFLAGS pthread rt dl ssl crypt crypto) +set(SWOOLE_VERSION 6.0.0) + set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") + +file(READ ./config.h SWOOLE_CONFIG_FILE) + +set(CMAKE_MACOSX_RPATH 1) +set(SWOOLE_LINK_LIBRARIES pthread dl) + +if (APPLE) + set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") + include_directories(BEFORE /usr/local/include) + link_directories(BEFORE /usr/local/lib) +else() + list(APPEND SWOOLE_LINK_LIBRARIES rt crypt) +endif() SET(CMAKE_BUILD_TYPE Debug) -file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc thirdparty/boost/asm/combined.S) +# Code Coverage Configuration +add_library(coverage_config INTERFACE) + +option(CODE_COVERAGE "Enable coverage reporting" OFF) +if(CODE_COVERAGE) + message(STATUS "Open coverage") + # --coverage => -fprofile-arcs -ftest-coverage + target_compile_options(coverage_config INTERFACE + -O0 + -g + --coverage + ) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) + target_link_options(coverage_config INTERFACE --coverage) + else() + target_link_libraries(coverage_config INTERFACE --coverage) + endif() +endif(CODE_COVERAGE) + +file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc + thirdparty/boost/asm/combined.S + thirdparty/hiredis/alloc.c + thirdparty/hiredis/async.c + thirdparty/hiredis/hiredis.c + thirdparty/hiredis/net.c + thirdparty/hiredis/read.c + thirdparty/hiredis/sds.c + thirdparty/swoole_http_parser.c + thirdparty/multipart_parser.c +) file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS include/*.h) file(GLOB_RECURSE HEAD_WAPPER_FILES FOLLOW_SYMLINKS include/wrapper/*.hpp) @@ -21,45 +64,127 @@ SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_definitions(-DHAVE_CONFIG_H) # test -#add_definitions(-DSW_CO_MT) #add_definitions(-DSW_USE_THREAD_CONTEXT) -INCLUDE_DIRECTORIES(BEFORE ./include ./include/wrapper ./) +include_directories(BEFORE ./include ./include/wrapper ext-src/ thirdparty/ ./) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) -#shared library -add_library(shared SHARED ${SRC_LIST}) -set_target_properties(shared PROPERTIES OUTPUT_NAME "swoole" VERSION ${SWOOLE_VERSION}) -target_link_libraries(shared ${SWOOLE_CLFLAGS}) +# find OpenSSL +if (DEFINED openssl_dir) + include_directories(BEFORE ${openssl_dir}/include) + link_directories(${openssl_dir}/lib) +else() + find_package(OpenSSL) + if (${OPENSSL_FOUND}) + message(STATUS "Found OpenSSL, ${OPENSSL_LIBRARIES}") + include_directories(BEFORE ${OPENSSL_INCLUDE_DIR}) + list(APPEND SWOOLE_LINK_LIBRARIES ssl crypto) + else() + message(STATUS "Not found OpenSSL") + endif() +endif() -#static library -# set_target_properties(static PROPERTIES OUTPUT_NAME "swoole" VERSION ${SWOOLE_VERSION}) -# add_library(static STATIC ${SRC_LIST}) -# target_link_libraries(static ${SWOOLE_CLFLAGS}) +if (DEFINED brotli_dir) + include_directories(BEFORE ${brotli_dir}/include) + link_directories(${brotli_dir}/lib) +endif() -LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH}) +foreach (LINE ${SWOOLE_CONFIG_FILE}) + if ("${LINE}" MATCHES "define SW_USE_CARES 1") + message(STATUS "enable c-ares") + list(APPEND SWOOLE_LINK_LIBRARIES cares) + endif() +endforeach() -#test_server -set(TEST_SRC_LIST examples/cpp/test_server.c) -add_executable(test_server ${TEST_SRC_LIST}) -add_dependencies(test_server shared) -target_link_libraries(test_server ${SWOOLE_CLFLAGS} swoole) +if (DEFINED enable_trace_log) + add_definitions(-DSW_LOG_TRACE_OPEN) +endif() + +if (DEFINED enable_asan) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +endif() + +execute_process(COMMAND php-config --includes OUTPUT_VARIABLE PHP_INCLUDES OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND php-config --extension-dir OUTPUT_VARIABLE PHP_EXTENSION_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PHP_INCLUDES}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PHP_INCLUDES}") -#mt_co -set(TEST_SRC_LIST examples/cpp/mt_co.cc) -add_executable(mt_co ${TEST_SRC_LIST}) -add_dependencies(mt_co shared) -target_link_libraries(mt_co ${SWOOLE_CLFLAGS} swoole) +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + execute_process(COMMAND ldconfig -p OUTPUT_VARIABLE LDCONFIG_LIST OUTPUT_STRIP_TRAILING_WHITESPACE) + #message(STATUS LDCONFIG_LIST) + if (LDCONFIG_LIST MATCHES brotlienc) + list(APPEND SWOOLE_LINK_LIBRARIES brotlienc) + endif() -#co + if (LDCONFIG_LIST MATCHES brotlidec) + list(APPEND SWOOLE_LINK_LIBRARIES brotlidec) + endif() +endif() + +# lib-swoole +link_directories(${LIBRARY_OUTPUT_PATH}) +add_library(lib-swoole SHARED ${SRC_LIST}) +set_target_properties(lib-swoole PROPERTIES OUTPUT_NAME "swoole" VERSION ${SWOOLE_VERSION}) +target_link_libraries(lib-swoole ${SWOOLE_LINK_LIBRARIES}) + +if (CODE_COVERAGE) + target_link_libraries(lib-swoole coverage_config gcov) +endif(CODE_COVERAGE) + +# test_server +set(TEST_SRC_LIST examples/cpp/test_server.cc) +add_executable(test_server ${TEST_SRC_LIST}) +add_dependencies(test_server lib-swoole) +target_link_libraries(test_server swoole pthread) + +# co set(TEST_SRC_LIST examples/cpp/co.cc) add_executable(co ${TEST_SRC_LIST}) -add_dependencies(co shared) -target_link_libraries(co ${SWOOLE_CLFLAGS} swoole) +add_dependencies(co lib-swoole) +target_link_libraries(co swoole) + +# ext-swoole +file(GLOB ext_cxx_files ext-src/*.cc) +set(ext_src_list ${ext_cxx_files} + thirdparty/php/curl/interface.cc + thirdparty/php/curl/multi.cc + thirdparty/php/sockets/multicast.cc + thirdparty/php/sockets/sendrecvmsg.cc + thirdparty/php/sockets/conversions.cc + thirdparty/php/sockets/sockaddr_conv.cc + thirdparty/php/standard/proc_open.cc + thirdparty/nghttp2/nghttp2_hd.c + thirdparty/nghttp2/nghttp2_rcbuf.c + thirdparty/nghttp2/nghttp2_helper.c + thirdparty/nghttp2/nghttp2_buf.c + thirdparty/nghttp2/nghttp2_mem.c + thirdparty/nghttp2/nghttp2_hd_huffman.c + thirdparty/nghttp2/nghttp2_hd_huffman_data.c + ) +add_library(ext-swoole SHARED ${ext_src_list}) +set_target_properties(ext-swoole PROPERTIES PREFIX "") +set_target_properties(ext-swoole PROPERTIES OUTPUT_NAME "swoole") +add_dependencies(ext-swoole lib-swoole) + +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + +# find libpq +if (DEFINED libpq_dir) + target_include_directories(ext-swoole BEFORE ${LIBPQ_INCLUDE_DIRS}) +else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBPQ REQUIRED libpq) + target_include_directories(ext-swoole PRIVATE ${LIBPQ_INCLUDE_DIRS}) +endif() + +target_link_libraries(ext-swoole swoole pq) -#install -INSTALL(CODE "MESSAGE(\"Are you run command using root user?\")") -INSTALL(TARGETS shared LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) +# install +INSTALL(TARGETS ext-swoole LIBRARY DESTINATION ${PHP_EXTENSION_DIR}) +INSTALL(TARGETS lib-swoole LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) INSTALL(FILES ${HEAD_FILES} DESTINATION include/swoole) INSTALL(FILES ${HEAD_WAPPER_FILES} DESTINATION include/swoole/wrapper) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/config.h DESTINATION include/swoole) diff --git a/CREDITS b/CREDITS deleted file mode 100644 index 7f55d94500b..00000000000 --- a/CREDITS +++ /dev/null @@ -1,3 +0,0 @@ -Tianfeng Han(mikan.tenny@gmail.com) -Swoole Inc. -QQ: 350749960 diff --git a/LICENSE b/LICENSE index ad00f19ae93..16248b4d9b2 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright Tianfeng.Han [mikan.tenny@gmail.com] + Copyright Tianfeng.Han [rango@swoole.com] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile.frag b/Makefile.frag new file mode 100644 index 00000000000..ddb3a5d6e86 --- /dev/null +++ b/Makefile.frag @@ -0,0 +1,11 @@ +swoole-build-coverage: + CCACHE_DISABLE=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" EXTRA_CXXFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) + +swoole-test-coverage: + CCACHE_DISABLE=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" EXTRA_CXXFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) && $(MAKE) install && $(top_srcdir)/tests/start.sh $(top_srcdir)/tests + +swoole-test-coverage-lcov: swoole-test-coverage + lcov -c --directory $(top_srcdir)/.libs --output-file $(top_srcdir)/coverage.info + +swoole-test-coverage-html: swoole-test-coverage-lcov + genhtml $(top_srcdir)/coverage.info --output-directory=$(top_srcdir)/html diff --git a/README-CN.md b/README-CN.md deleted file mode 100644 index 2f4691cd7df..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,530 +0,0 @@ -[English](./README.md) | 中文 - -# Swoole - -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg?style=flat-square)](https://github.com/swoole/swoole-src/releases) -[![Build Status](https://api.travis-ci.org/swoole/swoole-src.svg)](https://travis-ci.org/swoole/swoole-src) -[![License](https://img.shields.io/badge/license-apache2-blue.svg)](LICENSE) -[![Join the chat at https://gitter.im/swoole/swoole-src](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/swoole/swoole-src?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -[![Backers on Open Collective](https://opencollective.com/swoole-src/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/swoole-src/sponsors/badge.svg)](#sponsors) - -![](./mascot.png) - -**Swoole是一个为PHP用C和C++编写的基于事件的高性能异步&协程并行网络通信引擎** - -## ✨事件驱动 - -Swoole中的网络请求处理是基于事件的,并且充分利用了底层的epoll / kqueue实现,使得为数百万个请求提供服务变得非常容易。 - -Swoole4使用全新的协程内核引擎,现在它拥有一个全职的开发团队,因此我们正在进入PHP历史上前所未有的时期,为性能的高速提升提供了独一无二的可能性。 - -## ⚡️协程 - -Swoole4或更高版本拥有高可用性的内置协程,您可以使用完全同步的代码来实现异步性能,PHP代码没有任何额外的关键字,底层会自动进行协程调度。 - -开发者可以将协程理解为超轻量级的线程, 你可以非常容易地在一个进程中创建成千上万个协程。 - -### MySQL客户端 - -并发1万个请求从MySQL读取海量数据仅需要0.2秒 - -```php -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - $mysql = new Swoole\Coroutine\MySQL; - $mysql->connect([ - 'host' => '127.0.0.1', - 'user' => 'root', - 'password' => 'root', - 'database' => 'test' - ]); - $statement = $mysql->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $result = $statement->execute(); - assert(count($result) > 0); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -### 混合服务器 - -你可以在一个事件循环上创建多个服务:TCP,HTTP,Websocket和HTTP2,并且能轻松承载上万请求。 - -```php -function tcp_pack(string $data): string -{ - return pack('N', strlen($data)) . $data; -} -function tcp_unpack(string $data): string -{ - return substr($data, 4, unpack('N', substr($data, 0, 4))[1]); -} -$tcp_options = [ - 'open_length_check' => true, - 'package_length_type' => 'N', - 'package_length_offset' => 0, - 'package_body_offset' => 4 -]; -``` - -```php -$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE); -$server->set(['open_http2_protocol' => true]); -// http && http2 -$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $response->end('Hello ' . $request->rawcontent()); -}); -// websocket -$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) { - $server->push($frame->fd, 'Hello ' . $frame->data); -}); -// tcp -$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP); -$tcp_server->set($tcp_options); -$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) { - $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); -}); -$server->start(); -``` -### 多种客户端 - -不管是DNS查询抑或是发送请求和接收响应,都是协程调度的,不会产生任何阻塞。 - -```php -go(function () { - // http - $http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501); - assert($http_client->post('/', 'Swoole Http')); - var_dump($http_client->body); - // websocket - $http_client->upgrade('/'); - $http_client->push('Swoole Websocket'); - var_dump($http_client->recv()->data); -}); -go(function () { - // http2 - $http2_client = new Swoole\Coroutine\Http2\Client('localhost', 9501); - $http2_client->connect(); - $http2_request = new Swoole\Http2\Request; - $http2_request->method = 'POST'; - $http2_request->data = 'Swoole Http2'; - $http2_client->send($http2_request); - $http2_response = $http2_client->recv(); - var_dump($http2_response->data); -}); -go(function () use ($tcp_options) { - // tcp - $tcp_client = new Swoole\Coroutine\Client(SWOOLE_TCP); - $tcp_client->set($tcp_options); - $tcp_client->connect('127.0.0.1', 9502); - $tcp_client->send(tcp_pack('Swoole Tcp')); - var_dump(tcp_unpack($tcp_client->recv())); -}); -``` - -### 通道 - -通道(Channel)是协程之间通信交换数据的唯一渠道, 而协程+通道的开发组合即为著名的CSP编程模型。 - -在Swoole开发中,Channel常用于连接池的实现和协程并发的调度。 - -#### 连接池最简示例 - -在以下示例中,我们并发了一千个redis请求,通常的情况下,这已经超过了Redis最大的连接数,将会抛出连接异常, 但基于Channel实现的连接池可以完美地调度请求,开发者就无需担心连接过载。 - -```php -class RedisPool -{ - /**@var \Swoole\Coroutine\Channel */ - protected $pool; - - /** - * RedisPool constructor. - * @param int $size max connections - */ - public function __construct(int $size = 100) - { - $this->pool = new \Swoole\Coroutine\Channel($size); - for ($i = 0; $i < $size; $i++) { - $redis = new \Swoole\Coroutine\Redis(); - $res = $redis->connect('127.0.0.1', 6379); - if ($res == false) { - throw new \RuntimeException("failed to connect redis server."); - } else { - $this->put($redis); - } - } - } - - public function get(): \Swoole\Coroutine\Redis - { - return $this->pool->pop(); - } - - public function put(\Swoole\Coroutine\Redis $redis) - { - $this->pool->push($redis); - } - - public function close(): void - { - $this->pool->close(); - $this->pool = null; - } -} - -go(function () { - $pool = new RedisPool(); - // max concurrency num is more than max connections - // but it's no problem, channel will help you with scheduling - for ($c = 0; $c < 1000; $c++) { - go(function () use ($pool, $c) { - for ($n = 0; $n < 100; $n++) { - $redis = $pool->get(); - assert($redis->set("awesome-{$c}-{$n}", 'swoole')); - assert($redis->get("awesome-{$c}-{$n}") === 'swoole'); - assert($redis->delete("awesome-{$c}-{$n}")); - $pool->put($redis); - } - }); - } -}); -``` - -#### 生产和消费 - -Swoole的部分客户端实现了defer机制来进行并发,但你依然可以用协程和通道的组合来灵活地实现它。 - -```php -go(function () { - // User: I need you to bring me some information back. - // Channel: OK! I will be responsible for scheduling. - $channel = new Swoole\Coroutine\Channel; - go(function () use ($channel) { - // Coroutine A: Ok! I will show you the github addr info - $addr_info = Co::getaddrinfo('github.com'); - $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); - }); - go(function () use ($channel) { - // Coroutine B: Ok! I will show you what your code look like - $mirror = Co::readFile(__FILE__); - $channel->push(['B', $mirror]); - }); - go(function () use ($channel) { - // Coroutine C: Ok! I will show you the date - $channel->push(['C', date(DATE_W3C)]); - }); - for ($i = 3; $i--;) { - list($id, $data) = $channel->pop(); - echo "From {$id}:\n {$data}\n"; - } - // User: Amazing, I got every information at earliest time! -}); -``` - -### 定时器 - -```php -$id = Swoole\Timer::tick(100, function () { - echo "⚙️ Do something...\n"; -}); -Swoole\Timer::after(500, function () use ($id) { - Swoole\Timer::clear($id); - echo "⏰ Done\n"; -}); -Swoole\Timer::after(1000, function () use ($id) { - if (!Swoole\Timer::exists($id)) { - echo "✅ All right!\n"; - } -}); -``` - -#### 使用协程方式 -```php -go(function () { - $i = 0; - while (true) { - Co::sleep(0.1); - echo "📝 Do something...\n"; - if (++$i === 5) { - echo "🛎 Done\n"; - break; - } - } - echo "🎉 All right!\n"; -}); -``` - -### 命名空间 - -Swoole提供了多种类命名规则以满足不同开发者的爱好 - -1. 符合PSR规范的命名空间风格 -2. 便于键入的下划线风格 -3. 协程类短名风格 - -## 🔥 强大的运行时钩子 - -在最新版本的Swoole中,我们添加了一项新功能,使PHP原生的同步网络库一键化成为协程库。 - -只需在脚本顶部调用`Swoole\Runtime::enableCoroutine()`方法并使用`php-redis`,并发1万个请求从Redis读取数据仅需0.1秒! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - for ($c = 100; $c--;) { - go(function () { - ($redis = new Redis)->connect('127.0.0.1', 6379); - for ($n = 100; $n--;) { - assert($redis->get('awesome') === 'swoole'); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -调用它之后,Swoole内核将替换ZendVM中的Stream函数指针,如果使用基于`php_stream`的扩展,则所有套接字操作都可以在运行时动态转换为协程调度的异步IO。 - -### 你可以在一秒钟里做多少事? - -睡眠1万次,读取,写入,检查和删除文件1万次,使用PDO和MySQLi与数据库通信1万次,创建TCP服务器和多个客户端相互通信1万次,创建UDP服务器和多个客户端到相互通信1万次......一切都在一个进程中完美完成! - -```php -Swoole\Runtime::enableCoroutine(); -$s = microtime(true); -Co\run(function() { - // i just want to sleep... - for ($c = 100; $c--;) { - go(function () { - for ($n = 100; $n--;) { - usleep(1000); - } - }); - } - - // 10k file read and write - for ($c = 100; $c--;) { - go(function () use ($c) { - $tmp_filename = "/tmp/test-{$c}.php"; - for ($n = 100; $n--;) { - $self = file_get_contents(__FILE__); - file_put_contents($tmp_filename, $self); - assert(file_get_contents($tmp_filename) === $self); - } - unlink($tmp_filename); - }); - } - - // 10k pdo and mysqli read - for ($c = 50; $c--;) { - go(function () { - $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root'); - $statement = $pdo->prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $statement->execute(); - assert(count($statement->fetchAll()) > 0); - } - }); - } - for ($c = 50; $c--;) { - go(function () { - $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); - $statement = $mysqli->prepare('SELECT `id` FROM `user`'); - for ($n = 100; $n--;) { - $statement->bind_result($id); - $statement->execute(); - $statement->fetch(); - assert($id > 0); - } - }); - } - - // php_stream tcp server & client with 12.8k requests in single process - function tcp_pack(string $data): string - { - return pack('n', strlen($data)) . $data; - } - - function tcp_length(string $head): int - { - return unpack('n', $head)[1]; - } - - go(function () { - $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); - $socket = stream_socket_server( - 'tcp://0.0.0.0:9502', - $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx - ); - if (!$socket) { - echo "$errstr ($errno)\n"; - } else { - $i = 0; - while ($conn = stream_socket_accept($socket, 1)) { - stream_set_timeout($conn, 5); - for ($n = 100; $n--;) { - $data = fread($conn, tcp_length(fread($conn, 2))); - assert($data === "Hello Swoole Server #{$n}!"); - fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); - } - if (++$i === 128) { - fclose($socket); - break; - } - } - } - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - stream_set_timeout($fp, 5); - for ($n = 100; $n--;) { - fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); - $data = fread($fp, tcp_length(fread($fp, 2))); - assert($data === "Hello Swoole Client #{$n}!"); - } - fclose($fp); - } - }); - } - - // udp server & client with 12.8k requests in single process - go(function () { - $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9503); - $client_map = []; - for ($c = 128; $c--;) { - for ($n = 0; $n < 100; $n++) { - $recv = $socket->recvfrom($peer); - $client_uid = "{$peer['address']}:{$peer['port']}"; - $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; - assert($recv === "Client: Hello #{$id}!"); - $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); - } - } - $socket->close(); - }); - for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - for ($n = 0; $n < 100; $n++) { - fwrite($fp, "Client: Hello #{$n}!"); - $recv = fread($fp, 1024); - list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); - assert($address === '127.0.0.1' && (int)$port === 9503); - assert($recv === "Server: Hello #{$n}!"); - } - fclose($fp); - } - }); - } -}); -echo 'use ' . (microtime(true) - $s) . ' s'; -``` - -## ⌛️ 安装 - -> 和任何开源项目一样, Swoole总是在**最新的发行版**提供最可靠的稳定性和最强的功能, 请尽量保证你使用的是最新版本 - -### 1. 直接使用Swoole官方的二进制包 (初学者 + 开发环境) - -访问我们官网的[下载页面](https://www.swoole.com/page/download) - -### 编译需求 - -- Linux, OS X 系统 或 CygWin, WSL -- PHP 7.0.0 或以上版本 (版本越高性能越好) -- GCC 4.8 及以上 - -### 2. 使用PHP官方的PECL工具安装 (初学者) - -```shell -pecl install swoole -``` - -### 3. 从源码编译安装 (推荐) - -> 非内核开发研究之用途, 请下载[发布版本](https://github.com/swoole/swoole-src/releases)的源码编译 - -```shell -cd swoole-src && \ -phpize && \ -./configure && \ -make && sudo make install -``` - -#### 启用扩展 - -编译安装到系统成功后, 需要在`php.ini`中加入一行`extension=swoole.so`来启用Swoole扩展 - -#### 额外编译参数 - -> 使用例子: `./configure --enable-openssl --enable-sockets` - -- `--enable-openssl` 或 `--with-openssl-dir=DIR` -- `--enable-sockets` -- `--enable-http2` -- `--enable-mysqlnd` (需要 mysqlnd, 只是为了支持`mysql->escape`方法) - -### 升级 - -> ⚠️ 如果你要从源码升级, 别忘记在源码目录执行 `make clean` - -1. `pecl upgrade swoole` -2. `git pull && cd swoole-src && make clean && make && sudo make install` -3. 如果你改变了PHP版本, 请重新执行 `phpize clean && phpize`后重新编译 - -## 💎 框架 & 组件 - -- [**Hyperf**](https://github.com/hyperf-cloud/hyperf) 是一个高性能、高灵活性的协程框架,存在丰富的可能性,如实现分布式中间件,微服务架构等 -- [**Swoft**](https://github.com/swoft-cloud) 是一个现代化的面向切面的高性能协程全栈组件化框架 -- [**Easyswoole**](https://www.easyswoole.com) 是一个极简的高性能的框架,让代码开发就好像写`echo "hello world"`一样简单 -- [**Saber**](https://github.com/swlib/saber) 是一个人性化的高性能HTTP客户端组件,几乎拥有一切你可以想象的强大功能 - -## 🛠 开发 & 讨论 - -- __中文文档__: -- __Document__: -- __IDE Helper & API__: -- __中文社区及QQ群__: -- __Twitter__: -- __Slack Group__: - -## 🍭 性能测试 - -+ 在开源的 [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) 压测平台上,Swoole使用MySQL数据库压测的成绩一度位居首位, 所有IO性能测试都位列第一梯队。 -+ 你可以直接运行[Benchmark Script](./benchmark/benchmark.php)来快速地测试出Swoole提供的Http服务在你的机器上所能达到的最大QPS - -## 🖊️ 如何贡献 - -非常欢迎您对Swoole的开发作出贡献! - -你可以选择以下方式向Swoole贡献: - -- [发布issue进行问题反馈和建议](https://github.com/swoole/swoole-src/issues) -- 通过Pull Request提交修复 -- 完善我们的文档和例子 - -## ❤️ 贡献者 - -项目的发展离不开以下贡献者的努力! [[Contributor](https://github.com/swoole/swoole-src/graphs/contributors)]. - - -## 📃 开源协议 - -Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/README.md b/README.md index eb1003f2e03..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,142 @@ -English | [中文](./README-CN.md) +

+Swoole Logo
+ Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP. +

+ +[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole) +[![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole) +[![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux) +[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml) +[![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) + +[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole) +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev) +[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/) +[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -# Swoole +## ⚙️ Quick Start -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg?style=flat-square)](https://github.com/swoole/swoole-src/releases) -[![Build Status](https://api.travis-ci.org/swoole/swoole-src.svg)](https://travis-ci.org/swoole/swoole-src) -[![License](https://img.shields.io/badge/license-apache2-blue.svg)](LICENSE) -[![Join the chat at https://gitter.im/swoole/swoole-src](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/swoole/swoole-src?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -[![Backers on Open Collective](https://opencollective.com/swoole-src/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/swoole-src/sponsors/badge.svg)](#sponsors) +Run Swoole program by [Docker](https://github.com/swoole/docker-swoole) + +```bash +docker run --rm phpswoole/swoole "php --ri swoole" +``` + +> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). + +## Documentation + + +### HTTP Service +```php +$http = new Swoole\Http\Server('127.0.0.1', 9501); +$http->set(['hook_flags' => SWOOLE_HOOK_ALL]); + +$http->on('request', function ($request, $response) { + $result = []; + Co::join([ + go(function () use (&$result) { + $result['google'] = file_get_contents("https://www.google.com/"); + }), + go(function () use (&$result) { + $result['taobao'] = file_get_contents("https://www.taobao.com/"); + }) + ]); + $response->end(json_encode($result)); +}); + +$http->start(); +``` + +### Concurrency +```php +Co\run(function() { + Co\go(function() { + while(1) { + sleep(1); + $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); + echo fread($fp, 8192), PHP_EOL; + } + }); + + Co\go(function() { + $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); + while(1) { + $conn = stream_socket_accept($fp); + fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + while(true) { + $redis->subscribe(['test'], function ($instance, $channelName, $message) { + echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL; + }); + } + }); + + Co\go(function() { + $redis = new Redis(); + $redis->connect('127.0.0.1', 6379); + $count = 0; + while(true) { + sleep(2); + $redis->publish('test','hello, world, count='.$count++); + } + }); +}); +``` + +## Runtime Hook + +**Swoole hooks the blocking io function of PHP at the `bottom layer` and `automatically` converts it to a non-blocking function, so that these functions can be called concurrently in coroutines.** + +### Supported extension/functions -![](./mascot.png) +* `ext-curl` (Support `symfony` and `guzzle`) +* `ext-redis` +* `ext-mysqli` +* `ext-pdo_mysql` +* `ext-pdo_pgsql` +* `ext-pdo_sqlite` +* `ext-pdo_oracle` +* `ext-pdo_odbc` +* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe` +* `ext-sockets` +* `ext-soap` +* `sleep`/`usleep`/`time_sleep_until` +* `proc_open` +* `gethostbyname`/`shell_exec`/`exec` +* `fread`/`fopen`/`fsockopen`/`fwrite`/`flock` -**Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C and C++ for PHP.** -## ✨Event-based +## 🛠 Develop & Discussion + ++ __IDE Helper & API__: ++ __Twitter__: ++ __Discord__: ++ __中文社区__: + +## 💎 Awesome Swoole +Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including + +* Swoole-based frameworks and libraries. +* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii. +* Books, videos, and other learning materials about Swoole. +* Debugging, profiling, and testing tools for developing Swoole-based applications. +* Coroutine-friendly packages and libraries. +* Other Swoole related projects and resources. + +## ✨ Event-based The network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests. -Swoole 4.x use a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance. +Swoole 4.x uses a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance. -## ⚡️Coroutine +## ⚡ Coroutine Swoole 4.x or later supports the built-in coroutine with high availability, and you can use fully synchronized code to implement asynchronous performance. PHP code without any additional keywords, the underlying automatic coroutine-scheduling. @@ -305,7 +421,7 @@ Co\run(function() { } }); } - + // 10K file read and write for ($c = 100; $c--;) { go(function () use ($c) { @@ -318,7 +434,7 @@ Co\run(function() { unlink($tmp_filename); }); } - + // 10K pdo and mysqli read for ($c = 50; $c--;) { go(function () { @@ -342,18 +458,18 @@ Co\run(function() { } }); } - + // php_stream tcp server & client with 12.8K requests in single process function tcp_pack(string $data): string { return pack('n', strlen($data)) . $data; } - + function tcp_length(string $head): int { return unpack('n', $head)[1]; } - + go(function () { $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); $socket = stream_socket_server( @@ -394,7 +510,7 @@ Co\run(function() { } }); } - + // udp server & client with 12.8K requests in single process go(function () { $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); @@ -436,30 +552,26 @@ echo 'use ' . (microtime(true) - $s) . ' s'; > As with any open source project, Swoole always provides the most reliable stability and the most powerful features in **the latest released version**. Please ensure as much as possible that you are using the latest version. -### 1. From binary package (beginners + dev-env) - -See our [download page](https://www.swoole.com/page/download) - ### Compiling requirements -- Linux, OS X or Cygwin, WSL -- PHP 7.0.0 or later (The higher the version, the better the performance.) -- GCC 4.8 or later ++ Linux, OS X or Cygwin, WSL ++ PHP 8.1.0 or later (The higher the version, the better the performance.) ++ GCC 4.8 or later -### 2. Install via PECL (beginners) +### 1. Install via PECL (beginners) ```shell pecl install swoole ``` -### 3. Install from source (recommended) +### 2. Install from source (recommended) -Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or: +Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or clone a specific version. Don't use `master` branch as it may be in development. +To clone the source code from git specify a tag: ```shell -git clone https://github.com/swoole/swoole-src.git && \ -cd swoole-src && \ -git checkout v4.x.x +git clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \ +cd swoole-src ``` Compile and install at the source folder: @@ -478,27 +590,26 @@ After compiling and installing to the system successfully, you have to add a new > for example: `./configure --enable-openssl --enable-sockets` -- `--enable-openssl` or `--with-openssl-dir=DIR` -- `--enable-sockets` -- `--enable-http2` -- `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method) ++ `--enable-openssl` or `--with-openssl-dir=DIR` ++ `--enable-sockets` ++ `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method) ++ `--enable-swoole-curl` ### Upgrade > ⚠️ If you upgrade from source, don't forget to `make clean` before you upgrade your swoole 1. `pecl upgrade swoole` -2. `git pull && cd swoole-src && make clean && make && sudo make install` +2. `cd swoole-src && git pull && make clean && make && sudo make install` 3. if you change your PHP version, please re-run `phpize clean && phpize` then try to compile - ### Major change since version 4.3.0 Async clients and API are moved to a separate PHP extension `swoole_async` since version 4.3.0, install `swoole_async`: ```shell -git clone https://github.com/swoole/async-ext.git -cd async-src +git clone https://github.com/swoole/ext-async.git +cd ext-async phpize ./configure make -j 4 @@ -507,29 +618,14 @@ sudo make install Enable it by adding a new line `extension=swoole_async.so` to `php.ini`. -## 💎 Frameworks & Components -- [**Hyperf**](https://github.com/hyperf-cloud/hyperf) is a coroutine framework that focuses on hyperspeed and flexibility, specifically used for build microservices or middlewares. -- [**Swoft**](https://github.com/swoft-cloud/swoft) is a modern, high-performance AOP and coroutine PHP framework. -- [**Easyswoole**](https://www.easyswoole.com) is a simple, high-performance PHP framework, based on Swoole, which makes using Swoole as easy as `echo "hello world"`. -- [**Saber**](https://github.com/swlib/saber) Is a human-friendly, high-performance HTTP client component that has almost everything you can imagine. - -## 🛠 Develop & Discussion - -* __中文文档__: -* __Documentation__: -* __IDE Helper & API__: -* __中文社区__: -* __Twitter__: -* __Slack Group__: - ## 🍭 Benchmark + On the open source [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon. -+ You can just run [Benchmark Script](./benchmark/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine. ++ You can just run [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine. -## 🖊️ Security issues +## 🔰️ Security issues -Security issues should be reported privately, via email, to the Swoole develop team team@swoole.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. +Security issues should be reported privately, via email, to the Swoole develop team [team@swoole.com](mailto:team@swoole.com). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. ## 🖊️ Contribution diff --git a/benchmark/ab.sh b/benchmark/ab.sh deleted file mode 100644 index bbbf3f057b2..00000000000 --- a/benchmark/ab.sh +++ /dev/null @@ -1,4 +0,0 @@ -ab -c 1000 -n 1000000 -k http://127.0.0.1:9501/ -ab -c 1000 -n 1000000 -k -p post.data -T 'application/x-www-form-urlencoded' http://127.0.0.1:9501/ -#post 24K -ab -c 100 -n 100000 -k -p post.big.data -T 'application/x-www-form-urlencoded' http://127.0.0.1:9501/ diff --git a/benchmark/aio.php b/benchmark/aio.php deleted file mode 100644 index 7824e24e19f..00000000000 --- a/benchmark/aio.php +++ /dev/null @@ -1,36 +0,0 @@ -set(['log_file' => '/dev/null', 'log_level' => SWOOLE_LOG_INFO, 'worker_num' => swoole_cpu_num() * 2]); - $http->on('workerStart', function () use ($process) { $process->write('1'); }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { - $response->end('

Hello Swoole!

'); - }); - $http->start(); -}); -$process->start(); -$process->read(1); -System('ab -c 128 -n 100000 -k http://127.0.0.1:9501/ 2>&1'); -Swoole\Process::kill($process->pid); diff --git a/benchmark/bin/async.php b/benchmark/bin/async.php deleted file mode 100644 index 7f9412162ff..00000000000 --- a/benchmark/bin/async.php +++ /dev/null @@ -1,265 +0,0 @@ -nConcurrency = $opt['c']; - $this->nRequest = $opt['n']; - $serv = parse_url($opt['s']); - $this->host = $serv['host']; - $this->port = $serv['port']; - $this->testMethod = $opt['f']; - - //data length - if (isset($opt['l']) and intval($opt['l']) > 0) { - $this->setSentData(str_repeat('A', intval($opt['l']))); - } - - if (!method_exists($this, $this->testMethod)) - { - throw new RuntimeException("method [{$this->testMethod}] is not exists."); - } - } - - function setSentData($data) - { - self::$sentData = $data; - } - - protected function finish() - { - foreach($this->clients as $k => $cli) - { - /** - * @var $cli swoole\client - */ - if ($cli->isConnected()) - { - $cli->close(); - } - unset($this->clients[$k]); - } - echo "============================================================\n"; - echo " Swoole Version ".SWOOLE_VERSION."\n"; - echo "============================================================\n"; - echo "{$this->requestCount}\tbenchmark tests is finished.\n"; - echo "SendBytes:\t".number_format($this->nSendBytes)."\n"; - echo "nReceBytes:\t".number_format($this->nRecvBytes)."\n"; - echo "concurrency:\t".$this->nConcurrency,"\n"; - echo "connect failed:\t" . $this->connectErrorCount, "\n"; - echo "request num:\t" . $this->nRequest, "\n"; - $costTime = $this->format(microtime(true) - $this->startTime); - echo "total time:\t" . ($costTime) . "\n"; - if ($this->requestCount > 0) - { - echo "request per second:\t" . intval($this->requestCount / $costTime), "\n"; - } - else - { - echo "request per second:\t0\n"; - } - echo "connection time:\t" . $this->format($this->connectTime) . "\n"; - } - - function format($time) - { - return round($time, 4); - } - - function onReceive($cli, $data) - { - $this->nRecvBytes += strlen($data); - /** - * 请求已经发完了,关闭连接,等待所有连接结束 - */ - if ($this->requestCount >= $this->nRequest) - { - $cli->close(); - unset($this->clients[$cli->sock]); - if (count($this->clients) == 0) - { - $this->finish(); - } - } - else - { - $this->send($cli); - } - } - - function send($cli) - { - $data = self::$sentData; - $cli->send($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - } - - function push($cli) - { - $data = self::$sentData; - $cli->push($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - } - - function onClose($cli) - { - //echo "close\n"; - } - - function onError($cli) - { - $this->connectErrorCount ++; - if ($this->connectErrorCount >= $this->nConcurrency) - { - $this->finish(); - } - } - - function onConnect($cli) - { - $this->send($cli); - } - - function websocket() - { - $cli = new swoole\http\client($this->host, $this->port); - $cli->set(array('websocket_mask' => true)); - $cli->on('Message', function($cli, $frame) { - $this->nRecvBytes += strlen($frame->data); - /** - * 请求已经发完了,关闭连接,等待所有连接结束 - */ - if ($this->requestCount >= $this->nRequest) - { - $cli->close(); - unset($this->clients[$cli->sock]); - if (count($this->clients) == 0) - { - $this->finish(); - } - } - else - { - $this->push($cli); - } - }); - $cli->upgrade('/', function ($cli) { - $this->push($cli); - }); - return $cli; - } - - function long_tcp() - { - $cli = new swoole\client(SWOOLE_TCP | SWOOLE_ASYNC); - $cli->on('receive', [$this, 'onReceive']); - $cli->on('close', [$this, 'onClose']); - $cli->on('connect', [$this, 'onConnect']); - $cli->on('error', [$this, 'onError']); - $cli->connect($this->host, $this->port); - return $cli; - } - - function eof() - { - $eof = "\r\n\r\n"; - $cli = new swoole\client(SWOOLE_TCP | SWOOLE_ASYNC); - $cli->set(array('open_eof_check' => true, "package_eof" => $eof)); - $cli->on('receive', [$this, 'onReceive']); - $cli->on('close', [$this, 'onClose']); - $cli->on('connect', [$this, 'onConnect']); - $cli->on('error', [$this, 'onError']); - $cli->connect($this->host, $this->port); - self::$sentData .= $eof; - return $cli; - } - - function length() - { - $cli = new swoole\client(SWOOLE_TCP | SWOOLE_ASYNC); - $cli->set(array( - 'open_length_check' => true, - "package_length_type" => 'N', - 'package_body_offset' => 4, - )); - $cli->on('receive', [$this, 'onReceive']); - $cli->on('close', [$this, 'onClose']); - $cli->on('connect', [$this, 'onConnect']); - $cli->on('error', [$this, 'onError']); - $cli->connect($this->host, $this->port); - self::$sentData = pack('N', strlen(self::$sentData)) . self::$sentData; - return $cli; - } - - function udp() - { - $cli = new swoole\client(SWOOLE_UDP | SWOOLE_ASYNC); - $cli->on('receive', [$this, 'onReceive']); - $cli->on('close', [$this, 'onClose']); - $cli->on('connect', [$this, 'onConnect']); - $cli->on('error', [$this, 'onError']); - $cli->connect($this->host, $this->port); - return $cli; - } - - function run() - { - $this->startTime = microtime(true); - for ($i = 0; $i < $this->nConcurrency; $i++) - { - $cli = call_user_func([$this, $this->testMethod]); - $this->clients[$cli->sock] = $cli; - } - $this->beginSendTime = microtime(true); - $this->connectTime = $this->beginSendTime - $this->startTime; - } -} - -$bench = new BenchMark($opt); -$bench->run(); diff --git a/benchmark/bin/sync.php b/benchmark/bin/sync.php deleted file mode 100644 index 4065584a7ff..00000000000 --- a/benchmark/bin/sync.php +++ /dev/null @@ -1,573 +0,0 @@ -process_num = (int)$opt['c']; -$bc->request_num = (int)$opt['n']; -$bc->server_url = trim($opt['s']); -$bc->server_config = parse_url($bc->server_url); -$bc->send_data = "GET / HTTP/1.1\r\n"; -$bc->send_data .= "Host: www.baidu.com\r\n"; -$bc->send_data .= "Connection: keep-alive\r\n"; -$bc->send_data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n"; -$bc->send_data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\r\n\r\n"; - -$bc->read_len = 65536; -if(!empty($opt['p'])) $bc->show_detail = true; - -function eof(Swoole_Benchmark $bc) -{ - static $client = null; - static $i; - $start = microtime(true); - if (empty($client)) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - $client->set(array('open_eof_check' => true, "package_eof" => "\r\n\r\n")); - $end = microtime(true); - $conn_use = $end - $start; - $bc->max_conn_time = $conn_use; - $i = 0; - //echo "connect {$bc->server_url} \n"; - if (!$client->connect($bc->server_config['host'], $bc->server_config['port'], 2)) { - error: - echo "Error: " . swoole_strerror($client->errCode) . "[{$client->errCode}]\n"; - $client = null; - return false; - } - $start = $end; - } - /*--------写入Sokcet-------*/ - $data = str_repeat('A', rand(100, 200))."\r\n\r\n"; - if (!$client->send($data)) - { - goto error; - } - $end = microtime(true); - $write_use = $end - $start; - if ($write_use > $bc->max_write_time) $bc->max_write_time = $write_use; - $start = $end; - /*--------读取Sokcet-------*/ - $i ++; - $ret = $client->recv(); - if (empty($ret)) - { - echo $bc->pid, "#$i", " is lost\n"; - return false; - } - elseif(strlen($ret) != strlen($data)) - { - echo "#$i\tlength error\n"; - var_dump($ret); - echo "-----------------------------------\n"; - var_dump($data); - } - $end = microtime(true); - $read_use = $end - $start; - if ($read_use > $bc->max_read_time) $bc->max_read_time = $read_use; - return true; -} - -function long_tcp(Swoole_Benchmark $bc) -{ - static $fp = null; - static $i; - $start = microtime(true); - if(empty($fp)) - { - $fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - $end = microtime(true); - $conn_use = $end-$start; - $bc->max_conn_time = $conn_use; - $i = 0; - //echo "connect {$bc->server_url} \n"; - if (!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 2)) - { - error: - echo "Error: ".swoole_strerror($fp->errCode)."[{$fp->errCode}]\n"; - $fp = null; - return false; - } - $start = $end; - } - /*--------写入Sokcet-------*/ - if (!$fp->send($bc->send_data)) - { - goto error; - } - $end = microtime(true); - $write_use = $end - $start; - if ($write_use > $bc->max_write_time) - { - $bc->max_write_time = $write_use; - } - $start = $end; - /*--------读取Sokcet-------*/ - while(true) - { - $ret = $fp->recv(65530); - if (empty($ret) or substr($ret, -1, 1) == "\n") - { - break; - } - } - //var_dump($ret); - $i++; - if (empty($ret)) - { - echo $bc->pid, "#$i@", " is lost\n"; - return false; - } - $end = microtime(true); - $read_use = $end - $start; - if ($read_use > $bc->max_read_time) - { - $bc->max_read_time = $read_use; - } - return true; -} - -function websocket(Swoole_Benchmark $bc) -{ - static $client = null; - static $i; - $start = microtime(true); - - if (empty($client)) - { - $client = new WebSocketClient($bc->server_config['host'], $bc->server_config['port']); - if (!$client->connect()) - { - echo "connect failed\n"; - return false; - } - - $end = microtime(true); - $conn_use = $end - $start; - $bc->max_conn_time = $conn_use; - $i = 0; - $start = $end; - } - /*--------写入Sokcet-------*/ - if (!$client->send($bc->send_data)) - { - echo "send failed\n"; - return false; - } - $end = microtime(true); - $write_use = $end - $start; - if ($write_use > $bc->max_write_time) - { - $bc->max_write_time = $write_use; - } - $start = $end; - /*--------读取Sokcet-------*/ - $ret = $client->recv(); - //var_dump($ret); - $i++; - if (empty($ret)) - { - echo $bc->pid, "#$i@", " is lost\n"; - return false; - } - $end = microtime(true); - $read_use = $end - $start; - if ($read_use > $bc->max_read_time) - { - $bc->max_read_time = $read_use; - } - return true; -} - -/** - * 去掉计时信息的UDP - * @param $bc - * @return bool - */ -function udp(Swoole_Benchmark $bc) -{ - static $fp; - if (empty($fp)) - { - $fp = stream_socket_client($bc->server_url, $errno, $errstr, 1); - if (!$fp) - { - echo "{$errstr}[{$errno}]\n"; - return false; - } - } - /*--------写入Sokcet-------*/ - fwrite($fp, $bc->send_data); - /*--------读取Sokcet-------*/ - $ret = fread($fp, $bc->read_len); - if (empty($ret)) - { - return false; - } - return true; -} - -function udp2(Swoole_Benchmark $bc) -{ - static $fp; - $start = microtime(true); - if (empty($fp)) - { - $u = parse_url($bc->server_url); - $fp = new swoole_client(SWOOLE_SOCK_UDP); - $fp->connect($u['host'], $u['port'], 0.5, 0); - $end = microtime(true); - $conn_use = $end - $start; - $bc->max_conn_time = $conn_use; - $start = $end; - } - /*--------写入Sokcet-------*/ - $fp->send($bc->send_data); - $end = microtime(true); - $write_use = $end - $start; - if ($write_use > $bc->max_write_time) - { - $bc->max_write_time = $write_use; - } - $start = $end; - /*--------读取Sokcet-------*/ - $ret = $fp->recv(); - if (empty($ret)) - { - return false; - } - - $end = microtime(true); - $read_use = $end - $start; - if ($read_use > $bc->max_read_time) - { - $bc->max_read_time = $read_use; - } - return true; -} - -function short_tcp($bc) -{ - $fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - if(!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 1)) - { - error: - echo "Error: ".socket_strerror($fp->errCode)."[{$fp->errCode}]\n"; - return false; - } - else - { - if(!$fp->send($bc->send_data)) - { - goto error; - } - $ret = $fp->recv(); - $fp->close(); - if(!empty($ret)) return true; - else return false; - } -} - -function long_socks5($bc) -{ - static $fp = null; - static $i; - $start = microtime(true); - if(empty($fp)) - { - $fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC,5); - $end = microtime(true); - $conn_use = $end-$start; - $bc->max_conn_time = $conn_use; - $i = 0; - //echo "connect {$bc->server_url} \n"; - if (!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 2)) - { - error: - echo "Error: ".swoole_strerror($fp->errCode)."[{$fp->errCode}]\n"; - $fp = null; - return false; - } - - $fp->send(pack("C3", 0x05, 0x01, 0x00));//greet - $data = $fp->recv(); - $response = unpack("Cversion/Cmethod", $data); - if ($response['version'] != 0x05) - { - exit('SOCKS version is not supported.'); - } - $headers = getHeader($bc->send_data); - if (empty($headers['port'])) { - $headers['port'] = 80; - } - $g = pack("C5", 0x05, 0x01, 0x00, 0x03, strlen($headers['host'])) . $headers['host'] . pack("n", $headers['port']); - $fp->send($g); - $data = $fp->recv(); - $response = unpack("Cversion/Cresult/Creg/Ctype/Lip/Sport", $data); - if ($response['result'] != 0x00) - { - echo 'SOCKS connection request failed: ' . getSocksRefusalMsg($response['result']), $response['result'];exit; - } - - $start = $end; - } - /*--------写入Sokcet-------*/ - if (!$fp->send($bc->send_data)) - { - goto error; - } - $end = microtime(true); - $write_use = $end - $start; - if ($write_use > $bc->max_write_time) - { - $bc->max_write_time = $write_use; - } - $start = $end; - /*--------读取Sokcet-------*/ - while(true) - { - $ret = $fp->recv(65530); - if (empty($ret) or substr($ret, -1, 1) == "\n") - { - break; - } - } - //var_dump($ret); - $i++; - if (empty($ret)) - { - echo $bc->pid, "#$i@", " is lost\n"; - return false; - } - $end = microtime(true); - $read_use = $end - $start; - if ($read_use > $bc->max_read_time) - { - $bc->max_read_time = $read_use; - } - return true; -} - -function getHeader($message) -{ - // 标准每行应该以"\r\n"行终止,这里兼容以"\n"作为行终止的情况,所以按"\n"分割行 - $lines = explode("\n", $message); - foreach ($lines as &$line) - { - // 按"\n"分割行以后,某些行末可能存在"\r"字符,这里将其过滤掉 - $line = rtrim($line, "\r"); - } - unset($line); - - if (count($lines) <= 0) - { - return false; - } - $headers = []; - - foreach ($lines as $line) - { - $pos = strpos($line, ':'); - // 非标准首部,抛弃 - if ($pos === false) - { - continue; - } - $field = trim(substr($line, 0, $pos)); - $value = trim(substr($line, $pos + 1)); - - // 如果有host头部,重新设置host和port - if (strtolower($field) === 'host') - { - $segments = explode(':', $value); - $host = $segments[0]; - $headers['host'] = $host; - if (isset($segments[1])) - { - $port = intval($segments[1]); - $headers['port'] = $port; - } - } - $headers[$field] = $value; - } - return $headers; -} -//请求数量最好是进程数的倍数 -$bc->process_req_num = intval($bc->request_num/$bc->process_num); -$bc->run(); -$bc->report(); -$bc->end(); - -class Swoole_Benchmark -{ - public $test_func; - public $process_num; - public $request_num; - public $server_url; - public $server_config; - public $send_data; - public $read_len; - - public $time_end; - private $shm_key; - public $main_pid; - public $child_pid = array(); - - public $show_detail = false; - public $max_write_time = 0; - public $max_read_time = 0; - public $max_conn_time = 0; - - public $pid; - - protected $tmp_dir = '/tmp/swoole_bench/'; - - function __construct($func) - { - if (!function_exists($func)) - { - exit(__CLASS__ . ": function[$func] not exists\n"); - } - if (!is_dir($this->tmp_dir)) - { - mkdir($this->tmp_dir); - } - $this->test_func = $func; - } - function end() - { - unlink($this->shm_key); - foreach($this->child_pid as $pid) - { - $f = $this->tmp_dir . 'lost_' . $pid . '.log'; - if (is_file($f)) unlink($f); - } - } - - function run() - { - $this->main_pid = posix_getpid(); - $this->shm_key = $this->tmp_dir.'t.log'; - for ($i = 0; $i < $this->process_num; $i++) - { - $this->child_pid[] = $this->start(array($this, 'worker')); - } - for ($i = 0; $i < $this->process_num; $i++) - { - $status = 0; - $pid = pcntl_wait($status); - } - $this->time_end = microtime(true); - } - - function init_signal() - { - pcntl_signal(SIGUSR1,array($this, "sig_handle")); - } - - function sig_handle($sig) - { - switch ($sig) - { - case SIGUSR1: - return; - } - $this->init_signal(); - } - - function start($func) - { - $pid = pcntl_fork(); - if($pid>0) - { - return $pid; - } - elseif($pid==0) - { - $this->worker(); - } - else - { - echo "Error:fork fail\n"; - } - } - function worker() - { - $lost = 0; - if(!file_exists($this->shm_key)) - { - file_put_contents($this->shm_key,microtime(true)); - } - if($this->show_detail) $start = microtime(true); - $this->pid = posix_getpid(); - - for ($i = 0; $i < $this->process_req_num; $i++) - { - $func = $this->test_func; - if(!$func($this)) $lost++; - } - if ($this->show_detail) - { - $log = $this->pid . "#\ttotal_use(s):" . substr(microtime(true) - $start, 0, 5); - $log .= "\tconnect(ms):" . substr($this->max_conn_time * 1000, 0, 5); - $log .= "\twrite(ms):" . substr($this->max_write_time * 1000, 0, 5); - $log .= "\tread(ms):" . substr($this->max_read_time * 1000, 0, 5); - file_put_contents($this->tmp_dir.'lost_' . $this->pid . '.log', $lost . "\n" . $log); - } - else - { - file_put_contents($this->tmp_dir.'lost_'.$this->pid.'.log', $lost); - } - exit(0); - } - - function report() - { - $time_start = file_get_contents($this->shm_key); - $usetime = $this->time_end - $time_start; - $lost = 0; - - foreach ($this->child_pid as $f) - { - $file = $this->tmp_dir.'lost_'.$f.'.log'; - if (is_file($file)) - { - $_lost = file_get_contents($file); - $log = explode("\n",$_lost,2); - } - if (!empty($log)) - { - $lost += intval($log[0]); - if ($this->show_detail) echo $log[1], "\n"; - } - } - //并发量 - echo "concurrency:\t".$this->process_num,"\n"; - //请求量 - echo "request num:\t".$this->request_num,"\n"; - //请求量 - echo "lost num:\t".$lost,"\n"; - //请求量 - echo "success num:\t".($this->request_num-$lost),"\n"; - //总时间 - echo "total time:\t".substr($usetime,0,5),"\n"; - //每秒处理能力 - echo "req per second:\t".intval($this->request_num/$usetime),"\n"; - //每次请求平均时间ms - echo "one req use(ms):\t".substr($usetime/$this->request_num*1000,0,5),"\n"; - } -} diff --git a/benchmark/co_http_client.php b/benchmark/co_http_client.php deleted file mode 100644 index 45797a493e0..00000000000 --- a/benchmark/co_http_client.php +++ /dev/null @@ -1,111 +0,0 @@ - 50000, -)); - -$shortopts = "c:"; -$shortopts .= "n:"; -$shortopts .= "s:"; - -$opt = getopt($shortopts); -//并发数量 -if(!isset($opt['c'])) exit("require -c [process_num]. ep: -c 100\n"); -if(!isset($opt['n'])) exit("require -n [request_num]. ep: -n 10000\n"); -if(!isset($opt['s'])) exit("require -s [server_url]. ep: -s http://127.0.0.1:9999\n"); - -$bc = new Swoole_Benchmark(trim($opt['f'])); -$bc->c = (int)$opt['c']; -$bc->n = (int)$opt['n']; -$bc->server_url = trim($opt['s']); -$bc->server_config = parse_url($bc->server_url); -$bc->send_data = str_repeat("A", 100);//post data -$bc->test_time = empty($opt['t']) ? 0 : strtotime($opt['t']); - -//域名只解析一次 -if(!filter_var($bc->server_config['host'], FILTER_FLAG_IPV4) && $bc->server_config['host']) -{ - $bc->server_config['host'] = gethostbyname($bc->server_config['host']); -} -var_dump($bc->server_config);exit; -$bc->read_len = 65536; -if(!empty($opt['p'])) $bc->show_detail = true; -$bc->run(); -$bc->report(); - -class Swoole_Benchmark -{ - public $test_func; - public $c; - public $n; - public $req_num; - public $vars; - public $test_time; - public $server_url; - public $server_config; - public $send_data; - public $read_len; - public $time_start; - public $time_end; - - public $lost = 0; - public $success = 0; - - public $show_detail = false; - public $max_write_time = 0; - public $max_read_time = 0; - public $max_conn_time = 0; - - function __construct($func) - { - $this->test_func = $func; - } - - function run() - { - $this->time_start = microtime(true); - $chan = new Swoole\Coroutine\Channel($this->n); - - for ($i = 0; $i < $this->n; $i++) { - go(function () use ($chan, $path) { - $cli = new Co\http\Client( - $this->server_config['host'], - $this->server_config['port'] - ); - $cli->set(['timeout' => 2]); - $cli->get('/index.php'); - if (!empty($cli->body)) { - $this->success ++; - } else { - $this->lost ++; - } - if ($i/1000 == 0) { - echo ""; - } - }); - } - swoole_event::wait(); - $this->time_end = microtime(true); - } - - function report() - { - $lost = $this->lost; - $usetime = $this->time_end - $this->time_start; - //并发量 - echo "concurrency:\t".$this->c."\n"; - //请求量 - echo "request num:\t".$this->n."\n"; - //请求量 - echo "lost num:\t".$lost."\n"; - //请求量 - echo "success num:\t".($this->n-$lost)."\n"; - //总时间 - echo "total time:\t".substr($usetime,0,5)."\n"; - //每秒处理能力 - echo "req per second:\t".intval($this->n/$usetime)."\n"; - //每次请求平均时间ms - echo "one req use(ms):\t".substr($usetime/$this->n*1000,0,5)."\n"; - } -} diff --git a/benchmark/co_http_proxy.php b/benchmark/co_http_proxy.php deleted file mode 100644 index 58b3077ad7a..00000000000 --- a/benchmark/co_http_proxy.php +++ /dev/null @@ -1,41 +0,0 @@ - '127.0.0.1', 'port' => 80]; - protected $serv; - - function run() - { - Co::set(['enable_reuse_port' => true]); - $serv = new Swoole\Http\Server("127.0.0.1", 9509); - $serv->set(array( - 'worker_num' => 4, - 'max_coroutine' => 50000, - 'log_level' => SWOOLE_LOG_WARNING, - )); - $serv->on('Start', array($this, 'onStart')); - $serv->on('Request', array($this, 'onRequest')); - $serv->start(); - } - - function onStart($serv) - { - $this->serv = $serv; - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; - } - - function onRequest($req, $resp) - { - $bc = new Co\Http\Client($this->backend_server['host'], $this->backend_server['port']); - $retval = $bc->get($req->server['request_uri']); - if ($retval) { - $resp->end($bc->body); - } else { - $resp->end("failed to connect the backend server, errno=" . $bc->errCode . "\n"); - } - } -} - -$serv = new Co_Http_ProxyServer(); -$serv->run(); diff --git a/benchmark/co_switch.go b/benchmark/co_switch.go deleted file mode 100644 index cb8e682da24..00000000000 --- a/benchmark/co_switch.go +++ /dev/null @@ -1,83 +0,0 @@ -/** - * source : https://www.codercto.com/a/4467.html -*/ - -package main - -import ( - "fmt" - "os" - "strconv" - "time" -) - -func runCallback(in, out chan int64) { - for n, ok := <-in; ok; n, ok = <-in { - out <- n - } -} - -func runTest(round int, coroutineNum, switchTimes int64) { - fmt.Printf("##### Round: %v\n", round) - start := time.Now() - channelsIn, channelsOut := make([]chan int64, coroutineNum), make([]chan int64, coroutineNum) - for i := int64(0); i < coroutineNum; i++ { - channelsIn[i] = make(chan int64, 1) - channelsOut[i] = make(chan int64, 1) - } - end := time.Now() - fmt.Printf("Create %v goroutines and channels cost %vns, avg %vns\n", coroutineNum, end.Sub(start).Nanoseconds(), end.Sub(start).Nanoseconds()/coroutineNum) - - start = time.Now() - for i := int64(0); i < coroutineNum; i++ { - go runCallback(channelsIn[i], channelsOut[i]) - } - end = time.Now() - fmt.Printf("Start %v goroutines and channels cost %vns, avg %vns\n", coroutineNum, end.Sub(start).Nanoseconds(), end.Sub(start).Nanoseconds()/coroutineNum) - - var sum int64 = 0 - start = time.Now() - for i := int64(0); i < switchTimes; i++ { - for j := int64(0); j < coroutineNum; j++ { - channelsIn[j] <- 1 - sum += <-channelsOut[j] - } - } - end = time.Now() - fmt.Printf("Switch %v goroutines for %v times cost %vns, avg %vns\n", coroutineNum, sum, end.Sub(start).Nanoseconds(), end.Sub(start).Nanoseconds()/sum) - - start = time.Now() - for i := int64(0); i < coroutineNum; i++ { - close(channelsIn[i]) - close(channelsOut[i]) - } - end = time.Now() - fmt.Printf("Close %v goroutines cost %vns, avg %vns\n", coroutineNum, end.Sub(start).Nanoseconds(), end.Sub(start).Nanoseconds()/coroutineNum) -} - -func main() { - var coroutineNum, switchTimes int64 = 30000, 1000 - - fmt.Printf("### Run: ") - for _, v := range os.Args { - fmt.Printf(" \"%s\"", v) - } - fmt.Printf("\n") - - if (len(os.Args)) > 1 { - v, _ := strconv.Atoi(os.Args[1]) - coroutineNum = int64(v) - } - - if (len(os.Args)) > 2 { - v, _ := strconv.Atoi(os.Args[2]) - switchTimes = int64(v) - } - - for i := 1; i <= 5; i++ { - runTest(i, coroutineNum, switchTimes) - } -} - - - diff --git a/benchmark/co_switch.php b/benchmark/co_switch.php deleted file mode 100644 index 137c8ea60a4..00000000000 --- a/benchmark/co_switch.php +++ /dev/null @@ -1,24 +0,0 @@ - '127.0.0.1', 'port' => 80]; - protected $serv; - - function run() - { - Co::set(['enable_reuse_port' => true]); - $serv = new Swoole\Server("127.0.0.1", 9509); - $serv->set(array( - 'worker_num' => 4, - 'max_coroutine' => 50000, - 'log_level' => SWOOLE_LOG_WARNING, - )); - $serv->on('Start', array($this, 'onStart')); - $serv->on('Receive', array($this, 'onReceive')); - $serv->start(); - } - - function onStart($serv) - { - $this->serv = $serv; - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; - } - - function onReceive($serv, $fd, $tid, $data) - { - $socket = new Co\Client(SWOOLE_SOCK_TCP); - if ($socket->connect($this->backend_server['host'], $this->backend_server['port'], 2.0)) { - $socket->send($data); - $html = ''; - while (true) { - $output = $socket->recv(8192 * 512); - if (!$output) { - break; - } - $html .= $output; - if (strlen($html) >= 10918) { - break; - } - } - $serv->send($fd, $html); - if (strstr($data, 'Connection: Keep-Alive') === false) { - $serv->close($fd); - } - } else { - echo "failed to connect the backend server, errno=" . $socket->errCode . "\n"; - } - } -} - -$serv = new Co_TCP_ProxyServer(); -$serv->run(); diff --git a/benchmark/composer.json b/benchmark/composer.json deleted file mode 100644 index bad76e0d756..00000000000 --- a/benchmark/composer.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "autoload": { - "psr-4": { - "SwooleBench\\": "src/" - } - }, - "require": { - "symfony/console": "^4.3" - } -} diff --git a/benchmark/coroutine.php b/benchmark/coroutine.php deleted file mode 100644 index 9dc395a51b5..00000000000 --- a/benchmark/coroutine.php +++ /dev/null @@ -1,81 +0,0 @@ -server = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE); - $this->server->set([ - 'worker_num' => 1, - ]); - -// $this->server->on('Connect', [$this, 'onConnect']); - $this->server->on('Request', [$this, 'onRequest']); -// $this->server->on('Close', [$this, 'onClose']); - - $this->server->start(); - } - - function log($msg) - { - echo $msg."\n"; - } - - public function onConnect($serv, $fd, $reactorId) - { -// $this->log("onConnect: fd=$fd"); -// $redis = new Swoole\Coroutine\Redis(); -// $redis->connect('127.0.0.1', 6379); -// $this->redisPool[$fd] = $redis; - } - - public function onClose($serv, $fd, $reactorId) - { -// $this->log("onClose: fd=$fd"); -// $redis = $this->redisPool[$fd]; -// $redis->close(); -// unset($this->redisPool[$fd]); - } - - public function onRequest($request, $response) - { -// $this->log("onRequest: fd=$request->fd"); - - if (empty($this->redisPool[$request->fd])) - { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', 6379); - $this->redisPool[$request->fd] = $redis; - } - else - { - $redis = $this->redisPool[$request->fd]; - } - $ret = $redis->get('key'); - - if (empty($this->mysqlPool[$request->fd])) - { - $mysql = new Swoole\Coroutine\MySQL(); - $mysql->connect([ - 'host' => '127.0.0.1', - 'port' => 3306, - 'user' => 'root', - 'password' => 'root', - 'database' => 'test', - ]); - } - else - { - $mysql = $this->mysqlPool[$request->fd]; - } - - $ret2 = $mysql->query('show tables'); - $response->end('redis value=' . $ret.', mysql talbes='.var_export($ret2, true)); - } -} - -$server = new Server(); -$server->run(); diff --git a/benchmark/http.go b/benchmark/http.go deleted file mode 100644 index 7d05ed140c2..00000000000 --- a/benchmark/http.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" - "runtime" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU() - 1) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Add("Last-Modified", "Thu, 18 Jun 2015 10:24:27 GMT") - w.Header().Add("Accept-Ranges", "bytes") - w.Header().Add("E-Tag", "55829c5b-17") - w.Header().Add("Server", "golang-http-server") - fmt.Fprint(w, "

\nHello world!\n

\n") - }) - - log.Printf("Go http Server listen on :8080") - log.Fatal(http.ListenAndServe(":8080", nil)) -} diff --git a/benchmark/http.js b/benchmark/http.js deleted file mode 100644 index f8d8729d3f5..00000000000 --- a/benchmark/http.js +++ /dev/null @@ -1,8 +0,0 @@ -var http = require('http'); -http.createServer(function (req, res) { - res.writeHead(200, { - 'Server': "node.js"} - ); - res.end("

Hello World

"); -}).listen(8080, '127.0.0.1'); -console.log('Server running at http://127.0.0.1:8080/'); diff --git a/benchmark/http2.go b/benchmark/http2.go deleted file mode 100644 index be1e204abe8..00000000000 --- a/benchmark/http2.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "log" - "github.com/valyala/fasthttp" - "runtime" - "fmt" -) - -func main() { - runtime.GOMAXPROCS(runtime.NumCPU() - 1) - - m := func(ctx *fasthttp.RequestCtx) { - switch string(ctx.Path()) { - case "/": - ctx.Response.Header.Set("Last-Modified", "Thu, 18 Jun 2015 10:24:27 GMT") - ctx.Response.Header.Set("Accept-Ranges", "bytes") - ctx.Response.Header.Set("E-Tag", "55829c5b-17") - ctx.Response.Header.Set("Server", "golang-http-server") - fmt.Fprint(ctx, "

\nHello world!\n

\n") - default: - } - } - - log.Printf("Go fatshttp Server listen on :8888") - log.Fatal(fasthttp.ListenAndServe(":8888", m)) -} diff --git a/benchmark/larger_payload.php b/benchmark/larger_payload.php deleted file mode 100644 index 6c77e0a5936..00000000000 --- a/benchmark/larger_payload.php +++ /dev/null @@ -1,17 +0,0 @@ -set(['worker_num' => 1, 'enable_coroutine' => false, ]); - -$http->on('start', function ($server) { - echo 'Swoole http server is started at 127.0.0.1 on port ' . PORT; -}); - -$http->on('request', function ($request, $response) { - $response->header('Content-Type', 'text/plain'); - $response->end(str_repeat('A', 1 * 1024 * 1024)); -}); - -$http->start(); diff --git a/benchmark/main.php b/benchmark/main.php deleted file mode 100755 index 48dac933d0f..00000000000 --- a/benchmark/main.php +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env php -Swoole(v-" . SWOOLE_VERSION . ") Benchmark Test Tool \n" . - "============================================================" -); -$app->add(new SwooleBench\Command\RunTest()); -$app->run(); - diff --git a/benchmark/post.big.data b/benchmark/post.big.data deleted file mode 100644 index d8f81ce25f5..00000000000 --- a/benchmark/post.big.data +++ /dev/null @@ -1 +0,0 @@ -test1=hello&test2=world&myname=rango&go=start&language=php&big=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA diff --git a/benchmark/post.data b/benchmark/post.data deleted file mode 100644 index fce0bf6bb2e..00000000000 --- a/benchmark/post.data +++ /dev/null @@ -1 +0,0 @@ -test1=hello&test2=world&myname=rango&go=start&language=php diff --git a/benchmark/redis.go b/benchmark/redis.go deleted file mode 100644 index b0e84fcccae..00000000000 --- a/benchmark/redis.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" - "github.com/hoisie/redis" - "runtime" -) - -var ( - client *redis.Client -) - -func main() { - - client = &redis.Client{ - Addr: "127.0.0.1:6379", - Db: 0, - MaxPoolSize: 10000, - } - - // 限制为CPU的数量减一 - runtime.GOMAXPROCS( runtime.NumCPU() - 1 ) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - result, err := client.Get("hello") - if err != nil { - fmt.Fprint(w, err.Error()) - println(err.Error()) - } else { - fmt.Fprint(w, "

Hello world!. result="+ string(result)+"

") - } - }) - - log.Fatal(http.ListenAndServe(":8080", nil)) -} diff --git a/benchmark/runtime.php b/benchmark/runtime.php deleted file mode 100644 index 4db29aacfd1..00000000000 --- a/benchmark/runtime.php +++ /dev/null @@ -1,138 +0,0 @@ -prepare('SELECT * FROM `user`'); - for ($n = 100; $n--;) { - $statement->execute(); - assert(count($statement->fetchAll()) > 0); - } - }); -} -for ($c = 50; $c--;) { - go(function () { - $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test'); - $statement = $mysqli->prepare('SELECT `id` FROM `user`'); - for ($n = 100; $n--;) { - $statement->bind_result($id); - $statement->execute(); - $statement->fetch(); - assert($id > 0); - } - }); -} - -// php_stream tcp server & client with 12.8k requests in single process -function tcp_pack(string $data): string -{ - return pack('n', strlen($data)) . $data; -} - -function tcp_length(string $head): int -{ - return unpack('n', $head)[1]; -} - -go(function () { - $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]); - $socket = stream_socket_server( - 'tcp://0.0.0.0:9502', - $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx - ); - if (!$socket) { - echo "$errstr ($errno)\n"; - } else { - $i = 0; - while ($conn = stream_socket_accept($socket, 1)) { - stream_set_timeout($conn, 5); - for ($n = 100; $n--;) { - $data = fread($conn, tcp_length(fread($conn, 2))); - assert($data === "Hello Swoole Server #{$n}!"); - fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!")); - } - if (++$i === 128) { - fclose($socket); - break; - } - } - } -}); -for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - stream_set_timeout($fp, 5); - for ($n = 100; $n--;) { - fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!")); - $data = fread($fp, tcp_length(fread($fp, 2))); - assert($data === "Hello Swoole Client #{$n}!"); - } - fclose($fp); - } - }); -} - -// udp server & client with 12.8k requests in single process -go(function () { - $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); - $socket->bind('127.0.0.1', 9503); - $client_map = []; - for ($c = 128; $c--;) { - for ($n = 0; $n < 100; $n++) { - $recv = $socket->recvfrom($peer); - $client_uid = "{$peer['address']}:{$peer['port']}"; - $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1; - assert($recv === "Client: Hello #{$id}!"); - $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!"); - } - } - $socket->close(); -}); -for ($c = 128; $c--;) { - go(function () { - $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1); - if (!$fp) { - echo "$errstr ($errno)\n"; - } else { - for ($n = 0; $n < 100; $n++) { - fwrite($fp, "Client: Hello #{$n}!"); - $recv = fread($fp, 1024); - list($address, $port) = explode(':', (stream_socket_get_name($fp, true))); - assert($address === '127.0.0.1' && (int)$port === 9503); - assert($recv === "Server: Hello #{$n}!"); - } - fclose($fp); - } - }); -} - -swoole_event_wait(); -echo 'use ' . (microtime(true) - $s) . ' s'; diff --git a/benchmark/seria_bench.php b/benchmark/seria_bench.php deleted file mode 100644 index 9dd1e5045f3..00000000000 --- a/benchmark/seria_bench.php +++ /dev/null @@ -1,483 +0,0 @@ - 257 - ,'user_id'=> 41248 - ,'user_id2'=> 23989 - ,'player'=> 41248 - ,'formation'=> Array ('41248'=> 1 ,'23989'=> 2,'ssss'=>3) - ,'result'=> 1 - ,'battle_type'=> 1 - ,'speed'=> Array( '41248'=> 0,'23989'=> 0 ) - ,'attacker'=> Array( - '1'=> Array ( - 'user_id'=> 41248 - ,'soldier_id'=> 28 - ,'prototype_id'=> 4 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3997 - ,'hp'=> 3997 - ,'attack_general'=> 346 - ,'attack_skill'=> 596 - ,'attack_explode'=> 458 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 2 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0) - ,'4'=> Array ( - 'user_id'=> 41248 - ,'soldier_id'=> 29 - ,'prototype_id'=> 2 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3555 - ,'hp'=> 3555 - ,'attack_general'=> 396 - ,'attack_skill'=> 581 - ,'attack_explode'=> 418 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0 - ) - ,'5'=> Array ( - 'user_id'=> 41248 - ,'soldier_id'=> 30 - ,'prototype_id'=> 6 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3043 - ,'hp'=> 3043 - ,'attack_general'=> 351 - ,'attack_skill'=> 540 - ,'attack_explode'=> 474 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0) - ,'7'=> Array ( - 'user_id'=> 41248 - ,'soldier_id'=> 37 - ,'prototype_id'=> 2 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3491 - ,'hp'=> 3491 - ,'attack_general'=> 393 - ,'attack_skill'=> 532 - ,'attack_explode'=> 456 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0 )) - ,'defender'=> Array( - '2'=> Array( - 'user_id'=> 23989 - ,'soldier_id'=> 24 - ,'prototype_id'=> 1 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3230 - ,'hp'=> 3230 - ,'attack_general'=> 390 - ,'attack_skill'=> 567 - ,'attack_explode'=> 442 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0) - ,'5'=> Array( - 'user_id'=> 23989 - ,'soldier_id'=> 25 - ,'prototype_id'=> 2 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3400 - ,'hp'=> 3400 - ,'attack_general'=> 379 - ,'attack_skill'=> 536 - ,'attack_explode'=> 405 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0 ) - ,'7'=> Array( - 'user_id'=> 23989 - ,'soldier_id'=> 26 - ,'prototype_id'=> 6 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3669 - ,'hp'=> 3669 - ,'attack_general'=> 362 - ,'attack_skill'=> 549 - ,'attack_explode'=> 426 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0 ) - ,'9'=> Array( - 'user_id'=> 23989 - ,'soldier_id'=> 27 - ,'prototype_id'=> 1 - ,'bid'=> 1 - ,'level'=> 1 - ,'rare'=> 1 - ,'skill_id'=> 1 - ,'totalhp'=> 3618 - ,'hp'=> 3618 - ,'attack_general'=> 326 - ,'attack_skill'=> 510 - ,'attack_explode'=> 419 - ,'attack_type'=> 1 - ,'defense'=> 0 - ,'anger'=> 50 - ,'dodge'=> 2 - ,'crit'=> 2 - ,'block'=> 0 - ,'block_effect'=> 0.5 - ,'crit_effect'=> 2 - ,'foramtion_effect'=> 0) ) - ,'battle_process'=> Array( - '0'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'1'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'2'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'3'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'4'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'5'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'6'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'7'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'8'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'9'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'10'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'11'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'12'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'13'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'14'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'15'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'16'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'17'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'18'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ,'19'=> Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - - ,'20'=>Array( - 'user_id'=> 41248 - ,'asid'=> 28 - ,'bsid'=> 'danger' - ,'harm'=> 'danger2' - ,'dhp'=> Array('0'=> 2019 ) - ,'attacker_anger'=> 66 - ,'defender_anger'=> Array('0'=> 94 ) - ,'skill'=> 0 - ,'state'=> 0 - ) - ) - -); -$json = json_encode($target); -$seri = serialize($target); -$sw = swoole_serialize::pack($target); -var_dump("json :". strlen($json)); -var_dump("serialize :". strlen($seri)); -var_dump("swoole :". strlen($sw)); -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - json_encode($target); -} -$etime = microtime(true); -var_dump("json_encode :". ($etime - $stime)); -//---------------------------------- -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - json_decode($json. true); -} -$etime = microtime(true); -var_dump("json_decode :". ($etime - $stime)); -//---------------------------------- -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - serialize($target); -} -$etime = microtime(true); -var_dump("serialize :". ($etime - $stime)); -//---------------------------------- -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - unserialize($seri); -} -$etime = microtime(true); -var_dump("unserialize :". ($etime - $stime)); -//---------------------------------- -//---------------------------------- -//---------------------------------- -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - swoole_serialize::pack($target); -} -$etime = microtime(true); -var_dump("swoole_serialize :". ($etime - $stime)); -$stime = microtime(true); -for ($i = 0; $i < 50000; $i ++) { - swoole_serialize::unpack($sw); -} -$etime = microtime(true); -var_dump("swoole_unserialize :". ($etime - $stime)); -?> diff --git a/benchmark/server/eof_server.php b/benchmark/server/eof_server.php deleted file mode 100644 index fd4847724b7..00000000000 --- a/benchmark/server/eof_server.php +++ /dev/null @@ -1,33 +0,0 @@ - true)); -$serv = new swoole_server("0.0.0.0", 9502, SWOOLE_BASE); -//$serv = new swoole_server("0.0.0.0", 9502); -$serv->set(array( - 'worker_num' => 8, - 'open_eof_check' => true, - 'package_eof' => "\r\n\r\n", -)); -$serv->on('workerstart', function ($server, $id) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: worker"); -}); - -$serv->on('connect', function (swoole_server $serv, $fd, $from_id) -{ - //echo "connect\n";; -}); - -$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) -{ - $serv->send($fd, "Swoole: " . $data); - //$serv->close($fd); -}); - -$serv->on('close', function (swoole_server $serv, $fd, $from_id) -{ - //var_dump($serv->connection_info($fd)); - //echo "onClose\n"; -}); - -$serv->start(); diff --git a/benchmark/server/http.php b/benchmark/server/http.php deleted file mode 100644 index b24177d535c..00000000000 --- a/benchmark/server/http.php +++ /dev/null @@ -1,15 +0,0 @@ -set([ - 'worker_num' => 4, -]); - -$http->on('request', function ($request, swoole_http_response $response) { - $response->header('Last-Modified', 'Thu, 18 Jun 2015 10:24:27 GMT'); - $response->header('E-Tag', '55829c5b-17'); - $response->header('Accept-Ranges', 'bytes'); - $response->end("

\nHello Swoole.\n

"); -}); - -$http->start(); diff --git a/benchmark/server/length_server.php b/benchmark/server/length_server.php deleted file mode 100644 index 923efa79f1f..00000000000 --- a/benchmark/server/length_server.php +++ /dev/null @@ -1,36 +0,0 @@ - true)); -//$serv = new swoole_server("0.0.0.0", 9502, SWOOLE_BASE); - -$serv = new swoole_server("0.0.0.0", 9502); -$serv->set(array( -// 'worker_num' => 1, -// 'dispatch_mode' => 7, - 'open_length_check' => true, - "package_length_type" => 'N', - 'package_body_offset' => 4, -)); -$serv->on('workerstart', function ($server, $id) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: worker"); -}); - -$serv->on('connect', function (swoole_server $serv, $fd, $from_id) -{ - //echo "connect\n";; -}); - -$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) -{ - $serv->send($fd, $data); - //$serv->close($fd); -}); - -$serv->on('close', function (swoole_server $serv, $fd, $from_id) -{ - //var_dump($serv->connection_info($fd)); - //echo "onClose\n"; -}); - -$serv->start(); diff --git a/benchmark/server/random_data.php b/benchmark/server/random_data.php deleted file mode 100644 index 359c30457de..00000000000 --- a/benchmark/server/random_data.php +++ /dev/null @@ -1,43 +0,0 @@ -set(array( -// 'worker_num' => 1, -// 'dispatch_mode' => 7, - 'open_length_check' => true, - "package_length_type" => 'N', - 'package_body_offset' => 4, -)); - -$serv->on('workerstart', function ($server, $id) { - global $argv; - swoole_set_process_name("php {$argv[0]}: worker"); -}); - -$serv->on('connect', function (Server $serv, $fd, $rid) { - //echo "connect\n";; -}); - -$serv->on('receive', function (Server $serv, $fd, $rid, $data) { - $hash = substr($data, 4, 32); - if ($hash !== md5(substr($data, -128, 128))) { - echo "Client Request Data Error\n"; - $serv->close($fd); - } else { - $len = mt_rand(1024, 1024 * 1024); - $send_data = substr(R_DATA, rand(0, N - $len), $len); - $serv->send($fd, pack('N', $len + 32) . md5(substr($send_data, -128, 128)) . $send_data); - } -}); - -$serv->on('close', function (Server $serv, $fd, $from_id) { -}); - -$serv->start(); diff --git a/benchmark/server/random_data_eof.php b/benchmark/server/random_data_eof.php deleted file mode 100644 index df07d545ebf..00000000000 --- a/benchmark/server/random_data_eof.php +++ /dev/null @@ -1,40 +0,0 @@ -set(array( - 'open_eof_check' => true, - 'package_eof' => "\r\n", -)); - -$serv->on('workerstart', function ($server, $id) { - global $argv; - swoole_set_process_name("php {$argv[0]}: worker"); -}); - -$serv->on('connect', function (Server $serv, $fd, $rid) { - //echo "connect\n";; -}); - -$serv->on('receive', function (Server $serv, $fd, $rid, $data) { - $hash = substr($data, 0, 32); - if ($hash !== md5(substr($data, -130, 128))) { - echo "Client Request Data Error\n"; - $serv->close($fd); - } else { - $len = mt_rand(1024, 1024 * 1024); - $send_data = substr(R_DATA, rand(0, N - $len), $len); - $serv->send($fd, md5(substr($send_data, -128, 128)) . $send_data."\r\n"); - } -}); - -$serv->on('close', function (Server $serv, $fd, $from_id) { -}); - -$serv->start(); diff --git a/benchmark/server/tcp.php b/benchmark/server/tcp.php deleted file mode 100644 index e1120de5664..00000000000 --- a/benchmark/server/tcp.php +++ /dev/null @@ -1,31 +0,0 @@ - true)); -$serv = new swoole_server("0.0.0.0", 9502, SWOOLE_BASE); -//$serv = new swoole_server("0.0.0.0", 9502); -$serv->set(array( - 'worker_num' => 8, -)); -$serv->on('workerstart', function ($server, $id) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: worker"); -}); - -$serv->on('connect', function (swoole_server $serv, $fd, $from_id) -{ - //echo "connect\n";; -}); - -$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) -{ - $serv->send($fd, "Swoole: " . $data); - //$serv->close($fd); -}); - -$serv->on('close', function (swoole_server $serv, $fd, $from_id) -{ - //var_dump($serv->connection_info($fd)); - //echo "onClose\n"; -}); - -$serv->start(); diff --git a/benchmark/server/udp.php b/benchmark/server/udp.php deleted file mode 100644 index be1e68d427c..00000000000 --- a/benchmark/server/udp.php +++ /dev/null @@ -1,35 +0,0 @@ - true)); -//$serv = new swoole_server("0.0.0.0", 9502, SWOOLE_BASE, SWOOLE_SOCK_UDP); -$serv = new swoole_server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); -$serv->set(array( - 'dispatch_mode' => 1, - 'worker_num' => 8, //worker process num -// //'log_file' => '/tmp/swoole.log', -// //'daemonize' => true, -)); - -function my_onStart($serv) -{ - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; -} - -function my_onReceive(swoole_server $serv, $fd, $from_id, $data) -{ - //var_dump($serv->connection_info($fd, $from_id)); - //echo "worker_pid=".posix_getpid().PHP_EOL; - //var_dump($fd, $from_id); - $serv->send($fd, 'Swoole: ' . $data, $from_id); -} - -function my_onPacket(swoole_server $serv, $data, $addr) -{ -// var_dump($addr); - $serv->sendto($addr['address'], $addr['port'], 'Swoole: ' . $data); -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Receive', 'my_onReceive'); -//$serv->on('Packet', 'my_onPacket'); -$serv->start(); diff --git a/benchmark/server/websocket.php b/benchmark/server/websocket.php deleted file mode 100644 index 263438912be..00000000000 --- a/benchmark/server/websocket.php +++ /dev/null @@ -1,25 +0,0 @@ -set(['worker_num' => 4]); -// -//$server->on('open', function (swoole_websocket_server $_server, swoole_http_request $request) { -// //echo "server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\n"; -// -//// var_dump($request); -//}); - -$server->on('message', function (swoole_websocket_server $_server, $frame) { - //var_dump($frame); - //echo "received ".strlen($frame->data)." bytes\n"; - //echo "receive from {$fd}:{$data},opcode:{$opcode},fin:{$fin}\n"; - $_server->push($frame->fd, "server:" . $frame->data); - // $_server->close($frame->fd); -}); - -//$server->on('close', function ($_server, $fd) { -// echo "client {$fd} closed\n"; -//}); - - -$server->start(); diff --git a/benchmark/src/Base.php b/benchmark/src/Base.php deleted file mode 100644 index 59d2ecc8802..00000000000 --- a/benchmark/src/Base.php +++ /dev/null @@ -1,507 +0,0 @@ -host, $this->port) = explode(':', $s); - if (!filter_var($this->host, FILTER_VALIDATE_IP)) { - throw new ExitException("Invalid ip address: {$this->host}" . PHP_EOL); - } - if ($this->port > 65535 or $this->port < 1024) { - throw new ExitException("Invalid port [1024~65535]" . PHP_EOL); - } - - $this->nConcurrency = $c; - $this->nRequest = $n; - - if (!isset($f) or !method_exists($this, $f)) { - throw new ExitException("Not support pressure measurement objects [{$f}]." . PHP_EOL); - } - $this->testMethod = $f; - $this->nShow = $this->nRequest / 10; - } - - protected function parseOpts() - { - $shortOpts = "c:n:l:s:t:d:khv"; - $opts = getopt($shortOpts); - - if (isset($opts['h'])) { - $this->showHelp(); - } - - if (isset($opts['l']) and intval($opts['l']) > 0) { - $this->sentLen = intval($opts['l']); - } - - $this->path = $serv['path'] ?? self::PATH; - $this->query = $serv['query'] ?? self::QUERY; - $this->timeout = $opts['t'] ? intval($opts['t']) : self::TIMEOUT; - $this->keepAlive = isset($opts['k']); - $this->verbose = isset($opts['v']); - if (isset($opts['d'])) { - $this->setSentData($opts['d']); - } - } - - public function showHelp() - { - exit(<<sentData = $data; - $this->sentLen = strlen($data); - } - - protected function finish() - { - $costTime = $this->format(microtime(true) - $this->startTime); - $nRequest = number_format($this->nRequest); - $requestErrorCount = number_format($this->nRequest - $this->requestCount); - $connectErrorCount = number_format($this->connectErrorCount); - $nSendBytes = number_format($this->nSendBytes); - $nRecvBytes = number_format($this->nRecvBytes); - $requestPerSec = round($this->requestCount / $costTime, 2); - $connectTime = $this->format($this->connectTime); - - echo <<nConcurrency} -Time taken for tests: {$costTime} seconds -Complete requests: {$nRequest} -Failed requests: {$requestErrorCount} -Connect failed: {$connectErrorCount} -Total send: {$nSendBytes} bytes -Total reveive: {$nRecvBytes} bytes -Requests per second: {$requestPerSec} -Connection time: {$connectTime} seconds -\n -EOF; - } - - public function format($time) - { - return round($time, 4); - } - - /** - * @throws ExitException - */ - protected function ws() - { - $wsCli = new Coroutine\http\client($this->host, $this->port); - $n = $this->nRequest / $this->nConcurrency; - Coroutine::defer(function () use ($wsCli) { - $wsCli->close(); - }); - - $setting = [ - 'timeout' => $this->timeout, - 'websocket_mask' => true, - ]; - $wsCli->set($setting); - if (!$wsCli->upgrade($this->path)) { - if ($wsCli->errCode === 111) { - throw new ExitException(swoole_strerror($wsCli->errCode)); - } else if ($wsCli->errCode === 110) { - throw new ExitException(swoole_strerror($wsCli->errCode)); - } else { - throw new ExitException("Handshake failed"); - } - } - - if ($this->sentLen === 0) { - $this->sentLen = self::TCP_SENT_LEN; - } - $this->setSentData(str_repeat('A', $this->sentLen)); - - while ($n--) { - //requset - if (!$wsCli->push($this->data)) { - if ($wsCli->errCode === 8502) { - throw new ExitException("Error OPCODE"); - } else if ($wsCli->errCode === 8503) { - throw new ExitException("Not connected to the server or the connection has been closed"); - } else { - throw new ExitException("Handshake failed"); - } - } - $this->nSendBytes += $this->sentLen; - $this->requestCount++; - if (($this->requestCount % $this->nShow === 0) and $this->verbose) { - echo "Completed {$this->requestCount} requests" . PHP_EOL; - } - //response - $frame = $wsCli->recv(); - $this->nRecvBytes += strlen($frame->data); - } - } - - /** - * @throws ExitException - */ - protected function tcp() - { - $cli = new Coroutine\Client(SWOOLE_TCP); - $n = $this->nRequest / $this->nConcurrency; - Coroutine::defer(function () use ($cli) { - $cli->close(); - }); - - if ($this->sentLen === 0) { - $this->sentLen = self::TCP_SENT_LEN; - } - $this->setSentData(str_repeat('A', $this->sentLen)); - - if (!$cli->connect($this->host, $this->port)) { // connection failed - if ($cli->errCode === 111) { // connection refuse - throw new ExitException(swoole_strerror($cli->errCode)); - } - if ($cli->errCode === 110) { // connection timeout - $this->connectErrorCount++; - if ($this->verbose) { - echo swoole_strerror($cli->errCode) . PHP_EOL; - } - return; - } - } - - while ($n--) { - //requset - if (!$cli->send($this->sentData)) { - if ($this->verbose) { - echo swoole_strerror($cli->errCode) . PHP_EOL; - } - continue; - } - $this->nSendBytes += $this->sentLen; - $this->requestCount++; - if (($this->requestCount % $this->nShow === 0) and $this->verbose) { - echo "Completed {$this->requestCount} requests" . PHP_EOL; - } - //response - $recvData = $cli->recv(); - if ($recvData === false and $this->verbose) { - echo swoole_strerror($cli->errCode) . PHP_EOL; - } else { - $this->nRecvBytes += strlen($recvData); - } - } - } - - /** - * @throws ExitException - */ - protected function http() - { - $httpCli = new Coroutine\Http\Client($this->host, $this->port); - $n = $this->nRequest / $this->nConcurrency; - Coroutine::defer(function () use ($httpCli) { - $httpCli->close(); - }); - - $headers = [ - 'Host' => "{$this->host}:{$this->port}", - 'Accept' => 'text/html,application/xhtml+xml,application/xml', - 'content-type' => 'application/x-www-form-urlencoded', - ]; - $httpCli->setHeaders($headers); - - $setting = [ - 'timeout' => $this->timeout, - 'keep_alive' => $this->keepAlive, - ]; - $httpCli->set($setting); - if (isset($this->sentData)) { - $httpCli->setData($this->sentData); - } - - $query = empty($this->query) ? '' : "?$this->query"; - - while ($n--) { - $httpCli->execute("{$this->path}{$query}"); - - if (!$this->checkStatusCode($httpCli)) { - continue; - } - - $this->requestCount++; - if ($this->requestCount % $this->nShow === 0 and $this->verbose) { - echo "Completed {$this->requestCount} requests" . PHP_EOL; - } - $recvData = $httpCli->body; - $this->nRecvBytes += strlen($recvData); - } - } - - /** - * @param Coroutine\Http\Client $httpCli - * @return bool - * @throws ExitException - */ - protected function checkStatusCode(Coroutine\Http\Client $httpCli): bool - { - if ($httpCli->statusCode === -1) { // connection failed - if ($httpCli->errCode === 111) { // connection refused - throw new ExitException(swoole_strerror($httpCli->errCode)); - } - if ($httpCli->errCode === 110) { // connection timeout - $this->connectErrorCount++; - if ($this->verbose) { - echo swoole_strerror($httpCli->errCode) . PHP_EOL; - } - return false; - } - } - - if ($httpCli->statusCode === -2) { // request timeout - if ($this->verbose) { - echo swoole_strerror($httpCli->errCode) . PHP_EOL; - } - return false; - } - - if ($httpCli->statusCode === 404) { - $query = empty($this->query) ? '' : "?$this->query"; - $url = "{$this->scheme}://{$this->host}:{$this->port}{$this->path}{$query}"; - throw new ExitException("The URL [$url] is non-existent"); - } - - return true; - } - - protected function eof() - { - $eof = "\r\n\r\n"; - $cli = new Coroutine\Client(SWOOLE_TCP); - $cli->set(array('open_eof_check' => true, "package_eof" => $eof)); - $cli->connect($this->host, $this->port); - $n = $this->nRequest / $this->nConcurrency; - while ($n--) { - //requset - $data = $this->sentData . $eof; - $cli->send($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - //response - $rdata = $cli->recv(); - $this->nRecvBytes += strlen($rdata); - } - $cli->close(); - } - - protected function length() - { - $cli = new Coroutine\Client(SWOOLE_TCP); - $cli->set(array( - 'open_length_check' => true, - "package_length_type" => 'N', - 'package_body_offset' => 4, - )); - $cli->connect($this->host, $this->port); - $n = $this->nRequest / $this->nConcurrency; - while ($n--) { - //requset - $data = pack('N', strlen($this->sentData)) . $this->sentData; - $cli->send($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - //response - $rdata = $cli->recv(); - $this->nRecvBytes += strlen($rdata); - } - $cli->close(); - } - - /** - * @param $max - * @param bool $raw - * @return string - * @throws \Exception - */ - protected function getRandomData($max, $raw = true) - { - if (!$this->randomData) { - $this->randomData = random_bytes($max); - if (!$raw) { - $this->randomData = base64_encode($this->randomData); - } - } - return $this->randomData; - } - - /** - * @throws \Exception - */ - protected function random_data() - { - $cli = new Coroutine\Client(SWOOLE_TCP); - $cli->set(array( - 'open_length_check' => true, - "package_length_type" => 'N', - 'package_body_offset' => 4, - )); - $cli->connect($this->host, $this->port); - - $max = 32 * 1024 * 1024; - $random_data = $this->getRandomData($max); - $cid = Coroutine::getCid(); - - $n = $this->nRequest / $this->nConcurrency; - while ($n--) { - //requset - $len = mt_rand(1024, 1024 * 1024); - $send_data = substr($random_data, rand(0, $max - $len), $len); - $data = pack('N', $len + 32) . md5(substr($send_data, -128, 128)) . $send_data; - $cli->send($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - //response - $rdata = $cli->recv(); - $this->nRecvBytes += strlen($rdata); - $hash = substr($data, 4, 32); - if ($hash !== md5(substr($data, -128, 128))) { - echo "[Co-$cid]\tResponse Data Error\n"; - break; - } - } - $cli->close(); - } - - - /** - * @throws \Exception - */ - protected function random_data_eof() - { - $cli = new Coroutine\Client(SWOOLE_TCP); - $cli->set(array( - 'open_eof_check' => true, - 'package_eof' => "\r\n", - )); - $cli->connect($this->host, $this->port); - - $max = 32 * 1024 * 1024; - $random_data = $this->getRandomData($max, false); - $cid = Coroutine::getCid(); - - $n = $this->nRequest / $this->nConcurrency; - while ($n--) { - //requset - $len = mt_rand(1024, 1024 * 1024); - $send_data = substr($random_data, rand(0, $max - $len), $len); - $data = md5(substr($send_data, -128, 128)) . $send_data."\r\n"; - $cli->send($data); - $this->nSendBytes += strlen($data); - $this->requestCount++; - //response - $rdata = $cli->recv(); - $this->nRecvBytes += strlen($rdata); - $hash = substr($data, 0, 32); - if ($hash !== md5(substr($data, -130, 128))) { - echo "[Co-$cid]\tResponse Data Error\n"; - break; - } - } - $cli->close(); - } - - public function run() - { - $exitException = false; - $this->startTime = microtime(true); - - $sch = new Coroutine\Scheduler; - - $sch->parallel($this->nConcurrency, function () use (&$exitException, &$exitExceptiontMsg) { - try { - call_user_func([$this, $this->testMethod]); - } catch (ExitException $e) { - $exitException = true; - $exitExceptiontMsg = $e->getMessage(); - } - }); - $sch->start(); - - $this->beginSendTime = microtime(true); - $this->connectTime = $this->beginSendTime - $this->startTime; - if ($exitException) { - exit($exitExceptiontMsg . PHP_EOL); - } - $this->finish(); - } -} \ No newline at end of file diff --git a/benchmark/src/Command/RunTest.php b/benchmark/src/Command/RunTest.php deleted file mode 100644 index 5a72421cfc3..00000000000 --- a/benchmark/src/Command/RunTest.php +++ /dev/null @@ -1,54 +0,0 @@ -addArgument( - 'method', - InputArgument::REQUIRED, - 'Who do you want to test?' - ); - $this->addOption('concurrent', 'c', InputOption::VALUE_REQUIRED); - $this->addOption('requests', 'r', InputOption::VALUE_REQUIRED, 'number or requests'); - $this->addOption('server', 's', InputOption::VALUE_REQUIRED, 'server ip:port'); - $this->addOption('length', 'l', InputOption::VALUE_OPTIONAL, 'data length'); - $this->setName('run'); - $this->setHelp("run benchmark test"); - $this->setDescription("Run benchmark test."); - $this->addUsage('e.g: ./main -c 100 -r 10000 127.0.0.1:9501 length'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $c = $input->getOption('concurrent'); - if (!$c) { - throw new LogicException('Please enter the number of concurrent.'); - } - - $n = $input->getOption('requests'); - if (!$n) { - throw new LogicException('Please enter the number of requests.'); - } - - $s = $input->getOption('server'); - if (!$s) { - throw new LogicException('Please enter the server ip:port.'); - } - - $f = $input->getArgument('method'); - $test = new Base($c, $n, $s, $f); - $test->run(); - } - -} \ No newline at end of file diff --git a/benchmark/stream_mode.php b/benchmark/stream_mode.php deleted file mode 100644 index 0f9a0e6702a..00000000000 --- a/benchmark/stream_mode.php +++ /dev/null @@ -1,42 +0,0 @@ -server = new Swoole\Http\Server("0.0.0.0", 9501); - $this->server->set([ - 'worker_num' => 1, - 'dispatch_mode' => 7 - ]); - -// $this->server->on('Connect', [$this, 'onConnect']); - $this->server->on('Request', [$this, 'onRequest']); -// $this->server->on('Close', [$this, 'onClose']); - - $this->server->start(); - } - - function log($msg) - { - echo $msg."\n"; - } - - public function onConnect($serv, $fd, $reactorId) - { -// $this->log("onConnect: fd=$fd"); - } - - public function onClose($serv, $fd, $reactorId) - { -// $this->log("onClose: fd=$fd"); - } - - public function onRequest($request, $response) - { - $response->end("data:".str_repeat('A', rand(100, 200))); - } -} - -$server = new Server(); -$server->run(); diff --git a/benchmark/table.php b/benchmark/table.php deleted file mode 100644 index 39f885934e6..00000000000 --- a/benchmark/table.php +++ /dev/null @@ -1,80 +0,0 @@ -column('id', swoole_table::TYPE_INT, 4); -$table->column('name', swoole_table::TYPE_STRING, 32); -$table->column('num', swoole_table::TYPE_FLOAT); -$table->create(); - -define('N', 1000000); -define('C', 4); - -test1(); -test2(); -test3(); -test4(); - - -function test1() -{ - global $table; - - /** - * table_size = 1M - */ - $s = microtime(true); - $n = N; - while ($n--) { - $table->set('key_' . $n, array('id' => $n, 'name' => "swoole, value=$n\r\n", 'num' => 3.1415 * rand(10000, 99999))); - } - echo "set " . N . " keys, use: " . round((microtime(true) - $s) * 1000, 2) . "ms\n"; -} - -function test2() -{ - global $table; - $n = N; - $s = microtime(true); - while ($n--) { - $key = rand(0, N); - $data = $table->get('key_' . $key); - } - echo "get " . N . " keys, use: " . round((microtime(true) - $s) * 1000, 2) . "ms\n"; -} - -function test3() -{ - for ($i = C; $i--;) { - (new swoole_process(function () use ($i) { - global $table; - $n = N; - $s = microtime(true); - while ($n--) { - $key = rand(0, N); - $data = $table->get('key_' . $key); - } - echo "[Worker#$i]get " . N . " keys, use: " . round((microtime(true) - $s) * 1000, 2) . "ms\n"; - }))->start(); - } - for ($i = C; $i--;) { - swoole_process::wait(); - } -} - -function test4() -{ - for ($i = C; $i--;) { - (new swoole_process(function () use ($i) { - global $table; - $n = N; - $s = microtime(true); - while ($n--) { - $key = rand(0, N); - $table->set('key_' . $key, array('id' => $key, 'name' => "php, value=$n\r\n", 'num' => 3.1415 * rand(10000, 99999))); - } - echo "[Worker#$i]set " . N . " keys, use: " . round((microtime(true) - $s) * 1000, 2) . "ms\n"; - }))->start(); - } - for ($i = C; $i--;) { - swoole_process::wait(); - } -} \ No newline at end of file diff --git a/benchmark/tcp.go b/benchmark/tcp.go deleted file mode 100644 index fd13a2f8a45..00000000000 --- a/benchmark/tcp.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "net" - "runtime" -) - -func Echo(c net.Conn) { - defer c.Close() - for { - line, err := bufio.NewReader(c).ReadString('\n') - if err != nil { - //fmt.Printf("Failure to read:%s\n", err.Error()) - return - } - _, err = c.Write([]byte(line)) - if err != nil { - //fmt.Printf("Failure to write: %s\n", err.Error()) - return - } - } -} - -func main() { - - runtime.GOMAXPROCS(runtime.NumCPU() - 1) - - fmt.Printf("Server is ready...\n") - l, err := net.Listen("tcp", ":8053") - if err != nil { - fmt.Printf("Failure to listen: %s\n", err.Error()) - } - - for { - if c, err := l.Accept(); err == nil { - go Echo(c) //new thread - } - } -} diff --git a/benchmark/tcp.js b/benchmark/tcp.js deleted file mode 100644 index c6aacb95586..00000000000 --- a/benchmark/tcp.js +++ /dev/null @@ -1,8 +0,0 @@ -var net = require('net'); - -var server = net.createServer(function (socket) { -socket.write("Echo server\r\n"); -socket.pipe(socket); -}); - -server.listen(7001, "127.0.0.1"); diff --git a/benchmark/timer.php b/benchmark/timer.php deleted file mode 100644 index f74a32cbdd1..00000000000 --- a/benchmark/timer.php +++ /dev/null @@ -1,37 +0,0 @@ -= 8.1" + }, + "php-ext": { + "extension-name": "swoole", + "configure-options": [ + { + "name": "enable-sockets", + "description": "Enable sockets support" + }, + { + "name": "enable-openssl", + "description": "Enable openssl support" + }, + { + "name": "with-openssl-dir", + "description": "Include OpenSSL support (requires OpenSSL >= 1.0.2)", + "needs-value": true + }, + { + "name": "enable-mysqlnd", + "description": "Enable mysqlnd support" + }, + { + "name": "enable-swoole-curl", + "description": "Enable curl support" + }, + { + "name": "enable-cares", + "description": "Enable cares support" + }, + { + "name": "enable-brotli", + "description": "Enable brotli support" + }, + { + "name": "with-brotli-dir", + "description": "Include Brotli support", + "needs-value": true + }, + { + "name": "enable-swoole-pgsql", + "description": "Enable PostgreSQL database support" + }, + { + "name": "with-swoole-odbc", + "description": "Enable ODBC database support", + "needs-value": true + }, + { + "name": "with-swoole-oracle", + "description": "Enable Oracle database support", + "needs-value": true + }, + { + "name": "enable-swoole-sqlite", + "description": "Enable Sqlite database support" + }, + { + "name": "enable-swoole-thread", + "description": "Enable swoole thread support (need php zts support)" + }, + { + "name": "enable-iouring", + "description": "Enable iouring for file async support" + } + ] + } +} diff --git a/config.m4 b/config.m4 index 346828c6f2d..b1e237f77de 100644 --- a/config.m4 +++ b/config.m4 @@ -12,41 +12,140 @@ dnl | If you did not receive a copy of the Apache2.0 license and are unable| dnl | to obtain it through the world-wide-web, please send a note to | dnl | license@swoole.com so we can mail you a copy immediately. | dnl +----------------------------------------------------------------------+ -dnl | Author: Tianfeng Han | +dnl | Author: Tianfeng Han | +dnl | Author: Twosee | dnl +----------------------------------------------------------------------+ -PHP_ARG_ENABLE(debug-log, enable debug log, -[ --enable-debug-log Enable swoole debug log], no, no) - -PHP_ARG_ENABLE(trace-log, enable trace log, -[ --enable-trace-log Enable swoole trace log], no, no) - -PHP_ARG_ENABLE(sockets, enable sockets support, -[ --enable-sockets Do you have sockets extension?], no, no) - -PHP_ARG_ENABLE(openssl, enable openssl support, -[ --enable-openssl Use openssl?], no, no) - -PHP_ARG_ENABLE(http2, enable http2.0 support, -[ --enable-http2 Use http2.0?], no, no) - -PHP_ARG_ENABLE(swoole, swoole support, -[ --enable-swoole Enable swoole support], [enable_swoole="yes"]) - -PHP_ARG_ENABLE(mysqlnd, enable mysqlnd support, -[ --enable-mysqlnd Do you have mysqlnd?], no, no) - -PHP_ARG_WITH(openssl_dir, dir of openssl, -[ --with-openssl-dir[=DIR] Include OpenSSL support (requires OpenSSL >= 0.9.6)], no, no) - -PHP_ARG_WITH(jemalloc_dir, dir of jemalloc, -[ --with-jemalloc-dir[=DIR] Include jemalloc support], no, no) - -PHP_ARG_ENABLE(asan, enable asan, -[ --enable-asan Enable asan], no, no) - -PHP_ARG_ENABLE(gcov, enable gcov, -[ --enable-gcov Enable gcov], no, no) +PHP_ARG_ENABLE([debug-log], + [enable debug log], + [AS_HELP_STRING([--enable-debug-log], + [Enable swoole debug log])], [no], [no]) + +PHP_ARG_ENABLE([trace-log], + [enable trace log], + [AS_HELP_STRING([--enable-trace-log], + [Enable swoole trace log])], [no], [no]) + +PHP_ARG_ENABLE([sockets], + [enable sockets support], + [AS_HELP_STRING([--enable-sockets], + [Do you have sockets extension?])], [no], [no]) + +PHP_ARG_ENABLE([openssl], + [enable openssl support], + [AS_HELP_STRING([--enable-openssl], + [Use openssl])], [no], [no]) + +PHP_ARG_ENABLE([swoole], + [swoole support], + [AS_HELP_STRING([--enable-swoole], + [Enable swoole support])], [enable_swoole="yes"]) + +PHP_ARG_ENABLE([mysqlnd], + [enable mysqlnd support], + [AS_HELP_STRING([--enable-mysqlnd], + [Enable mysqlnd])], [no], [no]) + +PHP_ARG_ENABLE([cares], + [enable c-ares support], + [AS_HELP_STRING([--enable-cares], + [Enable cares])], [no], [no]) + +PHP_ARG_ENABLE([iouring], + [enable io-uring support], + [AS_HELP_STRING([--enable-iouring], + [Enable io-uring])], [no], [no]) + +PHP_ARG_WITH([openssl_dir], + [dir of openssl], + [AS_HELP_STRING([[--with-openssl-dir[=DIR]]], + [Include OpenSSL support (requires OpenSSL >= 1.0.2)])], [no], [no]) + +PHP_ARG_ENABLE([brotli], + [enable brotli support], + [AS_HELP_STRING([[--enable-brotli]], + [Use brotli])], [auto], [no]) + +PHP_ARG_WITH([brotli_dir], + [dir of brotli], + [AS_HELP_STRING([[--with-brotli-dir[=DIR]]], + [Include Brotli support])], [no], [no]) + +PHP_ARG_ENABLE([zstd], + [enable zstd support], + [AS_HELP_STRING([[--enable-zstd]], + [Use zstd])], [no], [no]) + +PHP_ARG_WITH([nghttp2_dir], + [dir of nghttp2], + [AS_HELP_STRING([[--with-nghttp2-dir[=DIR]]], + [Include nghttp2 support])], [no], [no]) + +PHP_ARG_ENABLE([asan], + [enable asan], + [AS_HELP_STRING([--enable-asan], + [Enable asan])], [no], [no]) + +PHP_ARG_ENABLE([swoole-coverage], + [whether to enable swoole coverage support], + [AS_HELP_STRING([--enable-swoole-coverage], + [Enable swoole coverage support])], [no], [no]) + +PHP_ARG_ENABLE([swoole-dev], + [whether to enable Swoole developer build flags], + [AS_HELP_STRING([--enable-swoole-dev], + [Enable developer flags])], [no], [no]) + +PHP_ARG_ENABLE([swoole-curl], + [whether to enable Swoole CURL build flags], + [AS_HELP_STRING([--enable-swoole-curl], + [Enable cURL support])], [no], [no]) + +PHP_ARG_ENABLE([swoole-pgsql], + [whether to enable postgresql build flags], + [AS_HELP_STRING([--enable-swoole-pgsql], + [Enable postgresql support])], [no], [no]) + +PHP_ARG_ENABLE([thread-context], + [whether to enable thread context], + [AS_HELP_STRING([--enable-thread-context], + [Use thread context])], [no], [no]) + +PHP_ARG_ENABLE([swoole-thread], + [whether to enable swoole thread support], + [AS_HELP_STRING([--enable-swoole-thread], + [Enable swoole thread support])], [no], [no]) + +PHP_ARG_ENABLE([swoole-coro-time], + [whether to enable coroutine execution time ], + [AS_HELP_STRING([--enable-swoole-coro-time], + [Calculating coroutine execution time])], [no], [no]) + +define([PDO_ODBC_HELP_TEXT],[[ + The include and lib dirs are looked for under 'dir'. The 'flavour' can be one + of: ibm-db2, iODBC, unixODBC, generic. If ',dir' part is omitted, default for + the flavour you have selected will be used. e.g.: --with-swoole-odbc=unixODBC + will check for unixODBC under /usr/local. You may attempt to use an otherwise + unsupported driver using the 'generic' flavour. The syntax for generic ODBC + support is: --with-swoole-odbc=generic,dir,libname,ldflags,cflags. When built as + 'shared' the extension filename is always pdo_odbc.so]]) + +PHP_ARG_WITH([swoole-odbc], + ["for ODBC v3 support for PDO"], + [AS_HELP_STRING([--with-swoole-odbc=flavour,dir], + ["PDO: Support for 'flavour' ODBC driver."]PDO_ODBC_HELP_TEXT)], [no], [no]) + +AC_DEFUN([PDO_ODBC_CHECK_HEADER],[ + AC_MSG_CHECKING([for $1 in $PDO_ODBC_INCDIR]) + if test -f "$PDO_ODBC_INCDIR/$1"; then + php_pdo_have_header=yes + AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), [1], + [Define to 1 if you have the <$1> header file.]) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) AC_DEFUN([SWOOLE_HAVE_PHP_EXT], [ extname=$1 @@ -84,6 +183,7 @@ AC_DEFUN([AC_SWOOLE_CPU_AFFINITY], #include typedef cpuset_t cpu_set_t; #else + #define _GNU_SOURCE 1 #include #endif ]], [[ @@ -136,6 +236,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT], [ AC_MSG_CHECKING([for ucontext]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _XOPEN_SOURCE #include #include #include @@ -153,7 +254,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT], AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], [ AC_MSG_CHECKING([for valgrind]) - AC_LANG([C++]) + AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ @@ -164,40 +265,67 @@ AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], ],[ AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) ]) -AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ - dnl Check for struct cmsghdr - AC_CACHE_CHECK([for struct cmsghdr], ac_cv_cmsghdr, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include ]], [[struct cmsghdr s; s]])], [ac_cv_cmsghdr=yes], [ac_cv_cmsghdr=no]) +AC_DEFUN([AC_SWOOLE_HAVE_BOOST_STACKTRACE], +[ + AC_MSG_CHECKING([for valgrind]) + AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + + ]])],[ + AC_DEFINE([HAVE_BOOST_STACKTRACE], 1, [have boost-stacktrace?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) +]) - if test "$ac_cv_cmsghdr" = yes; then - AC_DEFINE(HAVE_CMSGHDR,1,[Whether you have struct cmsghdr]) - fi +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_FUTEX], +[ + AC_MSG_CHECKING([for io_uring futex]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + ]], [[ + int op = IORING_OP_FUTEX_WAIT; + ]])],[ + AC_DEFINE([HAVE_IOURING_FUTEX], 1, [have io_uring futex?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) + +AC_DEFUN([AC_SWOOLE_HAVE_IOURING_STATX], +[ + AC_MSG_CHECKING([for io_uring statx]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _GNU_SOURCE + #include + #include + #include + ]], [[ + struct statx _statxbuf; + memset(&_statxbuf, 0, sizeof(_statxbuf)); + int op = IORING_OP_STATX; + ]])],[ + AC_DEFINE([HAVE_IOURING_STATX], 1, [have io_uring statx?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) +AC_DEFUN([AC_SWOOLE_CHECK_SOCKETS], [ AC_CHECK_FUNCS([hstrerror socketpair if_nametoindex if_indextoname]) AC_CHECK_HEADERS([netdb.h netinet/tcp.h sys/un.h sys/sockio.h]) AC_DEFINE([HAVE_SOCKETS], 1, [ ]) - dnl Check for fied ss_family in sockaddr_storage (missing in AIX until 5.3) - AC_CACHE_CHECK([for field ss_family in struct sockaddr_storage], ac_cv_ss_family, - [ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include -#include - ]], [[struct sockaddr_storage sa_store; sa_store.ss_family = AF_INET6;]])], - [ac_cv_ss_family=yes], [ac_cv_ss_family=no]) - ]) - - if test "$ac_cv_ss_family" = yes; then - AC_DEFINE(HAVE_SA_SS_FAMILY,1,[Whether you have sockaddr_storage.ss_family]) - fi - dnl Check for AI_V4MAPPED flag AC_CACHE_CHECK([if getaddrinfo supports AI_V4MAPPED],[ac_cv_gai_ai_v4mapped], [ @@ -249,9 +377,8 @@ AC_COMPILE_IFELSE([ ) AC_MSG_RESULT([$CLANG]) -if test "$CLANG" = "yes"; then - CFLAGS="$CFLAGS -std=gnu89" -fi +dnl AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 yet necessary for <= 2.69. +m4_version_prereq([2.70],,[AC_PROG_CC_C99]) AC_CANONICAL_HOST @@ -272,27 +399,534 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, inotify_init1, AC_DEFINE(HAVE_INOTIFY_INIT1, 1, [have inotify_init1])) AC_CHECK_LIB(c, gethostbyname2_r, AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [have gethostbyname2_r])) AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) + AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) + AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) + AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) AC_CHECK_LIB(pthread, pthread_barrier_init, AC_DEFINE(HAVE_PTHREAD_BARRIER, 1, [have pthread_barrier_init])) - AC_CHECK_LIB(pcre, pcre_compile, AC_DEFINE(HAVE_PCRE, 1, [have pcre])) + AC_CHECK_LIB(pthread, pthread_mutexattr_setpshared, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETPSHARED, 1, [have pthread_mutexattr_setpshared])) + AC_CHECK_LIB(pthread, pthread_mutexattr_setrobust, AC_DEFINE(HAVE_PTHREAD_MUTEXATTR_SETROBUST, 1, [have pthread_mutexattr_setrobust])) + AC_CHECK_LIB(pthread, pthread_mutex_consistent, AC_DEFINE(HAVE_PTHREAD_MUTEX_CONSISTENT, 1, [have pthread_mutex_consistent])) + + if test "$PHP_SWOOLE_DEV" = "yes"; then + AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") + AX_CHECK_COMPILE_FLAG(-Wignored-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wignored-qualifiers") + AX_CHECK_COMPILE_FLAG(-Wduplicate-enum, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wduplicate-enum") + AX_CHECK_COMPILE_FLAG(-Wempty-body, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wempty-body") + AX_CHECK_COMPILE_FLAG(-Wenum-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wenum-compare") + AX_CHECK_COMPILE_FLAG(-Wextra, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wextra") + AX_CHECK_COMPILE_FLAG(-Wformat-security, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wformat-security") + AX_CHECK_COMPILE_FLAG(-Wheader-guard, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wheader-guard") + AX_CHECK_COMPILE_FLAG(-Wincompatible-pointer-types-discards-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wincompatible-pointer-types-discards-qualifiers") + AX_CHECK_COMPILE_FLAG(-Winit-self, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Winit-self") + AX_CHECK_COMPILE_FLAG(-Wlogical-not-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-not-parentheses") + AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op-parentheses") + AX_CHECK_COMPILE_FLAG(-Wloop-analysis, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wloop-analysis") + AX_CHECK_COMPILE_FLAG(-Wuninitialized, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wuninitialized") + AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-missing-field-initializers") + AX_CHECK_COMPILE_FLAG(-Wno-sign-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-sign-compare") + AX_CHECK_COMPILE_FLAG(-Wno-unused-const-variable, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-const-variable") + AX_CHECK_COMPILE_FLAG(-Wno-unused-parameter, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-parameter") + AX_CHECK_COMPILE_FLAG(-Wno-variadic-macros, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-variadic-macros") + AX_CHECK_COMPILE_FLAG(-Wparentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wparentheses") + AX_CHECK_COMPILE_FLAG(-Wpointer-bool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wpointer-bool-conversion") + AX_CHECK_COMPILE_FLAG(-Wsizeof-array-argument, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wsizeof-array-argument") + AX_CHECK_COMPILE_FLAG(-Wwrite-strings, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wwrite-strings") + AX_CHECK_COMPILE_FLAG(-fdiagnostics-show-option, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fdiagnostics-show-option") + AX_CHECK_COMPILE_FLAG(-fno-omit-frame-pointer, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-omit-frame-pointer") + AX_CHECK_COMPILE_FLAG(-fno-optimize-sibling-calls, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-optimize-sibling-calls") + AX_CHECK_COMPILE_FLAG(-fsanitize-address, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fsanitize-address") + AX_CHECK_COMPILE_FLAG(-fstack-protector, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fstack-protector") + + EXTRA_CFLAGS="$_MAINTAINER_CFLAGS" + CFLAGS="-g -O0 -Wall $CFLAGS" + CXXFLAGS="-g -O0 -Wall $CXXFLAGS" + fi - AC_CHECK_LIB(brotlienc, BrotliEncoderCreateInstance, [ - AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) - PHP_ADD_LIBRARY(brotlienc, 1, SWOOLE_SHARED_LIBADD) + if test "$PHP_SWOOLE_CURL" = "yes"; then + PKG_CHECK_MODULES([CURL], [libcurl >= 7.56.0]) + PHP_EVAL_LIBLINE($CURL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CURL_CFLAGS) + AC_DEFINE(SW_USE_CURL, 1, [do we enable cURL native client]) + fi + + if test "$PHP_SWOOLE_CORO_TIME" = "yes"; then + AC_DEFINE(SW_CORO_TIME, 1, [do we enable to calculate coroutine execution time]) + fi + + dnl pgsql begin + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + dnl TODO macros below can be reused to find curl things + dnl prepare pkg-config + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + AC_MSG_CHECKING(for libpq) + if test "x${LIBPQ_LIBS+set}" = "xset" || test "x${LIBPQ_CFLAGS+set}" = "xset"; then + AC_MSG_RESULT([using LIBPQ_CFLAGS and LIBPQ_LIBS]) + elif test -x "$PKG_CONFIG" ; then + dnl find pkg using pkg-config cli tool + libpq_pkg_config_path="$PHP_SWOOLE_PGSQL/lib/pkgconfig" + if test "xyes" = "x$PHP_SWOOLE_PGSQL" ; then + libpq_pkg_config_path=/lib/pkgconfig + fi + if test "x" != "x$PKG_CONFIG_PATH"; then + libpq_pkg_config_path="$libpq_pkg_config_path:$PKG_CONFIG_PATH" + fi + + libpq_version_full=`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --modversion libpq` + AC_MSG_RESULT(${libpq_version_full}) + LIBPQ_CFLAGS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --cflags libpq`" + LIBPQ_LIBS="`env PKG_CONFIG_PATH=${libpq_pkg_config_path} $PKG_CONFIG --libs libpq`" + fi + + _libpq_saved_cflags="$CFLAGS" + CFLAGS="$CFLAGS $LIBPQ_CFLAGS" + AC_CHECK_HEADER(libpq-fe.h, [], [ + dnl this is too long, wht so chaos? + cat >&2 <&2 </dev/null` + if test "$SWOOLE_PDO_OCI_TAIL1" = "a"; then + SWOOLE_PDO_OCI_TAIL1="tail -n1" + else + SWOOLE_PDO_OCI_TAIL1="tail -1" + fi + + AC_DEFUN([AC_PDO_OCI_VERSION],[ + AC_MSG_CHECKING([Oracle version]) + PDO_OCI_LCS_BASE=$PDO_OCI_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME + dnl Oracle 10g, 11g, 12c etc + PDO_OCI_LCS=`ls $PDO_OCI_LCS_BASE.*.1 2> /dev/null | $SWOOLE_PDO_OCI_TAIL1` + if test -f "$PDO_OCI_LCS"; then + dnl Oracle 10g, 11g 12c etc. The x.2 version libraries are named x.1 for + dnl drop in compatibility + PDO_OCI_VERSION=`echo $PDO_OCI_LCS | $SWOOLE_PDO_OCI_SED -e 's/.*\.\(.*\)\.1$/\1.1/'` + elif test -f $PDO_OCI_LCS_BASE.9.0; then + dnl There is no case for Oracle 9.2. Oracle 9.2 libraries have a 9.0 suffix + dnl for drop-in compatibility with Oracle 9.0 + PDO_OCI_VERSION=9.0 + else + AC_MSG_ERROR(Oracle libclntsh.$SHLIB_SUFFIX_NAME client library not found or its version is lower than 9) + fi + AC_MSG_RESULT($PDO_OCI_VERSION) + ]) + + AC_DEFUN([AC_PDO_OCI_CHECK_LIB_DIR],[ + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + ],[ + AC_MSG_RESULT([yes]) + TMP_PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + ]) + + AC_MSG_CHECKING([OCI8 libraries dir]) + if test -d "$PDO_OCI_DIR/lib" && test ! -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib" + elif test ! -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR="$PDO_OCI_DIR/lib32" + elif test -d "$PDO_OCI_DIR/lib" && test -d "$PDO_OCI_DIR/lib32"; then + PDO_OCI_LIB_DIR=$TMP_PDO_OCI_LIB_DIR + else + AC_MSG_ERROR([Oracle required OCI8 libraries not found]) + fi + AC_MSG_RESULT($PDO_OCI_LIB_DIR) ]) + PHP_ARG_WITH([swoole-oracle], + [whether to enable oracle build flags], + [AS_HELP_STRING([[--with-swoole-oracle[=DIR]]], + ["PDO: Oracle OCI support. DIR defaults to ${ORACLE_HOME}. Use + --with-swoole-oracle=instantclient,/path/to/instant/client/lib for an Oracle + Instant Client installation."])], [no], [no]) + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + AC_MSG_CHECKING([Oracle Install-Dir]) + if test "$PHP_SWOOLE_ORACLE" = "yes" || test -z "$PHP_SWOOLE_ORACLE"; then + PDO_OCI_DIR=$ORACLE_HOME + else + PDO_OCI_DIR=$PHP_SWOOLE_ORACLE + fi + AC_MSG_RESULT($PHP_SWOOLE_ORACLE) + + AC_MSG_CHECKING([if that is sane]) + if test -z "$PDO_OCI_DIR"; then + AC_MSG_ERROR([You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_HOME.]) + else + AC_MSG_RESULT([yes]) + fi + + if test "instantclient" = "`echo $PDO_OCI_DIR | cut -d, -f1`" ; then + AC_CHECK_SIZEOF([long]) + AC_MSG_CHECKING([if we're at 64-bit platform]) + AS_IF([test "$ac_cv_sizeof_long" -eq 4],[ + AC_MSG_RESULT([no]) + PDO_OCI_CLIENT_DIR="client" + ],[ + AC_MSG_RESULT([yes]) + PDO_OCI_CLIENT_DIR="client64" + ]) + + PDO_OCI_LIB_DIR="`echo $PDO_OCI_DIR | cut -d, -f2`" + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + + AC_MSG_CHECKING([for oci.h]) + dnl Header directory for Instant Client SDK RPM install + OCISDKRPMINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib/oracle/\(.*\)/\('${PDO_OCI_CLIENT_DIR}'\)/lib[/]*$!\1/include/oracle/\2/\3!'` + + dnl Header directory for manual installation + OCISDKMANINC=`echo "$PDO_OCI_LIB_DIR" | $SWOOLE_PDO_OCI_SED -e 's!^\(.*\)/lib[/]*$!\1/include!'` + + dnl Header directory for Instant Client SDK zip file install + OCISDKZIPINC=$PDO_OCI_LIB_DIR/sdk/include + + if test -f "$OCISDKRPMINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKRPMINC) + AC_MSG_RESULT($OCISDKRPMINC) + elif test -f "$OCISDKMANINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKMANINC) + AC_MSG_RESULT($OCISDKMANINC) + elif test -f "$OCISDKZIPINC/oci.h" ; then + PHP_ADD_INCLUDE($OCISDKZIPINC) + AC_MSG_RESULT($OCISDKZIPINC) + else + AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install]) + fi + else + AC_PDO_OCI_CHECK_LIB_DIR($PDO_OCI_DIR) + + if test -d "$PDO_OCI_DIR/rdbms/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/public" + fi + if test -d "$PDO_OCI_DIR/rdbms/demo"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/rdbms/demo) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/rdbms/demo" + fi + if test -d "$PDO_OCI_DIR/network/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/network/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/network/public" + fi + if test -d "$PDO_OCI_DIR/plsql/public"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/plsql/public) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/plsql/public" + fi + if test -d "$PDO_OCI_DIR/include"; then + PHP_ADD_INCLUDE($PDO_OCI_DIR/include) + PDO_OCI_INCLUDES="$PDO_OCI_INCLUDES -I$PDO_OCI_DIR/include" + fi + + if test -f "$PDO_OCI_LIB_DIR/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_LIB_DIR/sysliblist`, SWOOLE_SHARED_LIBADD) + elif test -f "$PDO_OCI_DIR/rdbms/lib/sysliblist"; then + PHP_EVAL_LIBLINE(`cat $PDO_OCI_DIR/rdbms/lib/sysliblist`, SWOOLE_SHARED_LIBADD) + fi + AC_PDO_OCI_VERSION($PDO_OCI_LIB_DIR) + fi + + case $PDO_OCI_VERSION in + 7.3|8.0|8.1) + AC_MSG_ERROR([Oracle client libraries < 9 are not supported]) + ;; + esac + + PHP_ADD_LIBRARY(clntsh, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBPATH($PDO_OCI_LIB_DIR, SWOOLE_SHARED_LIBADD) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvCreate, + [ + AC_DEFINE(HAVE_OCIENVCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + PHP_CHECK_LIBRARY(clntsh, OCIEnvNlsCreate, + [ + AC_DEFINE(HAVE_OCIENVNLSCREATE,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Scrollable cursors? + PHP_CHECK_LIBRARY(clntsh, OCIStmtFetch2, + [ + AC_DEFINE(HAVE_OCISTMTFETCH2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + dnl Can handle bytes vs. characters? + PHP_CHECK_LIBRARY(clntsh, OCILobRead2, + [ + AC_DEFINE(HAVE_OCILOBREAD2,1,[ ]) + ], [], [ + -L$PDO_OCI_LIB_DIR $SWOOLE_SHARED_LIBADD + ]) + + EXTRA_CFLAGS="$EXTRA_CFLAGS -I$pdo_cv_inc_path $PDO_OCI_INCLUDE" + PHP_CHECK_PDO_INCLUDES + AC_DEFINE_UNQUOTED(SWOOLE_PDO_OCI_CLIENT_VERSION, "$PDO_OCI_VERSION", [ ]) + AC_DEFINE(SW_USE_ORACLE, 1, [do we enable oracle coro support]) + fi + dnl SWOOLE_ORACLE stop + + dnl sqlite start + PHP_ARG_ENABLE([swoole-sqlite], + ["for sqlite 3 support for PDO"], + [AS_HELP_STRING([--enable-swoole-sqlite], + [PDO: sqlite 3 support.])], [no], [no]) + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + + if test "$PHP_PDO" = "no" && test "$ext_shared" = "no"; then + AC_MSG_ERROR([PDO is not enabled! Add --enable-pdo to your configure line.]) + fi + + PHP_CHECK_PDO_INCLUDES + + PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.7]) + + PHP_EVAL_INCLINE($SQLITE_CFLAGS) + PHP_EVAL_LIBLINE($SQLITE_LIBS, SWOOLE_SHARED_LIBADD) + AC_DEFINE(HAVE_SW_PDO_SQLITELIB, 1, [Define to 1 if you have the pdo_sqlite extension enabled.]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_close_v2, [ + AC_DEFINE(HAVE_SW_SQLITE3_CLOSE_V2, 1, [have sqlite3_close_v2]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + PHP_CHECK_LIBRARY(sqlite3, sqlite3_column_table_name, [ + AC_DEFINE(HAVE_SW_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name]) + ], [], [$SWOOLE_SHARED_LIBADD]) + + AC_DEFINE(SW_USE_SQLITE, 1, [do we enable sqlite coro support]) + fi + dnl sqlite stop + AC_CHECK_LIB(z, gzgets, [ AC_DEFINE(SW_HAVE_ZLIB, 1, [have zlib]) PHP_ADD_LIBRARY(z, 1, SWOOLE_SHARED_LIBADD) ]) + if test "$PHP_BROTLI_DIR" != "no"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_ADD_INCLUDE("${PHP_BROTLI_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") + elif test "$PHP_BROTLI" = "yes"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc]) + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec]) + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + elif test "$PHP_BROTLI" = "auto"; then + PKG_CHECK_MODULES([BROTLIENC], [libbrotlienc], [found_brotlienc=yes], [found_brotlienc=no]) + if test "$found_brotlienc" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIENC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIENC_CFLAGS) + fi + + PKG_CHECK_MODULES([BROTLIDEC], [libbrotlidec], [found_brotlidec=yes], [found_brotlidec=no]) + if test "$found_brotlidec" = "yes"; then + AC_DEFINE(SW_HAVE_BROTLI, 1, [have brotli]) + PHP_EVAL_LIBLINE($BROTLIDEC_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($BROTLIDEC_CFLAGS) + fi + fi + + if test "$PHP_ZSTD" = "yes"; then + PKG_CHECK_MODULES([ZSTD], [libzstd >= 1.4.0]) + AC_DEFINE(SW_HAVE_ZSTD, 1, [have zstd]) + PHP_EVAL_LIBLINE($ZSTD_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($ZSTD_CFLAGS) + fi + PHP_ADD_LIBRARY(pthread) PHP_SUBST(SWOOLE_SHARED_LIBADD) AC_ARG_ENABLE(debug, - [ --enable-debug, compile with debug symbols], + [ --enable-debug Compile with debug symbols], [PHP_DEBUG=$enableval], [PHP_DEBUG=0] ) @@ -307,17 +941,15 @@ if test "$PHP_SWOOLE" != "no"; then CFLAGS="$CFLAGS -fsanitize=address -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=address -fno-omit-frame-pointer" fi - - if test "$PHP_GCOV" != "no"; then - PHP_DEBUG=1 - CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" - CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" - fi if test "$PHP_TRACE_LOG" != "no"; then AC_DEFINE(SW_LOG_TRACE_OPEN, 1, [enable trace log]) fi + if test "$PHP_SWOOLE_THREAD" != "no"; then + AC_DEFINE(SW_THREAD, 1, [enable swoole thread support]) + fi + if test "$PHP_SOCKETS" = "yes"; then AC_MSG_CHECKING([for php_sockets.h]) @@ -339,252 +971,248 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_THREAD, 1, [enable thread support]) fi + if test "$PHP_CARES" = "yes"; then + PKG_CHECK_MODULES([CARES], [libcares]) + PHP_EVAL_LIBLINE($CARES_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CARES_CFLAGS) + AC_DEFINE(SW_USE_CARES, 1, [do we enable c-ares support]) + AC_DEFINE(HAVE_CARES, 1, [have c-ares]) + fi + AC_SWOOLE_CPU_AFFINITY AC_SWOOLE_HAVE_REUSEPORT AC_SWOOLE_HAVE_FUTEX AC_SWOOLE_HAVE_UCONTEXT AC_SWOOLE_HAVE_VALGRIND AC_SWOOLE_CHECK_SOCKETS + AC_SWOOLE_HAVE_BOOST_STACKTRACE AS_CASE([$host_os], [darwin*], [SW_OS="MAC"], [cygwin*], [SW_OS="CYGWIN"], [mingw*], [SW_OS="MINGW"], [linux*], [SW_OS="LINUX"], + [*bsd*], [SW_OS="BSD"], [] ) CFLAGS="-Wall -pthread $CFLAGS" LDFLAGS="$LDFLAGS -lpthread" - if test "$SW_OS" = "MAC"; then - AC_CHECK_LIB(c, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) + if test "$PHP_IOURING" = "yes" && test "$SW_OS" = "LINUX"; then + PKG_CHECK_MODULES([URING], [liburing >= 2.0]) + + AC_SWOOLE_HAVE_IOURING_STATX + AC_SWOOLE_HAVE_IOURING_FUTEX + + PHP_EVAL_LIBLINE($URING_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($URING_CFLAGS) + AC_DEFINE(SW_USE_IOURING, 1, [have io_uring]) + fi + + dnl Check should we link to librt + + if test "$SW_OS" = "LINUX"; then + GLIBC_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}') + AC_MSG_NOTICE([glibc version: $GLIBC_VERSION]) + + GLIBC_MAJOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 1) + GLIBC_MINOR_VERSION=$(getconf GNU_LIBC_VERSION | awk '{print $2}' | cut -d '.' -f 2) + + if test $GLIBC_MAJOR_VERSION -lt 2 || (test $GLIBC_MAJOR_VERSION -eq 2 && test $GLIBC_MINOR_VERSION -lt 17); then + OS_SHOULD_HAVE_LIBRT=1 + else + AC_MSG_NOTICE([link with -lrt (only for glibc version before 2.17)]) + OS_SHOULD_HAVE_LIBRT=0 + fi + elif test "$SW_OS" = "MAC"; then + OS_SHOULD_HAVE_LIBRT=0 else - AC_CHECK_LIB(rt, clock_gettime, AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [have clock_gettime])) + AS_CASE([$host_os], + [openbsd*], [OS_SHOULD_HAVE_LIBRT=0] + [OS_SHOULD_HAVE_LIBRT=1] + ) + fi + + if test "x$OS_SHOULD_HAVE_LIBRT" = "x1"; then + AC_MSG_NOTICE([Librt is required on $host_os.]) + dnl Check for the existence of librt + AC_CHECK_LIB([rt], [clock_gettime], [], [ + AC_MSG_ERROR([We have to link to librt on your os, but librt not found.]) + ]) PHP_ADD_LIBRARY(rt, 1, SWOOLE_SHARED_LIBADD) + else + AC_MSG_NOTICE([$host_os doesn't have librt -- don't link to librt.]) fi + if test "$SW_OS" = "LINUX"; then LDFLAGS="$LDFLAGS -z now" fi if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then if test "$PHP_OPENSSL_DIR" != "no"; then - AC_DEFINE(HAVE_OPENSSL, 1, [have openssl]) PHP_ADD_INCLUDE("${PHP_OPENSSL_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(ssl, "${PHP_OPENSSL_DIR}/${PHP_LIBDIR}") + + PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) + PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) else - AC_CHECK_LIB(ssl, SSL_connect, AC_DEFINE(HAVE_OPENSSL, 1, [have openssl])) + PKG_CHECK_MODULES([SSL], [libssl]) + PHP_EVAL_LIBLINE($SSL_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($SSL_CFLAGS) + + PKG_CHECK_MODULES([CRYPTO], [libcrypto]) + PHP_EVAL_LIBLINE($CRYPTO_LIBS, SWOOLE_SHARED_LIBADD) + PHP_EVAL_INCLINE($CRYPTO_CFLAGS) fi AC_DEFINE(SW_USE_OPENSSL, 1, [enable openssl support]) - PHP_ADD_LIBRARY(ssl, 1, SWOOLE_SHARED_LIBADD) - PHP_ADD_LIBRARY(crypto, 1, SWOOLE_SHARED_LIBADD) fi - if test "$PHP_JEMALLOC_DIR" != "no"; then - AC_DEFINE(SW_USE_JEMALLOC, 1, [use jemalloc]) - PHP_ADD_INCLUDE("${PHP_JEMALLOC_DIR}/include") - PHP_ADD_LIBRARY_WITH_PATH(jemalloc, "${PHP_JEMALLOC_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY(jemalloc, 1, SWOOLE_SHARED_LIBADD) + if test "$PHP_NGHTTP2_DIR" != "no"; then + PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") + PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") + PHP_ADD_LIBRARY(nghttp2, 1, SWOOLE_SHARED_LIBADD) fi PHP_ADD_LIBRARY(pthread, 1, SWOOLE_SHARED_LIBADD) - if test "$PHP_HTTP2" = "yes" || test "$PHP_NGHTTP2_DIR" != "no"; then - AC_DEFINE(SW_USE_HTTP2, 1, [enable HTTP2 support]) - fi - if test "$PHP_MYSQLND" = "yes"; then PHP_ADD_EXTENSION_DEP(mysqli, mysqlnd) AC_DEFINE(SW_USE_MYSQLND, 1, [use mysqlnd]) fi - swoole_source_file=" \ - php_swoole_cxx.cc \ - src/core/array.c \ - src/core/base.c \ - src/core/channel.c \ - src/core/error.cc \ - src/core/hashmap.c \ - src/core/heap.c \ - src/core/list.c \ - src/core/log.c \ - src/core/rbtree.c \ - src/core/ring_queue.c \ - src/core/socket.c \ - src/core/string.c \ - src/coroutine/base.cc \ - src/coroutine/channel.cc \ - src/coroutine/context.cc \ - src/coroutine/file_lock.cc \ - src/coroutine/hook.cc \ - src/coroutine/socket.cc \ - src/coroutine/system.cc \ - src/coroutine/thread_context.cc \ - src/coroutine/ucontext.cc \ - src/lock/atomic.c \ - src/lock/cond.c \ - src/lock/file_lock.c \ - src/lock/mutex.c \ - src/lock/rw_lock.c \ - src/lock/semaphore.c \ - src/lock/spin_lock.c \ - src/memory/buffer.c \ - src/memory/fixed_pool.c \ - src/memory/global_memory.c \ - src/memory/malloc.c \ - src/memory/ring_buffer.c \ - src/memory/shared_memory.c \ - src/memory/table.c \ - src/network/client.c \ - src/network/connection.c \ - src/network/dns.cc \ - src/network/process_pool.c \ - src/network/stream.c \ - src/network/thread_pool.c \ - src/network/timer.c \ - src/os/async_thread.cc \ - src/os/base.c \ - src/os/msg_queue.c \ - src/os/sendfile.c \ - src/os/signal.c \ - src/os/timer.c \ - src/os/wait.cc \ - src/pipe/base.c \ - src/pipe/eventfd.c \ - src/pipe/unix_socket.c \ - src/protocol/base.cc \ - src/protocol/base64.c \ - src/protocol/http.c \ - src/protocol/http2.c \ - src/protocol/mime_types.cc \ - src/protocol/mqtt.c \ - src/protocol/redis.c \ - src/protocol/sha1.c \ - src/protocol/socks5.c \ - src/protocol/ssl.c \ - src/protocol/websocket.c \ - src/reactor/base.cc \ - src/reactor/epoll.cc \ - src/reactor/kqueue.c \ - src/reactor/poll.c \ - src/reactor/select.c \ - src/server/base.c \ - src/server/manager.cc \ - src/server/master.cc \ - src/server/port.cc \ - src/server/process.cc \ - src/server/reactor_process.cc \ - src/server/reactor_thread.cc \ - src/server/static_handler.cc \ - src/server/task_worker.cc \ - src/server/worker.cc \ - src/wrapper/event.cc \ - src/wrapper/server.cc \ - src/wrapper/timer.cc \ - swoole.cc \ - swoole_async_coro.cc \ - swoole_atomic.cc \ - swoole_buffer.c \ - swoole_channel_coro.cc \ - swoole_client.cc \ - swoole_client_coro.cc \ - swoole_coroutine.cc \ - swoole_coroutine_scheduler.cc \ - swoole_coroutine_system.cc \ - swoole_event.cc \ - swoole_http2_client_coro.cc \ - swoole_http2_server.cc \ - swoole_http_client_coro.cc \ - swoole_http_request.cc \ - swoole_http_response.cc \ - swoole_http_server.cc \ - swoole_http_server_coro.cc \ - swoole_lock.cc \ - swoole_mysql_coro.cc \ - swoole_mysql_proto.cc \ - swoole_process.cc \ - swoole_process_pool.cc \ - swoole_redis_coro.cc \ - swoole_redis_server.cc \ - swoole_runtime.cc \ - swoole_server.cc \ - swoole_server_port.cc \ - swoole_socket_coro.cc \ - swoole_table.cc \ - swoole_timer.cc \ - swoole_websocket_server.cc" + AC_MSG_CHECKING([for sources]) + if test -f "$abs_srcdir/ext-src/php_swoole.cc"; then + swoole_source_dir=$abs_srcdir + elif test -f "ext-src/php_swoole.cc"; then + swoole_source_dir=$(pwd) + else + swoole_source_dir="ext/swoole" + fi + AC_MSG_RESULT([$swoole_source_dir]) + + ext_src_files=$(cd $swoole_source_dir && find ext-src/ -name *.cc) + lib_src_files=$(cd $swoole_source_dir && find src/ -name *.cc) + + swoole_source_file="${ext_src_files} ${lib_src_files}" swoole_source_file="$swoole_source_file \ + thirdparty/php/curl/interface.cc \ + thirdparty/php/curl/multi.cc \ + thirdparty/php84/curl/interface.cc \ + thirdparty/php84/curl/multi.cc \ thirdparty/php/sockets/multicast.cc \ thirdparty/php/sockets/sendrecvmsg.cc \ thirdparty/php/sockets/conversions.cc \ thirdparty/php/sockets/sockaddr_conv.cc \ + thirdparty/php/standard/var_decoder.cc \ thirdparty/php/standard/proc_open.cc" swoole_source_file="$swoole_source_file \ thirdparty/swoole_http_parser.c \ - thirdparty/multipart_parser.c" + thirdparty/multipart_parser.c" - swoole_source_file="$swoole_source_file \ - thirdparty/hiredis/async.c \ - thirdparty/hiredis/hiredis.c \ - thirdparty/hiredis/net.c \ - thirdparty/hiredis/read.c \ - thirdparty/hiredis/sds.c" + if test "$PHP_NGHTTP2_DIR" = "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/nghttp2/nghttp2_hd.c \ + thirdparty/nghttp2/nghttp2_rcbuf.c \ + thirdparty/nghttp2/nghttp2_helper.c \ + thirdparty/nghttp2/nghttp2_buf.c \ + thirdparty/nghttp2/nghttp2_mem.c \ + thirdparty/nghttp2/nghttp2_hd_huffman.c \ + thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + fi - swoole_source_file="$swoole_source_file \ - thirdparty/nghttp2/nghttp2_hd.c \ - thirdparty/nghttp2/nghttp2_rcbuf.c \ - thirdparty/nghttp2/nghttp2_helper.c \ - thirdparty/nghttp2/nghttp2_buf.c \ - thirdparty/nghttp2/nghttp2_mem.c \ - thirdparty/nghttp2/nghttp2_hd_huffman.c \ - thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + if test -z "$PHP_CONFIG"; then + AC_MSG_ERROR([php-config not found]) + fi + + SW_PHP_VERSION=`$PHP_CONFIG --version` + SW_PHP_VERSION_ID=`echo "${SW_PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` + + if test "$SW_PHP_VERSION_ID" = "82"; then + SW_PHP_THIRDPARTY_DIR="thirdparty/php81" + else + SW_PHP_THIRDPARTY_DIR="thirdparty/php${SW_PHP_VERSION_ID}" + fi + + AC_MSG_NOTICE([php version: $SW_PHP_VERSION, version_id: $SW_PHP_VERSION_ID, thirdparty_dir: $SW_PHP_THIRDPARTY_DIR]) + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + fi + fi + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" + fi + + if test "$PHP_SWOOLE_ODBC" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" + fi + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + fi + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" AS_CASE([$host_cpu], [x86_64*], [SW_CPU="x86_64"], + [amd64*], [SW_CPU="x86_64"], [x86*], [SW_CPU="x86"], [i?86*], [SW_CPU="x86"], - [arm*], [SW_CPU="arm"], - [aarch64*], [SW_CPU="arm64"], [arm64*], [SW_CPU="arm64"], + [aarch64*], [SW_CPU="arm64"], + [arm*], [SW_CPU="arm32"], + [mips64*], [SW_CPU="mips64"], + [mips*], [SW_CPU="mips32"], + [riscv64*], [SW_CPU="riscv64"], + [loongarch64*], [SW_CPU="loongarch64"], [ SW_USE_ASM_CONTEXT="no" ] ) if test "$SW_OS" = "MAC"; then - if test "$SW_CPU" = "arm"; then - SW_CONTEXT_ASM_FILE="arm_aapcs_macho_gas.S" - elif test "$SW_CPU" = "arm64"; then - SW_CONTEXT_ASM_FILE="arm64_aapcs_macho_gas.S" - else - SW_CONTEXT_ASM_FILE="combined_sysv_macho_gas.S" - fi + SW_CONTEXT_ASM_FILE="combined_sysv_macho_gas.S" elif test "$SW_CPU" = "x86_64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="x86_64_sysv_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "x86"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="i386_sysv_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi - elif test "$SW_CPU" = "arm"; then - if test "$SW_OS" = "LINUX"; then + elif test "$SW_CPU" = "arm32"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="arm_aapcs_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "arm64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="arm64_aapcs_elf_gas.S" else SW_USE_ASM_CONTEXT="no" @@ -596,21 +1224,44 @@ if test "$PHP_SWOOLE" != "no"; then SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "ppc64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="ppc64_sysv_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "mips64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="mips64_n64_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi elif test "$SW_CPU" = "mips32"; then if test "$SW_OS" = "LINUX"; then SW_CONTEXT_ASM_FILE="mips32_o32_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "riscv64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="riscv64_sysv_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi + elif test "$SW_CPU" = "loongarch64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="loongarch64_sysv_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi else SW_USE_ASM_CONTEXT="no" fi + if test "$PHP_THREAD_CONTEXT" != "no"; then + AC_DEFINE(SW_USE_THREAD_CONTEXT, 1, [do we enable thread context]) + SW_USE_ASM_CONTEXT="no" + fi + if test "$SW_USE_ASM_CONTEXT" = "yes"; then swoole_source_file="$swoole_source_file \ ${SW_ASM_DIR}make_${SW_CONTEXT_ASM_FILE} \ @@ -618,13 +1269,29 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context]) fi - PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,,, cxx) + EXTRA_CFLAGS="$EXTRA_CFLAGS -DENABLE_PHP_SWOOLE" + + PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,, "$EXTRA_CFLAGS", cxx) PHP_ADD_INCLUDE([$ext_srcdir]) PHP_ADD_INCLUDE([$ext_srcdir/include]) - PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) + PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) + PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) + + AC_MSG_CHECKING([swoole coverage]) + if test "$PHP_SWOOLE_COVERAGE" != "no"; then + AC_MSG_RESULT([enabled]) + + PHP_ADD_MAKEFILE_FRAGMENT + else + AC_MSG_RESULT([disabled]) + fi - PHP_INSTALL_HEADERS([ext/swoole], [*.h config.h include/*.h thirdparty/*.h thirdparty/hiredis/*.h]) + PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.h php_swoole.h \ + include/*.h \ + stubs/*.h \ + thirdparty/*.h \ + thirdparty/nghttp2/*.h]) PHP_REQUIRE_CXX() @@ -636,10 +1303,14 @@ if test "$PHP_SWOOLE" != "no"; then CXXFLAGS="$CXXFLAGS -std=c++11" fi + if test "$SW_CPU" = "arm"; then + PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD) + fi + + PHP_ADD_BUILD_DIR($ext_builddir/ext-src) PHP_ADD_BUILD_DIR($ext_builddir/src/core) PHP_ADD_BUILD_DIR($ext_builddir/src/memory) PHP_ADD_BUILD_DIR($ext_builddir/src/reactor) - PHP_ADD_BUILD_DIR($ext_builddir/src/pipe) PHP_ADD_BUILD_DIR($ext_builddir/src/lock) PHP_ADD_BUILD_DIR($ext_builddir/src/os) PHP_ADD_BUILD_DIR($ext_builddir/src/network) @@ -649,8 +1320,31 @@ if test "$PHP_SWOOLE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl) + if test "$PHP_NGHTTP2_DIR" = "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) + fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql) + fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_odbc) + fi + if test "$PHP_SWOOLE_ORACLE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_oci) + fi + if test "$PHP_SWOOLE_SQLITE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_sqlite) + fi fi diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index 6d2b4645775..807f5337cf6 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,14 +1,74 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 2.8.12) + project(core_tests) + #set(CMAKE_BUILD_TYPE Released) set(CMAKE_CXX_STANDARD 11) -file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") + +file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) -#add_definitions(-DSW_CO_MT) add_definitions(-DHAVE_CONFIG_H) -link_directories($ENV{SWOOLE_DIR}/lib) -include_directories(./include ./ $ENV{SWOOLE_DIR}/ $ENV{SWOOLE_DIR}/include/ BEFORE) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include /usr/local/include) +set(core_tests_libraries) +set(core_tests_link_directories /usr/local/lib) + +list(APPEND core_tests_libraries pthread gtest gtest_main) + +# find GTest +find_package(GTest REQUIRED) +if (!${GTEST_FOUND}) + message(FATAL_ERROR "Not found GTest") +endif() +message(STATUS "Found GTest") + +list(APPEND core_tests_libraries ${GTEST_BOTH_LIBRARIES}) +list(APPEND core_tests_includes ${GTEST_INCLUDE_DIRS}) + +if (DEFINED swoole_dir) + add_definitions(-DHAVE_SWOOLE_DIR) + list(APPEND core_tests_includes ${swoole_dir} ${swoole_dir}/include) + list(APPEND core_tests_link_directories ${swoole_dir}/lib) +else() + list(APPEND core_tests_includes /usr/local/include/swoole /usr/include/swoole) +endif() + +list(APPEND core_tests_libraries swoole) + +# find OpenSSL +if (DEFINED openssl_dir) + list(APPEND core_tests_includes ${openssl_dir}/include) + list(APPEND core_tests_link_directories ${openssl_dir}/lib) + list(APPEND core_tests_libraries ssl crypto) +else() + find_package(OpenSSL) + if (${OPENSSL_FOUND}) + message(STATUS "Found OpenSSL") + list(APPEND core_tests_includes ${OPENSSL_INCLUDE_DIR}) + list(APPEND core_tests_libraries ${OPENSSL_LIBRARIES}) + else() + message(STATUS "Not found OpenSSL") + endif() +endif() + +if (DEFINED brotli_dir) + list(APPEND core_tests_includes ${brotli_dir}/include) + list(APPEND core_tests_link_directories ${brotli_dir}/lib) +endif() + +if (DEFINED enable_asan) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") +endif() + +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + +# should execute before the add_executable command +link_directories(${core_tests_link_directories}) + set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(core_tests ${SOURCE_FILES}) -target_link_libraries(core_tests gtest gtest_main pthread swoole) +target_include_directories(core_tests PRIVATE ${core_tests_includes}) +target_link_libraries(core_tests ${core_tests_libraries}) diff --git a/core-tests/README-CN.md b/core-tests/README-CN.md new file mode 100644 index 00000000000..46d12ec9a0c --- /dev/null +++ b/core-tests/README-CN.md @@ -0,0 +1,50 @@ +**Swoole 核心单元测试** +=========== +## **1. 编译googletest** +swoole单元测试依赖于googletest,因此第一步我们需要编译googletest。 + +这里要求gcc和和g++编译器的版本要大于4.8.5。 +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +如果你已经安装了更高版本的gcc和g++编译器,但是在执行cmake . 的过程中还是发现如下输出。 +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +请执行下面的代码并重试。 +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. 编译swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. 拉取基础镜像** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. 运行swoole单元测试** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/README.md b/core-tests/README.md new file mode 100644 index 00000000000..2f3c6644b9b --- /dev/null +++ b/core-tests/README.md @@ -0,0 +1,54 @@ +**Swoole core unit testing** +=========== + +> All test files have the `.cpp` extension + +## **1. Compiling googletest** +Since swoole core unit testing rely on googletest, we need compile googletest at first. + +gcc compiler version > 4.8.5 and gcc-c++ compiler version > 4.8.5. +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +The output still contains the following messages after compiling higher version gcc and gcc-c++. + +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +Please execute following code and retry. +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. How to compile swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. Pulling images for testing** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. Run swoole core testing** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/deps/llhttp/include/llhttp.h b/core-tests/deps/llhttp/include/llhttp.h new file mode 100644 index 00000000000..024392ace37 --- /dev/null +++ b/core-tests/deps/llhttp/include/llhttp.h @@ -0,0 +1,379 @@ +#ifndef INCLUDE_LLHTTP_H_ +#define INCLUDE_LLHTTP_H_ + +#define LLHTTP_VERSION_MAJOR 2 +#define LLHTTP_VERSION_MINOR 0 +#define LLHTTP_VERSION_PATCH 5 + +#ifndef INCLUDE_LLHTTP_ITSELF_H_ +#define INCLUDE_LLHTTP_ITSELF_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct llhttp__internal_s llhttp__internal_t; +struct llhttp__internal_s { + int32_t _index; + void* _span_pos0; + void* _span_cb0; + int32_t error; + const char* reason; + const char* error_pos; + void* data; + void* _current; + uint64_t content_length; + uint8_t type; + uint8_t method; + uint8_t http_major; + uint8_t http_minor; + uint8_t header_state; + uint16_t flags; + uint8_t upgrade; + uint16_t status_code; + uint8_t finish; + void* settings; +}; + +int llhttp__internal_init(llhttp__internal_t* s); +int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_ITSELF_H_ */ + +#ifndef LLLLHTTP_C_HEADERS_ +#define LLLLHTTP_C_HEADERS_ +#ifdef __cplusplus +extern "C" { +#endif + +enum llhttp_errno { + HPE_OK = 0, + HPE_INTERNAL = 1, + HPE_STRICT = 2, + HPE_LF_EXPECTED = 3, + HPE_UNEXPECTED_CONTENT_LENGTH = 4, + HPE_CLOSED_CONNECTION = 5, + HPE_INVALID_METHOD = 6, + HPE_INVALID_URL = 7, + HPE_INVALID_CONSTANT = 8, + HPE_INVALID_VERSION = 9, + HPE_INVALID_HEADER_TOKEN = 10, + HPE_INVALID_CONTENT_LENGTH = 11, + HPE_INVALID_CHUNK_SIZE = 12, + HPE_INVALID_STATUS = 13, + HPE_INVALID_EOF_STATE = 14, + HPE_INVALID_TRANSFER_ENCODING = 15, + HPE_CB_MESSAGE_BEGIN = 16, + HPE_CB_HEADERS_COMPLETE = 17, + HPE_CB_MESSAGE_COMPLETE = 18, + HPE_CB_CHUNK_HEADER = 19, + HPE_CB_CHUNK_COMPLETE = 20, + HPE_PAUSED = 21, + HPE_PAUSED_UPGRADE = 22, + HPE_USER = 23 +}; +typedef enum llhttp_errno llhttp_errno_t; + +enum llhttp_flags { + F_CONNECTION_KEEP_ALIVE = 0x1, + F_CONNECTION_CLOSE = 0x2, + F_CONNECTION_UPGRADE = 0x4, + F_CHUNKED = 0x8, + F_UPGRADE = 0x10, + F_CONTENT_LENGTH = 0x20, + F_SKIPBODY = 0x40, + F_TRAILING = 0x80, + F_LENIENT = 0x100, + F_TRANSFER_ENCODING = 0x200 +}; +typedef enum llhttp_flags llhttp_flags_t; + +enum llhttp_type { + HTTP_BOTH = 0, + HTTP_REQUEST = 1, + HTTP_RESPONSE = 2 +}; +typedef enum llhttp_type llhttp_type_t; + +enum llhttp_finish { + HTTP_FINISH_SAFE = 0, + HTTP_FINISH_SAFE_WITH_CB = 1, + HTTP_FINISH_UNSAFE = 2 +}; +typedef enum llhttp_finish llhttp_finish_t; + +enum llhttp_method { + HTTP_DELETE = 0, + HTTP_GET = 1, + HTTP_HEAD = 2, + HTTP_POST = 3, + HTTP_PUT = 4, + HTTP_CONNECT = 5, + HTTP_OPTIONS = 6, + HTTP_TRACE = 7, + HTTP_COPY = 8, + HTTP_LOCK = 9, + HTTP_MKCOL = 10, + HTTP_MOVE = 11, + HTTP_PROPFIND = 12, + HTTP_PROPPATCH = 13, + HTTP_SEARCH = 14, + HTTP_UNLOCK = 15, + HTTP_BIND = 16, + HTTP_REBIND = 17, + HTTP_UNBIND = 18, + HTTP_ACL = 19, + HTTP_REPORT = 20, + HTTP_MKACTIVITY = 21, + HTTP_CHECKOUT = 22, + HTTP_MERGE = 23, + HTTP_MSEARCH = 24, + HTTP_NOTIFY = 25, + HTTP_SUBSCRIBE = 26, + HTTP_UNSUBSCRIBE = 27, + HTTP_PATCH = 28, + HTTP_PURGE = 29, + HTTP_MKCALENDAR = 30, + HTTP_LINK = 31, + HTTP_UNLINK = 32, + HTTP_SOURCE = 33, + HTTP_PRI = 34 +}; +typedef enum llhttp_method llhttp_method_t; + +#define HTTP_ERRNO_MAP(XX) \ + XX(0, OK, OK) \ + XX(1, INTERNAL, INTERNAL) \ + XX(2, STRICT, STRICT) \ + XX(3, LF_EXPECTED, LF_EXPECTED) \ + XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ + XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ + XX(6, INVALID_METHOD, INVALID_METHOD) \ + XX(7, INVALID_URL, INVALID_URL) \ + XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ + XX(9, INVALID_VERSION, INVALID_VERSION) \ + XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ + XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ + XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ + XX(13, INVALID_STATUS, INVALID_STATUS) \ + XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ + XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ + XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ + XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ + XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ + XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ + XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ + XX(21, PAUSED, PAUSED) \ + XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ + XX(23, USER, USER) \ + + +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(34, PRI, PRI) \ + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* LLLLHTTP_C_HEADERS_ */ + +#ifndef INCLUDE_LLHTTP_API_H_ +#define INCLUDE_LLHTTP_API_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef llhttp__internal_t llhttp_t; +typedef struct llhttp_settings_s llhttp_settings_t; + +typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); +typedef int (*llhttp_cb)(llhttp_t*); + +struct llhttp_settings_s { + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_begin; + + llhttp_data_cb on_url; + llhttp_data_cb on_status; + llhttp_data_cb on_header_field; + llhttp_data_cb on_header_value; + + /* Possible return values: + * 0 - Proceed normally + * 1 - Assume that request/response has no body, and proceed to parsing the + * next message + * 2 - Assume absence of body (as above) and make `llhttp_execute()` return + * `HPE_PAUSED_UPGRADE` + * -1 - Error + * `HPE_PAUSED` + */ + llhttp_cb on_headers_complete; + + llhttp_data_cb on_body; + + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_complete; + + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ + llhttp_cb on_chunk_header; + llhttp_cb on_chunk_complete; +}; + +/* Initialize the parser with specific type and user settings */ +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings); + +/* Initialize the settings object */ +void llhttp_settings_init(llhttp_settings_t* settings); + +/* Parse full or partial request/response, invoking user callbacks along the + * way. + * + * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing + * interrupts, and such errno is returned from `llhttp_execute()`. If + * `HPE_PAUSED` was used as a errno, the execution can be resumed with + * `llhttp_resume()` call. + * + * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` + * is returned after fully parsing the request/response. If the user wishes to + * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. + * + * NOTE: if this function ever returns a non-pause type error, it will continue + * to return the same error upon each successive call up until `llhttp_init()` + * is called. + */ +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); + +/* This method should be called when the other side has no further bytes to + * send (e.g. shutdown of readable side of the TCP connection.) + * + * Requests without `Content-Length` and other messages might require treating + * all incoming bytes as the part of the body, up to the last byte of the + * connection. This method will invoke `on_message_complete()` callback if the + * request was terminated safely. Otherwise a error code would be returned. + */ +llhttp_errno_t llhttp_finish(llhttp_t* parser); + +/* Returns `1` if the incoming message is parsed until the last byte, and has + * to be completed by calling `llhttp_finish()` on EOF + */ +int llhttp_message_needs_eof(const llhttp_t* parser); + +/* Returns `1` if there might be any other messages following the last that was + * successfully parsed. + */ +int llhttp_should_keep_alive(const llhttp_t* parser); + +/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set + * appropriate error reason. + * + * Important: do not call this from user callbacks! User callbacks must return + * `HPE_PAUSED` if pausing is required. + */ +void llhttp_pause(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. + */ +void llhttp_resume(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` + */ +void llhttp_resume_after_upgrade(llhttp_t* parser); + +/* Returns the latest return error */ +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); + +/* Returns the verbal explanation of the latest returned error. + * + * Note: User callback should set error reason when returning the error. See + * `llhttp_set_error_reason()` for details. + */ +const char* llhttp_get_error_reason(const llhttp_t* parser); + +/* Assign verbal description to the returned error. Must be called in user + * callbacks right before returning the errno. + * + * Note: `HPE_USER` error code might be useful in user callbacks. + */ +void llhttp_set_error_reason(llhttp_t* parser, const char* reason); + +/* Returns the pointer to the last parsed byte before the returned error. The + * pointer is relative to the `data` argument of `llhttp_execute()`. + * + * Note: this method might be useful for counting the number of parsed bytes. + */ +const char* llhttp_get_error_pos(const llhttp_t* parser); + +/* Returns textual name of error code */ +const char* llhttp_errno_name(llhttp_errno_t err); + +/* Returns textual name of HTTP method */ +const char* llhttp_method_name(llhttp_method_t method); + + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **(USE AT YOUR OWN RISK)** + */ +void llhttp_set_lenient(llhttp_t* parser, int enabled); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_API_H_ */ + +#endif /* INCLUDE_LLHTTP_H_ */ diff --git a/core-tests/deps/llhttp/src/api.c b/core-tests/deps/llhttp/src/api.c new file mode 100644 index 00000000000..6f7246546df --- /dev/null +++ b/core-tests/deps/llhttp/src/api.c @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "llhttp.h" + +#define CALLBACK_MAYBE(PARSER, NAME, ...) \ + do { \ + llhttp_settings_t* settings; \ + settings = (llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME(__VA_ARGS__); \ + } while (0) + +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings) { + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; +} + + +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { + return llhttp__internal_execute(parser, data, data + len); +} + + +void llhttp_settings_init(llhttp_settings_t* settings) { + memset(settings, 0, sizeof(*settings)); +} + + +llhttp_errno_t llhttp_finish(llhttp_t* parser) { + int err; + + /* We're in an error state. Don't bother doing anything. */ + if (parser->error != 0) { + return 0; + } + + switch (parser->finish) { + case HTTP_FINISH_SAFE_WITH_CB: + CALLBACK_MAYBE(parser, on_message_complete, parser); + if (err != HPE_OK) return err; + + /* FALLTHROUGH */ + case HTTP_FINISH_SAFE: + return HPE_OK; + case HTTP_FINISH_UNSAFE: + parser->reason = "Invalid EOF state"; + return HPE_INVALID_EOF_STATE; + default: + abort(); + } +} + + +void llhttp_pause(llhttp_t* parser) { + if (parser->error != HPE_OK) { + return; + } + + parser->error = HPE_PAUSED; + parser->reason = "Paused"; +} + + +void llhttp_resume(llhttp_t* parser) { + if (parser->error != HPE_PAUSED) { + return; + } + + parser->error = 0; +} + + +void llhttp_resume_after_upgrade(llhttp_t* parser) { + if (parser->error != HPE_PAUSED_UPGRADE) { + return; + } + + parser->error = 0; +} + + +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { + return parser->error; +} + + +const char* llhttp_get_error_reason(const llhttp_t* parser) { + return parser->reason; +} + + +void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { + parser->reason = reason; +} + + +const char* llhttp_get_error_pos(const llhttp_t* parser) { + return parser->error_pos; +} + + +const char* llhttp_errno_name(llhttp_errno_t err) { +#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; + switch (err) { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + default: abort(); + } +#undef HTTP_ERRNO_GEN +} + + +const char* llhttp_method_name(llhttp_method_t method) { +#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; + switch (method) { + HTTP_METHOD_MAP(HTTP_METHOD_GEN) + default: abort(); + } +#undef HTTP_METHOD_GEN +} + + +void llhttp_set_lenient(llhttp_t* parser, int enabled) { + if (enabled) { + parser->flags |= F_LENIENT; + } else { + parser->flags &= ~F_LENIENT; + } +} + + +/* Callbacks */ + + +int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_begin, s); + return err; +} + + +int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_url, s, p, endp - p); + return err; +} + + +int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_status, s, p, endp - p); + return err; +} + + +int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_field, s, p, endp - p); + return err; +} + + +int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_value, s, p, endp - p); + return err; +} + + +int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_headers_complete, s); + return err; +} + + +int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_complete, s); + return err; +} + + +int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_body, s, p, endp - p); + return err; +} + + +int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_header, s); + return err; +} + + +int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_complete, s); + return err; +} + + +/* Private */ + + +void llhttp__debug(llhttp_t* s, const char* p, const char* endp, + const char* msg) { + if (p == endp) { + fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, + s->flags, msg); + } else { + fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, + s->type, s->flags, *p, msg); + } +} diff --git a/core-tests/deps/llhttp/src/http.c b/core-tests/deps/llhttp/src/http.c new file mode 100644 index 00000000000..6e4906d840b --- /dev/null +++ b/core-tests/deps/llhttp/src/http.c @@ -0,0 +1,150 @@ +#include +#ifndef LLHTTP__TEST +# include "llhttp.h" +#else +# define llhttp_t llparse_t +#endif /* */ + +int llhttp_message_needs_eof(const llhttp_t* parser); +int llhttp_should_keep_alive(const llhttp_t* parser); + +int llhttp__before_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + return 0; +} + + +/* Return values: + * 0 - No body, `restart`, message_complete + * 1 - CONNECT request, `restart`, message_complete, and pause + * 2 - chunk_size_start + * 3 - body_identity + * 4 - body_identity_eof + * 5 - invalid transfer-encoding for request + */ +int llhttp__after_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + int hasBody; + + hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + return 1; + } + + if (parser->flags & F_SKIPBODY) { + return 0; + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header, prepare for a chunk */ + return 2; + } else if (parser->flags & F_TRANSFER_ENCODING) { + if (parser->type == HTTP_REQUEST && (parser->flags & F_LENIENT) == 0) { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field + * is present in a request and the chunked transfer coding is not + * the final encoding, the message body length cannot be determined + * reliably; the server MUST respond with the 400 (Bad Request) + * status code and then close the connection. + */ + return 5; + } else { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field is present in a response and + * the chunked transfer coding is not the final encoding, the + * message body length is determined by reading the connection until + * it is closed by the server. + */ + return 4; + } + } else { + if (!(parser->flags & F_CONTENT_LENGTH)) { + if (!llhttp_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + return 0; + } else { + /* Read body until EOF */ + return 4; + } + } else if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + return 0; + } else { + /* Content-Length header given and non-zero */ + return 3; + } + } +} + + +int llhttp__after_message_complete(llhttp_t* parser, const char* p, + const char* endp) { + int should_keep_alive; + + should_keep_alive = llhttp_should_keep_alive(parser); + parser->finish = HTTP_FINISH_SAFE; + + /* Keep `F_LENIENT` flag between messages, but reset every other flag */ + parser->flags &= F_LENIENT; + + /* NOTE: this is ignored in loose parsing mode */ + return should_keep_alive; +} + + +int llhttp_message_needs_eof(const llhttp_t* parser) { + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ + return 0; + } + + /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ + if ((parser->flags & F_TRANSFER_ENCODING) && + (parser->flags & F_CHUNKED) == 0) { + return 1; + } + + if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { + return 0; + } + + return 1; +} + + +int llhttp_should_keep_alive(const llhttp_t* parser) { + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !llhttp_message_needs_eof(parser); +} diff --git a/core-tests/deps/llhttp/src/llhttp.c b/core-tests/deps/llhttp/src/llhttp.c new file mode 100644 index 00000000000..e146d2591ba --- /dev/null +++ b/core-tests/deps/llhttp/src/llhttp.c @@ -0,0 +1,6528 @@ +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + +#include "llhttp.h" + +typedef int (*llhttp__internal__span_cb)( + llhttp__internal_t*, const char*, const char*); + +static const unsigned char llparse_blob0[] = { + 'C', 'L' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob1[] = { + 0x9, 0x9, 0xc, 0xc, '!', '"', '$', '>', '@', '~', 0x80, + 0xff, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob2[] = { + 'o', 'n' +}; +static const unsigned char llparse_blob3[] = { + 'e', 'c', 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob4[] = { + 'l', 'o', 's', 'e' +}; +static const unsigned char llparse_blob5[] = { + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' +}; +static const unsigned char llparse_blob6[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob7[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob8[] = { + 'c', 'h', 'u', 'n', 'k', 'e', 'd' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob9[] = { + ' ', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob10[] = { + '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob11[] = { + 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' +}; +static const unsigned char llparse_blob12[] = { + 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', + 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob13[] = { + 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', + 'o', 'd', 'i', 'n', 'g' +}; +static const unsigned char llparse_blob14[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob15[] = { + 0xd, 0xa +}; +static const unsigned char llparse_blob16[] = { + 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob17[] = { + 'C', 'E', '/' +}; +static const unsigned char llparse_blob18[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob19[] = { + 'E', 'C', 'K', 'O', 'U', 'T' +}; +static const unsigned char llparse_blob20[] = { + 'N', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob21[] = { + 'E', 'L', 'E', 'T', 'E' +}; +static const unsigned char llparse_blob22[] = { + 'E', 'T' +}; +static const unsigned char llparse_blob23[] = { + 'E', 'A', 'D' +}; +static const unsigned char llparse_blob24[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob25[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob26[] = { + 'S', 'E', 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob27[] = { + 'R', 'G', 'E' +}; +static const unsigned char llparse_blob28[] = { + 'C', 'T', 'I', 'V', 'I', 'T', 'Y' +}; +static const unsigned char llparse_blob29[] = { + 'L', 'E', 'N', 'D', 'A', 'R' +}; +static const unsigned char llparse_blob30[] = { + 'V', 'E' +}; +static const unsigned char llparse_blob31[] = { + 'O', 'T', 'I', 'F', 'Y' +}; +static const unsigned char llparse_blob32[] = { + 'P', 'T', 'I', 'O', 'N', 'S' +}; +static const unsigned char llparse_blob33[] = { + 'T', 'C', 'H' +}; +static const unsigned char llparse_blob34[] = { + 'S', 'T' +}; +static const unsigned char llparse_blob35[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob36[] = { + 'A', 'T', 'C', 'H' +}; +static const unsigned char llparse_blob37[] = { + 'G', 'E' +}; +static const unsigned char llparse_blob38[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob39[] = { + 'O', 'R', 'T' +}; +static const unsigned char llparse_blob40[] = { + 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob41[] = { + 'U', 'R', 'C', 'E' +}; +static const unsigned char llparse_blob42[] = { + 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob43[] = { + 'R', 'A', 'C', 'E' +}; +static const unsigned char llparse_blob44[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob45[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob46[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob47[] = { + 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob48[] = { + 'H', 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob49[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob50[] = { + 'T', 'P', '/' +}; + +enum llparse_match_status_e { + kMatchComplete, + kMatchPause, + kMatchMismatch +}; +typedef enum llparse_match_status_e llparse_match_status_t; + +struct llparse_match_s { + llparse_match_status_t status; + const unsigned char* current; +}; +typedef struct llparse_match_s llparse_match_t; + +static llparse_match_t llparse__match_sequence_id( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = *p; + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_to_lower_unsafe( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) | 0x20); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +enum llparse_state_e { + s_error, + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, + s_n_llhttp__internal__n_pause_1, + s_n_llhttp__internal__n_invoke_is_equal_upgrade, + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, + s_n_llhttp__internal__n_chunk_data_almost_done_skip, + s_n_llhttp__internal__n_chunk_data_almost_done, + s_n_llhttp__internal__n_consume_content_length, + s_n_llhttp__internal__n_span_start_llhttp__on_body, + s_n_llhttp__internal__n_invoke_is_equal_content_length, + s_n_llhttp__internal__n_chunk_size_almost_done, + s_n_llhttp__internal__n_chunk_parameters, + s_n_llhttp__internal__n_chunk_size_otherwise, + s_n_llhttp__internal__n_chunk_size, + s_n_llhttp__internal__n_chunk_size_digit, + s_n_llhttp__internal__n_invoke_update_content_length, + s_n_llhttp__internal__n_consume_content_length_1, + s_n_llhttp__internal__n_span_start_llhttp__on_body_1, + s_n_llhttp__internal__n_eof, + s_n_llhttp__internal__n_span_start_llhttp__on_body_2, + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, + s_n_llhttp__internal__n_headers_almost_done, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value, + s_n_llhttp__internal__n_header_value_discard_lws, + s_n_llhttp__internal__n_header_value_discard_ws_almost_done, + s_n_llhttp__internal__n_header_value_lws, + s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_header_value_lenient, + s_n_llhttp__internal__n_header_value_otherwise, + s_n_llhttp__internal__n_header_value_connection_token, + s_n_llhttp__internal__n_header_value_connection_ws, + s_n_llhttp__internal__n_header_value_connection_1, + s_n_llhttp__internal__n_header_value_connection_2, + s_n_llhttp__internal__n_header_value_connection_3, + s_n_llhttp__internal__n_header_value_connection, + s_n_llhttp__internal__n_error_18, + s_n_llhttp__internal__n_header_value, + s_n_llhttp__internal__n_header_value_discard_rws, + s_n_llhttp__internal__n_error_19, + s_n_llhttp__internal__n_header_value_content_length_ws, + s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_header_value_te_chunked_last, + s_n_llhttp__internal__n_header_value_te_token_ows, + s_n_llhttp__internal__n_header_value_te_token, + s_n_llhttp__internal__n_header_value_te_chunked, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, + s_n_llhttp__internal__n_header_value_discard_ws, + s_n_llhttp__internal__n_header_field_general_otherwise, + s_n_llhttp__internal__n_header_field_general, + s_n_llhttp__internal__n_header_field_colon, + s_n_llhttp__internal__n_header_field_3, + s_n_llhttp__internal__n_header_field_4, + s_n_llhttp__internal__n_header_field_2, + s_n_llhttp__internal__n_header_field_1, + s_n_llhttp__internal__n_header_field_5, + s_n_llhttp__internal__n_header_field_6, + s_n_llhttp__internal__n_header_field_7, + s_n_llhttp__internal__n_header_field, + s_n_llhttp__internal__n_span_start_llhttp__on_header_field, + s_n_llhttp__internal__n_header_field_start, + s_n_llhttp__internal__n_url_skip_to_http09, + s_n_llhttp__internal__n_url_skip_lf_to_http09, + s_n_llhttp__internal__n_req_http_end_1, + s_n_llhttp__internal__n_req_http_end, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_req_http_start_1, + s_n_llhttp__internal__n_req_http_start_2, + s_n_llhttp__internal__n_req_http_start, + s_n_llhttp__internal__n_url_skip_to_http, + s_n_llhttp__internal__n_url_fragment, + s_n_llhttp__internal__n_span_end_stub_query_3, + s_n_llhttp__internal__n_url_query, + s_n_llhttp__internal__n_url_query_or_fragment, + s_n_llhttp__internal__n_url_path, + s_n_llhttp__internal__n_span_start_stub_path_2, + s_n_llhttp__internal__n_span_start_stub_path, + s_n_llhttp__internal__n_span_start_stub_path_1, + s_n_llhttp__internal__n_url_server_with_at, + s_n_llhttp__internal__n_url_server, + s_n_llhttp__internal__n_url_schema_delim_1, + s_n_llhttp__internal__n_url_schema_delim, + s_n_llhttp__internal__n_span_end_stub_schema, + s_n_llhttp__internal__n_url_schema, + s_n_llhttp__internal__n_url_start, + s_n_llhttp__internal__n_span_start_llhttp__on_url_1, + s_n_llhttp__internal__n_span_start_llhttp__on_url, + s_n_llhttp__internal__n_req_spaces_before_url, + s_n_llhttp__internal__n_req_first_space_before_url, + s_n_llhttp__internal__n_start_req_1, + s_n_llhttp__internal__n_start_req_2, + s_n_llhttp__internal__n_start_req_4, + s_n_llhttp__internal__n_start_req_6, + s_n_llhttp__internal__n_start_req_7, + s_n_llhttp__internal__n_start_req_5, + s_n_llhttp__internal__n_start_req_3, + s_n_llhttp__internal__n_start_req_8, + s_n_llhttp__internal__n_start_req_9, + s_n_llhttp__internal__n_start_req_10, + s_n_llhttp__internal__n_start_req_12, + s_n_llhttp__internal__n_start_req_13, + s_n_llhttp__internal__n_start_req_11, + s_n_llhttp__internal__n_start_req_15, + s_n_llhttp__internal__n_start_req_16, + s_n_llhttp__internal__n_start_req_18, + s_n_llhttp__internal__n_start_req_20, + s_n_llhttp__internal__n_start_req_21, + s_n_llhttp__internal__n_start_req_19, + s_n_llhttp__internal__n_start_req_17, + s_n_llhttp__internal__n_start_req_22, + s_n_llhttp__internal__n_start_req_14, + s_n_llhttp__internal__n_start_req_23, + s_n_llhttp__internal__n_start_req_24, + s_n_llhttp__internal__n_start_req_26, + s_n_llhttp__internal__n_start_req_27, + s_n_llhttp__internal__n_start_req_31, + s_n_llhttp__internal__n_start_req_32, + s_n_llhttp__internal__n_start_req_30, + s_n_llhttp__internal__n_start_req_29, + s_n_llhttp__internal__n_start_req_28, + s_n_llhttp__internal__n_start_req_34, + s_n_llhttp__internal__n_start_req_33, + s_n_llhttp__internal__n_start_req_25, + s_n_llhttp__internal__n_start_req_37, + s_n_llhttp__internal__n_start_req_38, + s_n_llhttp__internal__n_start_req_36, + s_n_llhttp__internal__n_start_req_35, + s_n_llhttp__internal__n_start_req_40, + s_n_llhttp__internal__n_start_req_41, + s_n_llhttp__internal__n_start_req_42, + s_n_llhttp__internal__n_start_req_39, + s_n_llhttp__internal__n_start_req_43, + s_n_llhttp__internal__n_start_req_46, + s_n_llhttp__internal__n_start_req_48, + s_n_llhttp__internal__n_start_req_49, + s_n_llhttp__internal__n_start_req_47, + s_n_llhttp__internal__n_start_req_50, + s_n_llhttp__internal__n_start_req_45, + s_n_llhttp__internal__n_start_req_44, + s_n_llhttp__internal__n_start_req, + s_n_llhttp__internal__n_res_line_almost_done, + s_n_llhttp__internal__n_res_status, + s_n_llhttp__internal__n_span_start_llhttp__on_status, + s_n_llhttp__internal__n_res_status_start, + s_n_llhttp__internal__n_res_status_code_otherwise, + s_n_llhttp__internal__n_res_status_code, + s_n_llhttp__internal__n_res_http_end, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_start_res, + s_n_llhttp__internal__n_req_or_res_method_2, + s_n_llhttp__internal__n_req_or_res_method_3, + s_n_llhttp__internal__n_req_or_res_method_1, + s_n_llhttp__internal__n_req_or_res_method, + s_n_llhttp__internal__n_start_req_or_res, + s_n_llhttp__internal__n_invoke_load_type, + s_n_llhttp__internal__n_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llhttp__on_url( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_field( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_body( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_status( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 2; + return 0; +} + +int llhttp__on_message_begin( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->type; +} + +int llhttp__internal__c_store_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->method = match; + return 0; +} + +int llhttp__internal__c_is_equal_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 5; +} + +int llhttp__internal__c_update_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_major = 0; + return 0; +} + +int llhttp__internal__c_update_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_minor = 9; + return 0; +} + +int llhttp__internal__c_test_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 128) == 128; +} + +int llhttp__on_chunk_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->upgrade == 1; +} + +int llhttp__after_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 0; + return 0; +} + +int llhttp__internal__c_test_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 544) == 544; +} + +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 256) == 256; +} + +int llhttp__internal__c_test_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 40) == 40; +} + +int llhttp__before_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__after_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->content_length = 0; + return 0; +} + +int llhttp__internal__c_mul_add_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 16) { + return 1; + } + + state->content_length *= 16; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__on_chunk_header( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->content_length == 0; +} + +int llhttp__internal__c_or_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 128; + return 0; +} + +int llhttp__internal__c_update_finish_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 1; + return 0; +} + +int llhttp__internal__c_or_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 64; + return 0; +} + +int llhttp__internal__c_update_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->upgrade = 1; + return 0; +} + +int llhttp__internal__c_store_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->header_state = match; + return 0; +} + +int llhttp__internal__c_load_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->header_state; +} + +int llhttp__internal__c_or_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 1; + return 0; +} + +int llhttp__internal__c_update_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 1; + return 0; +} + +int llhttp__internal__c_or_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 2; + return 0; +} + +int llhttp__internal__c_or_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 4; + return 0; +} + +int llhttp__internal__c_or_flags_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 8; + return 0; +} + +int llhttp__internal__c_update_header_state_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 6; + return 0; +} + +int llhttp__internal__c_update_header_state_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 0; + return 0; +} + +int llhttp__internal__c_update_header_state_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 5; + return 0; +} + +int llhttp__internal__c_update_header_state_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 7; + return 0; +} + +int llhttp__internal__c_test_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 32) == 32; +} + +int llhttp__internal__c_mul_add_content_length_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 10) { + return 1; + } + + state->content_length *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_or_flags_15( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 32; + return 0; +} + +int llhttp__internal__c_or_flags_16( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 512; + return 0; +} + +int llhttp__internal__c_update_header_state_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 8; + return 0; +} + +int llhttp__internal__c_or_flags_17( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 16; + return 0; +} + +int llhttp__internal__c_store_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_major = match; + return 0; +} + +int llhttp__internal__c_store_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_minor = match; + return 0; +} + +int llhttp__internal__c_is_equal_method_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 33; +} + +int llhttp__internal__c_update_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->status_code = 0; + return 0; +} + +int llhttp__internal__c_mul_add_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->status_code > 0xffff / 10) { + return 1; + } + + state->status_code *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->status_code > 0xffff - match) { + return 1; + } + } else { + if (state->status_code < 0 - match) { + return 1; + } + } + state->status_code += match; + + /* Enforce maximum */ + if (state->status_code > 999) { + return 1; + } + return 0; +} + +int llhttp__internal__c_update_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 1; + return 0; +} + +int llhttp__internal__c_update_type_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 2; + return 0; +} + +int llhttp__internal_init(llhttp__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; + return 0; +} + +static llparse_state_t llhttp__internal__run( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { + switch (llhttp__after_message_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_finish_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_pause_1: + s_n_llhttp__internal__n_pause_1: { + state->error = 0x16; + state->reason = "Pause on CONNECT/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_upgrade: + s_n_llhttp__internal__n_invoke_is_equal_upgrade: { + switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + default: + goto s_n_llhttp__internal__n_pause_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_5; + default: + goto s_n_llhttp__internal__n_error_9; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done_skip: + s_n_llhttp__internal__n_chunk_data_almost_done_skip: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done_skip; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done: + s_n_llhttp__internal__n_chunk_data_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done; + } + p++; + goto s_n_llhttp__internal__n_chunk_data_almost_done_skip; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length: + s_n_llhttp__internal__n_consume_content_length: { + size_t avail; + size_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body: + s_n_llhttp__internal__n_span_start_llhttp__on_body: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_content_length: + s_n_llhttp__internal__n_invoke_is_equal_content_length: { + switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body; + default: + goto s_n_llhttp__internal__n_invoke_or_flags; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_almost_done: + s_n_llhttp__internal__n_chunk_size_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_almost_done; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_parameters: + s_n_llhttp__internal__n_chunk_parameters: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_parameters; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + default: { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_otherwise: + s_n_llhttp__internal__n_chunk_size_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_otherwise; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + default: { + goto s_n_llhttp__internal__n_error_6; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size: + s_n_llhttp__internal__n_chunk_size: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_chunk_size_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_digit: + s_n_llhttp__internal__n_chunk_size_digit: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_digit; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_error_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_content_length: + s_n_llhttp__internal__n_invoke_update_content_length: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_chunk_size_digit; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length_1: + s_n_llhttp__internal__n_consume_content_length_1: { + size_t avail; + size_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: + s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_eof: + s_n_llhttp__internal__n_eof: { + if (p == endp) { + return s_n_llhttp__internal__n_eof; + } + p++; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: + s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { + switch (llhttp__after_headers_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_content_length; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + case 4: + goto s_n_llhttp__internal__n_invoke_update_finish_2; + case 5: + goto s_n_llhttp__internal__n_error_10; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_almost_done: + s_n_llhttp__internal__n_headers_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_almost_done; + } + p++; + goto s_n_llhttp__internal__n_invoke_test_flags; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_lws: + s_n_llhttp__internal__n_header_value_discard_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_lws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: + s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lws: + s_n_llhttp__internal__n_header_value_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lws; + } + switch (*p) { + case 9: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + case ' ': { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_almost_done: + s_n_llhttp__internal__n_header_value_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_lws; + } + default: { + goto s_n_llhttp__internal__n_error_15; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_otherwise: + s_n_llhttp__internal__n_header_value_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_flags_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_token: + s_n_llhttp__internal__n_header_value_connection_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_connection_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_ws: + s_n_llhttp__internal__n_header_value_connection_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + case ',': { + p++; + goto s_n_llhttp__internal__n_invoke_load_header_state_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_1: + s_n_llhttp__internal__n_header_value_connection_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_1; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_2: + s_n_llhttp__internal__n_header_value_connection_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_2; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_5; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_3: + s_n_llhttp__internal__n_header_value_connection_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_3; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_6; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection: + s_n_llhttp__internal__n_header_value_connection: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection; + } + switch (((*p) | 0x20)) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_1; + } + case 'k': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_2; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_3; + } + default: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_18: + s_n_llhttp__internal__n_error_18: { + state->error = 0xb; + state->reason = "Content-Length overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value: + s_n_llhttp__internal__n_header_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_rws: + s_n_llhttp__internal__n_header_value_discard_rws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_rws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_rws; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_19: + s_n_llhttp__internal__n_error_19: { + state->error = 0xb; + state->reason = "Invalid character in Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length_ws: + s_n_llhttp__internal__n_header_value_content_length_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_or_flags_15; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_or_flags_15; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length: + s_n_llhttp__internal__n_header_value_content_length: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked_last: + s_n_llhttp__internal__n_header_value_te_chunked_last: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked_last; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token_ows: + s_n_llhttp__internal__n_header_value_te_token_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token_ows; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token: + s_n_llhttp__internal__n_header_value_te_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked: + s_n_llhttp__internal__n_header_value_te_chunked: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_invoke_load_header_state_2; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws: + s_n_llhttp__internal__n_header_value_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general_otherwise: + s_n_llhttp__internal__n_header_field_general_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general_otherwise; + } + switch (*p) { + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; + } + default: { + goto s_n_llhttp__internal__n_error_20; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general: + s_n_llhttp__internal__n_header_field_general: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob10); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_field_general; + } + default: { + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon: + s_n_llhttp__internal__n_header_field_colon: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_field_colon; + } + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_3: + s_n_llhttp__internal__n_header_field_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_3; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_4: + s_n_llhttp__internal__n_header_field_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_4; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob11, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_2: + s_n_llhttp__internal__n_header_field_2: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_2; + } + switch (((*p) | 0x20)) { + case 'n': { + p++; + goto s_n_llhttp__internal__n_header_field_3; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_1: + s_n_llhttp__internal__n_header_field_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_1; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_field_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_5: + s_n_llhttp__internal__n_header_field_5: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_5; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob12, 15); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_5; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_6: + s_n_llhttp__internal__n_header_field_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_6; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob13, 16); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_7: + s_n_llhttp__internal__n_header_field_7: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_7; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob14, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_7; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field: + s_n_llhttp__internal__n_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field; + } + switch (((*p) | 0x20)) { + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_field_1; + } + case 'p': { + p++; + goto s_n_llhttp__internal__n_header_field_5; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_6; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_field_7; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: + s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_field; + goto s_n_llhttp__internal__n_header_field; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_start: + s_n_llhttp__internal__n_header_field_start: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_start; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_headers_almost_done; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_headers_almost_done; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http09: + s_n_llhttp__internal__n_url_skip_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http09; + } + p++; + goto s_n_llhttp__internal__n_invoke_update_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09: + s_n_llhttp__internal__n_url_skip_lf_to_http09: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_21; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_end_1: + s_n_llhttp__internal__n_req_http_end_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_end_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + default: { + goto s_n_llhttp__internal__n_error_22; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_end: + s_n_llhttp__internal__n_req_http_end: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_end; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_req_http_end_1; + } + default: { + goto s_n_llhttp__internal__n_error_22; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_minor: + s_n_llhttp__internal__n_req_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_23; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_dot: + s_n_llhttp__internal__n_req_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_req_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_24; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_major: + s_n_llhttp__internal__n_req_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + default: { + goto s_n_llhttp__internal__n_error_25; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_1: + s_n_llhttp__internal__n_req_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_req_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_2: + s_n_llhttp__internal__n_req_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_is_equal_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start: + s_n_llhttp__internal__n_req_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_http_start; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_http_start_2; + } + default: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http: + s_n_llhttp__internal__n_url_skip_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http; + } + p++; + goto s_n_llhttp__internal__n_req_http_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_fragment: + s_n_llhttp__internal__n_url_fragment: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_fragment; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; + } + default: { + goto s_n_llhttp__internal__n_error_28; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_query_3: + s_n_llhttp__internal__n_span_end_stub_query_3: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_query_3; + } + p++; + goto s_n_llhttp__internal__n_url_fragment; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query: + s_n_llhttp__internal__n_url_query: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_query; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_stub_query_3; + } + default: { + goto s_n_llhttp__internal__n_error_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query_or_fragment: + s_n_llhttp__internal__n_url_query_or_fragment: { + if (p == endp) { + return s_n_llhttp__internal__n_url_query_or_fragment; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; + } + case ' ': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; + } + case '#': { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case '?': { + p++; + goto s_n_llhttp__internal__n_url_query; + } + default: { + goto s_n_llhttp__internal__n_error_30; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_path: + s_n_llhttp__internal__n_url_path: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_path; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob1); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 12, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_url_path; + } + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_path; + } + default: { + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_2: + s_n_llhttp__internal__n_span_start_stub_path_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_2; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path: + s_n_llhttp__internal__n_span_start_stub_path: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_1: + s_n_llhttp__internal__n_span_start_stub_path_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_1; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server_with_at: + s_n_llhttp__internal__n_url_server_with_at: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server_with_at; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 5: { + goto s_n_llhttp__internal__n_span_start_stub_path_1; + } + case 6: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_error_31; + } + default: { + goto s_n_llhttp__internal__n_error_32; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server: + s_n_llhttp__internal__n_url_server: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 5: { + goto s_n_llhttp__internal__n_span_start_stub_path; + } + case 6: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_server_with_at; + } + default: { + goto s_n_llhttp__internal__n_error_33; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim_1: + s_n_llhttp__internal__n_url_schema_delim_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim_1; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_url_server; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim: + s_n_llhttp__internal__n_url_schema_delim: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case '/': { + p++; + goto s_n_llhttp__internal__n_url_schema_delim_1; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_schema: + s_n_llhttp__internal__n_span_end_stub_schema: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_schema; + } + p++; + goto s_n_llhttp__internal__n_url_schema_delim; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema: + s_n_llhttp__internal__n_url_schema: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_schema; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_stub_schema; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_36; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_start: + s_n_llhttp__internal__n_url_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 2: { + goto s_n_llhttp__internal__n_span_start_stub_path_2; + } + case 3: { + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_37; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: + s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url: + s_n_llhttp__internal__n_span_start_llhttp__on_url: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_server; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_spaces_before_url: + s_n_llhttp__internal__n_req_spaces_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_spaces_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_invoke_is_equal_method; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_first_space_before_url: + s_n_llhttp__internal__n_req_first_space_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_first_space_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_error_38; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_1: + s_n_llhttp__internal__n_start_req_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob0, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 19; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_2: + s_n_llhttp__internal__n_start_req_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 16; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_4: + s_n_llhttp__internal__n_start_req_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_4; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 22; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_6: + s_n_llhttp__internal__n_start_req_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_6; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_7: + s_n_llhttp__internal__n_start_req_7: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_7; + } + switch (*p) { + case 'Y': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_5: + s_n_llhttp__internal__n_start_req_5: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_5; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_6; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_7; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_3: + s_n_llhttp__internal__n_start_req_3: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_3; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_start_req_4; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_5; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_8: + s_n_llhttp__internal__n_start_req_8: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_8; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_8; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_9: + s_n_llhttp__internal__n_start_req_9: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_9; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_9; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_10: + s_n_llhttp__internal__n_start_req_10: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_10; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_10; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_12: + s_n_llhttp__internal__n_start_req_12: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_12; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 31; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_12; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_13: + s_n_llhttp__internal__n_start_req_13: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_13; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_13; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_11: + s_n_llhttp__internal__n_start_req_11: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_11; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_start_req_12; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_13; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_15: + s_n_llhttp__internal__n_start_req_15: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_15; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 24; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_15; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_16: + s_n_llhttp__internal__n_start_req_16: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_16; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 23; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_16; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_18: + s_n_llhttp__internal__n_start_req_18: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_18; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 21; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_18; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_20: + s_n_llhttp__internal__n_start_req_20: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_20; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 30; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_20; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_21: + s_n_llhttp__internal__n_start_req_21: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_21; + } + switch (*p) { + case 'L': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_19: + s_n_llhttp__internal__n_start_req_19: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_19; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_20; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_21; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_17: + s_n_llhttp__internal__n_start_req_17: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_17; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_18; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_start_req_19; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_22: + s_n_llhttp__internal__n_start_req_22: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_22; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_22; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_14: + s_n_llhttp__internal__n_start_req_14: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_14; + } + switch (*p) { + case '-': { + p++; + goto s_n_llhttp__internal__n_start_req_15; + } + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_16; + } + case 'K': { + p++; + goto s_n_llhttp__internal__n_start_req_17; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_22; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_23: + s_n_llhttp__internal__n_start_req_23: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_23; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 25; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_23; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_24: + s_n_llhttp__internal__n_start_req_24: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_24; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_24; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_26: + s_n_llhttp__internal__n_start_req_26: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_26; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 28; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_26; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_27: + s_n_llhttp__internal__n_start_req_27: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_27; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_27; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_31: + s_n_llhttp__internal__n_start_req_31: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_31; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_31; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_32: + s_n_llhttp__internal__n_start_req_32: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_32; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_32; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_30: + s_n_llhttp__internal__n_start_req_30: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_30; + } + switch (*p) { + case 'F': { + p++; + goto s_n_llhttp__internal__n_start_req_31; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_32; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_29: + s_n_llhttp__internal__n_start_req_29: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_29; + } + switch (*p) { + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_30; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_28: + s_n_llhttp__internal__n_start_req_28: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_28; + } + switch (*p) { + case 'I': { + p++; + match = 34; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_29; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_34: + s_n_llhttp__internal__n_start_req_34: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_34; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 29; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_34; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_33: + s_n_llhttp__internal__n_start_req_33: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_33; + } + switch (*p) { + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_34; + } + case 'T': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_25: + s_n_llhttp__internal__n_start_req_25: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_25; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_26; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_27; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_28; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_33; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_37: + s_n_llhttp__internal__n_start_req_37: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_37; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 17; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_37; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_38: + s_n_llhttp__internal__n_start_req_38: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_38; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 20; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_38; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_36: + s_n_llhttp__internal__n_start_req_36: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_36; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_37; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_38; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_35: + s_n_llhttp__internal__n_start_req_35: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_35; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_36; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_40: + s_n_llhttp__internal__n_start_req_40: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_40; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_40; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_41: + s_n_llhttp__internal__n_start_req_41: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_41; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 33; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_41; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_42: + s_n_llhttp__internal__n_start_req_42: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_42; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 26; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_42; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_39: + s_n_llhttp__internal__n_start_req_39: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_39; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_40; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_41; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_42; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_43: + s_n_llhttp__internal__n_start_req_43: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_43; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_43; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_46: + s_n_llhttp__internal__n_start_req_46: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_46; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 18; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_46; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_48: + s_n_llhttp__internal__n_start_req_48: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_48; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 32; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_48; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_49: + s_n_llhttp__internal__n_start_req_49: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_49; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_49; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_47: + s_n_llhttp__internal__n_start_req_47: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_47; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_start_req_48; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_49; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_50: + s_n_llhttp__internal__n_start_req_50: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_50; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 8); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 27; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_50; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_45: + s_n_llhttp__internal__n_start_req_45: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_45; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_46; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_start_req_47; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_start_req_50; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_44: + s_n_llhttp__internal__n_start_req_44: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_44; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_45; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req: + s_n_llhttp__internal__n_start_req: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_1; + } + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_2; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_start_req_3; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_start_req_8; + } + case 'G': { + p++; + goto s_n_llhttp__internal__n_start_req_9; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_start_req_10; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_start_req_11; + } + case 'M': { + p++; + goto s_n_llhttp__internal__n_start_req_14; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_23; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_24; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_25; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_35; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_start_req_39; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_start_req_43; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_44; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_line_almost_done: + s_n_llhttp__internal__n_res_line_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_res_line_almost_done; + } + p++; + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status: + s_n_llhttp__internal__n_res_status: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; + } + default: { + p++; + goto s_n_llhttp__internal__n_res_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_status: + s_n_llhttp__internal__n_span_start_llhttp__on_status: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_status; + goto s_n_llhttp__internal__n_res_status; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_start: + s_n_llhttp__internal__n_res_status_start: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_otherwise: + s_n_llhttp__internal__n_res_status_code_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_res_status_start; + } + case 13: { + goto s_n_llhttp__internal__n_res_status_start; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_res_status_start; + } + default: { + goto s_n_llhttp__internal__n_error_40; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code: + s_n_llhttp__internal__n_res_status_code: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + default: { + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_end: + s_n_llhttp__internal__n_res_http_end: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_end; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_update_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_41; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_minor: + s_n_llhttp__internal__n_res_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + default: { + goto s_n_llhttp__internal__n_error_42; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_dot: + s_n_llhttp__internal__n_res_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_res_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_43; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_major: + s_n_llhttp__internal__n_res_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + default: { + goto s_n_llhttp__internal__n_error_44; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_res: + s_n_llhttp__internal__n_start_res: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_res; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_res_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_res; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_47; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_2: + s_n_llhttp__internal__n_req_or_res_method_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_3: + s_n_llhttp__internal__n_req_or_res_method_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_type_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_1: + s_n_llhttp__internal__n_req_or_res_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_1; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_2; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_3; + } + default: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method: + s_n_llhttp__internal__n_req_or_res_method: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_or_res: + s_n_llhttp__internal__n_start_req_or_res: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_or_res; + } + switch (*p) { + case 'H': { + goto s_n_llhttp__internal__n_req_or_res_method; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_type_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_type: + s_n_llhttp__internal__n_invoke_load_type: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_start_req; + case 2: + goto s_n_llhttp__internal__n_start_res; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start: + s_n_llhttp__internal__n_start: { + if (p == endp) { + return s_n_llhttp__internal__n_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_finish; + } + } + /* UNREACHABLE */; + abort(); + } + default: + /* UNREACHABLE */ + abort(); + } + s_n_llhttp__internal__n_error_34: { + state->error = 0x7; + state->reason = "Invalid characters in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_1: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_5: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_9: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_7: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_13: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_7; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_11: { + state->error = 0x4; + state->reason = "Content-Length can't be present with Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_12: { + state->error = 0x4; + state->reason = "Content-Length can't be present with chunked encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_2: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_3: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_pause_1; + case 21: + goto s_n_llhttp__internal__n_pause_2; + default: + goto s_n_llhttp__internal__n_error_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_7: { + state->error = 0xc; + state->reason = "Chunk size overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_3: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_5: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_3; + default: + goto s_n_llhttp__internal__n_error_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_chunk_data_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags: { + switch (llhttp__internal__c_or_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_4: { + state->error = 0x15; + state->reason = "on_chunk_header pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_4: { + state->error = 0x13; + state->reason = "`on_chunk_header` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { + switch (llhttp__on_chunk_header(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_4; + default: + goto s_n_llhttp__internal__n_error_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_6: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length: { + switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_7; + default: + goto s_n_llhttp__internal__n_chunk_size; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_8: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_2: { + switch (llhttp__internal__c_update_finish_2(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_10: { + state->error = 0xf; + state->reason = "Request has invalid `Transfer-Encoding`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_2: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + case 21: + goto s_n_llhttp__internal__n_pause; + default: + goto s_n_llhttp__internal__n_error_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_1: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_2: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_6: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_1: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_6; + default: + goto s_n_llhttp__internal__n_error_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_error_12; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_11; + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_3; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_1: { + switch (llhttp__internal__c_test_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_2; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; + default: + goto s_n_llhttp__internal__n_invoke_test_flags_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_14: { + state->error = 0xb; + state->reason = "Empty Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + } + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_3: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_4: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_5: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_6: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_1: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_3; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_4; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_5; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_6; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_error_14; + default: + goto s_n_llhttp__internal__n_invoke_load_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_1: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_7: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_8: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_9: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_10: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_3: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_7; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_8; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_9; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_10; + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_15: { + state->error = 0x3; + state->reason = "Missing expected LF after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_16: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_4: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_error_16; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_3: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_11: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_12: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_13: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_14: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_4: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_11; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_12; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_13; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_14; + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_4: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_token; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_2: { + switch (llhttp__internal__c_update_header_state_2(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_5: { + switch (llhttp__internal__c_update_header_state_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_6: { + switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_18; + return s_error; + } + goto s_n_llhttp__internal__n_error_18; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { + switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + default: + goto s_n_llhttp__internal__n_header_value_content_length; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_7: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_15: { + switch (llhttp__internal__c_or_flags_15(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_discard_rws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_19; + return s_error; + } + goto s_n_llhttp__internal__n_error_19; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_17: { + state->error = 0x4; + state->reason = "Duplicate Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_5: { + switch (llhttp__internal__c_test_flags_5(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_value_content_length; + default: + goto s_n_llhttp__internal__n_error_17; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_8: { + switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_16: { + switch (llhttp__internal__c_or_flags_16(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_17: { + switch (llhttp__internal__c_or_flags_17(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_2: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_connection; + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_5; + case 3: + goto s_n_llhttp__internal__n_invoke_or_flags_16; + case 4: + goto s_n_llhttp__internal__n_invoke_or_flags_17; + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_20: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_9: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_header_state: { + switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_header_field_colon; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_10: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_minor: { + switch (llhttp__internal__c_update_http_minor(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_major: { + switch (llhttp__internal__c_update_http_major(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_http_minor; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_21: { + state->error = 0x7; + state->reason = "Expected CRLF"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_22: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_end; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_23: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_24: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_25: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_27: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_26: { + state->error = 0x8; + state->reason = "Expected SOURCE method for ICE/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method_1: { + switch (llhttp__internal__c_is_equal_method_1(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_26; + default: + goto s_n_llhttp__internal__n_req_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_28: { + state->error = 0x7; + state->reason = "Invalid char in url fragment start"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_29: { + state->error = 0x7; + state->reason = "Invalid char in url query"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_30: { + state->error = 0x7; + state->reason = "Invalid char in url path"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_31: { + state->error = 0x7; + state->reason = "Double @ in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_32: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_33: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_35: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_36: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_37: { + state->error = 0x7; + state->reason = "Unexpected start char in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method: { + switch (llhttp__internal__c_is_equal_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_38: { + state->error = 0x6; + state->reason = "Expected space after method"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method_1: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_first_space_before_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_46: { + state->error = 0x6; + state->reason = "Invalid method encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_39: { + state->error = 0xd; + state->reason = "Response overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_39; + default: + goto s_n_llhttp__internal__n_res_status_code; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_40: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_status_code: { + switch (llhttp__internal__c_update_status_code(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_status_code; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_41: { + state->error = 0x9; + state->reason = "Expected space after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor_1: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_end; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_42: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_43: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major_1: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_44: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_47: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_req_first_space_before_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_update_type; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_45: { + state->error = 0x8; + state->reason = "Invalid word encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_1: { + switch (llhttp__internal__c_update_type_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_2: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start_req; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_8: { + state->error = 0x15; + state->reason = "on_message_begin pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error: { + state->error = 0x10; + state->reason = "`on_message_begin` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { + switch (llhttp__on_message_begin(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_type; + case 21: + goto s_n_llhttp__internal__n_pause_8; + default: + goto s_n_llhttp__internal__n_error; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish: { + switch (llhttp__internal__c_update_finish(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; + } + /* UNREACHABLE */; + abort(); + } +} + +int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} \ No newline at end of file diff --git a/core-tests/docker-compose.yml b/core-tests/docker-compose.yml new file mode 100755 index 00000000000..4188b9de5f6 --- /dev/null +++ b/core-tests/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' +services: + httpbin: + container_name: "httpbin" + image: "kennethreitz/httpbin" + tinyproxy: + container_name: "tinyproxy" + image: "vimagick/tinyproxy" + socks5: + container_name: "socks5" + image: "xkuma/socks5" + ports: + - "8080:1080" + environment: + - PROXY_USER=user + - PROXY_PASSWORD=password + - PROXY_SERVER=0.0.0.0:1080 + diff --git a/core-tests/fuzz/cases/req1.bin b/core-tests/fuzz/cases/req1.bin new file mode 100644 index 00000000000..fc9b786372c --- /dev/null +++ b/core-tests/fuzz/cases/req1.bin @@ -0,0 +1,19 @@ +POST / HTTP/1.1 +Host: 127.0.0.1:35347 +Accept: */* +Content-Length: 11199 +Content-Type: multipart/form-data; boundary=------------------------18610972117ffaf2 + +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="test" + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="hello" + +ibOnkVsuKvLnzrTeS81ueAmswSWiUUvcOtHAamPBw2NIgEM3286PmK0lvCcPDnsYQ0lm8/emwynbcngwmUJ5983lmEBj49th5ED73eFUGHSLQzTY1nAaD54f +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="world" + +NDdKoaAEmCjD+MFAo1tOBTJ0yPTWG2amx0f8wI/PJf/sIB+XAxRmdftsyd48o47Lt8U8pgIxBd4xcUsLGVomZZT13eIpsCHnPjSdgAr8vSBrxFciyHBIzkhFX8IxTBFs+YqMqiH5/YWx/TQnxoqYWcqa86RShKne2JmF6+JXZUkB6O3I21X1cioUmdK9d3pOBWGUviL9oY99FA0fVAC9qH01hyO2AQyh3hjhvpq1uPYhjnCwetQlpYExhxPC2Q1gJ9edA8pmccWFSRlS079wq8l1ikkQqNnLvzp15pzNmUbvDGXAywsQrpJco3G9b8gwljHGUPpctxBB8DyO1LfavJ8Zusa9rSA4NAA3NzZpZ5bbKy5rYFQASuFXd7orlXdoPkyZ7ERSqgIT6y8Y3pnPrzAs8LLEmGuWbjN6ME7PtUuT87f10GtobckOUqYJktX9fmD4lxgJT0uUam++HG1j3ztFzyaY1pCREYmKewy2Q5FTgYQxKNzN4WdRmz+X8k2upRFDEr22Gm1wUEct2vOZDKhp0XBtV20uGKg0kIb4OM8RU5b/vymTss5+eHX5G/zy09+fXyzs7RpUls/yavSiZJT6iYXciBINqfYcmG6f3arBdP3sgR59UhLnWn3wfNeKyYshiy1HcKDvOEZea/A81XSULo4iVmlYghACFrwvkyHU6oaV/chWID0IsNWtylIo5OaDFDuZLGm3YzQ58ew21dQZ+Nifl2QydeGBO1siZt9mbcIAXqIylvP3iT1CVab7PCiGPXNtYntLa7581Zceg73i4g1ZzuaWULUqkF5WPLWroTCWzDl+/2WsU3+WAIOON3dRTz/DirZG7e0UxmaseAT+xoDyVMIZK0fuaoc3s0ozivKE4bItfYMMhkyRQpDIjzTKB3YHBCH9aWnVcxLRnVF6qt6hO/bZfh/isskDbiCoBD8YcSPWgN/qLYDBOJFy8us9TdiWPZdBebdHmqR889s4Mow3TYcrbmaVmnwwPqSWgsfhKiLoaK2MKWgSxVlGGVQuB8Odw/YrAvFAEANKNv0D92pUfWBY8Rolh6MMECiYTuHLeGBRENx/1TWaZ1ASP4hfp/fP0ylTSimfxexOEHKDJGlQdsSgQNt7uXYlFuwhRVu78FacQcLhSDMAJT9oXmmy9/BX4x8F68IkV9A2XYEfa4G6KzM4reMyccXIM2GnBKkoQgfwcPNuRwo/riyqQyMbf03MINJ/IwwmI1lnXRL6Uzmn3zUV9aurDrhRTrVfqAhPqEJLrqsPduI2DXQWvBXNiWQctK5pTCWnV9qj9qy25CBggvP8c749mlPPhTmrFt2Nk2QeS7t+oiq/E+G1ir0daEk+3M3oqEVRMbcnkftu+d2aMPuBSDwt6l7nV+GCQ89DXp20enwbeUU/JVLRGYkqpj+17Tw/8YxxYTTkVbNNotv8tOKqmwyN0XzRffQ03aVbNhjkUgESQahE05swWxRXrNmiUpIBFT/yMSSoqGl5aTbDtBjNPSpX7oSCCBNdoT3MnVdPCbPAMW4PxenhPUKWLLOVtFztMnFsl4/IIMtUjkBbt3cONJCIsNuXzSvfsEso9CYjH9VUWyDcZj614Ov8KIEYJZkJsF1nlxCJyChQedCDzLZNCR5sJY/R9dv5A88ixB1GhRSd5qjWpjnXTAm8YLVtDWzH9v0ACVNsq4zYYUQioJk6ck13meDgBEu4etqqyeVUdbJAkHo2R4MAo4uJ2/sMQL5xniZ5WRYTBP4JFJxkz7qNIzn8n+7XxuotnfvqShCuUC/Xt/SZFHD2INMBG2u4OLlARBWj7TPkLsQwczaGyXC9j85mbXvOpxbqlBAtADQlIOfqaeROahEkNKYnmCb09PF1Sss3l4BKnYaVShO+pZ7MnJSR0Mpx6R5uV0dGW/VXmeOSrSCouDkunAnX1jMEEje35szpDkqgMaYqlaoOkMRrhCZwL7YhQjSBe1GnFDHGvQxX8i3AnXfgzA+9AAiITOv7k0h/PxgGNUHlaBkWyaGIi3pS0Dz/4yGmP3BYr0H/qHpeXte1kd/s8JRxyILGDgC51Y+ExYvjZL63PsmmPNfHyY7LDTX/UfTweISeF7q1b8HuoQuwxvVmq6l/C+/ewDXVeort1tbDED+h1dCDqOEBXGupZu2uKHfWICgdLOHzPEnzyNsykqCAAlKTtFp6VAwLo3b3xWFSvhXfk4Yi/23+rZc3eL2JFwaHcULVkCr6AzOwriALLBBU2RT2qj1cAwqWNQQUbH7tHViuBxBT8/B9MjcU9HQHbdKSvEocs/4fqgMJfvj2YD4O7qGzLaBghkfKS1HAOyt3MNf5q4k7lQndHi0U8cAYMo6aAuxYOccxOzobU2uKiyZUx0WpH3ZE+Ge8W7Hc7bMSoH5ISNKTti8tIzNmt+CyIRPVzSjiodhaUr82ErCKq3UJTe3Kr+JAWkthB5BtcZWFVqMT7yb6cwtzoOiJKc+WaPX0rnwiHLJTQEhOGR/4Jh1YI39M8kpZG0HPPJbiK3bZuDgVyRgLsGm17vBpTv+dp93Kp3wquuATyGloMu7/GnjSrXzTOcVdkW0F5qMI+QSG0eK3130ndIw9Qt+Ir/UtYvqcTDCdrX5LJrJ3VLKSGqr5MPc8uHHhjZvWUeUtR8Dt7b9Qy8M7UZ+fWcRzVHMnqFzxbsOTkI9Ck1jlBe4p/Qks8EZ74zmc3SL2ZH672U9CQXhOLzyd/E435m60Pz5yqXjq/ZYdTl566IEoOA1YoqbbA+A2Co11Xjp3P2Mj3ns82ubjp9vkIkJboe9O4WghDbcPSWyiyioIUhp7mSrhgrZn8khgY0akKuRplrD58dhH0f+QCbk4/ovPjKLFwoiksfsvfQrO/5wSLAKQA+5rycB4JQj4gkDtKxW5/I2Mf0y/Js6gxAFMqCcRykc9Gm7WGRMmRDefluPLlajxOdj6jbA20RH23ydw4E5a0cRPYiZXlssEbrzu7qsdfisG4jmLoApu1PyokcshUWzkvJnevFg3LIgW9Xl3hKLyGLXQQR8f+ngVbP/rBkhY7R3lramIo7npGsCKQlv41/4Z6Vk+JTKd1mfvXq6mYJeeYHCAFw+BP8KcXFRav3J0XJccb69wuziZtu/h3KCOqL7q9mN0Y7ftOR+l+hJ+k+v/T0fbvHkwlBsI/I15f5r8S6KK3pnBcL14RD3dluZlr9GuR9hJtGc6GKmkV+QqPph7tPvSqtOaYI4CYrbHegBcWOirj7BEF0qGuNV6hYOxkZa6cSQP2p2rPTvzv3L2usPbCLDlgN7Yu+RJ0icpFCpkZE0V6w0sbr0ZBvYO8eaFu8TVwT84cXD2A8XSSCMm5w5HCQpIThfQzvW+xWTW7nO5y9LEPUQbh+fZMs+KABuEM5zIXyavqMdSumlWyX51FhAqhb4ys5LqQSrKM0u+qxUcweXOt2lsK1WgibFVIhuPzuCYo+hVFHNXQpxjpvX89N4Oxjg3jb3OWHOUqZsGU23aWLpOP8Vpmh4RxYZE6k07C16DZwHbe86tbSLrt6ezzjcmwZbtGwHd8iTi2sLjVZGplYENTw7Mglgr33ImoOD1MRjfWzTr2xwsfOzR+emSOnL3PBB1Xh6usXsyop/yeJQfJN4J+epduKW/2V6Egzc7t4Klf3wBtjysoFnTHiQ9u9IY81F0YjfHWUcAETiAZoGkl1LNCxRxa5a7zKHapEzu/xslNjnsXtNI7bghsUix3biyHDjU+eEBsO968R7haIoe3l/qF+/15EJKB0zCbiz/+muAU9/Q6Y6NZFWmKVz4TibFS7GdxMmaB5kM2Z3fEkOv27UqUk0p9J3n0lD9y14Zw+C4zyd15Ue2WVZVVAqwysjqAIPMfhDaV2f3Tvt6H6Ft8zolb1pjIl/EH09jUW2cRg7B3Q97qGFA0NVJ9CKDTbCwETEBiqnxiDrvHc2Pa4W19ZCIR3Ne3LorlaXLjMGSEmOmWBB10YH58FyIupUCCOLsK5PcBQoD+KoeVXkF4IhZJtajdlzpH4AmKP9MfxTZvQZO0vtixzYDyJ3PlNBa8zDKvJoJ01PIqZ1Z+AVo0pzesQ42Hz8Ch1V8wouwFN97Ah8QZzPJdR+kUcuBbqJ6u8WWZM3ippzpVdjQg5QwzjDxWxmgqgnTFXyW00yqNPQQDxi0bCXRD8x2f8P0Weh+NeWwajz4lXY310FfqWsCP07UsnsYxIk3vG1n5U37pJJZiJb3m07zH/HTwELeobIzYs2292tqW6yDSH8IZEHyBnvdtcIJ9ZrykG3NLjWByTA4w0j+VaiamfHmUIzY13q/PxCJxZUn8VnlZFJHXrI7iTO+FWKD39EZsKflsqslJZfFfVGGaJlPehq4nkiV4/YYP4Yi92DIkjCHiXXO4BlxWJbX+nPX1NF2B4ImHOThZM5Qr1yjv0oGKtxIx0zGuBLYiOWMTPGGLV4O+DBOkPm2XK3BUfoHjEIdAVprwqWK+4iah8XWmOrWuyk41OlvAd51h3LZRvRnkw+skhB+GOYDeQHKcmtSH645Hl2SU/abghLs8owaCPGorTDXNIBxbhoAFCRXVl5hMzA0hXnqWzWHHNpq3BKXGRCSovuYEjPlXM4KRR6n+cNzhWqXdpCkaNpXz8sy6rw43BM98Tf6djTdYR6QN+YYSC4/CD+ddxtmFI0l/tsNb5VU8fntbDQx5PS4Y7J37PbKvKS1gNCQo0Rlyc3GteFTKl/eg6nJMed86KaUYgO7I2XL8zzQ7C8ssgh6z07U9TyIHVw/ld80/B6UjwJdzu+o2izrQtOotgO1kZ2R6hhP5gA6z23UQ8RcOLPmOcHbq25kNGTsLmMLM6beFUQfn+W3TsWi0Nz5SLm+qOCbc/LEPNV0Y1ZcS1XwLHpr/feeflmOjc02/NCJrOxtRh4u/xPQtNQ90nGGXnajzaHY8Cmtl1bSBQ5ldfBevc1F0jGQeXQh26wwB7O/E6ypDyQw3glxzn2whATTBUGRNb5aM1b8QG7HwGDapucIGlc7EvGqnIOHKWZ7yYhmfZzAgW45pdtOybKQrzmrwIjngD/9z2a4BduwJwR40kS8n3am0Ao7UcLSv1LWL1Yn4rn8TiGhsBMOuGtepECOABBhqkB8Z0YXxgf0tUytSapuFX2NV45QXocoxd4xoLcyNeq1dWyTdlgqOzkvdsjUlMDyccmHUC0uQGaVnt7tV2D9lOZW0YgGcHFJN/MfhnVuoYLyiX+tJDsXXOvA5wMuU+jawoJ4a1MTiUBVLzS5W1iyO56od06nJhmM5ld0coIGW9bal//LSM2jG3hqeHjggy+QoDyCO+3gCYGAP7spmyb+UA0bjLnUROEKp843kx87iRnFb31dzX3PhvYEO5mRFnbAFpsxp+w99Z1Aspc0cJn0+4hTNTZ3ffRWmNSfhKkuitqeIUSLfIyZIQ5qbgeisH0bl783wm4f/1NKB/x0fWbrs3JOag+7BN3YJeihEx+YG8SQlyrNiZ8eBV8WLPl6BbML8ap5fxEwghKQkBVBnPNkRRxVSvT1bQetqWgtJHd0GpTyLp9oyMuOeSAobxk+dV0c+BXxD1WIuy/2pa7lOEiYTTVjlsXVI2QS6maV3dxf1T9gZHFaLPBKnHajQErCD71zpEHiByCRUQ0oO9bMvCpRyi8SYZX4mxFiT2KenJpqWHc6SPGpz1fq/RzzSAf3hhgRSYGZjhg+MLDIh3QhgB2oxE7aoNR5Wi2BpkPlh3cpg+KJXNjKs5DX6csBu1Ooeprc9jclolszA3jdFiNCI3vcCfLkHiZ0nmtAin9UzinLss/M2Dfzt6CZKM5s0JrNfpOxMIV+nOGR8YU0+BlkEych3vFqtQ3ETKpVNCesMGzYIAbsn4lehYv97lbneHJP6/zMlenEqF4etlaE/OkORz2LVcmPtfMYdetcWK+xZN/syzSYvYfmNTGJNppvhuKHnviAaL3TBx3PlTAFm3waadzWBitNzSVjdqtnNmgiOWaHYK6j3LosuigFnoqb0XtxP4zXMcruF/ikg1tFa4R0BSPXXikibnbqU7odFPT0jfY4KQn37rYu9U8iYqfmni7l/fu/MNH9qf7e/AQaKWL9cJATn48iIqLtT1OX4SEuPJUG1gYaQgZx5lVAQIKjI64hIq36c7syBGP1rlzmy7NKEbuE3kvQV0qOm2VUTA6u2lRxBTZfJu0VVJViVRdY1Jxw9aX/SFR1A2peGTXv4L7IdnGdRS971EkroEm/LiYibySrxxKhIMgr34jKa1p7mgjuGlPk7mvGkY079zlWsPr0UypSTQeMMThqNNZiycPQBjuMEkj2TxF11+5X1V+Xg/Bef/o9hYR0cOFh+zIjyd5O+Wj+dSfhEiBlFX5mdR+Av+00Ryvs0ePrDAOs2gfBGNqWpROMMwILTc6e4VgYn6+f4g8DYJzuS8UnlvIq1aH9W/UB+x9gH7tDogA9rIHn/XRSNiuJJA7Sqr2O1aB+iA/AokQFwISfi9Sjf57ih844QES0G9yDgTOCiylpkrR/FYtZcB2LlocbQvydmZwxoXpnHER8x/SW3YG85i6e1E2utK0MWN+WQs3QrHtttdfnp/j7QjKJpsLT2dS24MCgj7CDNJcY0SXyDWs4rmVj50zvzZhs3W+jNrvDIsh3FRGSu/fXiezJWIb4m3gevc8lomm5hzar1eBKSnNm5yZDU81HBKtxeDjmGLQ7jmxaqLb3+DvIs4v9HWYqs0uZ2CU1MA79FCAnX1LVJKzYROxAvhQPsukZ+tk38lClzkPryN7HGHR0xF0+UP9wVX9hK5Ss1CX+2lGTU0XkE8kufXTYh1R/Xque9AORgwT9IaumMnlPC9K1i64txCbHHmRgYtNcnwlbVMYy7UFH1Bc7IqYKQnu2l6cNoococsfwWJ6ciAJ+w8hMs2/ILnFvfvQunYVdEqu35GLlTZVF8O/+j9VydwwwzpEynZG6AzoKyP8GlPhQ8V/CcYXVBkEFMq4qUgvHhjoHRjuUom0IAx6InT4H8dG36UvslmzL2cNwP0NEDMu59lEGBAaa7RCjvR7frUMjjEXkcnXZH3KDPy/dwN5UX7FyghMdM4GxfSy7p79+ccJhjtTPoXrcVsMJdEntXhznJoSt51r3ZgeJNpwtrK/5d1jlLd5VGQDzH6UzH2gYxllNLjjoxaMNT30eGJ0nzaVWzn0X2JwLug1ZMOFPqyMDfrl94LzuxNWOUCw9cJ9pbqVaPmIzFM7o88O7BYJ5M90vgYqQvaTz0a2poECEoah/z2bxYrmDSIYiWnJ5TDFy3sOV2OHn8LLMS5JTV1bJh1uaht9pbjJjj1Rp9FetVUwXixiDNbSYPFUhkl3SXFeYTf5YWEpp6sVbMSKatWTUenQ4qESuZN+lKDSrOeIN1PheQkLwEAgIJYvMQkB23khDTqvMnVHahNU4OKZn+Xs+iyWa42/K2neqT+D4IOTAfUUXX0HGQJI0+8KE5NZrEMB+bB7mKRTNzW7Rg1aRmrgZoLgnb3hIft5VMrgebQSQ5UY0YwTNXg4fko+TlKoyEbO8PrYpawQ0vJOMt3T7lELcG+mWqHla7ba0o997l6KGrKcDT/cEe4z/hU5AQi391v+WfwSjxaMQewxCwh93+rUqqMXCWjVAi8SoxH514jVlMz1ukEXPMxiiwxb4+feME5aYN3pqdoPEccJZzEQpYQy9z02hLdTLMnrT3But8n1JjoT4AwTqBmzHlxPMSoWJvXKQ3nkUVb6BKhp3EM+kOd1kSw8HsRG/aWeZtTdm+CEOqX7YQYm47QKNOb3xS3dhzv4JsDvM/kkkfwYN/h/hOjbSZXUwdgvFtVznSu//3I5rxg6g6kDew1H8Y4Ax0yMhPSwycXtHqfbBptXOFfhvoJoYiz1w0nw5DhjFlLqMjBvj0mbWWpzIikpbl4cSpmmX2UP/HTfsf5BEull4YmY6/wNIc8jRlJbaENYf30De9ENhBjQRPDsjSXyeJb/2sa0Izb00G+bZtF63iRtPRgiBr2rJd1BDKOYSZcAMbB56o1dUwOc755Nhww/FGU9do3vpbFKgaFME8GXngdguC7+fh8DaFwe0B3J0iAhSQ9wX9VrU+XWsUCrK6Zlo2/33i0+5bbzprIhVBbnw78N1efYLOmEEkZvnRVXMyXmTapgDRmOVSuaK3vH5RKhFl28oUd4EQNALQ9r3vtAVr9sLkL55neaBAbGJ1HJeLATcgr5IcXkg3oI8mP2qDrWjD3NtQfezYej7uYslASEHMWeZ4EwyJDAVYaV1y6XPv10I6AFzT1iTwEnG614Ky9goFJDimDcIpnaDxZIe0A81kNF20y5ia7M3ycJK/G5QwvVxvy3qh4tDQKBn7GBbjFXmeVRasZtGiMgcyn4DpO6vABa3OMc+BAE8lekI8qdEDxo2c5ef09y63gL8TELxHfMEHoqUfDebn1miYLGVax5yqkvKbGME67SBJ2Y2GvFsfckeOU59GfzuVIpimjN+mqHy7FVryZVmdxSp4Klqdoq50ef0GstffYrw7Hb5HKRL/Hnjssxs+MqI0+b1M6DABP8WGsmFyU+7aToPMZGDmbEqr2K7/rCCrGwyYAcYOLjrT4sgGLeiNE9omcgYLPRYX0IV2QyKb7TmxCLQfB/J5Ea0MwIjqQZGZNzsCM8ox1TR+P5n/TQM+H+zk3miFzsbWRz6YYcLCKrGIr/jwXH++fsDl7bPWOakRR7xVnJs4/h260OvB9Q/xf/0QdeNMZK3XlK7n/gnFUsWFJCYISyLmVnl1qglxu4ohqS9r16Yra4HcqozrJfP6BWh/URq8lK8a1ssw7i3erS8umy4PZcrlI7H6YArt1v3K7tp3eQWhU0AngaQ6tguLoWTBzk46qBC4FV04suKlw0Prz0rOd0RM5g3ozm1NbbFvjUFBdRJDLBDaLpJRJajS6HpFZpNPaC7wQxBvOX1uMjuqfFB55VYT9sTOhIG1Rh5E8MiLulPFpotx6AkwgjnrRVhnd/plrSn8JRIXkXV+h4T69R7k+xHvOq6cJUwOlxyQXlgmM3M8UgYMSTRjjHhVPKF8ex3ikm2GPqEpsaaDn0RdvHAYja8333e2Ug8Q/1umXGi2XaOE1pIIWDF7Qry78tpAn1Mi2Xn3Ylo1AJXRRrj9AeJCMBNikTXEDvIwK5E+/luwOELnE1YslkQrZGPzAEoM+5GsqHZOpvI6F3dykob9wHIwrO6TShpcOlAc6LPqVmXYLXLMgnHCmE6/Cbl9000A6sXRmfNxIk/3+Eo3eY2WqW+YVt9XSWYQ56+/NV2HHVSMsGObNFZEGrUGyzc5L5+OrQVG0dVe/zGE5e0hERk0qj0l3JWapFIbzfm5Pw+qtg4IC6ylw0qiLma5XiDFQoxLMyCMXGEb0PbAdx02wZpxGK5LPsJf0falMrQOO6NSvDNb6+SBNvv2PoT5n1Vf9i7k8bwQ8ZR0FK34fA2WtlktA9ngh72lQcrxH+3wJ3sHUrqd1c0cgWNvEtmtRBS0dC7AR6Eveh/X4kHLQadOQDga8SD+WXyNPnlAzPoHmYU9zzJ9cM+9AXtKQ95kbm4iJFxu2i5ajv8LMgECo8CnnVt8ftW8FPhnYDeYtDSVY4A+aZd+Wmsr+SLzeFs8/R28VbUkgsL36/nLoGS+0s+SG+OSFWKtYSncPitI87m22Hsz4ZY+1Jynf+FbMoDrP7uDf5UGCaZIynsMq8rwaZzzQWNPK7aP1Za/yKRr2PhOTHHXFoQuwPlNPx1FO0UHKquzNirkhjRCApk+sHFsY9mkUahC33aXnQ08YEITCloGZQb+FIL5cE7NhqDa+UvDyWOBLQ6zZeHwQXrs3NY6RgnQ5mCJiIXSA45SJ1gEnlYvnGX9bj+z5et0QX0wflcQayV/B+sC48M59QKesGGAPhsH5wCRXAj7GsCQuAJjn5l8M8zx95dQoQf3wfltMOSh+mE6jy8q4ukof7dvlOeOD9unPKx56BSKFeX59T3pEM9gYMR9EN4IN7U5XGWbEi+6EfcTfUxgfNboFPykakTzku3uUABUpo3/8UMAsydGVg1cudDrBF+RnZT2vgK5i/1w5O9FPZvonXUq+5nHRJVUYn33GyccNkjrs+Y+yt08+QVDy+6wsX1SPOtEUaE59cpEdsjhbguOTn7/Kb6IbfeCnVChk6sBTLsvh7D4LUOQtbkzxhz+XCGFeRhvp4l3pBwmP8dv4PDROrAqp5UBIhKaJcZvvAhSwY2cbo+CNUYCcwrjWqjOwyEmedd2kCJp4s2b9bewldPHan5WX6AMecl/4NXp1sQrHPjO9en0Xz5n92qKpdZQC8p2AdSlASNhAAKBu6nBzlMbzoyex/vlJcWtAzMyleHdvHOHpFF0z0SF4kmkpGMz/WKxpH2cagE973ExU1ClJi/BIcjP+p203Z/YBTkZCFhy2GH8DK4jXWvPJIfkG5m5Z1xbrkNwJIfUfd90ubiYgbhxawEbKRjhFDtl7PV1wA8fHKOxCY6I0DN0mqxIDYgxc0FVE85+NSYR8z5ucjFCHQmCoSwWohznWvIANmVgDt5tu6HeCirL02uGNgmvpStMxosgYKU4Xt84XstQm19eQnJXTiceTogNzh3X3UWxELUnc46UC9NiMOZrOq/vKRgRUvW1JhwBhpmYOOyOdn8EqsJyAsjgFWNZE3gwi3RIaA6MIjkO6yCtJPs2g== +--------------------------18610972117ffaf2-- diff --git a/core-tests/fuzz/cases/req2.bin b/core-tests/fuzz/cases/req2.bin new file mode 100644 index 00000000000..7c0015fa2b0 Binary files /dev/null and b/core-tests/fuzz/cases/req2.bin differ diff --git a/core-tests/fuzz/project.json b/core-tests/fuzz/project.json new file mode 100644 index 00000000000..161f3f2dbf1 --- /dev/null +++ b/core-tests/fuzz/project.json @@ -0,0 +1,16 @@ +{ + "project": { + "name": "fuzz", + "type": "bin" + }, + "build": { + "ldflags": "", + "cxxflags": "", + "cflags": "", + "c_std": "", + "cxx_std": "" + }, + "install": { + "target": "" + } +} \ No newline at end of file diff --git a/core-tests/fuzz/src/main.cpp b/core-tests/fuzz/src/main.cpp new file mode 100644 index 00000000000..d945a40e675 --- /dev/null +++ b/core-tests/fuzz/src/main.cpp @@ -0,0 +1,48 @@ +#include "phpx_embed.h" +#include +#include +#include +#include + +using namespace php; +using namespace std; + +int main(int argc, char * argv[]) +{ + VM vm(argc, argv); + cout << "hello world" << endl; + + char buf[8192]; + ssize_t n; + + int fd = 0; + if (argc > 0) { + fd = open(argv[1], O_RDONLY); + } + + n = read(fd, buf, 8192); + if (n < 0) { + fprintf(stderr, "failed to read data\n"); + return 1; + } + + auto req_var = exec("Swoole\\Http\\Request::create"); + + var_dump(req_var); + + if (!req_var.isObject()) { + fprintf(stderr, "cannot create object of Swoole\\Http\\Request\n"); + return 2; + } + + Variant data(buf, n); + + auto req = Object(req_var); + auto retval = req.exec("parse", data); + + printf("retval=%ld", retval.toInt()); + + var_dump(req); + + return 0; +} diff --git a/core-tests/include/httplib_client.h b/core-tests/include/httplib_client.h new file mode 100644 index 00000000000..aa22c03a87a --- /dev/null +++ b/core-tests/include/httplib_client.h @@ -0,0 +1,4039 @@ +/** + * httplib.h + * + * Copyright (c) 2020 Yuji Hirose. All rights reserved. + * MIT License + * GitHub: https://github.com/yhirose/cpp-httplib + */ + +#pragma once + +#include "swoole_websocket.h" + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY true +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0)) +#endif + +// Prefer gnu::deprecated, otherwise gcc complains if we use +// [[deprecated]] together with pedantic. +#ifndef CPPHTTPLIB_DEPRECATED +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(gnu::deprecated) +#define CPPHTTPLIB_DEPRECATED [[gnu::deprecated]] +#else +#if __has_cpp_attribute(deprecated) +#define CPPHTTPLIB_DEPRECATED [[deprecated]] +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = int; +#endif + +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#include +#include +#include +#include +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include + +using socket_t = int; +#define INVALID_SOCKET (-1) +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#include +#include +#include + +#include +#include +#include + +// #if OPENSSL_VERSION_NUMBER < 0x1010100fL +// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported +// #endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} +#endif +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif +/* + * Declaration + */ +namespace httplib { + +const std::string USER_AGENT = "cpp-httplib/0.7"; + +namespace detail { + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } +}; + +} // namespace detail + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +struct WebSocketFrame; + +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { + public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function done; + std::function is_writable; + std::ostream os; + + private: + class data_sink_streambuf : public std::streambuf { + public: + data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = std::function; + +using ChunkedContentProvider = std::function; + +using ContentReceiver = std::function; + +using MultipartContentHeader = std::function; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + + // for client + size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; + ResponseHandler response_handler; + ContentReceiver content_receiver; + size_t content_length = 0; + ContentProvider content_provider; + Progress progress; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl; +#endif + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; + + bool is_multipart_form_data() const; + + bool has_file(const char *key) const; + MultipartFormData get_file_value(const char *key) const; + + // private members... + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + Headers headers; + std::string body; + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + void set_redirect(const char *url, int status = 302); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(std::string s, const char *content_type); + + void set_content_provider(size_t length, ContentProvider provider, std::function resource_releaser = [] {}); + + void set_chunked_content_provider(ChunkedContentProvider provider, std::function resource_releaser = [] {}); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + std::function content_provider_resource_releaser_; +}; + +struct WebSocketFrame : public swoole::websocket::Frame { + WebSocketFrame() = default; + ~WebSocketFrame() { + if (payload) { + sw_free(payload - header_length); + } + } +}; + +class Stream { + public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + + template + ssize_t write_format(const char *fmt, const Args &... args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), sizeof(yes)); +#endif +#endif +} + +class Client { + public: + explicit Client(const std::string &host); + + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char *path); + + std::shared_ptr Get(const char *path, const Headers &headers); + + std::shared_ptr Get(const char *path, Progress progress); + + std::shared_ptr Get(const char *path, const Headers &headers, Progress progress); + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver, Progress progress); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress); + + bool Upgrade(const char *path, Headers &headers); + + std::shared_ptr Head(const char *path); + + std::shared_ptr Head(const char *path, const Headers &headers); + + std::shared_ptr Post(const char *path); + + std::shared_ptr Post(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, const Params ¶ms); + + std::shared_ptr Post(const char *path, const Headers &headers, const Params ¶ms); + + std::shared_ptr Post(const char *path, const MultipartFormDataItems &items); + + std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items); + + std::shared_ptr Put(const char *path); + + std::shared_ptr Put(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, const Params ¶ms); + + std::shared_ptr Put(const char *path, const Headers &headers, const Params ¶ms); + + // websocket + inline bool Push(const std::string &data, int opcode = swoole::websocket::OPCODE_TEXT) { + return Push(data.c_str(), data.length(), swoole::websocket::OPCODE_TEXT); + } + bool Push(const char *data, size_t length, int opcode = swoole::websocket::OPCODE_TEXT); + std::shared_ptr Recv(); + + std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Delete(const char *path); + + std::shared_ptr Delete(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Delete(const char *path, const Headers &headers); + + std::shared_ptr Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Options(const char *path); + + std::shared_ptr Options(const char *path, const Headers &headers); + + bool send(const Request &req, Response &res); + + size_t is_socket_open() const; + + void stop(); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + CPPHTTPLIB_DEPRECATED void set_timeout_sec(time_t timeout_sec); + void set_connection_timeout(time_t sec, time_t usec = 0); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + + void set_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + + void set_logger(Logger logger); + + protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { + return sock != INVALID_SOCKET; + } + }; + + virtual bool create_and_connect_socket(Socket &socket); + virtual void close_socket(Socket &socket, bool process_socket_ret); + + bool process_request(Stream &strm, const Request &req, Response &res, bool close_connection); + + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = 80; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + + Logger logger_; + + void copy_settings(const Client &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif + logger_ = rhs.logger_; + } + + private: + socket_t create_client_socket() const; + bool read_response_line(Stream &strm, Response &res); + bool write_request(Stream &strm, const Request &req, bool close_connection); + bool redirect(const Request &req, Response &res); + bool handle_request(Stream &strm, const Request &req, Response &res, bool close_connection); + + std::shared_ptr send_with_content_provider(const char *method, + const char *path, + const Headers &headers, + const std::string &body, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + virtual bool process_socket(Socket &socket, std::function callback); + virtual bool is_ssl() const; +}; + +inline void Get(std::vector &requests, const char *path, const Headers &headers) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + requests.emplace_back(std::move(req)); +} + +inline void Get(std::vector &requests, const char *path) { + Get(requests, path, Headers()); +} + +inline void Post(std::vector &requests, + const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "POST"; + req.path = path; + req.headers = headers; + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + req.body = body; + requests.emplace_back(std::move(req)); +} + +inline void Post(std::vector &requests, const char *path, const std::string &body, const char *content_type) { + Post(requests, path, Headers(), body, content_type); +} + +inline void Post(std::vector &requests, + const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + Request req; + req.method = "POST"; + req.headers = Headers(); + req.path = path; + req.content_length = content_length; + req.content_provider = content_provider; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + + requests.emplace_back(std::move(req)); +} + +class Client2 { + public: + explicit Client2(const char *scheme_host_port) : Client2(scheme_host_port, std::string(), std::string()) {} + + explicit Client2(const char *scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re(R"(^(https?)://([^:/?#]+)(?::(\d+))?)"); + + std::cmatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + auto host = m[2].str(); + auto port_str = m[3].str(); + + auto port = !port_str.empty() ? std::stoi(port_str) : (scheme == "https" ? 443 : 80); + + if (scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + is_ssl_ = true; + cli_ = std::make_shared(host.c_str(), port, client_cert_path, client_key_path); +#endif + } else { + cli_ = std::make_shared(host.c_str(), port, client_cert_path, client_key_path); + } + } + } + + ~Client2() {} + + bool is_valid() const { + return cli_ != nullptr; + } + + std::shared_ptr Get(const char *path) { + return cli_->Get(path); + } + + std::shared_ptr Get(const char *path, const Headers &headers) { + return cli_->Get(path, headers); + } + + std::shared_ptr Get(const char *path, Progress progress) { + return cli_->Get(path, progress); + } + + std::shared_ptr Get(const char *path, const Headers &headers, Progress progress) { + return cli_->Get(path, headers, progress); + } + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver) { + return cli_->Get(path, content_receiver); + } + + std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver) { + return cli_->Get(path, headers, content_receiver); + } + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, response_handler, content_receiver); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, response_handler, content_receiver, progress); + } + + std::shared_ptr Head(const char *path) { + return cli_->Head(path); + } + + std::shared_ptr Head(const char *path, const Headers &headers) { + return cli_->Head(path, headers); + } + + std::shared_ptr Post(const char *path) { + return cli_->Post(path); + } + + std::shared_ptr Post(const char *path, const std::string &body, const char *content_type) { + return cli_->Post(path, body, content_type); + } + + std::shared_ptr Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Post(path, headers, body, content_type); + } + + std::shared_ptr Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, content_length, content_provider, content_type); + } + + std::shared_ptr Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Post(const char *path, const Params ¶ms) { + return cli_->Post(path, params); + } + + std::shared_ptr Post(const char *path, const Headers &headers, const Params ¶ms) { + return cli_->Post(path, headers, params); + } + + std::shared_ptr Post(const char *path, const MultipartFormDataItems &items) { + return cli_->Post(path, items); + } + + std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); + } + + std::shared_ptr Put(const char *path) { + return cli_->Put(path); + } + + std::shared_ptr Put(const char *path, const std::string &body, const char *content_type) { + return cli_->Put(path, body, content_type); + } + + std::shared_ptr Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Put(path, headers, body, content_type); + } + + std::shared_ptr Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, content_length, content_provider, content_type); + } + + std::shared_ptr Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Put(const char *path, const Params ¶ms) { + return cli_->Put(path, params); + } + + std::shared_ptr Put(const char *path, const Headers &headers, const Params ¶ms) { + return cli_->Put(path, headers, params); + } + + std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type) { + return cli_->Patch(path, body, content_type); + } + + std::shared_ptr Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Patch(path, headers, body, content_type); + } + + std::shared_ptr Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, content_length, content_provider, content_type); + } + + std::shared_ptr Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Delete(const char *path) { + return cli_->Delete(path); + } + + std::shared_ptr Delete(const char *path, const std::string &body, const char *content_type) { + return cli_->Delete(path, body, content_type); + } + + std::shared_ptr Delete(const char *path, const Headers &headers) { + return cli_->Delete(path, headers); + } + + std::shared_ptr Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Delete(path, headers, body, content_type); + } + + std::shared_ptr Options(const char *path) { + return cli_->Options(path); + } + + std::shared_ptr Options(const char *path, const Headers &headers) { + return cli_->Options(path, headers); + } + + bool send(const Request &req, Response &res) { + return cli_->send(req, res); + } + + bool is_socket_open() { + return cli_->is_socket_open(); + } + + void stop() { + cli_->stop(); + } + + void set_tcp_nodelay(bool on) { + cli_->set_tcp_nodelay(on); + } + + void set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(socket_options); + } + + Client2 &set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); + return *this; + } + + Client2 &set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); + return *this; + } + + Client2 &set_basic_auth(const char *username, const char *password) { + cli_->set_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_digest_auth(const char *username, const char *password) { + cli_->set_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_keep_alive(bool on) { + cli_->set_keep_alive(on); + return *this; + } + + Client2 &set_follow_location(bool on) { + cli_->set_follow_location(on); + return *this; + } + + Client2 &set_compress(bool on) { + cli_->set_compress(on); + return *this; + } + + Client2 &set_decompress(bool on) { + cli_->set_decompress(on); + return *this; + } + + Client2 &set_interface(const char *intf) { + cli_->set_interface(intf); + return *this; + } + + Client2 &set_proxy(const char *host, int port) { + cli_->set_proxy(host, port); + return *this; + } + + Client2 &set_proxy_basic_auth(const char *username, const char *password) { + cli_->set_proxy_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_proxy_digest_auth(const char *username, const char *password) { + cli_->set_proxy_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_logger(Logger logger) { + cli_->set_logger(logger); + return *this; + } + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); + } + return *this; + } + + Client2 &set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } + return *this; + } + + Client2 &enable_server_certificate_verification(bool enabled) { + if (is_ssl_) { + static_cast(*cli_).enable_server_certificate_verification(enabled); + } + return *this; + } + + long get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? + } + + SSL_CTX *ssl_context() const { + if (is_ssl_) { + return static_cast(*cli_).ssl_context(); + } + return nullptr; + } +#endif + + private: +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif + std::shared_ptr cli_; +}; + +// ---------------------------------------------------------------------------- + +/* + * Implementation + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) { + if (i >= s.size()) { + return false; + } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + int val = 0; + int valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { + out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); + } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { + return m[1].str(); + } + return std::string(); +} + +template +void split(const char *b, const char *e, char d, Fn fn) { + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { + fn(&b[beg], &b[i]); + } +} + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { + public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {} + + const char *ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } + + bool end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + + private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template +inline ssize_t handle_EINTR(T fn) { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { + continue; + } + break; + } + return res; +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); }); +#endif +} + +inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; +#else + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); }); + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) >= 0 && !error; + } + return false; +#endif +} + +class SocketStream : public Stream { + public: + SocketStream(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { + public: + SSLSocketStream(socket_t sock, + SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +class BufferStream : public Stream { + public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + const std::string &get_buffer() const; + + private: + std::string buffer; + size_t position = 0; +}; + +inline bool keep_alive(socket_t sock) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 100 + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + if (duration.count() > timeout) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool process_client_socket(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char *host, + int port, + int socket_flags, + bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { + continue; + } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + continue; + } +#endif + + if (tcp_nodelay) { + int yes = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(yes)); + } + + if (socket_options) { + socket_options(sock); + } + + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), sizeof(no)); + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const char *host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host, "0", &hints, &result)) { + return false; + } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#ifndef _WIN32 +inline std::string if2ip(const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); +} +#endif + +inline socket_t create_client_socket(const char *host, + int port, + bool tcp_nodelay, + SocketOptions socket_options, + time_t timeout_sec, + time_t timeout_usec, + const std::string &intf) { + return create_socket(host, port, 0, tcp_nodelay, socket_options, [&](socket_t sock, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifndef _WIN32 + auto ip = if2ip(intf); + if (ip.empty()) { + ip = intf; + } + if (!bind_ip_address(sock, ip.c_str())) { + return false; + } +#endif + } + + set_nonblocking(sock, true); + + auto ret = ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { + if (is_connection_error() || !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { + close_socket(sock); + return false; + } + } + + set_nonblocking(sock, false); + return true; + }); +} + +inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, + std::string &ip, + int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = ntohs(reinterpret_cast(&addr)->sin6_port); + } + + std::array ipstr{}; + if (!getnameinfo(reinterpret_cast(&addr), + addr_len, + ipstr.data(), + static_cast(ipstr.size()), + nullptr, + 0, + NI_NUMERICHOST)) { + ip = ipstr.data(); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } +} + +inline const char *find_content_type(const std::string &path, const std::map &user_data) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { + return it->second.c_str(); + } + + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html" || ext == "htm") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "wasm") { + return "application/wasm"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; +} + +inline const char *status_message(int status) { + switch (status) { + case 100: + return "Continue"; + case 101: + return "Switching Protocol"; + case 102: + return "Processing"; + case 103: + return "Early Hints"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choice"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "unused"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 418: + return "I'm a teapot"; + case 421: + return "Misdirected Request"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; + + default: + case 500: + return "Internal Server Error"; + } +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline bool can_compress(const std::string &content_type) { + return !content_type.find("text/") || content_type == "image/svg+xml" || content_type == "application/javascript" || + content_type == "application/json" || content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline bool compress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + return false; + } + + strm.avail_in = static_cast(content.size()); + strm.next_in = const_cast(reinterpret_cast(content.data())); + + std::string compressed; + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + ret = deflate(&strm, Z_FINISH); + assert(ret != Z_STREAM_ERROR); + compressed.append(buff.data(), buff.size() - strm.avail_out); + } while (strm.avail_out == 0); + + assert(ret == Z_STREAM_END); + assert(strm.avail_in == 0); + + content.swap(compressed); + + deflateEnd(&strm); + return true; +} + +class decompressor { + public: + decompressor() { + std::memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm, 32 + 15) == Z_OK; + } + + ~decompressor() { + inflateEnd(&strm); + } + + bool is_valid() const { + return is_valid_; + } + + template + bool decompress(const char *data, size_t data_length, T callback) { + int ret = Z_OK; + + strm.avail_in = static_cast(data_length); + strm.next_in = const_cast(reinterpret_cast(data)); + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + return false; + } + + if (!callback(buff.data(), buff.size() - strm.avail_out)) { + return false; + } + } while (strm.avail_out == 0); + + return ret == Z_OK || ret == Z_STREAM_END; + } + + private: + bool is_valid_; + z_stream strm; +}; +#endif + +inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, const char *key, size_t id = 0, const char *def = nullptr) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second.c_str(); + } + return def; +} + +inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, uint64_t def = 0) { + auto it = headers.find(key); + if (it != headers.end()) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +inline void parse_header(const char *beg, const char *end, Headers &headers) { + auto p = beg; + while (p < end && *p != ':') { + p++; + } + if (p < end) { + auto key_end = p; + p++; // skip ':' + while (p < end && (*p == ' ' || *p == '\t')) { + p++; + } + if (p < end) { + auto val_begin = p; + while (p < end) { + p++; + } + headers.emplace(std::string(beg, key_end), std::string(val_begin, end)); + } + } +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { + return false; + } + + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { + break; + } + } else { + continue; // Skip invalid line. + } + + // Skip trailing spaces and tabs. + auto end = line_reader.ptr() + line_reader.size() - 2; + while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) { + end--; + } + + parse_header(line_reader.ptr(), end, headers); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return false; + } + + if (!out(buf, static_cast(n))) { + return false; + } + + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { + return false; + } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return; + } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + if (!out(buf, static_cast(n))) { + return false; + } + } + + return true; +} + +inline bool read_content_chunked(Stream &strm, ContentReceiver out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { + return false; + } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { + return false; + } + if (chunk_len == ULONG_MAX) { + return false; + } + + if (chunk_len == 0) { + break; + } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { + return false; + } + + if (strcmp(line_reader.ptr(), "\r\n")) { + break; + } + + if (!line_reader.getline()) { + return false; + } + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) return false; + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); +} + +template +bool read_content(Stream &strm, + T &x, + size_t payload_max_length, + int &status, + Progress progress, + ContentReceiver receiver, + bool decompress) { + ContentReceiver out = [&](const char *buf, size_t n) { return receiver(buf, n); }; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor decompressor; +#endif + + if (decompress) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + std::string content_encoding = x.get_header_value("Content-Encoding"); + if (content_encoding.find("gzip") != std::string::npos || + content_encoding.find("deflate") != std::string::npos) { + if (!decompressor.is_valid()) { + status = 500; + return false; + } + + out = [&](const char *buf, size_t n) { + return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); }); + }; + } +#else + if (x.get_header_value("Content-Encoding") == "gzip") { + status = 415; + return false; + } +#endif + } + + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, progress, out); + } + } + + if (!ret) { + status = exceed_payload_max_length ? 413 : 400; + } + return ret; +} + +template +inline ssize_t write_headers(Stream &strm, const T &info, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : info.headers) { + if (x.first == "EXCEPTION_WHAT") { + continue; + } + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; + } + for (const auto &x : headers) { + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { + return len; + } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { + return false; + } + offset += static_cast(length); + } + return true; +} + +template +inline ssize_t write_content( + Stream &strm, ContentProvider content_provider, size_t offset, size_t length, T is_shutting_down) { + size_t begin_offset = offset; + size_t end_offset = offset + length; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (ok && offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } + + return static_cast(offset - begin_offset); +} + +template +inline ssize_t write_content_chunked(Stream &strm, ContentProvider content_provider, T is_shutting_down) { + size_t offset = 0; + auto data_available = true; + ssize_t total_written_length = 0; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + data_available = l > 0; + offset += l; + + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; + if (write_data(strm, chunk.data(), chunk.size())) { + total_written_length += chunk.size(); + } else { + ok = false; + } + } + }; + data_sink.done = [&](void) { + data_available = false; + if (ok) { + static const std::string done_marker("0\r\n\r\n"); + if (write_data(strm, done_marker.data(), done_marker.size())) { + total_written_length += done_marker.size(); + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } + + return total_written_length; +} + +template +inline bool redirect(T &cli, const Request &req, Response &res, const std::string &path) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count -= 1; + + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res); + if (ret) { + res = new_res; + } + return ret; +} + +inline std::string encode_url(const std::string &s) { + std::string result; + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': + result += "%20"; + break; + case '+': + result += "%2B"; + break; + case '\r': + result += "%0D"; + break; + case '\n': + result += "%0A"; + break; + case '\'': + result += "%27"; + break; + case ',': + result += "%2C"; + break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': + result += "%3B"; + break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += detail::encode_url(it->second); + } + + return query; +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + params.emplace(decode_url(key, true), decode_url(val, true)); + }); +} + +inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { + return false; + } + boundary = content_type.substr(pos + 9); + if (boundary.length() >= 2 && boundary.front() == '"' && boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); +} + +inline bool parse_range_header(const std::string &s, Ranges &ranges) { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } + + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); + } + }); + return all_valid_ranges; + } + return false; +} + +class MultipartFormDataParser { + public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { + boundary_ = boundary; + } + + bool is_valid() const { + return is_valid_; + } + + template + bool parse(const char *buf, size_t n, T content_callback, U header_callback) { + static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)", std::regex_constants::icase); + + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + auto pos = buf_.find(pattern); + if (pos != 0) { + return false; + } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + auto header = buf_.substr(0, pos); + { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file_.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { + return true; + } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos == std::string::npos) { + pos = buf_.size(); + while (pos > 0) { + auto c = buf_[pos - 1]; + if (c != '\r' && c != '\n' && c != '-') { + break; + } + pos--; + } + } + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { + return true; + } + if (buf_.find(crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + if (buf_.find(pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } + } + + return true; + } + + private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + std::string boundary_; + + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string make_multipart_data_boundary() { + static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); + + std::string result = "--cpp-httplib-multipart-data-"; + + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + + return result; +} + +inline std::pair get_range_offset_and_length(const Request &req, size_t content_length, size_t index) { + auto r = req.ranges[index]; + + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } + + auto slen = static_cast(content_length); + + if (r.first == -1) { + r.first = slen - r.second; + r.second = slen - 1; + } + + if (r.second == -1) { + r.second = slen - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline std::string make_content_range_header_field(size_t offset, size_t length, size_t content_length) { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + SToken stoken, + CToken ctoken, + Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { + return false; + } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); + + return true; +} + +inline std::string make_multipart_ranges_data(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type) { + std::string data; + + process_multipart_ranges_data(req, + res, + boundary, + content_type, + [&](const std::string &token) { data += token; }, + [&](const char *token) { data += token; }, + [&](size_t offset, size_t length) { + data += res.body.substr(offset, length); + return true; + }); + + return data; +} + +inline size_t get_multipart_ranges_data_length(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type) { + size_t data_length = 0; + + process_multipart_ranges_data(req, + res, + boundary, + content_type, + [&](const std::string &token) { data_length += token.size(); }, + [&](const char *token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool write_multipart_ranges_data(Stream &strm, + const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + T is_shutting_down) { + return process_multipart_ranges_data( + req, + res, + boundary, + content_type, + [&](const std::string &token) { strm.write(token); }, + [&](const char *token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, is_shutting_down) >= 0; + }); +} + +inline std::pair get_range_offset_and_length(const Request &req, const Response &res, size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" || + (req.method == "DELETE" && req.has_header("Content-Length"))) { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const char *s) { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { + return true; + } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +template +inline std::string message_digest(const std::string &s, Init init, Update update, Final final, size_t digest_length) { + using namespace std; + + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); + + stringstream ss; + for (auto c : md) { + ss << setfill('0') << setw(2) << hex << (unsigned int) c; + } + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, MD5_DIGEST_LENGTH); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH); +} +#endif + +#ifdef _WIN32 +class WSInit { + public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { + WSACleanup(); + } +}; + +static WSInit wsinit_; +#endif + +} // namespace detail + +// Header utilities +inline std::pair make_range_header(Ranges ranges) { + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { + field += ", "; + } + if (r.first != -1) { + field += std::to_string(r.first); + } + field += '-'; + if (r.second != -1) { + field += std::to_string(r.second); + } + i++; + } + return std::make_pair("Range", field); +} + +inline std::pair make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, + const std::map &auth, + size_t cnonce_count, + const std::string &cnonce, + const std::string &username, + const std::string &password, + bool is_proxy = false) { + using namespace std; + + string nc; + { + stringstream ss; + ss << setfill('0') << setw(8) << hex << cnonce_count; + nc = ss.str(); + } + + auto qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else { + qop = "auth"; + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { + algo = auth.at("algorithm"); + } + + string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { + A2 += ":" + H(req.body); + } + + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2)); + } + + auto field = "Digest username=\"" + username + "\", realm=\"" + auth.at("realm") + "\", nonce=\"" + + auth.at("nonce") + "\", uri=\"" + req.path + "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + + nc + "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, std::map &auth, bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 +inline std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; +} + +// Request implementation +inline bool Request::has_header(const char *key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Request::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const char *key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char *key, size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second; + } + return std::string(); +} + +inline size_t Request::get_param_value_count(const char *key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); +} + +inline bool Request::has_file(const char *key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const char *key) const { + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFormData(); +} + +// Response implementation +inline bool Response::has_header(const char *key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const char *url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } + } +} + +inline void Response::set_content(const char *s, size_t n, const char *content_type) { + body.assign(s, n); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(std::string s, const char *content_type) { + body = std::move(s); + set_header("Content-Type", content_type); +} + +inline void Response::set_content_provider(size_t in_length, + ContentProvider provider, + std::function resource_releaser) { + assert(in_length > 0); + content_length_ = in_length; + content_provider_ = [provider](size_t offset, size_t length, DataSink &sink) { + return provider(offset, length, sink); + }; + content_provider_resource_releaser_ = resource_releaser; +} + +inline void Response::set_chunked_content_provider(ChunkedContentProvider provider, + std::function resource_releaser) { + content_length_ = 0; + content_provider_ = [provider](size_t offset, size_t, DataSink &sink) { return provider(offset, sink); }; + content_provider_resource_releaser_ = resource_releaser; +} + +// Rstream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &... args) { + std::array buf; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...); +#else + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); +#endif + if (sn <= 0) { + return sn; + } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = static_cast( + _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...)); +#else + n = static_cast(snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); +#endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), + read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SocketStream::~SocketStream() {} + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { + if (!is_readable()) { + return -1; + } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return recv(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); }); +#endif +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { + return -1; + } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return send(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return send(sock_, ptr, size, 0); }); +#endif +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { + return true; +} + +inline bool BufferStream::is_writable() const { + return true; +} + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {} + +inline const std::string &BufferStream::get_buffer() const { + return buffer; +} + +} // namespace detail + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + return nullptr; + } + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, bool process_socket_ret) { + if (process_socket_ret) { + SSL_shutdown(ssl); // shutdown only if not already closed by remote + } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +inline bool process_client_socket_ssl(SSL *ssl, + socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static std::shared_ptr> openSSL_locks_; + +class SSLThreadLocks { + public: + SSLThreadLocks() { + openSSL_locks_ = std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } + + ~SSLThreadLocks() { + CRYPTO_set_locking_callback(nullptr); + } + + private: + static void locking_callback(int mode, int type, const char * /*file*/, int /*line*/) { + auto &lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); + } else { + lk.unlock(); + } + } +}; + +#endif + +class SSLInit { + public: + SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + SSL_load_error_strings(); + SSL_library_init(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif + } + + ~SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + ERR_free_strings(); +#endif + } + + private: +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLThreadLocks thread_init_; +#endif +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, + SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), + ssl_(ssl), + read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0 || is_readable()) { + return SSL_read(ssl_, ptr, static_cast(size)); + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { + return SSL_write(ssl_, ptr, static_cast(size)); + } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +static SSLInit sslinit_; + +} // namespace detail + +class SSLClient : public Client { + public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + void enable_server_certificate_verification(bool enabled); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + + private: + bool create_and_connect_socket(Socket &socket) override; + void close_socket(Socket &socket, bool process_socket_ret) override; + + bool process_socket(Socket &socket, std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success); + bool initialize_ssl(Socket &socket); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::vector host_components_; + + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + X509_STORE *ca_cert_store_ = nullptr; + bool server_certificate_verification_ = false; + long verify_result_ = 0; + + friend class Client; +}; + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : Client(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key) + : Client(host, port) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLClient::is_valid() const { + return ctx_; +} + +inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path) { + if (ca_cert_file_path) { + ca_cert_file_path_ = ca_cert_file_path; + } + if (ca_cert_dir_path) { + ca_cert_dir_path_ = ca_cert_dir_path; + } +} + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + ca_cert_store_ = ca_cert_store; + } +} + +inline void SSLClient::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { + return ctx_; +} + +inline bool SSLClient::create_and_connect_socket(Socket &socket) { + return is_valid() && Client::create_and_connect_socket(socket); +} + +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success) { + success = true; + Response res2; + + if (!detail::process_client_socket(socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) { + std::map auth; + if (parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket(socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(make_digest_authentication_header( + req3, + auth, + 1, + random_string(10), + proxy_digest_auth_username_, + proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } + + return true; +} + +inline bool SSLClient::initialize_ssl(Socket &socket) { + auto ssl = detail::ssl_new(socket.sock, + ctx_, + ctx_mutex_, + [&](SSL *ssl) { + if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) { + SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); + } else if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) { + return false; + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } else if (ca_cert_store_ != nullptr) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) { + SSL_CTX_set_cert_store(ctx_, ca_cert_store_); + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } + + if (SSL_connect(ssl) != 1) { + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { + return false; + } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + close_socket(socket, false); + return false; +} + +inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); + socket_.ssl = nullptr; + } +} + +inline bool SSLClient::process_socket(Socket &socket, std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl(socket.ssl, + socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + callback); +} + +inline bool SSLClient::is_ssl() const { + return true; +} + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert); +} + +inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char *) ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t) ASN1_STRING_length(val->d.ia5); + + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: + dsn_matched = check_host_name(name, name_len); + break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } + } + } + + if (dsn_matched || ip_mached) { + ret = true; + } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *) alt_names); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { + return true; + } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + + if (host_components_.size() != pattern_components.size()) { + return false; + } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h)); + if (!partial_match) { + return false; + } + } + ++itr; + } + + return true; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib diff --git a/core-tests/include/httplib_server.h b/core-tests/include/httplib_server.h new file mode 100644 index 00000000000..489893f2837 --- /dev/null +++ b/core-tests/include/httplib_server.h @@ -0,0 +1,1060 @@ +/** + * httplib.h + * + * Copyright (c) 2020 Yuji Hirose. All rights reserved. + * MIT License + * GitHub: https://github.com/yhirose/cpp-httplib + */ + +#pragma once + +#include "swoole_coroutine_system.h" +#include "swoole_coroutine_socket.h" +#include "httplib_client.h" + +using swoole::Coroutine; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using swoole::network::Address; + +namespace httplib { + +class ContentReader { + public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(reader), multipart_reader_(multipart_reader) {} + + bool operator()(MultipartContentHeader header, ContentReceiver receiver) const { + return multipart_reader_(header, receiver); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(receiver); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +class Server { + public: + using Handler = std::function; + using HandlerWithContentReader = + std::function; + using Expect100ContinueHandler = std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const char *pattern, Handler handler); + Server &Post(const char *pattern, Handler handler); + Server &Post(const char *pattern, HandlerWithContentReader handler); + Server &Put(const char *pattern, Handler handler); + Server &Put(const char *pattern, HandlerWithContentReader handler); + Server &Patch(const char *pattern, Handler handler); + Server &Patch(const char *pattern, HandlerWithContentReader handler); + Server &Delete(const char *pattern, Handler handler); + Server &Delete(const char *pattern, HandlerWithContentReader handler); + Server &Options(const char *pattern, Handler handler); + + CPPHTTPLIB_DEPRECATED bool set_base_dir(const char *dir, const char *mount_point = nullptr); + bool set_mount_point(const char *mount_point, const char *dir); + bool remove_mount_point(const char *mount_point); + void set_file_extension_and_mimetype_mapping(const char *ext, const char *mime); + void set_file_request_handler(Handler handler); + + void set_error_handler(Handler handler); + void set_expect_100_continue_handler(Expect100ContinueHandler handler); + void set_logger(Logger logger); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_keep_alive_max_count(size_t count); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + void set_idle_interval(time_t sec, time_t usec = 0); + + void set_payload_max_length(size_t length); + + bool bind_to_port(const char *host, int port, int socket_flags = 0); + int bind_to_any_port(const char *host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + inline void BeforeListen(std::function fn) { + before_listen_callback_ = fn; + } + + bool is_running() const; + void stop(); + + protected: + bool process_request(Stream &strm, + bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + Socket *svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + + std::function before_listen_callback_ = nullptr; + + private: + using Handlers = std::vector>; + using HandlersForContentReader = std::vector>; + + Socket *create_server_socket(const char *host, int port, int socket_flags, SocketOptions socket_options) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(Request &req, Response &res, bool head = false); + bool dispatch_request(Request &req, Response &res, Handlers &handlers); + bool dispatch_request_for_content_reader(Request &req, + Response &res, + ContentReader content_reader, + HandlersForContentReader &handlers); + + bool parse_request_line(const char *s, Request &req); + bool write_response(Stream &strm, bool close_connection, const Request &req, Response &res); + bool write_content_with_provider( + Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool read_content_with_content_receiver(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); + + virtual bool process_and_close_socket(Socket *sock); + + std::atomic is_running_; + std::vector> base_dirs_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; +}; + +// HTTP server implementation +inline Server::Server() : svr_sock_(nullptr), is_running_(false) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() { + if (svr_sock_) { + delete svr_sock_; + } +} + +inline Server &Server::Get(const char *pattern, Handler handler) { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, Handler handler) { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, HandlerWithContentReader handler) { + post_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, Handler handler) { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, HandlerWithContentReader handler) { + put_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, Handler handler) { + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, Handler handler) { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Options(const char *pattern, Handler handler) { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline bool Server::set_base_dir(const char *dir, const char *mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const char *mount_point, const char *dir) { + if (detail::is_dir(dir)) { + std::string mnt = mount_point ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.emplace_back(mnt, dir); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const char *mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->first == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline void Server::set_file_extension_and_mimetype_mapping(const char *ext, const char *mime) { + file_extension_and_mimetype_map_[ext] = mime; +} + +inline void Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); +} + +inline void Server::set_error_handler(Handler handler) { + error_handler_ = std::move(handler); +} + +inline void Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; +} + +inline void Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = socket_options; +} + +inline void Server::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +inline void Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); +} + +inline void Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; +} + +inline void Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; +} + +inline void Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; +} + +inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; +} +inline int Server::bind_to_any_port(const char *host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + return listen_internal(); +} + +inline bool Server::listen(const char *host, int port, int socket_flags) { + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { + return is_running_; +} + +inline void Server::stop() { + if (is_running_) { + is_running_ = false; + svr_sock_->cancel(SW_EVENT_READ); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) { + const static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " + "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3], false); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { + detail::parse_query_text(m[4], req.params); + } + + return true; + } + + return false; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, const Request &req, Response &res) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } + + detail::BufferStream bstrm; + + // Response line + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, detail::status_message(res.status))) { + return false; + } + + // Headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!close_connection && req.get_header_value("Connection") == "Keep-Alive") { + res.set_header("Connection", "Keep-Alive"); + } + + if (!res.has_header("Content-Type") && (!res.body.empty() || res.content_length_ > 0)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + std::string content_type; + std::string boundary; + + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); + + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + res.headers.emplace("Content-Type", "multipart/byteranges; boundary=" + boundary); + } + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field(offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + } else { + res.set_header("Content-Length", "0"); + } + } + } else { + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field(offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + res.body = res.body.substr(offset, length); + } else { + res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + const auto &encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && detail::can_compress(res.get_header_value("Content-Type"))) { + if (detail::compress(res.body)) { + res.set_header("Content-Encoding", "gzip"); + } + } +#endif + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } + + if (!detail::write_headers(bstrm, res, Headers())) { + return false; + } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + + // Body + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!strm.write(res.body)) { + return false; + } + } else if (res.content_provider_) { + if (!write_content_with_provider(strm, req, res, boundary, content_type)) { + return false; + } + } + } + + // Log + if (logger_) { + logger_(req, res); + } + + return true; +} + +inline bool Server::write_content_with_provider( + Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type) { + auto is_shutting_down = [this]() { return this->svr_sock_ == nullptr; }; + + if (res.content_length_) { + if (req.ranges.empty()) { + if (detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down) < 0) { + return false; + } + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + if (detail::write_content(strm, res.content_provider_, offset, length, is_shutting_down) < 0) { + return false; + } + } else { + if (!detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, is_shutting_down)) { + return false; + } + } + } else { + if (detail::write_content_chunked(strm, res.content_provider_, is_shutting_down) < 0) { + return false; + } + } + return true; +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + if (read_content_core(strm, + req, + res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { + return false; + } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { + return false; + } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, receiver, multipart_header, multipart_receiver); +} + +inline bool Server::read_content_core(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiver out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = std::min(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, mulitpart_header); + }; + } else { + out = receiver; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, Progress(), out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(Request &req, Response &res, bool head) { + for (const auto &kv : base_dirs_) { + const auto &mount_point = kv.first; + const auto &base_dir = kv.second; + + // Prefix match + if (!req.path.find(mount_point)) { + std::string sub_path = "/" + req.path.substr(mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = base_dir + sub_path; + if (path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } + } + } + return false; +} + +inline Socket *Server::create_server_socket(const char *host, + int port, + int socket_flags, + SocketOptions socket_options) const { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (swoole_coroutine_getaddrinfo(host, service.c_str(), &hints, &result)) { + return nullptr; + } + + Socket *sock = nullptr; + for (auto rp = result; rp; rp = rp->ai_next) { + sock = new Socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock->get_fd() == INVALID_SOCKET) { + delete sock; + continue; + } + if (tcp_nodelay_) { + sock->set_option(IPPROTO_TCP, TCP_NODELAY, 1); + } + if (socket_options) { + socket_options(sock->get_fd()); + } + if (rp->ai_family == AF_INET6) { + sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0); + } + if (!sock->bind(rp->ai_addr, static_cast(rp->ai_addrlen))) { + delete sock; + return nullptr; + } + if (!sock->listen(512)) { + delete sock; + return nullptr; + } + break; + } + + freeaddrinfo(result); + return sock; +} + +inline int Server::bind_internal(const char *host, int port, int socket_flags) { + if (!is_valid()) { + return -1; + } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == nullptr) { + return -1; + } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_->get_fd(), reinterpret_cast(&addr), &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +struct CoroutineArg { + Socket *client_socket; + Server *this_; +}; + +inline bool Server::listen_internal() { + if (!svr_sock_) { + return false; + } + + is_running_ = true; + + if (before_listen_callback_) { + before_listen_callback_(); + } + + while (is_running_) { + auto client_sock = svr_sock_->accept(); + if (client_sock) { + auto arg = new CoroutineArg; + arg->client_socket = client_sock; + arg->this_ = this; + Coroutine::create( + [](void *arg) { + CoroutineArg *_arg = (CoroutineArg *) arg; + _arg->this_->process_and_close_socket(_arg->client_socket); + delete _arg; + }, + arg); + continue; + } + if (svr_sock_->errCode == EMFILE || svr_sock_->errCode == ENFILE) { + System::sleep(SW_ACCEPT_RETRY_TIME); + continue; + } else if (svr_sock_->errCode == ETIMEDOUT || svr_sock_->errCode == SW_ERROR_SSL_BAD_CLIENT) { + continue; + } else if (svr_sock_->errCode == ECANCELED) { + break; + } else { + swoole_warning("accept failed, Error: %s[%d]", svr_sock_->errMsg, svr_sock_->errCode); + break; + } + } + + is_running_ = false; + return true; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, receiver, nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, header, receiver); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader(req, res, reader, post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader(req, res, reader, put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader(req, res, reader, patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader(req, res, reader, delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { + return false; + } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = 400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, Handlers &handlers) { + try { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + } catch (const std::exception &ex) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", ex.what()); + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + return false; +} + +inline bool Server::dispatch_request_for_content_reader(Request &req, + Response &res, + ContentReader content_reader, + HandlersForContentReader &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool Server::process_request(Stream &strm, + bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { + return false; + } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + // TODO: error + } + } + + if (setup_request) { + setup_request(req); + } + + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, detail::status_message(status)); + break; + default: + return write_response(strm, close_connection, req, res); + } + } + + // Rounting + if (routing(req, res, strm)) { + if (res.status == -1) { + res.status = req.ranges.empty() ? 200 : 206; + } + } else { + if (res.status == -1) { + res.status = 404; + } + } + + return write_response(strm, close_connection, req, res); +} + +inline bool Server::is_valid() const { + return true; +} + +namespace detail { + +template +inline bool process_server_socket_core(socket_t sock, size_t keep_alive_max_count, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { + break; + } + count--; + } + return ret; +}; + +template +inline bool process_server_socket(socket_t sock, + size_t keep_alive_max_count, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + return process_server_socket_core(sock, keep_alive_max_count, [&](bool close_connection, bool connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +} // namespace detail + +class CoSocketStream : public detail::SocketStream { + public: + CoSocketStream(Socket *sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : detail::SocketStream( + sock->get_fd(), read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec) { + sock_ = sock; + sock->set_timeout((double) read_timeout_sec + ((double) read_timeout_usec / 1000000), Socket::TIMEOUT_READ); + sock->set_timeout((double) write_timeout_sec + ((double) write_timeout_usec / 1000000), Socket::TIMEOUT_WRITE); + } + ~CoSocketStream() {} + bool is_readable() const { + return true; + } + bool is_writable() const { + return true; + } + ssize_t read(char *ptr, size_t size) { + return sock_->recv_with_buffer(ptr, size); + } + ssize_t write(const char *ptr, size_t size) { + return sock_->write(ptr, size); + } + void get_remote_ip_and_port(std::string &ip, int &port) const { + Address sa; + sock_->getpeername(&sa); + ip = std::string(sa.get_ip()); + port = sa.get_port(); + } + + private: + Socket *sock_; +}; + +inline bool Server::process_and_close_socket(Socket *sock) { + size_t keep_alive_max_count = keep_alive_max_count_; + time_t read_timeout_sec = read_timeout_sec_; + time_t read_timeout_usec = read_timeout_usec_; + time_t write_timeout_sec = write_timeout_sec_; + time_t write_timeout_usec = write_timeout_usec_; + + CoSocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + + assert(keep_alive_max_count > 0); + + auto ret = false; + auto count = keep_alive_max_count; + + do { + auto close_connection = count == 1; + auto connection_closed = false; + ret = process_request(strm, close_connection, connection_closed, nullptr); + if (!ret || connection_closed) { + break; + } + count--; + } while (count > 0 && sock->check_liveness()); + + sock->shutdown(); + sock->close(); + delete sock; + + return ret; +} + +} // namespace httplib diff --git a/core-tests/include/redis_client.h b/core-tests/include/redis_client.h new file mode 100644 index 00000000000..fdcbf6a450f --- /dev/null +++ b/core-tests/include/redis_client.h @@ -0,0 +1,67 @@ +#pragma once + +#include "swoole.h" + +#include +#include +#include + +#include "hiredis.h" + +namespace swoole { + +class RedisReply { + private: + redisReply *ptr_; + + public: + RedisReply(void *ptr) : ptr_(reinterpret_cast(ptr)) {} + RedisReply(RedisReply &&_o) { + ptr_ = _o.ptr_; + _o.ptr_ = nullptr; + } + ~RedisReply() { + if (ptr_) { + freeReplyObject(ptr_); + } + } + redisReply *operator->() { + return ptr_; + } + + inline bool empty() { + return ptr_ == nullptr; + } + inline const char *str() { + return ptr_->str; + } + inline size_t len() { + return ptr_->len; + } + inline size_t type() { + return ptr_->type; + } +}; + +class RedisClient { + private: + redisContext *ctx = nullptr; + + public: + RedisClient() = default; + + ~RedisClient() { + if (ctx) { + redisFree(ctx); + } + } + + RedisReply Request(const std::vector &args); + RedisReply Request(int argc, const char **argv, const size_t *argvlen); + + bool Connect(const std::string &host = "127.0.0.1", int port = 6379, struct timeval timeout = {}); + std::string Get(const std::string &key); + bool Set(const std::string &key, const std::string &value); +}; + +} // namespace swoole diff --git a/core-tests/include/test_core.h b/core-tests/include/test_core.h new file mode 100644 index 00000000000..21feb2da8b6 --- /dev/null +++ b/core-tests/include/test_core.h @@ -0,0 +1,64 @@ +#pragma once + +#include "swoole_api.h" +#include "swoole_client.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_HOST "127.0.0.1" +#define TEST_PORT 9501 +#define TEST_TMP_FILE "/tmp/swoole_core_test_file" +#define TEST_TMP_DIR "/tmp/swoole_core_test_dir" +#define TEST_JPG_FILE "/examples/test.jpg" +#define TEST_JPG_MD5SUM "64a42b4c0f3c65a14c23b60d3880a917" + +#define TEST_HTTP_PROXY_HOST "127.0.0.1" +#define TEST_HTTP_PROXY_PORT 8888 +#define TEST_HTTP_PROXY_USER "user" +#define TEST_HTTP_PROXY_PASSWORD "password" + +#define TEST_SOCKS5_PROXY_HOST "127.0.0.1" +#define TEST_SOCKS5_PROXY_PORT 8080 +#define TEST_SOCKS5_PROXY_USER "user" +#define TEST_SOCKS5_PROXY_PASSWORD "password" + +#define TEST_DOMAIN_BAIDU "www.baidu.com" + +#define TEST_REQUEST_BAIDU \ + "GET / HTTP/1.1\r\n" \ + "Host: www.baidu.com\r\n" \ + "Connection: close\r\n" \ + "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " \ + "Chrome/51.0.2704.106 Safari/537.36" \ + "\r\n\r\n" + +#define ASSERT_MEMEQ(x, y, n) ASSERT_EQ(memcmp((x), (y), n), 0) +#define EXPECT_MEMEQ(x, y, n) EXPECT_EQ(memcmp((x), (y), n), 0) + +namespace swoole { +struct HttpProxy; +struct Socks5Proxy; +namespace test { + +const std::string &get_root_path(); +std::string get_jpg_file(); +bool is_github_ci(); + +int get_random_port(); + +Socks5Proxy *create_socks5_proxy(); +HttpProxy *create_http_proxy(); + +} // namespace test +}; // namespace swoole diff --git a/core-tests/include/test_coroutine.h b/core-tests/include/test_coroutine.h new file mode 100644 index 00000000000..8843f5f0028 --- /dev/null +++ b/core-tests/include/test_coroutine.h @@ -0,0 +1,73 @@ +#pragma once + +#include "test_core.h" + +#include "swoole_coroutine.h" +#include "swoole_coroutine_channel.h" +#include "swoole_coroutine_system.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_c_api.h" + +namespace swoole { +namespace test { + +class coroutine { + public: + coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) + : fn(_fn), arg(_arg), complete_num(_complete_num) {} + + void start() { + fn(arg); + (*complete_num)++; + } + + inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num) { + auto test = new coroutine(fn, arg, complete_num); + + long cid = swoole::Coroutine::create( + [](void *arg) { + ((coroutine *) arg)->start(); + delete (coroutine *) arg; + }, + test); + ASSERT_GT(cid, 0); + } + + inline static void run(std::initializer_list> args) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); + for (const auto &arg : args) { + create(arg.first, arg.second, &complete_num); + } + swoole_event_wait(); + Coroutine::deactivate(); + } + + inline static void run(std::initializer_list fns) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); + for (const auto &fn : fns) { + create(fn, nullptr, &complete_num); + } + swoole_event_wait(); + Coroutine::deactivate(); + } + + inline static void run(const CoroutineFunc &fn, void *arg = nullptr) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); + create(fn, arg, &complete_num); + swoole_event_wait(); + Coroutine::deactivate(); + } + + private: + CoroutineFunc fn; + void *arg; + int *complete_num; +}; +} // namespace test +} // namespace swoole diff --git a/core-tests/include/test_process.h b/core-tests/include/test_process.h new file mode 100644 index 00000000000..b680861b960 --- /dev/null +++ b/core-tests/include/test_process.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "test_core.h" +#include "swoole_process_pool.h" + +using namespace std; + +namespace swoole { namespace test { +//------------------------------------------------------------------------------- +class Process +{ + +private: + std::function handler; + +public: + Worker worker = {}; + + Process(std::function fn, int pipe_type = SOCK_DGRAM); + ~Process(); + pid_t start(); + ssize_t write(const void *__buf, size_t __n); + ssize_t read(void *__buf, size_t __nbytes); +}; +//------------------------------------------------------------------------------- +}} diff --git a/core-tests/include/test_server.h b/core-tests/include/test_server.h new file mode 100644 index 00000000000..6199b4cf8ff --- /dev/null +++ b/core-tests/include/test_server.h @@ -0,0 +1,66 @@ +#pragma once + +#include "test_core.h" +#include "swoole_server.h" + +#define SERVER_THIS ((swoole::test::Server *) serv->private_data_2) + +#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id +#define ON_PACKET_PARAMS swServer *serv, swRecvData *req +#define ON_RECEIVE_PARAMS swServer *serv, swRecvData *req + +typedef void (*_onStart)(swServer *serv); +typedef void (*_onShutdown)(swServer *serv); +typedef void (*_onPipeMessage)(swServer *, swEventData *data); +typedef void (*_onWorkerStart)(swServer *serv, swoole::Worker *worker); +typedef void (*_onWorkerStop)(swServer *serv, swoole::Worker *worker); +typedef int (*_onReceive)(swServer *, swRecvData *); +typedef int (*_onPacket)(swServer *, swRecvData *); +typedef void (*_onClose)(swServer *serv, swDataHead *); +typedef void (*_onConnect)(swServer *serv, swDataHead *); + +using on_workerstart_lambda_type = void (*)(ON_WORKERSTART_PARAMS); +using on_receive_lambda_type = void (*)(ON_RECEIVE_PARAMS); +using on_packet_lambda_type = void (*)(ON_PACKET_PARAMS); + +namespace swoole { +namespace test { +//-------------------------------------------------------------------------------------------------------- +class Server { + private: + swoole::Server serv; + std::vector ports; + std::unordered_map private_data; + std::string host; + int port; + int mode; + int type; + + public: + DgramPacket *packet = nullptr; + + Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type); + ~Server(); + void on(std::string event, void *fn); + bool start(); + bool listen(std::string host, int port, enum swSocketType type); + int send(int session_id, const void *data, uint32_t length); + ssize_t sendto(const swoole::network::Address &address, const char *__buf, size_t __n, int server_socket = -1); + int close(int session_id, int reset); + + inline void *get_private_data(const std::string &key) { + auto it = private_data.find(key); + if (it == private_data.end()) { + return nullptr; + } else { + return it->second; + } + } + + inline void set_private_data(const std::string &key, void *data) { + private_data[key] = data; + } +}; +//-------------------------------------------------------------------------------------------------------- +} // namespace test +} // namespace swoole diff --git a/core-tests/include/tests.h b/core-tests/include/tests.h deleted file mode 100755 index c2766670ad4..00000000000 --- a/core-tests/include/tests.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "swoole.h" -#include "client.h" -#include "server.h" -#include "coroutine.h" - -#include -#include -#include - -class coro_test -{ -public: - coro_test(coroutine_func_t _fn, void *_arg, int *_complete_num) : fn(_fn), arg(_arg), complete_num(_complete_num) - { - - } - - void run() - { - fn(arg); - (*complete_num)++; - } - -private: - coroutine_func_t fn; - void *arg; - int *complete_num; -}; - -static void coro_test_fn(void *arg) -{ - ((coro_test*) arg)->run(); - delete (coro_test*) arg; -} - -static inline void coro_test_create(coroutine_func_t fn, void *arg, int *complete_num) -{ - auto test = new coro_test(fn, arg, complete_num); - long cid = swoole::Coroutine::create(coro_test_fn, test); - ASSERT_GT(cid, 0); -} - -static inline void coro_test(std::initializer_list> args) -{ - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - for (const auto &arg : args) - { - coro_test_create(arg.first, arg.second, &complete_num); - } - swoole_event_wait(); -} - -static inline void coro_test(std::initializer_list args) -{ - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - for (const auto &arg : args) - { - coro_test_create(arg, nullptr, &complete_num); - } - swoole_event_wait(); -} - -static inline void coro_test(coroutine_func_t fn, void *arg = nullptr) -{ - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - coro_test_create(fn, arg, &complete_num); - swoole_event_wait(); -} diff --git a/core-tests/run.sh b/core-tests/run.sh index 5a7e60da3b8..eac533dc4d8 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,2 +1,32 @@ #!/bin/bash -cmake . && make -j8 && ./bin/core_tests +__DIR__=$(cd "$(dirname "$0")";pwd) +__SWOOLE_DIR__=$(cd "$(dirname "${__DIR__}")";pwd) + +if [ "${SWOOLE_ENABLE_ASAN}" = 1 ]; then + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 -D enable_asan=1 +else + cmake . -D swoole_dir="${__SWOOLE_DIR__}" -D enable_thread=1 +fi +make -j8 +ipcs -q + +tasks=$(./bin/core_tests --gtest_list_tests | awk '/\./') +for task in $tasks; do + + if [ "${SWOOLE_VALGRIND}" = 1 ]; then + # --leak-check=full --show-leak-kinds=all --track-origins=yes + execute_command="valgrind ./bin/core_tests" + else + execute_command="./bin/core_tests" + fi + + if [ $task = "log." ]; then + $execute_command --gtest_filter=$task* + else + sudo -E $execute_command --gtest_filter=$task* + fi + + if [ $? -ne 0 ] && [ "${GITHUB_ACTIONS}" = true ]; then + exit 255 + fi +done diff --git a/core-tests/samples/CMakeLists.txt b/core-tests/samples/CMakeLists.txt index 97074e8d933..408e7ab9318 100755 --- a/core-tests/samples/CMakeLists.txt +++ b/core-tests/samples/CMakeLists.txt @@ -2,12 +2,12 @@ cmake_minimum_required(VERSION 2.8) project(samples) #set(CMAKE_BUILD_TYPE Released) set(CMAKE_CXX_STANDARD 11) -file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS *.cc) +file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cc) add_definitions(-DHAVE_CONFIG_H) link_directories($ENV{SWOOLE_DIR}/lib) -include_directories(./include ./ $ENV{SWOOLE_DIR}/ $ENV{SWOOLE_DIR}/include/ BEFORE) +include_directories(. ./include $ENV{SWOOLE_DIR} $ENV{SWOOLE_DIR}/include BEFORE) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(core_samples ${SOURCE_FILES}) target_link_libraries(core_samples swoole) diff --git a/core-tests/samples/README.md b/core-tests/samples/README.md deleted file mode 100644 index 5af1d542c70..00000000000 --- a/core-tests/samples/README.md +++ /dev/null @@ -1,20 +0,0 @@ -编译 swoole.so ------- -`git clone`源代码,然后设置环境变量`export SWOOLE_DIR=/home/htf/workspace/swoole-src` - -```shell -cd swoole-src -phpize -./configure -cmake . -make -j -``` - -编译示例程序 ------ -```shell -cd swoole-src/core-tests/samples -cmake . -make -j -./bin/core_samples -``` diff --git a/core-tests/samples/s1.cc b/core-tests/samples/s1.cc deleted file mode 100644 index 74067cc6258..00000000000 --- a/core-tests/samples/s1.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include "swoole.h" -#include "swoole_api.h" -#include "client.h" -#include "server.h" -#include "coroutine.h" -#include "coroutine_socket.h" -#include "coroutine_system.h" - -#include - -using swoole::Coroutine; -using swoole::coroutine::System; -using swoole::coroutine::Socket; -using namespace std; - -struct A -{ - int x; - int *y; -}; - -static A G_a = -{ 0, 0 }; - -int main(int argc, char **argv) -{ - swoole_event_init(); - /** - * 协程1 - */ - Coroutine::create([](void *arg) - { - - G_a.x = 1234; - int y = 5678; - G_a.y = &y; - /** - * 这里协程挂起后,协程2 会执行,在协程2中修改了 x, y 值 - * 协程2 退出或挂起后,重新回到协程1,这里的x和y的值已经不符合预期了 - */ - System::sleep(1); - //这里会显示 100 - cout << "X=" << G_a.x << endl; - //这里会读到空指针 - cout << "Y=" << *G_a.y << endl; - }); - - /** - * 协程2 - */ - Coroutine::create([](void *arg) - { - G_a.x = 100; - G_a.y = nullptr; - }); - - swoole_event_wait(); - - return 0; -} diff --git a/core-tests/samples/src/s1.cc b/core-tests/samples/src/s1.cc new file mode 100644 index 00000000000..e8865cc48c6 --- /dev/null +++ b/core-tests/samples/src/s1.cc @@ -0,0 +1,49 @@ +#include "swoole.h" +#include "swoole_api.h" +#include "swoole_client.h" +#include "swoole_server.h" +#include "swoole_coroutine.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" + +#include + +using swoole::Coroutine; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using namespace std; + +struct A { + int x; + int *y; +}; + +static A G_a = {0, 0}; + +int main(int argc, char **argv) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + // coroutine 1 + Coroutine::create([](void *arg) { + G_a.x = 1234; + int y = 5678; + G_a.y = &y; + // After the coroutine 1 is suspended here, the coroutine 2 will be executed, and the x, y values is updated in + // the coroutine 2. After the coroutine 2 suspends, go back to coroutine 1, where the values of x and y will be + // no longer as expected + System::sleep(1); + // output 100 + cout << "X=" << G_a.x << endl; + // read invalid point + cout << "Y=" << *G_a.y << endl; + }); + + // coroutine 2 + Coroutine::create([](void *arg) { + G_a.x = 100; + G_a.y = nullptr; + }); + + swoole_event_wait(); + + return 0; +} diff --git a/core-tests/server/tcp.php b/core-tests/server/tcp.php deleted file mode 100644 index ef9615487fc..00000000000 --- a/core-tests/server/tcp.php +++ /dev/null @@ -1,20 +0,0 @@ -on('receive', function ($server, $fd, $reactor_id, $data) { - $data = trim($data); - file_put_contents(LOG_FILE, '[server/tcp]: ' . $data . "\n", FILE_APPEND); - if ($data == 'echo') { - $server->send($fd, "hello world\n"); - } elseif ($data == 'close') { - $server->close($fd); - } -}); - -$server->on('shutdown', function () { - unlink(LOG_FILE); -}); - -$server->start(); diff --git a/core-tests/src/_lib/http.cpp b/core-tests/src/_lib/http.cpp new file mode 100644 index 00000000000..3f6f3fe1395 --- /dev/null +++ b/core-tests/src/_lib/http.cpp @@ -0,0 +1,921 @@ +#include "test_core.h" +#include "httplib_client.h" +#include "swoole_http.h" + +namespace websocket = swoole::websocket; + +namespace httplib { + +bool Client::Upgrade(const char *_path, Headers &_headers) { + set_keep_alive(true); + _headers.emplace("Connection", "Upgrade"); + _headers.emplace("Upgrade", "websocket"); + _headers.emplace("Sec-Websocket-Key", "sN9cRrP/n9NdMgdcy2VJFQ=="); + _headers.emplace("Sec-WebSocket-Version", "13"); + + auto resp = Get(_path, _headers); + if (resp == nullptr or resp->status != SW_HTTP_SWITCHING_PROTOCOLS) { + return false; + } + + return true; +} + +bool Client::Push(const char *data, size_t length, int opcode) { + if (!socket_.is_open()) { + return false; + } + return process_socket(socket_, [&](Stream &strm) { + swString buffer = {}; + char buf[32]; + buffer.size = sizeof(buf); + buffer.str = buf; + + websocket::encode(&buffer, data, length, opcode, websocket::FLAG_FIN | websocket::FLAG_ENCODE_HEADER_ONLY); + strm.write(buffer.str, buffer.length); + strm.write(data, length); + return true; + }); +} + +std::shared_ptr Client::Recv() { + auto msg = std::make_shared(); + auto retval = process_socket(socket_, [&](Stream &strm) { + swProtocol proto = {}; + proto.package_length_size = SW_WEBSOCKET_HEADER_LEN; + proto.get_package_length = websocket::get_package_length; + proto.package_max_length = SW_INPUT_BUFFER_SIZE; + + char buf[1024]; + ssize_t packet_len; + + if (strm.read(buf, SW_WEBSOCKET_HEADER_LEN) <= 0) { + return false; + } + swoole::PacketLength pl { + buf, + SW_WEBSOCKET_HEADER_LEN, + }; + packet_len = proto.get_package_length(&proto, nullptr, &pl); + if (packet_len < 0) { + return false; + } + if (packet_len == 0) { + if (strm.read(buf + SW_WEBSOCKET_HEADER_LEN, pl.header_len - SW_WEBSOCKET_HEADER_LEN) <= 0) { + return false; + } + pl.buf_size = pl.header_len; + packet_len = proto.get_package_length(&proto, nullptr, &pl); + if (packet_len <= 0) { + return false; + } + } + + char *data = (char *) malloc(packet_len + 1); + if (data == nullptr) { + return false; + } + data[packet_len] = 0; + + uint32_t header_len = pl.header_len > 0 ? pl.header_len : SW_WEBSOCKET_HEADER_LEN; + memcpy(data, buf, header_len); + + ssize_t read_bytes = header_len; + while (read_bytes < packet_len) { + auto n_read = strm.read(data + read_bytes, packet_len - read_bytes); + if (n_read <= 0) { + free(data); + return false; + } + read_bytes += n_read; + } + + return websocket::decode(msg.get(), data, packet_len); + }); + + return retval ? msg : nullptr; +} + +// HTTP client implementation +Client::Client(const std::string &host) : Client(host, 80, std::string(), std::string()) {} + +Client::Client(const std::string &host, int port) : Client(host, port, std::string(), std::string()) {} + +Client::Client(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), + port_(port), + host_and_port_(host_ + ":" + std::to_string(port_)), + client_cert_path_(client_cert_path), + client_key_path_(client_key_path) {} + +Client::~Client() { + stop(); +} + +bool Client::is_valid() const { + return true; +} + +socket_t Client::create_client_socket() const { + if (!proxy_host_.empty()) { + return detail::create_client_socket(proxy_host_.c_str(), + proxy_port_, + tcp_nodelay_, + socket_options_, + connection_timeout_sec_, + connection_timeout_usec_, + interface_); + } + return detail::create_client_socket(host_.c_str(), + port_, + tcp_nodelay_, + socket_options_, + connection_timeout_sec_, + connection_timeout_usec_, + interface_); +} + +bool Client::create_and_connect_socket(Socket &socket) { + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { + return false; + } + socket.sock = sock; + return true; +} + +void Client::close_socket(Socket &socket, bool /*process_socket_ret*/) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + socket_.ssl = nullptr; +#endif +} + +bool Client::read_response_line(Stream &strm, Response &res) { + std::array buf; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { + return false; + } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + + std::cmatch m; + if (std::regex_match(line_reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; +} + +bool Client::send(const Request &req, Response &res) { + std::lock_guard request_mutex_guard(request_mutex_); + + { + std::lock_guard guard(socket_mutex_); + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { + close_socket(socket_, false); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_)) { + return false; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty()) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_)) { + return false; + } + } +#endif + } + } + + auto close_connection = !keep_alive_; + + auto ret = process_socket(socket_, [&](Stream &strm) { return handle_request(strm, req, res, close_connection); }); + + if (close_connection) { + stop(); + } + + return ret; +} + +bool Client::handle_request(Stream &strm, const Request &req, Response &res, bool close_connection) { + if (req.path.empty()) { + return false; + } + + bool ret; + + if (!is_ssl() && !proxy_host_.empty()) { + auto req2 = req; + req2.path = "https://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection); + } else { + ret = process_request(strm, req, res, close_connection); + } + + if (!ret) { + return false; + } + + if (300 < res.status && res.status < 400 && follow_location_) { + ret = redirect(req, res); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + new_req.headers.erase(key); + new_req.headers.insert(make_digest_authentication_header( + req, auth, new_req.authorization_count_, random_string(10), username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res); + if (ret) { + res = new_res; + } + } + } + } +#endif + + return ret; +} + +bool Client::redirect(const Request &req, Response &res) { + if (req.redirect_count == 0) { + return false; + } + + auto location = res.get_header_value("location"); + if (location.empty()) { + return false; + } + + const static std::regex re(R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { + return false; + } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + auto port_str = m[3].str(); + auto next_path = m[4].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { + next_scheme = scheme; + } + if (next_host.empty()) { + next_host = host_; + } + if (next_path.empty()) { + next_path = "/"; + } + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); +#else + return false; +#endif + } else { + Client cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); + } + } +} + +bool Client::write_request(Stream &strm, const Request &req, bool close_connection) { + detail::BufferStream bstrm; + + // Request line + const auto &path = detail::encode_url(req.path); + + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + // Additonal headers + Headers headers; + if (close_connection) { + headers.emplace("Connection", "close"); + } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } else { + if (port_ == 80) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { + headers.emplace("Accept", "*/*"); + } + + if (!req.has_header("User-Agent")) { + headers.emplace("User-Agent", USER_AGENT); + } + + if (req.body.empty()) { + if (req.content_provider) { + auto length = std::to_string(req.content_length); + headers.emplace("Content-Length", length); + } else { + headers.emplace("Content-Length", "0"); + } + } else { + if (!req.has_header("Content-Type")) { + headers.emplace("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + headers.emplace("Content-Length", length); + } + } + + if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_, false)); + } + + if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + + detail::write_headers(bstrm, req, headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + return false; + } + + // Body + if (req.body.empty()) { + if (req.content_provider) { + size_t offset = 0; + size_t end_offset = req.content_length; + + bool ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + if (detail::write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset) { + if (!req.content_provider(offset, end_offset - offset, data_sink)) { + return false; + } + if (!ok) { + return false; + } + } + } + } else { + return detail::write_data(strm, req.body.data(), req.body.size()); + } + + return true; +} + +std::shared_ptr Client::send_with_content_provider(const char *method, + const char *path, + const Headers &headers, + const std::string &body, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + if (content_provider) { + size_t offset = 0; + + DataSink data_sink; + data_sink.write = [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + offset += data_len; + }; + data_sink.is_writable = [&](void) { return true; }; + + while (offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + return nullptr; + } + } + } else { + req.body = body; + } + + if (!detail::compress(req.body)) { + return nullptr; + } + req.headers.emplace("Content-Encoding", "gzip"); + } else +#endif + { + if (content_provider) { + req.content_length = content_length; + req.content_provider = content_provider; + } else { + req.body = body; + } + } + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +bool Client::process_request(Stream &strm, const Request &req, Response &res, bool close_connection) { + // Send request + if (!write_request(strm, req, close_connection)) { + return false; + } + + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + return false; + } + + if (req.response_handler) { + if (!req.response_handler(res)) { + return false; + } + } + + // Body + if (req.method != "HEAD" && req.method != "CONNECT") { + auto out = + req.content_receiver + ? static_cast([&](const char *buf, size_t n) { return req.content_receiver(buf, n); }) + : static_cast([&](const char *buf, size_t n) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + int dummy_status; + if (!detail::read_content( + strm, res, (std::numeric_limits::max)(), dummy_status, req.progress, out, decompress_)) { + return false; + } + } + + if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { + stop(); + } + + // Log + if (logger_) { + logger_(req, res); + } + + return true; +} + +bool Client::process_socket(Socket &socket, std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, callback); +} + +bool Client::is_ssl() const { + return false; +} + +std::shared_ptr Client::Get(const char *path) { + return Get(path, Headers(), Progress()); +} + +std::shared_ptr Client::Get(const char *path, Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers) { + return Get(path, headers, Progress()); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers, Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); +} + +std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver, Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), Progress()); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), content_receiver, Progress()); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = std::move(content_receiver); + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Head(const char *path) { + return Head(path, Headers()); +} + +std::shared_ptr Client::Head(const char *path, const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Post(const char *path) { + return Post(path, std::string(), nullptr); +} + +std::shared_ptr Client::Post(const char *path, const std::string &body, const char *content_type) { + return Post(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Post(const char *path, const Params ¶ms) { + return Post(path, Headers(), params); +} + +std::shared_ptr Client::Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Post(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "POST", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Post(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +std::shared_ptr Client::Post(const char *path, const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +std::shared_ptr Client::Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) { + auto boundary = detail::make_multipart_data_boundary(); + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); +} + +std::shared_ptr Client::Put(const char *path) { + return Put(path, std::string(), nullptr); +} + +std::shared_ptr Client::Put(const char *path, const std::string &body, const char *content_type) { + return Put(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Put(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "PUT", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Put(const char *path, const Params ¶ms) { + return Put(path, Headers(), params); +} + +std::shared_ptr Client::Put(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +std::shared_ptr Client::Patch(const char *path, const std::string &body, const char *content_type) { + return Patch(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Patch(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "PATCH", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Delete(const char *path) { + return Delete(path, Headers(), std::string(), nullptr); +} + +std::shared_ptr Client::Delete(const char *path, const std::string &body, const char *content_type) { + return Delete(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Delete(const char *path, const Headers &headers) { + return Delete(path, headers, std::string(), nullptr); +} + +std::shared_ptr Client::Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Options(const char *path) { + return Options(path, Headers()); +} + +std::shared_ptr Client::Options(const char *path, const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +size_t Client::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +void Client::stop() { + std::lock_guard guard(socket_mutex_); + if (socket_.is_open()) { + detail::shutdown_socket(socket_.sock); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + close_socket(socket_, true); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +void Client::set_timeout_sec(time_t timeout_sec) { + set_connection_timeout(timeout_sec, 0); +} + +void Client::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +void Client::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +void Client::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +void Client::set_basic_auth(const char *username, const char *password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +void Client::set_digest_auth(const char *username, const char *password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +void Client::set_keep_alive(bool on) { + keep_alive_ = on; +} + +void Client::set_follow_location(bool on) { + follow_location_ = on; +} + +void Client::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; +} + +void Client::set_socket_options(SocketOptions socket_options) { + socket_options_ = socket_options; +} + +void Client::set_compress(bool on) { + compress_ = on; +} + +void Client::set_decompress(bool on) { + decompress_ = on; +} + +void Client::set_interface(const char *intf) { + interface_ = intf; +} + +void Client::set_proxy(const char *host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +void Client::set_proxy_basic_auth(const char *username, const char *password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +void Client::set_proxy_digest_auth(const char *username, const char *password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} +#endif + +void Client::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +} // namespace httplib diff --git a/core-tests/src/_lib/process.cpp b/core-tests/src/_lib/process.cpp new file mode 100644 index 00000000000..f81b4fec296 --- /dev/null +++ b/core-tests/src/_lib/process.cpp @@ -0,0 +1,48 @@ +#include "test_process.h" + +using swoole::test::Process; +using swoole::UnixSocket; + +Process::Process(std::function fn, int pipe_type) : handler(fn) { + if (pipe_type > 0) { + auto pipe = new UnixSocket(true, SOCK_DGRAM); + + worker.pipe_master = pipe->get_socket(true); + worker.pipe_worker = pipe->get_socket(false); + + worker.pipe_object = pipe; + worker.pipe_current = worker.pipe_master; + } +} + +Process::~Process() { + if (worker.pipe_object) { + delete worker.pipe_object; + } +} + +pid_t Process::start() { + // std::system("ls /proc/self/task"); + pid_t pid = swoole_fork(0); + if (pid < 0) { + printf("[Worker] Fatal Error: fork() failed"); + exit(1); + } else if (pid == 0) { + worker.child_process = 1; + worker.pipe_current = worker.pipe_worker; + handler(this); + exit(0); + } else { + worker.pid = pid; + worker.child_process = 0; + return pid; + } +} + +ssize_t Process::write(const void *__buf, size_t __n) { + return worker.pipe_current->write(__buf, __n); +} + +ssize_t Process::read(void *__buf, size_t __nbytes) { + return worker.pipe_current->read(__buf, __nbytes); +} diff --git a/core-tests/src/_lib/redis.cpp b/core-tests/src/_lib/redis.cpp new file mode 100644 index 00000000000..b64a6794fab --- /dev/null +++ b/core-tests/src/_lib/redis.cpp @@ -0,0 +1,74 @@ +#include "redis_client.h" +#include "test_core.h" + +#include + +using namespace std; + +namespace swoole { +bool RedisClient::Connect(const string &host, int port, struct timeval timeout) { + redisContext *c = redisConnectWithTimeout(host.c_str(), port, timeout); + if (c == NULL) { + printf("Connection error: can't allocate redis context\n"); + return false; + } + + if (c->err) { + printf("Connection error: %s\n", c->errstr); + redisFree(c); + return false; + } + + ctx = c; + return true; +} + +string RedisClient::Get(const string &key) { + const char *argv[] = {"GET", key.c_str()}; + size_t argvlen[] = {strlen(argv[0]), key.length()}; + + auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen); + if (!reply.empty() && reply->str) { + return string(reply->str, reply->len); + } else { + return ""; + } +} + +bool RedisClient::Set(const string &key, const string &value) { + const char *argv[] = {"SET", key.c_str(), value.c_str()}; + size_t argvlen[] = {strlen(argv[0]), key.length(), value.length()}; + + auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen); + if (!reply.empty() && reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, "OK", 2) == 0) { + return true; + } else { + return false; + } +} + +RedisReply RedisClient::Request(int argc, const char **argv, const size_t *argvlen) { + return redisCommandArgv(ctx, argc, argv, argvlen); +} + +RedisReply RedisClient::Request(const vector &args) { + ctx->err = 0; + + size_t n = args.size(); + const char **argv = new const char *[n]; + size_t *argvlen = new size_t[n]; + + for (size_t i = 0; i < args.size(); i++) { + argv[i] = args[i].c_str(); + argvlen[i] = args[i].length(); + } + + auto reply = Request(args.size(), (const char **) argv, (const size_t *) argvlen); + + delete[] argv; + delete[] argvlen; + + return reply; +} + +} // namespace swoole diff --git a/core-tests/src/_lib/server.cpp b/core-tests/src/_lib/server.cpp new file mode 100644 index 00000000000..dac4afe9093 --- /dev/null +++ b/core-tests/src/_lib/server.cpp @@ -0,0 +1,104 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_server.h" +#include "swoole_memory.h" + +using namespace swoole::test; +using swoole::network::Address; + +Server::Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type) + : serv(_mode), host(_host), port(_port), mode(_mode), type(_type) { + serv.worker_num = 1; + + if (mode == swoole::Server::MODE_BASE) { + serv.reactor_num = 1; + serv.worker_num = 1; + } + + serv.dispatch_mode = 2; + serv.private_data_2 = this; + + if (!listen(host, port, (swSocketType) type)) { + swoole_warning("listen(%s:%d) fail[error=%d].", host.c_str(), port, errno); + exit(0); + } + + if (serv.create() < 0) { + swoole_warning("create server fail[error=%d].", errno); + exit(0); + } +} + +Server::~Server() {} + +void Server::on(std::string event, void *fn) { + if (event == "Start") { + serv.onStart = (_onStart) fn; + } else if (event == "onShutdown") { + serv.onShutdown = (_onShutdown) fn; + } else if (event == "onPipeMessage") { + serv.onPipeMessage = (_onPipeMessage) fn; + } else if (event == "onWorkerStart") { + serv.onWorkerStart = (_onWorkerStart) fn; + } else if (event == "onWorkerStop") { + serv.onWorkerStop = (_onWorkerStop) fn; + } else if (event == "onReceive") { + serv.onReceive = (_onReceive) fn; + } else if (event == "onPacket") { + serv.onPacket = (_onPacket) fn; + } else if (event == "onClose") { + serv.onClose = (_onClose) fn; + } else { + serv.onConnect = (_onConnect) fn; + } +} + +bool Server::start() { + return serv.start() == 0; +} + +bool Server::listen(std::string host, int port, enum swSocketType type) { + ListenPort *ls = serv.add_port(type, (char *) host.c_str(), port); + if (ls == nullptr) { + return false; + } + + ports.push_back(ls); + return true; +} + +int Server::send(int session_id, const void *data, uint32_t length) { + return serv.send(session_id, data, length); +} + +ssize_t Server::sendto(const Address &address, const char *__buf, size_t __n, int server_socket_fd) { + network::Socket *server_socket; + if (server_socket_fd < 0) { + server_socket = serv.udp_socket_ipv6 ? serv.udp_socket_ipv6 : serv.udp_socket_ipv4; + } else { + server_socket = serv.get_server_socket(server_socket_fd); + } + return server_socket->sendto(address, __buf, __n, 0); +} + +int Server::close(int session_id, int reset) { + return serv.close(session_id, reset); +} diff --git a/core-tests/src/client.cpp b/core-tests/src/client.cpp deleted file mode 100644 index ffc8d588cf8..00000000000 --- a/core-tests/src/client.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "tests.h" - -TEST(client, tcp) -{ - int ret; - swClient cli; - char buf[128]; - - ret = swClient_create(&cli, SW_SOCK_TCP, SW_SOCK_SYNC); - ASSERT_EQ(ret, 0); - ret = cli.connect(&cli, (char *) "127.0.0.1", 9501, -1, 0); - ASSERT_EQ(ret, 0); - ret = cli.send(&cli, (char *) SW_STRS("echo"), 0); - ASSERT_GT(ret, 0); - ret = cli.recv(&cli, buf, 128, 0); - ASSERT_GT(ret, 0); - cli.close(&cli); - ASSERT_EQ(strncmp(buf, SW_STRL("hello world\n")), 0); -} diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp new file mode 100644 index 00000000000..671a0aa7235 --- /dev/null +++ b/core-tests/src/core/base.cpp @@ -0,0 +1,311 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_server.h" +#include "swoole_file.h" +#include "swoole_util.h" +#include "swoole.h" +#include "swoole_config.h" + +using namespace swoole; +using namespace std; + +static const string test_data("hello world\n"); + +TEST(base, datahead_dump) { + swDataHead data = {}; + data.fd = 123; + char buf[128]; + size_t n = data.dump(buf, sizeof(buf)); + data.print(); + + ASSERT_GT(std::string(buf, n).find("int fd = 123;"), 1); +} + +TEST(base, dec2hex) { + auto result = swoole_dec2hex(2684326179, 16); + ASSERT_STREQ(result, "9fff9123"); + sw_free(result); +} + +TEST(base, hex2dec) { + size_t n_parsed; + ASSERT_EQ(swoole_hex2dec("9fff9123", &n_parsed), 2684326179); + ASSERT_EQ(n_parsed, 8); + ASSERT_EQ(swoole_hex2dec("0x9fff9123", &n_parsed), 2684326179); + ASSERT_EQ(n_parsed, 10); + ASSERT_EQ(swoole_hex2dec("f", &n_parsed), 15); + ASSERT_EQ(n_parsed, 1); +} + +TEST(base, random_string) { + char buf[1024] = {}; + swoole_random_string(buf, sizeof(buf) - 1); + ASSERT_EQ(strlen(buf), sizeof(buf) - 1); +} + +TEST(base, file_put_contents) { + char buf[65536]; + swoole_random_string(buf, sizeof(buf) - 1); + ASSERT_TRUE(file_put_contents(TEST_TMP_FILE, buf, sizeof(buf))); + auto result = file_get_contents(TEST_TMP_FILE); + ASSERT_STREQ(buf, result->value()); +} + +TEST(base, file_get_size) { + File f(TEST_TMP_FILE, File::WRITE | File::CREATE); + char buf[65536]; + swoole_random_string(buf, sizeof(buf) - 1); + + ASSERT_TRUE(f.ready()); + f.truncate(0); + f.set_offset(0); + f.write(buf, sizeof(buf) - 1); + f.close(); + + ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) - 1); +} + +TEST(base, version_compare) { + ASSERT_EQ(swoole_version_compare("1.2.1", "1.2.0"), 1); + ASSERT_EQ(swoole_version_compare("1.2.3", "1.3.0"), -1); + ASSERT_EQ(swoole_version_compare("1.2.3", "1.2.9"), -1); + ASSERT_EQ(swoole_version_compare("1.2.0", "1.2.0"), 0); +} + +TEST(base, common_divisor) { + ASSERT_EQ(swoole_common_divisor(16, 12), 4); + ASSERT_EQ(swoole_common_divisor(6, 15), 3); + ASSERT_EQ(swoole_common_divisor(32, 16), 16); +} + +TEST(base, common_multiple) { + ASSERT_EQ(swoole_common_multiple(16, 12), 48); + ASSERT_EQ(swoole_common_multiple(6, 15), 30); + ASSERT_EQ(swoole_common_multiple(32, 16), 32); +} + +TEST(base, shell_exec) { + pid_t pid; + string str = "md5sum " + test::get_jpg_file(); + int _pipe = swoole_shell_exec(str.c_str(), &pid, 0); + ASSERT_GT(_pipe, 0); + ASSERT_GT(pid, 0); + char buf[1024] = {}; + ssize_t n = read(_pipe, buf, sizeof(buf) - 1); + ASSERT_GT(n, 0); + ASSERT_STREQ(string(buf).substr(0, sizeof(TEST_JPG_MD5SUM) - 1).c_str(), TEST_JPG_MD5SUM); + close(_pipe); + + str = "md5sum test.abcdef"; + _pipe = swoole_shell_exec(str.c_str(), &pid, 1); + memset(buf, 0, sizeof(buf)); + ssize_t length = 0; + while (1) { + n = read(_pipe, buf + length, sizeof(buf) - 1 - length); + length += n; + if (n > 0) { + continue; + } + break; + } + ASSERT_GT(length, 0); + + ASSERT_STREQ(buf, string("md5sum: test.abcdef: No such file or directory\n").c_str()); + close(_pipe); +} + +TEST(base, file_size) { + auto file = test::get_jpg_file(); + ssize_t file_size = file_get_size(file); + ASSERT_GT(file_size, 0); + auto fp = fopen(file.c_str(), "r+"); + ASSERT_TRUE(fp); + ASSERT_EQ(file_get_size(fp), file_size); + fclose(fp); +} + +TEST(base, eventdata_pack) { + EventData ed1{}; + + ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length())); + ASSERT_EQ(string(ed1.data, ed1.info.len), test_data); + + EventData ed2{}; + ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG); + ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); + + String _buffer(SW_BUFFER_SIZE_BIG); + PacketPtr packet; + ASSERT_TRUE(Server::task_unpack(&ed2, &_buffer, &packet)); + ASSERT_EQ(memcmp(sw_tg_buffer()->str, _buffer.str, SW_BUFFER_SIZE_BIG), 0); +} + +TEST(base, stack_defer_fn) { + int count = 0; + + ON_SCOPE_EXIT { + count++; + ASSERT_EQ(count, 2); + }; + + ON_SCOPE_EXIT { + count++; + ASSERT_EQ(count, 1); + }; +} + +TEST(base, string_format) { + char *data = swoole_string_format(128, "hello %d world, %s is best.", 2020, "swoole"); + ASSERT_STREQ(data, "hello 2020 world, swoole is best."); + sw_free(data); +} + +TEST(base, dirname) { + ASSERT_EQ(dirname("/hello/world/index.html.abc"), "/hello/world"); + ASSERT_EQ(dirname("/hello/world"), "/hello"); + ASSERT_EQ(dirname("/root"), "/"); + ASSERT_EQ(dirname("/"), "/"); +} + +TEST(base, set_task_tmpdir) { + ASSERT_FALSE(swoole_set_task_tmpdir("aaa")); + + size_t length = SW_TASK_TMP_PATH_SIZE + 1; + char too_long_dir[length] = {}; + swoole_random_string(too_long_dir + 1, length - 1); + too_long_dir[0] = '/'; + ASSERT_FALSE(swoole_set_task_tmpdir(too_long_dir)); + + const char *tmpdir = "/tmp/swoole/core_tests/base"; + ASSERT_TRUE(swoole_set_task_tmpdir(tmpdir)); + File fp = swoole::make_tmpfile(); + ASSERT_TRUE(fp.ready()); + + char buf[128]; + swoole_random_string(buf, sizeof(buf) - 2); + buf[sizeof(buf) - 2] = '\n'; + + fp.write(buf, sizeof(buf) - 1); + fp.close(); + + ASSERT_EQ(swoole::dirname(fp.get_path()), tmpdir); + ASSERT_STREQ(swoole::file_get_contents(fp.get_path())->str, buf); + + unlink(fp.get_path().c_str()); + rmdir(tmpdir); +} + +TEST(base, version) { + ASSERT_STREQ(swoole_version(), SWOOLE_VERSION); + ASSERT_EQ(swoole_version_id(), SWOOLE_VERSION_ID); + ASSERT_EQ(swoole_api_version_id(), SWOOLE_API_VERSION_ID); +} + +static std::string test_func(std::string test_data_2) { + return test_data + test_data_2; +} + +TEST(base, hook) { + int count = 0; + swoole_add_hook( + SW_GLOBAL_HOOK_END, + [](void *data) -> void { + int *_count = (int *) data; + *_count = 9999; + }, + 1); + ASSERT_TRUE(swoole_isset_hook(SW_GLOBAL_HOOK_END)); + swoole_call_hook(SW_GLOBAL_HOOK_END, &count); + ASSERT_EQ(count, 9999); +} + +TEST(base, intersection) { + std::vector vec1{"index.php", "index.html", "default.html"}; + + std::set vec2{".", "..", "default.html", "index.php", "test.html", "a.json", "index.php"}; + ASSERT_EQ("index.php", swoole::intersection(vec1, vec2)); + + std::set vec3{"a", "zh中", "、r\n"}; + ASSERT_EQ("", swoole::intersection(vec1, vec3)); +} + +TEST(base, itoa) { + char buf[128]; + long value = 123456987; + int n = swoole_itoa(buf, value); + + ASSERT_EQ(n, 9); + ASSERT_STREQ(buf, "123456987"); +} + +TEST(base, get_systemd_listen_fds) { + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT + 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), -1); + setenv("LISTEN_FDS", to_string(SW_MAX_LISTEN_PORT - 1).c_str(), 1); + ASSERT_EQ(swoole_get_systemd_listen_fds(), SW_MAX_LISTEN_PORT - 1); +} + +TEST(base, type_size) { + ASSERT_EQ(swoole_type_size('c'), 1); + ASSERT_EQ(swoole_type_size('s'), 2); + ASSERT_EQ(swoole_type_size('l'), 4); + ASSERT_EQ(swoole_type_size('b'), 0); // default value +} + +size_t swoole_fatal_error_impl(const char *format, ...) { + size_t retval = 0; + va_list args; + va_start(args, format); + + char buf[128]; + retval += sw_vsnprintf(buf, 128, format, args); + va_end(args); + return retval; +} + +TEST(base, vsnprintf) { + ASSERT_GT(swoole_fatal_error_impl("Hello %s", "World!!!"), 0); +} + +TEST(base, log_level) { + int level = sw_logger()->get_level(); + swoole_set_log_level(SW_LOG_TRACE); + swoole_print_backtrace(); + EXPECT_EQ(SW_LOG_TRACE, sw_logger()->get_level()); + swoole_set_log_level(level); +} + +TEST(base, trace_flag) { + int flags = SwooleG.trace_flags; + swoole_set_trace_flags(SW_TRACE_CARES); + EXPECT_EQ(SW_TRACE_CARES, SwooleG.trace_flags); + swoole_set_trace_flags(flags); +} + +TEST(base, only_dump) { + // just dump something + std::string data = "hello world"; + swoole_dump_ascii(data.c_str(), data.length()); + swoole_dump_bin(data.c_str(), 'C', data.length()); + swoole_dump_hex(data.c_str(), data.length()); + ASSERT_TRUE(true); +} diff --git a/core-tests/src/core/channel.cpp b/core-tests/src/core/channel.cpp new file mode 100644 index 00000000000..ec756f092e4 --- /dev/null +++ b/core-tests/src/core/channel.cpp @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_channel.h" + +using namespace std; +using namespace swoole; + +const int N = 10000000; + +TEST(channel, push) { + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + map m; + + size_t bytes = 0; + int index = 0; + + while (bytes < N) { + char buf[8000]; + int n = swoole_random_bytes(buf, (rand() % (sizeof(buf) / 2)) + (sizeof(buf) / 2)); + if (n <= 0) { + swoole_trace("no enough data, n=%d, errno=%d\n", n, errno); + continue; + } + m[index++] = string(buf, n); + bytes += n; + } + + swoole_trace("size=%lu", m.size()); + + thread t1([&]() { + auto next = m.find(0); + int index = 1; + size_t bytes = 0; + + while (bytes < N) { + if (c->push(next->second.c_str(), next->second.length()) == SW_OK) { + swoole_trace("[PUSH] index=%d, size=%lu", index, next->second.length()); + bytes += next->second.length(); + next = m.find(index++); + if (next == m.end()) { + break; + } + } else { + usleep(10); + } + } + }); + + thread t2([&]() { + char buf[8000]; + size_t bytes = 0; + int index = 0; + while (bytes < N) { + int retval = c->pop(buf, sizeof(buf)); + if (retval > 0) { + swoole_trace("[POP] index=%d, size=%d", index, retval); + string &_data = m[index++]; + bytes += retval; + ASSERT_EQ(_data, string(buf, retval)); + } else { + usleep(10); + } + } + }); + + t1.join(); + t2.join(); + + c->destroy(); +} + +TEST(channel, peek) { + char buf[8000]; + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + ASSERT_EQ(c->peek(buf, sizeof(buf)), SW_ERR); + + string value = "test"; + c->push(value.c_str(), value.length()); + ASSERT_EQ(c->peek((void *) buf, sizeof(buf)), value.length()); + c->destroy(); +} + +TEST(channel, notify) { + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + thread t1([&]() { + sleep(0.02); + string value = "test"; + c->push(value.c_str(), value.length()); + c->notify(); + }); + + thread t2([&]() { + while (c->wait()) { + char buf[8000]; + ASSERT_GT(c->pop((void *) buf, sizeof(buf)), 0); + break; + } + }); + + t1.join(); + t2.join(); + + c->print(); + c->destroy(); +} diff --git a/core-tests/src/core/hash.cpp b/core-tests/src/core/hash.cpp new file mode 100644 index 00000000000..7d7f1cd5ebb --- /dev/null +++ b/core-tests/src/core/hash.cpp @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_hash.h" + +static const char *data = "hello world, PHP the best."; + +TEST(hash, crc32) { + ASSERT_EQ(swoole_crc32(data, strlen(data)), 2962796788); +} + +TEST(hash, php) { + ASSERT_GT(swoole_hash_php(data, strlen(data)), 0); +} + +TEST(hash, jenkins) { + ASSERT_GT(swoole_hash_jenkins(data, strlen(data)), 0); +} + +TEST(hash, austin) { + ASSERT_GT(swoole_hash_austin(data, strlen(data)), 0); +} diff --git a/core-tests/src/core/heap.cpp b/core-tests/src/core/heap.cpp new file mode 100644 index 00000000000..226c9838031 --- /dev/null +++ b/core-tests/src/core/heap.cpp @@ -0,0 +1,39 @@ +#include "test_core.h" +#include "swoole_heap.h" +#include + +typedef struct node_t { + int pri; + int val; +} node_t; + +#define SIZE 100 + +TEST(heap, random) { + node_t *ns; + node_t *n; + swoole::Heap pq(SIZE, swoole::Heap::MAX_HEAP); + std::map _map; + ASSERT_EQ(pq.peek(), nullptr); + + int i; + for (i = 0; i < SIZE * 2 - 1; i++) { + int pri = swoole_system_random(10000, 99999); + ns = (node_t *) malloc(sizeof(node_t)); + ns->val = i; + ns->pri = pri; + pq.push(pri, ns); + _map[i] = pri; + + if (0 == i) { + pq.print(); // print once + } + } + + n = (node_t *) pq.peek(); + ASSERT_EQ(_map[n->val], n->pri); + while ((n = (node_t *) pq.pop())) { + ASSERT_EQ(_map[n->val], n->pri); + free(n); + } +} diff --git a/core-tests/src/core/log.cpp b/core-tests/src/core/log.cpp new file mode 100644 index 00000000000..af76a7483cb --- /dev/null +++ b/core-tests/src/core/log.cpp @@ -0,0 +1,215 @@ +#include "test_core.h" +#include "swoole_file.h" +#include +#include + +using namespace swoole; + +const char *file = "/tmp/swoole_log_test.log"; + +TEST(log, level) { + std::vector processTypes = {SW_PROCESS_MASTER, SW_PROCESS_MANAGER, SW_PROCESS_WORKER, SW_PROCESS_TASKWORKER}; + + int originType = swoole_get_process_type(); + for (auto iter = processTypes.begin(); iter != processTypes.end(); iter++) { + SwooleG.process_type = *iter; + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); + + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + ASSERT_FALSE(content->contains(SW_STRL("hello info"))); + ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); + ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + + SwooleG.process_type = originType; + } +} + +TEST(log, date_format) { + sw_logger()->reset(); + sw_logger()->set_date_format("day %d of %B in the year %Y. Time: %I:%S %p"); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + int data[16]; + char *month = nullptr; + char *am = nullptr; + + int n = std::sscanf(content->value(), + "[day %d of %s in the year %d. Time: %d:%d %s @%d.%d]\tWARNING\thello world", + data, + month, + data + 1, + data + 2, + data + 3, + am, + data + 4, + data + 5); + + ASSERT_TRUE(n); +} + +TEST(log, date_format_long_string) { + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_ERROR); + std::unique_ptr content(new String(256)); + auto str = content.get(); + + str->repeat("x", 1, 120); + str->append(SW_STRL("day %d of %B in the year %Y. Time: %I:%S %p")); + + bool retval = sw_logger()->set_date_format(str->str); + + ASSERT_FALSE(retval); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS); +} + +TEST(log, date_with_microseconds) { + sw_logger()->reset(); + sw_logger()->set_date_with_microseconds(true); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + std::regex e("\\[\\S+\\s\\d{2}:\\d{2}:\\d{2}\\<\\.(\\d+)\\>\\s@\\d+\\.\\d+\\]\tWARNING\thello world"); + ASSERT_TRUE(std::regex_search(content->value(), e)); +} + +TEST(log, rotation) { + std::vector types = { + SW_LOG_ROTATION_DAILY, SW_LOG_ROTATION_EVERY_MINUTE, SW_LOG_ROTATION_HOURLY, SW_LOG_ROTATION_MONTHLY}; + for (auto iter = types.begin(); iter != types.end(); iter++) { + sw_logger()->reset(); + sw_logger()->set_rotation(*iter); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_DEBUG, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_TRACE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_ERROR, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello world")); + + ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); + ASSERT_EQ(errno, ENOENT); + ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); + + sw_logger()->close(); + unlink(sw_logger()->get_real_file()); + } +} + +TEST(log, redirect) { + if (test::is_github_ci()) { + return; + } + sw_logger()->reset(); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // no log file opened + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(0)); // no redirected + + ASSERT_TRUE(sw_logger()->open(file)); + ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(1)); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // has been redirected + + printf("hello world\n"); + auto content = file_get_contents(file); + ASSERT_NE(content.get(), nullptr); + + sw_logger()->close(); + ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(0)); + unlink(sw_logger()->get_real_file()); + + ASSERT_TRUE(content->contains(SW_STRL("hello world\n"))); +} + +namespace TestA { +class TestPrettyName { + public: + static void fun(bool strip, const char *expect_str); +}; + +void TestPrettyName::fun(bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); +} + +static void test_pretty_name(bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); +} + +static void test_pretty_name_lambda(bool strip, const char *expect_str) { + auto fn = [](bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); + }; + fn(strip, expect_str); +} + +} // namespace TestA + +static void test_pretty_name(bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); +} + +static void test_pretty_name_lambda(bool strip, const char *expect_str) { + auto fn = [](bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), expect_str); + }; + fn(strip, expect_str); +} + +TEST(log, pretty_name) { + TestA::TestPrettyName::fun(false, "TestA::TestPrettyName::fun"); + TestA::test_pretty_name(false, "TestA::test_pretty_name"); + test_pretty_name(false, "test_pretty_name"); + + TestA::TestPrettyName::fun(true, "TestPrettyName::fun"); + TestA::test_pretty_name(true, "test_pretty_name"); + test_pretty_name(true, "test_pretty_name"); +} + +TEST(log, pretty_name_lambda) { + TestA::test_pretty_name_lambda(true, "test_pretty_name_lambda"); + test_pretty_name_lambda(true, "test_pretty_name_lambda"); + + TestA::test_pretty_name_lambda(false, "TestA::test_pretty_name_lambda"); + test_pretty_name_lambda(false, "test_pretty_name_lambda"); +} + +TEST(log, ignore_error) { + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); + + const int ignored_errcode = 999999; + const int errcode = 888888; + + swoole_ignore_error(ignored_errcode); + + swoole_error_log(SW_LOG_WARNING, ignored_errcode, "error 1"); + swoole_error_log(SW_LOG_WARNING, errcode, "error 2"); + + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + ASSERT_FALSE(content->contains(SW_STRL("error 1"))); + ASSERT_TRUE(content->contains(SW_STRL("error 2"))); +} diff --git a/core-tests/src/core/string.cpp b/core-tests/src/core/string.cpp new file mode 100644 index 00000000000..6e285616e34 --- /dev/null +++ b/core-tests/src/core/string.cpp @@ -0,0 +1,273 @@ +#include "test_core.h" +#include "swoole_util.h" + +using namespace std; +using swoole::String; + +TEST(string, ltrim) { + char buf[1024]; + char *ptr_buf; + strcpy(buf, " hello world"); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("hello world", ptr_buf), 0); + ASSERT_NE(strcmp(" hello world", ptr_buf), 0); + + strcpy(buf, " "); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strlen(ptr_buf), 0); + + memcpy(buf, " a\0b\0", 6); + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("a", ptr_buf), 0); + + buf[0] = '\0'; + ptr_buf = buf; + swoole::ltrim(&ptr_buf, strlen(buf)); + ASSERT_EQ(strcmp("", ptr_buf), 0); +} + +TEST(string, rtrim) { + char buf[1024]; + strcpy(buf, "hello world "); + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strcmp("hello world", buf), 0); + ASSERT_NE(strcmp("hello world ", buf), 0); + + strcpy(buf, " "); + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strlen(buf), 0); + + buf[0] = '\0'; + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strcmp("", buf), 0); +} + +TEST(string, strnpos) { + { + string haystack = "hello world"; + string needle = " "; + int pos; + + pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(pos, 5); + } + { + string haystack = "hello world"; + string needle = "*"; + int pos; + + pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(-1, pos); + } +} + +TEST(string, strnstr) { + { + string haystack = "hello world"; + string needle = " "; + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(haystack.c_str() + 5, pos); + } + { + string haystack = "hello world"; + string needle = "*"; + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(NULL, pos); + } + { + string haystack = "hello world\r\n"; + string needle = "\r\n\r\n"; + const char *pos = swoole_strnstr(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(NULL, pos); + } +} + +TEST(string, explode) { + string haystack = "hello world"; + string needle = " "; + + String str; + str.str = (char *) haystack.c_str(); + str.length = haystack.length(); + + int value_1 = 0; + + const char *explode_str = nullptr; + size_t explode_length = 0; + + str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int { + explode_str = data; + explode_length = length; + value_1 = 5; + return false; + }); + + ASSERT_EQ(haystack, explode_str); + ASSERT_EQ(6, explode_length); + ASSERT_EQ(5, value_1); +} + +TEST(string, explode_2) { + string haystack = "hello,world,swoole,php,last"; + string needle = ","; + + String str; + str.str = (char *) haystack.c_str(); + str.length = haystack.length(); + + int count = 0; + vector list; + + size_t n = str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int { + list.push_back(string(data, length - 1)); + count++; + return true; + }); + + ASSERT_EQ(list[0], string("hello")); + ASSERT_EQ(list[1], string("world")); + ASSERT_EQ(list[2], string("swoole")); + ASSERT_EQ(list[3], string("php")); + ASSERT_EQ("last", string(str.str + n, str.length - n)); + ASSERT_EQ(4, count); + ASSERT_EQ(list.size(), count); +} + +static const int init_size = 1024; +static string test_data = "hello,world,swoole,php,last"; + +TEST(string, pop_1) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + char *str_1 = str->str; + + const int len_1 = 11; + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + char *str_2 = str->pop(init_size); + + EXPECT_EQ(str_1, str_2); + EXPECT_EQ(string("hello,world"), string(str_2, len_1)); + EXPECT_EQ(string(",swoole,php,last"), string(str->str, str->length)); + EXPECT_EQ(init_size, str->size); + + str->allocator->free(str_1); +} + +TEST(string, pop_2) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + char *str_1 = str->str; + + const int len_1 = test_data.length(); + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + char *str_2 = str->pop(init_size); + + EXPECT_EQ(str_1, str_2); + EXPECT_EQ(test_data, string(str_2, len_1)); + EXPECT_EQ(str->length, 0); + EXPECT_EQ(init_size, str->size); + + str->allocator->free(str_1); +} + +TEST(string, reduce_1) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + const int len_1 = 11; + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + + str->reduce(str->offset); + + EXPECT_EQ(string(",swoole,php,last"), string(str->str, str->length)); +} + +TEST(string, reduce_2) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + str->append(test_data.c_str(), test_data.length()); + str->offset = str->length; + + str->reduce(str->offset); + + EXPECT_EQ(str->length, 0); +} + +TEST(string, reduce_3) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + str->append(test_data.c_str(), test_data.length()); + str->offset = 0; + + str->reduce(str->offset); + + EXPECT_EQ(str->length, test_data.length()); +} + +const auto FORMAT_INT = 999999999999999; +const auto FORMAT_STR = "hello world"; + +TEST(string, format_1) { + String str1(1024); + str1.append_random_bytes(1024, true); + + size_t n = str1.format("str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + std::string str2("str=hello world, value=999999999999999"); + + ASSERT_EQ(str1.get_length(), n); + ASSERT_MEMEQ(str1.value(), str2.c_str(), n); +} + +TEST(string, format_2) { + String str1(1024); + str1.append_random_bytes(1024, true); + + std::string str2(str1.value(), str1.get_length()); + + size_t n = str1.format_impl(String::FORMAT_APPEND, "str=%s, value=%ld", FORMAT_STR, FORMAT_INT); + str2 += std::string(str1.value() + str2.length(), n); + + EXPECT_MEMEQ(str1.value(), str2.c_str(), str1.get_length()); +} + +TEST(string, substr_len) { + const char *str1 = "hello: swoole & world"; + ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', true), 5); + ASSERT_EQ(swoole::substr_len(str1, strlen(str1), ':', false), 15); +} + +TEST(string, starts_with) { + const char *str1 = "hello world"; + ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), SW_STRL("hello"))); + ASSERT_FALSE(swoole::starts_with(str1, strlen(str1), SW_STRL("php"))); + ASSERT_TRUE(swoole::starts_with(str1, strlen(str1), str1, strlen(str1))); +} + +TEST(string, ends_with) { + const char *str1 = "hello world"; + ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), SW_STRL("world"))); + ASSERT_FALSE(swoole::ends_with(str1, strlen(str1), SW_STRL("php"))); + ASSERT_TRUE(swoole::ends_with(str1, strlen(str1), str1, strlen(str1))); +} + +TEST(string, append_number) { + string data = "hello"; + auto str = swoole::make_string(data.length() + 32); + str->append(data.c_str(), data.length()); + str->append(123); + str->set_null_terminated(); + EXPECT_STREQ(str->str, data.append("123").c_str()); + + str->print(true); + str->print(false); +} diff --git a/core-tests/src/core/time.cpp b/core-tests/src/core/time.cpp new file mode 100644 index 00000000000..80107f62d24 --- /dev/null +++ b/core-tests/src/core/time.cpp @@ -0,0 +1,28 @@ +#include "test_core.h" +#include "swoole_util.h" + +TEST(time, get_ms) { + const int us = 3000; + long ms1 = swoole::time(); + usleep(us); + long ms2 = swoole::time(); + EXPECT_GE(ms2 - ms1, us / 1000); +} + +TEST(time, get_ms_steady) { + const int us = 3000; + long ms1 = swoole::time(true); + usleep(us); + long ms2 = swoole::time(true); + EXPECT_GE(ms2 - ms1, us / 1000); +} + +TEST(time, get_seconds) { + long sec1 = swoole::time(); + time_t sec2 = time(NULL); + ASSERT_TRUE(sec1 == sec2 or sec1 == sec2 - 1); +} + +TEST(time, get_timezone) { + ASSERT_GE(swoole::get_timezone(), 0); +} diff --git a/core-tests/src/core/util.cpp b/core-tests/src/core/util.cpp new file mode 100644 index 00000000000..d756d542321 --- /dev/null +++ b/core-tests/src/core/util.cpp @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_util.h" + +TEST(util, bitmap) { + swoole::BitMap m(4096); + + m.set(199); + m.set(1234); + m.set(3048); + + ASSERT_EQ(m.get(199), true); + ASSERT_EQ(m.get(1234), true); + ASSERT_EQ(m.get(3048), true); + + ASSERT_EQ(m.get(2048), false); + ASSERT_EQ(m.get(128), false); + + m.unset(1234); + ASSERT_EQ(m.get(1234), false); + + m.clear(); +} diff --git a/core-tests/src/core/wheel_timer.cpp b/core-tests/src/core/wheel_timer.cpp new file mode 100644 index 00000000000..1fa688a02e6 --- /dev/null +++ b/core-tests/src/core/wheel_timer.cpp @@ -0,0 +1,31 @@ +#include "test_core.h" +#include "swoole_wheel_timer.h" + +using namespace swoole; +using namespace std; + + +TEST(wheel_timer, next) { + constexpr int size = 5; + WheelTimer wt(size); + + auto t1 = wt.add([&](WheelTimerNode *){ + ASSERT_EQ(wt.get_round(), 6); + }); + ASSERT_EQ(t1->index_, size - 1); + wt.next(); + + auto t2 = wt.add([&](WheelTimerNode *){ + ASSERT_EQ(wt.get_round(), 9); + }); + ASSERT_EQ(t2->index_, 0); + wt.update(t1); + + for (int i = 0; i < 10; i++) { + if (i < 4) { + wt.update(t2); + } + wt.next(); + } +} + diff --git a/core-tests/src/coroutine/async.cpp b/core-tests/src/coroutine/async.cpp index d115df21906..4de8af590bf 100644 --- a/core-tests/src/coroutine/async.cpp +++ b/core-tests/src/coroutine/async.cpp @@ -1,19 +1,61 @@ -#include "tests.h" +#include "test_coroutine.h" +#include "swoole_async.h" -using namespace swoole::coroutine; +#include +#include + +using namespace std; +using swoole::AsyncEvent; +using swoole::test::coroutine; const int magic_code = 0x7009501; -TEST(coroutine_async, usleep) -{ - coro_test([](void *arg) - { - swAio_event ev; - bool retval = async([](swAio_event *event) { - usleep(1000); - event->ret = magic_code; - }, ev); +TEST(coroutine_async, usleep) { + coroutine::run([](void *arg) { + AsyncEvent ev = {}; + bool retval = swoole::coroutine::async( + [](AsyncEvent *event) { + usleep(1000); + event->retval = magic_code; + }, + ev); + ASSERT_EQ(retval, true); + ASSERT_EQ(ev.retval, magic_code); + }); +} + +TEST(coroutine_async, gethostbyname) { + coroutine::run([](void *arg) { + string domain("www.baidu.com"), ip; + + bool retval = swoole::coroutine::async([&]() { + char buf[128]; + if (swoole::network::gethostbyname(AF_INET, domain.c_str(), buf) == SW_OK) { + char addr[128]; + inet_ntop(AF_INET, buf, addr, sizeof(addr)); + ip = addr; + } else { + ip = "unknown"; + } + }); + ASSERT_EQ(retval, true); - ASSERT_EQ(ev.ret, magic_code); + match_results result; + try { + const regex pattern("(\\d{1,3}).(\\d{1,3}).(\\d{1,3}).(\\d{1,3})"); + ASSERT_EQ(regex_match(ip, result, pattern), true); + } catch (std::exception &ex) { + std::cerr << "regex error: gcc version must be 4.9+" << std::endl; + } + }); +} + +TEST(coroutine_async, error) { + coroutine::run([](void *arg) { + int retval = 0x7009501; + const char *test_file = "/tmp/swoole_core_test_file_not_exists"; + swoole::coroutine::async([&](void) { retval = open(test_file, O_RDONLY); }); + ASSERT_EQ(retval, -1); + ASSERT_EQ(errno, ENOENT); }); } diff --git a/core-tests/src/coroutine/base.cpp b/core-tests/src/coroutine/base.cpp index 0817a9fa703..442bb477d6f 100644 --- a/core-tests/src/coroutine/base.cpp +++ b/core-tests/src/coroutine/base.cpp @@ -1,44 +1,236 @@ -#include "tests.h" +#include "test_coroutine.h" using namespace swoole; +using swoole::coroutine::System; -TEST(coroutine_base, create) -{ +TEST(coroutine_base, create) { long _cid; - long cid = Coroutine::create([](void *arg) - { - *(long *) arg = Coroutine::get_current_cid(); - }, &_cid); + long cid = Coroutine::create([](void *arg) { *(long *) arg = Coroutine::get_current_cid(); }, &_cid); ASSERT_GT(cid, 0); ASSERT_EQ(cid, _cid); } -TEST(coroutine_base, get_current) -{ +TEST(coroutine_base, get_current) { long _cid; - long cid = Coroutine::create([](void *arg) - { - auto co = Coroutine::get_current(); - *(long *) arg = co->get_cid(); - }, &_cid); + long cid = Coroutine::create( + [](void *arg) { + auto co = Coroutine::get_current(); + *(long *) arg = co->get_cid(); + }, + &_cid); ASSERT_GT(cid, 0); ASSERT_EQ(cid, _cid); } -TEST(coroutine_base, yield_resume) -{ +TEST(coroutine_base, get_init_msec) { + Coroutine::create([](void *arg) { + auto co = Coroutine::get_current(); + long init_msec = co->get_init_msec(); + + ASSERT_GT(init_msec, 0); + }); +} + +TEST(coroutine_base, yield_resume) { + Coroutine::set_on_yield([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + + Coroutine::set_on_resume([](void *arg) { + Coroutine *current = Coroutine::get_current(); + ASSERT_EQ(current, nullptr); + }); + + Coroutine::set_on_close([](void *arg) { + long task = *(long *) Coroutine::get_current_task(); + ASSERT_EQ(task, Coroutine::get_current_cid()); + }); + long _cid; - long cid = Coroutine::create([](void *arg) - { - long cid = Coroutine::get_current_cid(); - Coroutine *co = Coroutine::get_by_cid(cid); - co->yield(); - *(long *) arg = Coroutine::get_current_cid(); - }, &_cid); + long cid = Coroutine::create( + [](void *arg) { + long cid = Coroutine::get_current_cid(); + Coroutine *co = Coroutine::get_by_cid(cid); + co->set_task((void *) &cid); + co->yield(); + *(long *) arg = Coroutine::get_current_cid(); + }, + &_cid); ASSERT_GT(cid, 0); Coroutine::get_by_cid(cid)->resume(); + Coroutine::set_on_close(nullptr); ASSERT_EQ(cid, _cid); } + +TEST(coroutine_base, get_cid) { + Coroutine::create([](void *arg) { + auto co = Coroutine::get_current(); + long cid = co->get_cid(); + + ASSERT_GT(cid, 0); + }); +} + +TEST(coroutine_base, get_origin) { + Coroutine::create([](void *arg) { + auto *co = Coroutine::get_current(); + + Coroutine::create( + [](void *arg) { + auto current_co = Coroutine::get_current(); + auto origin_co = current_co->get_origin(); + + ASSERT_EQ(arg, origin_co); + }, + co); + }); +} + +TEST(coroutine_base, get_origin_cid) { + Coroutine::create([](void *arg) { + auto _cid = Coroutine::get_current_cid(); + + Coroutine::create( + [](void *arg) { + auto origin_cid = Coroutine::get_current()->get_origin_cid(); + + ASSERT_EQ(*(long *) arg, origin_cid); + }, + &_cid); + }); +} + +TEST(coroutine_base, is_end) { + Coroutine::create([](void *_arg) { + auto co = Coroutine::get_current(); + ASSERT_FALSE(co->is_end()); + }); +} + +TEST(coroutine_base, set_task) { + Coroutine::create([](void *_arg) { + int task; + auto co = Coroutine::get_current(); + co->set_task(&task); + void *actual = co->get_task(); + ASSERT_EQ(actual, &task); + }); +} + +TEST(coroutine_base, get_current_task) { + Coroutine::create([](void *_arg) { + int task; + auto co = Coroutine::get_current(); + co->set_task(&task); + void *actual = co->get_task(); + ASSERT_EQ(actual, Coroutine::get_current_task()); + }); +} + +TEST(coroutine_base, get_current_cid) { + Coroutine::create([](void *_arg) { + auto co = Coroutine::get_current(); + auto actual = co->get_cid(); + ASSERT_EQ(actual, Coroutine::get_current_cid()); + ASSERT_EQ(actual, swoole_coroutine_get_current_id()); + }); +} + +TEST(coroutine_base, get_by_cid) { + Coroutine::create([](void *_arg) { + auto actual = Coroutine::get_current(); + auto cid = actual->get_cid(); + ASSERT_EQ(actual, Coroutine::get_by_cid(cid)); + }); +} + +TEST(coroutine_base, get_task_by_cid) { + Coroutine::create([](void *_arg) { + int task; + auto co = Coroutine::get_current(); + co->set_task(&task); + auto actual = co->get_task(); + ASSERT_EQ(actual, Coroutine::get_task_by_cid(co->get_cid())); + }); +} + +TEST(coroutine_base, get_last_cid) { + Coroutine::create([](void *_arg) {}); + Coroutine::create([](void *_arg) {}); + long cid = Coroutine::create([](void *_arg) {}); + + ASSERT_EQ(Coroutine::get_last_cid(), cid); +} + +TEST(coroutine_base, count) { + Coroutine::create([](void *_arg) { + ASSERT_EQ(Coroutine::count(), 1); + Coroutine::create([](void *_arg) { ASSERT_EQ(Coroutine::count(), 2); }); + }); + ASSERT_EQ(Coroutine::count(), 0); +} + +TEST(coroutine_base, get_peak_num) { + Coroutine::create( + [](void *_arg) { Coroutine::create([](void *_arg) { ASSERT_GE(Coroutine::get_peak_num(), 2); }); }); +} + +TEST(coroutine_base, get_elapsed) { + long elapsed_time = 0; + Coroutine::create( + [](void *arg) { + auto co = Coroutine::get_current(); + usleep(2000); + *(long *) arg = Coroutine::get_elapsed(co->get_cid()); + }, + &elapsed_time); + ASSERT_GE(elapsed_time, 2); +} + +TEST(coroutine_base, run) { + long cid = coroutine::run([](void *ptr) { + + }); + ASSERT_GE(cid, 1); +} + +TEST(coroutine_base, cancel) { + coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + Coroutine::create([co](void *) { + System::sleep(0.002); + co->cancel(); + }); + ASSERT_EQ(co->yield_ex(-1), false); + ASSERT_EQ(co->is_canceled(), true); + }); +} + +TEST(coroutine_base, timeout) { + coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + ASSERT_EQ(co->yield_ex(0.005), false); + ASSERT_EQ(co->is_timedout(), true); + }); +} + +TEST(coroutine_base, gdb) { + Coroutine::create([](void *) { + Coroutine *current = Coroutine::get_current(); + long cid = current->get_cid(); + ASSERT_EQ(swoole_coroutine_count(), 1); + ASSERT_EQ(swoole_coroutine_get(cid), current); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + ASSERT_EQ(swoole_coroutine_iterator_each(), nullptr); + + swoole_coroutine_iterator_reset(); + ASSERT_EQ(swoole_coroutine_iterator_each(), current); + Coroutine::print_list(); + }); +} diff --git a/core-tests/src/coroutine/channel.cpp b/core-tests/src/coroutine/channel.cpp index fc0a498dc60..cb24673e5c0 100644 --- a/core-tests/src/coroutine/channel.cpp +++ b/core-tests/src/coroutine/channel.cpp @@ -1,16 +1,12 @@ -#include "tests.h" - -#include "coroutine_channel.h" +#include "test_coroutine.h" using swoole::coroutine::Channel; -using namespace swoole; using namespace std; +using namespace swoole::test; -TEST(coroutine_channel, push_pop) -{ - coro_test([](void *arg) - { +TEST(coroutine_channel, push_pop) { + coroutine::run([](void *arg) { Channel chan(1); int i = 1; bool ret; @@ -21,63 +17,59 @@ TEST(coroutine_channel, push_pop) }); } -TEST(coroutine_channel, push_yield) -{ +TEST(coroutine_channel, push_yield) { Channel chan(1); - coro_test({ - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - int i = 1; - bool ret; - - ret = chan->push(&i); - ASSERT_TRUE(ret); - ret = chan->push(&i); - ASSERT_TRUE(ret); - }, &chan), - - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - ASSERT_EQ(*(int *) chan->pop(), 1); - ASSERT_EQ(*(int *) chan->pop(), 1); - }, &chan) - }); + coroutine::run({make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + int i = 1; + bool ret; + + ret = chan->push(new int(i)); + ASSERT_TRUE(ret); + ret = chan->push(new int(i)); + ASSERT_TRUE(ret); + }, + &chan), + + make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + ASSERT_EQ(*(int *) chan->pop(), 1); + ASSERT_EQ(*(int *) chan->pop(), 1); + }, + &chan)}); } -TEST(coroutine_channel, pop_yield) -{ +TEST(coroutine_channel, pop_yield) { Channel chan(1); - coro_test({ - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - - ASSERT_EQ(*(int *) chan->pop(), 1); - ASSERT_EQ(*(int *) chan->pop(), 1); - }, &chan), - - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - int i = 1; - bool ret; - - ret = chan->push(&i); - ASSERT_TRUE(ret); - ret = chan->push(&i); - ASSERT_TRUE(ret); - }, &chan) - }); + coroutine::run({make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + + ASSERT_EQ(*(int *) chan->pop(), 1); + ASSERT_EQ(*(int *) chan->pop(), 1); + }, + &chan), + + make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + int i = 1; + bool ret; + + ret = chan->push(&i); + ASSERT_TRUE(ret); + ret = chan->push(&i); + ASSERT_TRUE(ret); + }, + &chan)}); } -TEST(coroutine_channel, push_timeout) -{ - coro_test([](void *arg) - { +TEST(coroutine_channel, push_timeout) { + coroutine::run([](void *arg) { Channel chan(1); bool ret; @@ -88,10 +80,8 @@ TEST(coroutine_channel, push_timeout) }); } -TEST(coroutine_channel, pop_timeout) -{ - coro_test([](void *arg) - { +TEST(coroutine_channel, pop_timeout) { + coroutine::run([](void *arg) { Channel chan(1); void *ret; @@ -99,3 +89,39 @@ TEST(coroutine_channel, pop_timeout) ASSERT_EQ(ret, nullptr); }); } + +TEST(coroutine_channel, close) { + Channel chan(1); + coroutine::run( + [](void *arg) { + int value = 1; + auto chan = (Channel *) arg; + while (1) { + if (!chan->push((void *) &value)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_FALSE(chan->push(nullptr)); + break; + } + } + }, + &chan); + + ASSERT_TRUE(chan.close()); + ASSERT_FALSE(chan.close()); + + Channel chan2(1); + coroutine::run( + [](void *arg) { + auto chan = (Channel *) arg; + while (1) { + if (!chan->pop(0)) { + ASSERT_EQ(chan->get_error(), Channel::ErrorCode::ERROR_CLOSED); + ASSERT_EQ(chan->pop(), nullptr); + break; + } + } + }, + &chan2); + + ASSERT_TRUE(chan2.close()); +} diff --git a/core-tests/src/coroutine/gethostbyname.cpp b/core-tests/src/coroutine/gethostbyname.cpp index 58fff1aa3a4..042999d61af 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -1,56 +1,46 @@ -#include "tests.h" - -#include "coroutine_system.h" +#include "test_coroutine.h" using swoole::Coroutine; +using swoole::Timer; using swoole::coroutine::System; +using swoole::test::coroutine; -TEST(coroutine_gethostbyname, resolve_with_cache) -{ - coro_test([](void *arg) - { - System::set_dns_cache_capacity(10); +const char *domain_baidu = "www.baidu.com"; +const char *domain_tencent = "www.tencent.com"; - std::string addr1 = System::gethostbyname("www.baidu.com", AF_INET); +TEST(coroutine_gethostbyname, resolve_cache) { + coroutine::run([](void *arg) { + System::set_dns_cache_capacity(10); + std::string addr1 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr1, ""); - - int64_t start = swTimer_get_absolute_msec(); - - for (int i = 0; i < 100; ++i) - { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + int64_t with_cache = Timer::get_absolute_msec(); + for (int i = 0; i < 100; ++i) { + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_EQ(addr1, addr2); } + with_cache = Timer::get_absolute_msec() - with_cache; - ASSERT_LT(swTimer_get_absolute_msec() - start, 5); - }); -} - -TEST(coroutine_gethostbyname, resolve_without_cache) -{ - coro_test([](void *arg) - { System::set_dns_cache_capacity(0); - - std::string addr1 = System::gethostbyname("www.baidu.com", AF_INET); - ASSERT_NE(addr1, ""); - - int64_t start = swTimer_get_absolute_msec(); - - for (int i = 0; i < 5; ++i) - { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + int64_t without_cache = Timer::get_absolute_msec(); + for (int i = 0; i < 5; ++i) { + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr2, ""); } + without_cache = Timer::get_absolute_msec() - without_cache; - ASSERT_GT(swTimer_get_absolute_msec() - start, 5); + ASSERT_GT(without_cache, with_cache); }); } -TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) -{ - coro_test([](void *arg) - { +TEST(coroutine_gethostbyname, impl_async) { + coroutine::run([](void *arg) { + auto result = swoole::coroutine::gethostbyname_impl_with_async(domain_baidu, AF_INET); + ASSERT_EQ(result.empty(), false); + }); +} + +TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) { + coroutine::run([](void *arg) { System::set_dns_cache_capacity(10); std::string addr1 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET); @@ -61,10 +51,9 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) ASSERT_EQ(addr1.find(":"), addr1.npos); ASSERT_NE(addr2.find(":"), addr2.npos); - int64_t start = swTimer_get_absolute_msec(); + int64_t start = Timer::get_absolute_msec(); - for (int i = 0; i < 100; ++i) - { + for (int i = 0; i < 100; ++i) { std::string addr3 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET); std::string addr4 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET6); @@ -72,6 +61,26 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) ASSERT_EQ(addr2, addr4); } - ASSERT_LT(swTimer_get_absolute_msec() - start, 5); + ASSERT_LT(Timer::get_absolute_msec() - start, 5); + }); +} + +TEST(coroutine_gethostbyname, dns_expire) { + coroutine::run([](void *arg) { + time_t expire = 0.2; + System::set_dns_cache_expire(expire); + System::gethostbyname(domain_tencent, AF_INET); + + int64_t with_cache = Timer::get_absolute_msec(); + System::gethostbyname(domain_tencent, AF_INET); + with_cache = Timer::get_absolute_msec() - with_cache; + + sleep(0.3); + int64_t without_cache = Timer::get_absolute_msec(); + System::gethostbyname(domain_tencent, AF_INET); + without_cache = Timer::get_absolute_msec() - without_cache; + + ASSERT_GE(without_cache, with_cache); + System::clear_dns_cache(); }); } diff --git a/core-tests/src/coroutine/hook.cpp b/core-tests/src/coroutine/hook.cpp new file mode 100644 index 00000000000..1aee8368af9 --- /dev/null +++ b/core-tests/src/coroutine/hook.cpp @@ -0,0 +1,611 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_coroutine.h" +#include "swoole_file.h" +#include "swoole_util.h" + +using namespace swoole::test; + +using swoole::Coroutine; +using swoole::String; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using swoole::test::coroutine; + +const char *host_1 = "www.baidu.com"; +const char *host_2 = "www.xxxxxxxxxxxxxxxxxxxxx00000xxxxxxxxx----not_found.com"; +static const char *test_file = "/tmp/swoole-core-test"; + +TEST(coroutine_hook, file) { + coroutine::run([](void *arg) { + char buf[8192]; + size_t n_buf = sizeof(buf); + ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf); + + int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666); + ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf); + swoole_coroutine_close(fd); + + fd = swoole_coroutine_open(test_file, O_RDONLY, 0); + char data[8192]; + ASSERT_EQ(swoole_coroutine_read(fd, data, n_buf), n_buf); + ASSERT_EQ(std::string(buf, n_buf), std::string(data, n_buf)); + swoole_coroutine_close(fd); + + ASSERT_EQ(swoole_coroutine_unlink(test_file), 0); + }); +} + +TEST(coroutine_hook, gethostbyname) { + coroutine::run([](void *arg) { + auto result1 = swoole_coroutine_gethostbyname(host_1); + ASSERT_NE(result1, nullptr); + + auto result2 = swoole_coroutine_gethostbyname(host_2); + ASSERT_EQ(result2, nullptr); + ASSERT_EQ(h_errno, HOST_NOT_FOUND); + }); +} + +TEST(coroutine_hook, getaddrinfo) { + coroutine::run([](void *arg) { + struct addrinfo hints; + sw_memset_zero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + struct addrinfo *result, *curr; + int count; + + result = nullptr; + auto result1 = swoole_coroutine_getaddrinfo(host_1, "http", &hints, &result); + ASSERT_EQ(result1, 0); + + curr = result; + count = 0; + while (curr && curr->ai_addr) { + curr = curr->ai_next; + count++; + } + ASSERT_GE(count, 1); + freeaddrinfo(result); + + result = nullptr; + auto result2 = swoole_coroutine_getaddrinfo(host_2, nullptr, &hints, &result); + ASSERT_EQ(result2, EAI_NONAME); + ASSERT_EQ(result, nullptr); + freeaddrinfo(result); + }); +} + +TEST(coroutine_hook, fstat) { + coroutine::run([](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_RDONLY, 0); + struct stat statbuf_1; + swoole_coroutine_fstat(fd, &statbuf_1); + + struct stat statbuf_2; + fstat(fd, &statbuf_2); + + ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0); + + swoole_coroutine_close(fd); + }); +} + +TEST(coroutine_hook, statvfs) { + coroutine::run([](void *arg) { + struct statvfs statbuf_1; + swoole_coroutine_statvfs("/tmp", &statbuf_1); + + struct statvfs statbuf_2; + statvfs("/tmp", &statbuf_2); + + ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0); + }); +} + +TEST(coroutine_hook, dir) { + coroutine::run([](void *arg) { + ASSERT_EQ(swoole_coroutine_mkdir(TEST_TMP_DIR, 0666), 0); + ASSERT_EQ(swoole_coroutine_access(TEST_TMP_DIR, R_OK), 0); + ASSERT_EQ(swoole_coroutine_rmdir(TEST_TMP_DIR), 0); + ASSERT_EQ(access(TEST_TMP_DIR, R_OK), -1); + }); +} + +TEST(coroutine_hook, socket) { + coroutine::run([](void *arg) { + int sock = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GT(sock, 0); + swoole::network::Address sa; + std::string ip = System::gethostbyname("www.baidu.com", AF_INET, 10); + sa.assign(SW_SOCK_TCP, ip, 80); + ASSERT_EQ(swoole_coroutine_connect(sock, &sa.addr.ss, sa.len), 0); + ASSERT_EQ(swoole_coroutine_socket_wait_event(sock, SW_EVENT_WRITE, 5), SW_OK); + + const char req[] = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\nKeepAlive: off\r\n\r\n"; + ASSERT_EQ(swoole_coroutine_send(sock, req, strlen(req), 0), strlen(req)); + + swoole::String resp(1024); + + while (1) { + ssize_t n = swoole_coroutine_recv(sock, resp.value() + resp.length, resp.size - resp.length, 0); + if (n <= 0) { + break; + } + resp.length += n; + if (resp.length == resp.size) { + resp.reserve(resp.size * 2); + } + } + + ASSERT_GT(resp.length, 100); + ASSERT_TRUE(resp.contains("baidu.com")); + swoole_coroutine_close(sock); + }); +} + +TEST(coroutine_hook, rename) { + coroutine::run([](void *arg) { + char buf[8192]; + size_t n_buf = sizeof(buf); + ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf); + + int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666); + ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf); + swoole_coroutine_close(fd); + + std::string to_file_name = std::string(test_file, ".bak"); + ASSERT_EQ(swoole_coroutine_rename(test_file, to_file_name.c_str()), 0); + ASSERT_EQ(access(TEST_TMP_DIR, F_OK), -1); + ASSERT_EQ(access(to_file_name.c_str(), F_OK), 0); + + swoole_coroutine_unlink(to_file_name.c_str()); + }); +} + +TEST(coroutine_hook, flock) { + long start_time = swoole::time(); + coroutine::run([&](void *arg) { + swoole::Coroutine::create([&](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_EX), 0); + System::sleep(0.1); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + ASSERT_LE(swoole::time() - start_time, 1000); + swoole_coroutine_close(fd); + }); + swoole::Coroutine::create([&](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0); + System::sleep(2); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + swoole_coroutine_close(fd); + }); + }); + // LOCK_NB + coroutine::run([](void *arg) { + int fd1 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_EX), 0); + int fd2 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd2, LOCK_EX | LOCK_NB), -1); + ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_UN), 0); + swoole_coroutine_close(fd1); + swoole_coroutine_close(fd2); + }); +} + +TEST(coroutine_hook, read_dir) { + auto fp = opendir("/tmp"); + std::string dir1(readdir(fp)->d_name); + std::string dir2(readdir(fp)->d_name); + closedir(fp); + + auto fn = [&]() { + auto fp = swoole_coroutine_opendir("/tmp"); + ASSERT_NE(fp, nullptr); + struct dirent *entry; + + entry = swoole_coroutine_readdir(fp); + ASSERT_NE(entry, nullptr); + ASSERT_STREQ(entry->d_name, dir1.c_str()); + + entry = swoole_coroutine_readdir(fp); + ASSERT_NE(entry, nullptr); + ASSERT_STREQ(entry->d_name, dir2.c_str()); + + swoole_coroutine_closedir(fp); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, readlink) { + auto fn = []() { + char buf1[1024] = {}; + char buf2[1024] = {}; + + auto retval = swoole_coroutine_readlink("/proc/self/cwd", buf1, sizeof(buf1)); + ASSERT_NE(retval, -1); + + getcwd(buf2, sizeof(buf2)); + ASSERT_STREQ(buf1, buf2); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, stdio_1) { + auto fn = []() { + FILE *fp1 = swoole_coroutine_fopen(test_file, "w+"); + const char *str = "hello world"; + int n = swoole_coroutine_fputs(str, fp1); + ASSERT_TRUE(n); + swoole_coroutine_fclose(fp1); + + FILE *fp2 = swoole_coroutine_fopen(test_file, "r+"); + char buf[1024]; + char *str2 = swoole_coroutine_fgets(buf, sizeof(buf), fp2); + + ASSERT_STREQ(str2, str); + swoole_coroutine_fclose(fp2); + + unlink(test_file); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, stdio_2) { + auto fn = []() { + size_t size = 1024; + + FILE *fp1 = swoole_coroutine_fopen(test_file, "w+"); + String str(size); + str.append_random_bytes(size); + size_t n = swoole_coroutine_fwrite(str.str, 1, size, fp1); + ASSERT_EQ(n, size); + swoole_coroutine_fclose(fp1); + + FILE *fp2 = swoole_coroutine_fopen(test_file, "r+"); + char buf[size]; + size_t len = swoole_coroutine_fread(buf, 1, size, fp2); + ASSERT_EQ(len, size); + + len = swoole_coroutine_fread(buf, 1, size, fp2); + ASSERT_EQ(len, 0); + + ASSERT_TRUE(swoole_coroutine_feof(fp2)); + + ASSERT_MEMEQ(buf, str.str, size); + swoole_coroutine_fclose(fp2); + + unlink(test_file); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, sleep) { + coroutine::run([&](void *arg) { + const int sec = 1; + long sec_1 = swoole::time(); + swoole_coroutine_sleep(sec); + long sec_2 = swoole::time(); + ASSERT_LE(sec_2 - sec_1, sec); + + const int us = 2000; + long us_1 = swoole::time(); + swoole_coroutine_usleep(us); + long us_2 = swoole::time(); + ASSERT_LE(us_2 - us_1, us / 1000); + }); +} + +TEST(coroutine_hook, exists) { + coroutine::run([&](void *arg) { + const int fd = 100; // fake fd + ASSERT_EQ(swoole_coroutine_socket_create(fd), 0); + ASSERT_TRUE(swoole_coroutine_socket_exists(fd)); + auto sock = swoole_coroutine_get_socket_object(fd); + ASSERT_EQ(sock->get_fd(), fd); + swoole_coroutine_close(fd); + }); +} + +TEST(coroutine_hook, timeout) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + // unregister fd + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05), -1); + + swoole::Coroutine::create([&](void *) { + ASSERT_EQ(swoole_coroutine_socket_create(pairs[0]), 0); + + // unknown which + ASSERT_EQ(swoole_coroutine_socket_set_timeout(pairs[0], 100, 0.05), -1); + + swoole_coroutine_socket_set_timeout(pairs[0], SO_SNDTIMEO, 0.05); + size_t result = swoole_coroutine_write(pairs[0], text.c_str(), length); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(result, length); + }); + + char data[length + 1]; + ASSERT_EQ(swoole_coroutine_socket_create(pairs[1]), 0); + swoole_coroutine_socket_set_timeout(pairs[1], SO_RCVTIMEO, 0.05); + size_t result = swoole_coroutine_read(pairs[1], data, length); + data[result] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_EQ(result, length); + ASSERT_STREQ(data, text.c_str()); + }); +} + +TEST(coroutine_hook, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + swoole::Coroutine::create([&](void *) { + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_sendmsg(pairs[0], &msg, 0); + ASSERT_EQ(swoole_coroutine_close(pairs[0]), 0); + ASSERT_EQ(ret, length); + }); + + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = swoole_coroutine_recvmsg(pairs[1], &msg, 0); + buf[ret] = '\0'; + ASSERT_EQ(swoole_coroutine_close(pairs[1]), 0); + ASSERT_STREQ(buf, text.c_str()); + }); +} + +TEST(coroutine_hook, lseek) { + std::string file = get_jpg_file(); + int fd = swoole_coroutine_open(file.c_str(), O_RDONLY, 'r'); + off_t offset = swoole_coroutine_lseek(fd, 0, SEEK_SET); + swoole_coroutine_close(fd); + ASSERT_EQ(offset, 0); +} + +extern std::pair, std::shared_ptr> create_socket_pair(); + +TEST(coroutine_hook, socket_close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + auto _fd = _sock->move_fd(); + swoole_coroutine_socket_create(_fd); + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = swoole_coroutine_write(_fd, buffer->value(), buffer->get_length()); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = swoole_coroutine_read(_fd, buf, sizeof(buf)); + if (result < 0 && errno == ECANCELED) { + ASSERT_EQ(swoole_coroutine_close(_fd), 0); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_EQ(swoole_coroutine_close(_fd), -1); + ASSERT_EQ(errno, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + }); +} + +TEST(coroutine_hook, poll) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 1); + ASSERT_TRUE(fds[1].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN; + fds[1].fd = _fd1; + fds[1].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + fds[1].fd = _fd1; + fds[1].events = POLLIN | POLLOUT; + + ASSERT_EQ(swoole_coroutine_poll(fds, 2, 1000), 2); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[1].revents & POLLIN); + ASSERT_FALSE(fds[0].revents & POLLOUT); // not writable + ASSERT_TRUE(fds[1].revents & POLLOUT); + result = swoole_coroutine_read(_fd0, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + }); +} + +TEST(coroutine_hook, poll_fake) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock0 = pair.first; + auto _fd0 = _sock0->move_fd(); + swoole_coroutine_socket_create(_fd0); + + auto _sock1 = pair.second; + auto _fd1 = _sock1->move_fd(); + swoole_coroutine_socket_create(_fd1); + + Coroutine::create([&](void *) { + ssize_t result; + result = swoole_coroutine_write(_fd0, buffer->value(), buffer->get_length()); + ASSERT_GT(result, 0); + System::sleep(0.01); + result = swoole_coroutine_write(_fd1, buffer->value(), 16 * 1024); + ASSERT_GT(result, 0); + }); + + struct pollfd fds[2]; + char buf[4096]; + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd1; + fds[0].events = POLLIN; + + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + + ssize_t result = swoole_coroutine_read(_fd1, buf, sizeof(buf)); + ASSERT_GT(result, 1024); + + bzero(fds, sizeof(pollfd)); + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 2, 1000), -1); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS); + + System::sleep(0.02); + + bzero(fds, sizeof(pollfd)); + fds[0].fd = _fd0; + fds[0].events = POLLIN | POLLOUT; + ASSERT_EQ(swoole_coroutine_poll_fake(fds, 1, 1000), 1); + ASSERT_TRUE(fds[0].revents & POLLIN); + ASSERT_TRUE(fds[0].revents & POLLOUT); + }); +} \ No newline at end of file diff --git a/core-tests/src/coroutine/http_server.cpp b/core-tests/src/coroutine/http_server.cpp new file mode 100644 index 00000000000..373d2d8afb1 --- /dev/null +++ b/core-tests/src/coroutine/http_server.cpp @@ -0,0 +1,101 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_coroutine.h" +#include "httplib_server.h" + +using namespace swoole::test; +using namespace httplib; +using namespace std; + +TEST(coroutine_http_server, get) { + Server svr; + mutex lock; + int port = swoole::test::get_random_port(); + lock.lock(); + + thread t1([&lock, port]() { + lock.lock(); + Client cli(TEST_HOST, port); + auto resp1 = cli.Get("/hi"); + EXPECT_EQ(resp1->status, 200); + EXPECT_EQ(resp1->body, string("Hello World!")); + + auto resp2 = cli.Get("/stop"); + EXPECT_EQ(resp2->status, 200); + EXPECT_EQ(resp2->body, string("Stop Server!")); + }); + + coroutine::run([&lock, &svr, port](void *arg) { + svr.Get("/hi", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); + + svr.Get("/stop", [&svr](const Request &req, Response &res) { + res.set_content("Stop Server!", "text/plain"); + svr.stop(); + }); + + svr.Post("/post", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); + + svr.BeforeListen([&lock]() { lock.unlock(); }); + + ASSERT_TRUE(svr.listen(TEST_HOST, port)); + }); + + t1.join(); +} + +TEST(coroutine_http_server, post) { + Server svr; + mutex lock; + int port = swoole::test::get_random_port(); + lock.lock(); + + std::thread t1([&lock, port]() { + lock.lock(); + + Client cli(TEST_HOST, port); + + httplib::Params params; + params.emplace("name", "john"); + params.emplace("note", "coder"); + + auto resp1 = cli.Post("/post", params); + EXPECT_EQ(resp1->status, 200); + EXPECT_EQ(resp1->body, string("Hello World!")); + + auto resp2 = cli.Get("/stop"); + EXPECT_EQ(resp2->status, 200); + EXPECT_EQ(resp2->body, string("Stop Server!")); + }); + + coroutine::run([&lock, &svr, port](void *arg) { + svr.Get("/stop", [&svr](const Request &req, Response &res) { + res.set_content("Stop Server!", "text/plain"); + svr.stop(); + }); + + svr.Post("/post", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); + + svr.BeforeListen([&lock]() { lock.unlock(); }); + + svr.listen(TEST_HOST, port); + }); + + t1.join(); +} diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index c1ebaa119ce..e4787c7cd5b 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -1,26 +1,54 @@ -#include "tests.h" -#include "coroutine_socket.h" +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ +#include "test_process.h" +#include "test_coroutine.h" +#include "test_server.h" + +using namespace swoole::test; + +using swoole::Coroutine; +using swoole::HttpProxy; +using swoole::Protocol; +using swoole::Socks5Proxy; +using swoole::String; using swoole::coroutine::Socket; +using swoole::coroutine::System; +using swoole::network::Address; +using swoole::network::IOVector; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; +using swoole::test::Server; -TEST(coroutine_socket, connect_refused) -{ - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; +const std::string host = "www.baidu.com"; - coro_test([](void *arg) - { +TEST(coroutine_socket, connect_refused) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9801, 0.5); + bool retval = sock.connect("127.0.0.1", 9801); ASSERT_EQ(retval, false); ASSERT_EQ(sock.errCode, ECONNREFUSED); }); } -TEST(coroutine_socket, connect_timeout) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, connect_timeout) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); sock.set_timeout(0.5); bool retval = sock.connect("192.0.0.1", 9801); @@ -29,39 +57,87 @@ TEST(coroutine_socket, connect_timeout) }); } -TEST(coroutine_socket, connect_with_dns) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, connect_with_dns) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("www.baidu.com", 80, 0.5); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); }); } -TEST(coroutine_socket, recv_success) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, tcp6) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP6); + bool retval = sock.connect("::1", 80); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + }); +} + +TEST(coroutine_socket, unixsock_fail) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_UNIX_STREAM); + bool retval = sock.connect("/tmp/unix.sock"); + ASSERT_EQ(retval, false); + ASSERT_EQ(sock.errCode, ENOENT); + }); +} + +TEST(coroutine_socket, recv_success) { + pid_t pid; + int port = swoole::test::get_random_port(); + + Process proc([port](Process *proc) { + on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { + SERVER_THIS->send(req->info.fd, req->data, req->info.len); + }; + + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + serv.on("onReceive", (void *) receive_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9501, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); - sock.send("echo", 5); + sock.send(SW_STRS("hello world\n")); char buf[128]; int n = sock.recv(buf, sizeof(buf)); buf[n] = 0; ASSERT_EQ(strcmp(buf, "hello world\n"), 0); }); + + kill(pid, SIGTERM); + int status; + wait(&status); } -TEST(coroutine_socket, recv_fail) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, recv_fail) { + pid_t pid; + int port = swoole::test::get_random_port(); + + Process proc([port](Process *proc) { + on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { SERVER_THIS->close(req->info.fd, 0); }; + + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + serv.on("onReceive", (void *) receive_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + coroutine::run([port](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9501, -1); + bool retval = sock.connect(TEST_HOST, port, -1); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); sock.send("close", 6); @@ -69,33 +145,39 @@ TEST(coroutine_socket, recv_fail) int n = sock.recv(buf, sizeof(buf)); ASSERT_EQ(n, 0); }); + + kill(pid, SIGTERM); + int status; + wait(&status); } -TEST(coroutine_socket, bind_success) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, bind_success) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("127.0.0.1", 9909); ASSERT_EQ(retval, true); + + Socket sock_1(SW_SOCK_UNIX_DGRAM); + retval = sock_1.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); }); } -TEST(coroutine_socket, bind_fail) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, bind_fail) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("192.111.11.1", 9909); ASSERT_EQ(retval, false); ASSERT_EQ(sock.errCode, EADDRNOTAVAIL); + + Socket sock_1(SW_SOCK_TCP); + retval = sock_1.bind("127.0.0.1", 70000); + ASSERT_EQ(retval, false); }); } -TEST(coroutine_socket, listen) -{ - coro_test([](void *arg) - { +TEST(coroutine_socket, listen) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("127.0.0.1", 9909); ASSERT_EQ(retval, true); @@ -103,27 +185,1047 @@ TEST(coroutine_socket, listen) }); } -TEST(coroutine_socket, accept) -{ - coro_test({ - [](void *arg) - { - Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", 9909); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.listen(128), true); +TEST(coroutine_socket, accept) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + ASSERT_NE(conn, nullptr); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.close(); + }}); +} + +#define CRLF "\r\n" +#define EOF_PACKET "hello world" CRLF +#define EOF_PACKET_2 "php&swoole, java&golang" CRLF +#define RECV_TIMEOUT 10.0 + +static void socket_set_eof_protocol(Socket &sock) { + memcpy(sock.protocol.package_eof, SW_STRL(CRLF)); + sock.protocol.package_eof_len = 2; + sock.open_eof_check = true; +} + +TEST(coroutine_socket, eof_1) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET)); + ASSERT_EQ(buf->length, eof_packet_len); + ASSERT_EQ(buf->offset, eof_packet_len); + }}); +} + +TEST(coroutine_socket, eof_2) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET EOF_PACKET_2); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + // packet 1 + { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET)); + ASSERT_EQ(buf->length, strlen(EOF_PACKET EOF_PACKET_2)); + ASSERT_EQ(buf->offset, eof_packet_len); + } + // packet 2 + { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET_2); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET_2)); + ASSERT_EQ(buf->length, strlen(EOF_PACKET_2)); + ASSERT_EQ(buf->offset, eof_packet_len); + } + }}); +} + +TEST(coroutine_socket, eof_3) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->shutdown(); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 0); + }}); +} + +TEST(coroutine_socket, eof_4) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET, strlen(EOF_PACKET) - strlen(CRLF)); // no eof + conn->shutdown(); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 0); + + auto buf = sock.get_read_buffer(); + ASSERT_EQ(string(buf->str, 10), string(EOF_PACKET, 10)); + }}); +} + +TEST(coroutine_socket, eof_5) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + + swString *s = swoole::make_string(128 * 1024); + s->repeat("A", 1, 128 * 1024 - 16); + s->append(SW_STRL(CRLF)); + + conn->send_all(s->str, s->length); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 128 * 1024 - 14); + }}); +} + +TEST(coroutine_socket, eof_6) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + + swString s(128 * 1024); + s.repeat("A", 1, 128 * 1024 - 16); + s.append(SW_STRL(CRLF)); + + conn->send_all(s.value(), s.get_length()); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + sock.protocol.package_max_length = 1024 * 64; + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, -1); + ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); + }}); +} + +static void socket_set_length_protocol_1(Socket &sock) { + sock.protocol = {}; + + sock.protocol.package_length_type = 'n'; + sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type); + sock.protocol.package_body_offset = 2; + sock.protocol.get_package_length = Protocol::default_length_func; + sock.protocol.package_max_length = 65535; + + sock.open_length_check = true; +} + +static void socket_set_length_protocol_2(Socket &sock) { + sock.protocol = {}; + + sock.protocol.package_length_type = 'N'; + sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type); + sock.protocol.package_body_offset = 4; + sock.protocol.get_package_length = Protocol::default_length_func; + sock.protocol.package_max_length = 2 * 1024 * 1024; + + sock.open_length_check = true; +} + +TEST(coroutine_socket, length_1) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + ssize_t l = swoole_random_bytes(buf + 2, sizeof(buf) - 2); + *(uint16_t *) buf = htons(l); + + conn->send(buf, l + 2); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_1(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 1024); + ASSERT_EQ(buf->length, l); + ASSERT_EQ(buf->offset, l); + }}); +} + +TEST(coroutine_socket, length_2) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + *(uint16_t *) buf = htons(0); + + conn->send(buf, 2); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_1(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 2); + ASSERT_EQ(buf->length, 2); + ASSERT_EQ(buf->offset, 2); + }}); +} + +TEST(coroutine_socket, length_3) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + memset(buf, 'A', sizeof(buf)); + *(uint16_t *) buf = htons(65530); + + conn->send(buf, sizeof(buf)); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_1(sock); + sock.protocol.package_max_length = 4096; + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, -1); + ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); + }}); +} + +static string pkt_1; +static string pkt_2; + +static void length_protocol_server_func(void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + String strbuf(256 * 1024); + + uint32_t pack_len; + + size_t l_1 = swoole_rand(65536, 65536 * 2); + pack_len = htonl(l_1); + strbuf.append((char *) &pack_len, sizeof(pack_len)); + strbuf.append_random_bytes(l_1); + + pkt_1 = string(strbuf.str, l_1 + 4); + + size_t l_2 = swoole_rand(65536, 65536 * 2); + pack_len = htonl(l_2); + strbuf.append((char *) &pack_len, sizeof(pack_len)); + strbuf.append_random_bytes(l_2); + + pkt_2 = string(strbuf.str + pkt_1.length(), l_2 + 4); + + conn->send_all(strbuf.str, strbuf.length); +} + +TEST(coroutine_socket, length_4) { + coroutine::run({length_protocol_server_func, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_2(sock); + + size_t bytes = 0; + for (int i = 0; i < 2; i++) { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + bytes += l; + auto buf = sock.get_read_buffer(); + uint32_t unpack_len = ntohl(*(uint32_t *) buf->str); + + if (i == 0) { + ASSERT_EQ(pkt_1, string(buf->str, buf->length)); + } else { + ASSERT_EQ(pkt_2, string(buf->str, buf->length)); + } + + ASSERT_EQ(unpack_len, l - 4); + ASSERT_EQ(buf->length, l); + ASSERT_EQ(buf->offset, l); + } + ASSERT_GE(bytes, 65536 * 2); + }}); +} + +TEST(coroutine_socket, length_5) { + coroutine::run({length_protocol_server_func, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_2(sock); + + size_t bytes = 0; + for (int i = 0; i < 2; i++) { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + bytes += l; + char *data = sock.pop_packet(); + uint32_t unpack_len = ntohl(*(uint32_t *) data); + ASSERT_EQ(unpack_len, l - 4); + + if (i == 0) { + ASSERT_EQ(pkt_1, string(data, l)); + } else { + ASSERT_EQ(pkt_2, string(data, l)); + } + } + ASSERT_GE(bytes, 65536 * 2); + }}); +} + +TEST(coroutine_socket, length_7) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + char buf[1024]; + *(uint32_t *) buf = htons(0); + + conn->send(buf, 2); + System::sleep(0.01); + conn->send(buf + 2, 2); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + socket_set_length_protocol_2(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 4); + ASSERT_EQ(buf->length, 4); + ASSERT_EQ(buf->offset, 4); + }}); +} + +TEST(coroutine_socket, event_hup) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9502); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + System::sleep(0.05); + char buf[1024]; + auto ret_n = conn->recv(buf, sizeof(buf)); + ASSERT_EQ(ret_n, 0); + delete conn; + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9502, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + auto buf = sock.get_read_buffer(); + Coroutine::create([&sock](void *args) { + System::sleep(0.01); + sock.shutdown(SHUT_RDWR); + }); + auto n = sock.recv_all(buf->str, buf->size); + ASSERT_EQ(sock.get_socket()->event_hup, 1); + ASSERT_EQ(n, 0); + }}); +} + +TEST(coroutine_socket, recv_line) { + coroutine::run({[](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.bind("127.0.0.1", 9909); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.listen(128), true); + + Socket *conn = sock.accept(); + conn->send("hello world\n"); + conn->send("\r"); + char buf[256]; + memset(buf, 'A', 128); + memset(buf + 128, 'B', 125); + conn->send(buf, 253); + delete conn; + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + + size_t n; + auto buf = sock.get_read_buffer(); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 12); + ASSERT_MEMEQ(buf->str, "hello world\n", 12); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 1); + ASSERT_MEMEQ(buf->str, "\r", 1); + + char buf_2[256]; + memset(buf_2, 'A', 128); + memset(buf_2 + 128, 'B', 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 128); + ASSERT_MEMEQ(buf->str, buf_2, 128); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 125); + ASSERT_MEMEQ(buf->str, buf_2 + 128, 125); + + n = sock.recv_line(buf->str, 128); + ASSERT_EQ(n, 0); + }}); +} + +TEST(coroutine_socket, getsockname) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect(host, 80); + ASSERT_EQ(retval, true); + + Address sa; + bool result = sock.getsockname(&sa); + sock.close(); + ASSERT_EQ(result, true); + }); +} + +TEST(coroutine_socket, check_liveness) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect(host, 80); + ASSERT_EQ(retval, true); + + bool result = sock.check_liveness(); + sock.close(); + ASSERT_EQ(result, true); + result = sock.check_liveness(); + ASSERT_EQ(result, false); + }); +} + +TEST(coroutine_socket, write_and_read) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); - Socket *conn = sock.accept(); - ASSERT_NE(conn, nullptr); - }, + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); - [](void *arg) - { - Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9909, -1); - ASSERT_EQ(retval, true); - ASSERT_EQ(sock.errCode, 0); + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); +} + +TEST(coroutine_socket, write_and_read_2) { + // test for Socket::Socket(int _fd, int _domain, int _type, int _protocol) construct function + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.write(text.c_str(), length); sock.close(); + ASSERT_EQ(result, length); + }); + + char data[128]; + Socket sock(pairs[1], AF_UNIX, SOCK_STREAM, 0); + ssize_t result = sock.read(data, 128); + sock.close(); + ASSERT_GT(result, 0); + data[result] = '\0'; + ASSERT_STREQ(text.c_str(), data); + }); +} + +TEST(coroutine_socket, writev_and_readv) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); + + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); } }); } + +TEST(coroutine_socket, writevall_and_readvall) { + coroutine::run([&](void *arg) { + int iovcnt = 3; + int pairs[2]; + std::string text = "Hello World"; + size_t length = text.length(); + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + Coroutine::create([&](void *) { + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) text.c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.writev_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + }); + + std::vector results(iovcnt); + std::unique_ptr iov(new iovec[iovcnt]); + for (int i = 0; i < iovcnt; i++) { + iov[i].iov_base = (void *) results[i].c_str(); + iov[i].iov_len = length; + } + IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.readv_all(&io_vector); + sock.close(); + ASSERT_EQ(result, length * 3); + + for (auto iter = results.begin(); iter != results.end(); iter++) { + (*iter)[length] = '\0'; + ASSERT_STREQ(text.c_str(), (*iter).c_str()); + } + }); +} + +TEST(coroutine_socket, sendfile) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + Coroutine::create([&](void *) { + std::string file = get_jpg_file(); + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + bool result = sock.sendfile(file.c_str(), 0, 0); + sock.close(); + ASSERT_TRUE(result); + }); + + char data[250000]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.read(data, 250000); + data[result] = '\0'; + sock.close(); + ASSERT_GT(result, 0); + }); +} + +void test_sendto_recvfrom(enum swSocketType sock_type) { + coroutine::run([&](void *arg) { + std::string server_text = "hello world!!!"; + size_t server_length = server_text.length(); + std::string client_text = "hello swoole!!!"; + size_t client_length = client_text.length(); + + const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; + + int port = swoole::test::get_random_port(); + + Socket sock_server(sock_type); + Socket sock_client(sock_type); + sock_server.bind(ip, port); + sock_client.bind(ip, port + 1); + + ON_SCOPE_EXIT { + sock_server.close(); + sock_client.close(); + }; + + sock_server.sendto(ip, port + 1, (const void *) server_text.c_str(), server_length); + + char data_from_server[128] = {}; + struct sockaddr_in serveraddr; + bzero(&serveraddr, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = inet_addr(ip); + serveraddr.sin_port = htons(port); + socklen_t addr_length = sizeof(serveraddr); + + // receive data from server + ssize_t result = + sock_client.recvfrom(data_from_server, server_length, (struct sockaddr *) &serveraddr, &addr_length); + data_from_server[result] = '\0'; + ASSERT_EQ(result, server_length); + ASSERT_STREQ(data_from_server, server_text.c_str()); + + // receive data from client + char data_from_client[128] = {}; + sock_client.sendto(ip, port, (const void *) client_text.c_str(), client_length); + result = sock_server.recvfrom(data_from_client, client_length); + data_from_client[client_length] = '\0'; + ASSERT_EQ(result, client_length); + ASSERT_STREQ(data_from_client, client_text.c_str()); + }); +} + +TEST(coroutine_socket, sendto_recvfrom_udp) { + test_sendto_recvfrom(SW_SOCK_UDP); + test_sendto_recvfrom(SW_SOCK_UDP6); +} + +void proxy_test(Socket &sock, bool https) { + if (https) { + sock.enable_ssl_encrypt(); + } + + bool retval = sock.connect(host, https ? 443 : 80); + ON_SCOPE_EXIT { + sock.close(); + }; + ASSERT_EQ(retval, true); + + if (https) { + ASSERT_NE(sock.ssl_get_peer_cert(), ""); + } + + ASSERT_GT(sock.send(SW_STRL(TEST_REQUEST_BAIDU)), 0); + + String buf(65536); + while (true) { + char rbuf[4096]; + ssize_t nr = sock.recv(rbuf, sizeof(rbuf)); + if (nr <= 0) { + break; + } + buf.append(rbuf, nr); + } + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +TEST(coroutine_socket, http_get_with_socks5_proxy) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, false); + }); +} + +TEST(coroutine_socket, http_get_with_http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, false); + }); +} + +TEST(coroutine_socket, https_get_with_socks5_proxy) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, true); + }); +} + +TEST(coroutine_socket, https_get_with_http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, true); + }); +} + +#ifdef SW_USE_OPENSSL +TEST(coroutine_socket, ssl) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + + sock.enable_ssl_encrypt(); + sock.get_ssl_context()->cert_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.crt"; + sock.get_ssl_context()->key_file = swoole::test::get_root_path() + "/tests/include/ssl_certs/client.key"; + sock.get_ssl_context()->verify_peer = false; + sock.get_ssl_context()->allow_self_signed = true; + sock.get_ssl_context()->cafile = swoole::test::get_root_path() + "/tests/include/ssl_certs/ca.crt"; + + proxy_test(sock, 443); + }); +} +#endif + +TEST(coroutine_socket, peek) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.write(text.c_str(), length); + sock.close(); + ASSERT_EQ(result, length); + }); + + char data[128]; + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + ssize_t result = sock.peek(data, 5); + sock.close(); + ASSERT_EQ(result, 5); + data[result] = '\0'; + ASSERT_STREQ("Hello", data); + }); +} + +TEST(coroutine_socket, sendmsg_and_recvmsg) { + coroutine::run([&](void *arg) { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + std::string text = "Hello World"; + size_t length = text.length(); + + Coroutine::create([&](void *) { + Socket sock(pairs[0], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = (void *) text.c_str(); + ivec.iov_len = length; + + ssize_t ret = sock.sendmsg(&msg, 0); + sock.close(); + ASSERT_EQ(ret, length); + }); + + Socket sock(pairs[1], SW_SOCK_UNIX_STREAM); + struct msghdr msg; + struct iovec ivec; + char buf[length + 1]; + + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &ivec; + msg.msg_iovlen = 1; + + ivec.iov_base = buf; + ivec.iov_len = length; + + ssize_t ret = sock.recvmsg(&msg, 0); + buf[ret] = '\0'; + sock.close(); + ASSERT_STREQ(buf, text.c_str()); + }); +} + +std::pair, std::shared_ptr> create_socket_pair() { + int pairs[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, pairs); + + auto sock0 = new Socket(pairs[0], SW_SOCK_UNIX_STREAM); + auto sock1 = new Socket(pairs[1], SW_SOCK_UNIX_STREAM); + + sock0->get_socket()->set_buffer_size(65536); + sock1->get_socket()->set_buffer_size(65536); + + std::pair, std::shared_ptr> result(sock0, sock1); + return result; +} + +TEST(coroutine_socket, close) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + auto _sock = pair.first; + + // write co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + ssize_t result = _sock->write(buffer->value(), buffer->get_length()); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + results["write"] = true; + ASSERT_EQ(_sock->write(buffer->value(), buffer->get_length()), -1); + ASSERT_EQ(_sock->errCode, EBADF); + break; + } + } + }); + + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = _sock->read(buf, sizeof(buf)); + if (result < 0 && _sock->errCode == ECANCELED) { + ASSERT_TRUE(_sock->close()); + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, SW_ERROR_CO_SOCKET_CLOSE_WAIT); + ASSERT_TRUE(_sock->is_closed()); + ASSERT_TRUE(results["write"]); + ASSERT_TRUE(results["read"]); + ASSERT_FALSE(_sock->close()); + ASSERT_EQ(_sock->errCode, EBADF); + }); +} + +TEST(coroutine_socket, cancel) { + coroutine::run([&](void *arg) { + auto pair = create_socket_pair(); + + auto buffer = sw_tg_buffer(); + buffer->clear(); + buffer->append_random_bytes(256 * 1024, false); + + std::map results; + // read co + Coroutine::create([&](void *) { + SW_LOOP_N(32) { + char buf[4096]; + ssize_t result = pair.first->read(buf, sizeof(buf)); + if (result < 0 && pair.first->errCode == ECANCELED) { + results["read"] = true; + break; + } + } + }); + + System::sleep(0.1); + pair.first->cancel(SW_EVENT_READ); + ASSERT_TRUE(results["read"]); + }); +} diff --git a/core-tests/src/coroutine/system.cpp b/core-tests/src/coroutine/system.cpp new file mode 100644 index 00000000000..0552a909bfe --- /dev/null +++ b/core-tests/src/coroutine/system.cpp @@ -0,0 +1,280 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_coroutine.h" +#include "swoole_pipe.h" + +using namespace swoole; +using namespace swoole::test; + +using swoole::coroutine::Socket; +using swoole::coroutine::System; + +static const char *test_file = "/tmp/swoole-core-test"; + +static constexpr int DATA_SIZE = 8 * 1024 * 1024; +static constexpr int DATA_SIZE_2 = 64 * 1024; + +TEST(coroutine_system, file) { + test::coroutine::run([](void *arg) { + std::shared_ptr buf = std::make_shared(DATA_SIZE); + ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); + buf->str[buf->size - 1] = 0; + ASSERT_EQ(System::write_file(test_file, buf->str, buf->size, true, 0), buf->size); + auto data = System::read_file(test_file, true); + ASSERT_TRUE(data.get()); + ASSERT_STREQ(buf->str, data->str); + unlink(test_file); + }); +} + +TEST(coroutine_system, flock) { + std::shared_ptr buf = std::make_shared(65536); + ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); + buf->str[buf->size - 1] = 0; + + test::coroutine::run([&buf](void *) { + int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); + ASSERT_TRUE(fd > 0); + swoole_coroutine_flock_ex(test_file, fd, LOCK_EX); + + for (int i = 0; i < 4; i++) { + Coroutine::create([&buf](void *) { + int fd = swoole_coroutine_open(test_file, File::READ, 0); + ASSERT_TRUE(fd > 0); + swoole_coroutine_flock_ex(test_file, fd, LOCK_SH); + String read_buf(DATA_SIZE_2); + auto rn = swoole_coroutine_read(fd, read_buf.str, read_buf.size - 1); + ASSERT_EQ(rn, read_buf.size - 1); + read_buf.str[read_buf.size - 1] = 0; + swoole_coroutine_flock_ex(test_file, fd, LOCK_UN); + EXPECT_STREQ(read_buf.str, buf->str); + swoole_coroutine_close(fd); + }); + } + + auto wn = swoole_coroutine_write(fd, buf->str, buf->size - 1); + ASSERT_EQ(wn, buf->size - 1); + swoole_coroutine_flock_ex(test_file, fd, LOCK_UN); + swoole_coroutine_close(fd); + }); + + unlink(test_file); +} + +TEST(coroutine_system, flock_nb) { + coroutine::run([&](void *arg) { + int fd = swoole_coroutine_open(test_file, File::WRITE | File::CREATE, 0666); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX | LOCK_NB), 0); + + swoole::Coroutine::create([&](void *arg) { + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_EX), 0); + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + swoole_coroutine_close(fd); + unlink(test_file); + }); + + ASSERT_EQ(swoole_coroutine_flock_ex(test_file, fd, LOCK_UN), 0); + }); +} + +TEST(coroutine_system, cancel_sleep) { + test::coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + Coroutine::create([co](void *) { + System::sleep(0.002); + co->cancel(); + }); + System::sleep(1000); + }); +} + +TEST(coroutine_system, getaddrinfo) { + test::coroutine::run([](void *arg) { + std::vector ip_list = System::getaddrinfo("www.baidu.com", AF_INET, SOCK_STREAM, 0, "http", 1); + ASSERT_GT(ip_list.size(), 0); + for (auto &ip : ip_list) { + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); + } + }); +} + +TEST(coroutine_system, wait_signal) { + test::coroutine::run([](void *arg) { + Coroutine::create([](void *) { + System::sleep(0.002); + kill(getpid(), SIGUSR1); + }); + ASSERT_EQ(System::wait_signal(SIGUSR1, 1.0), SIGUSR1); + ASSERT_EQ(System::wait_signal(SIGUSR2, 0.1), -1); + }); +} + +static const char *GREETING = "hello world, hello swoole"; + +TEST(coroutine_system, wait_event_readable) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + test::coroutine::run([&p](void *arg) { + Coroutine::create([&p](void *) { + System::sleep(0.002); + ASSERT_GT(p.write(GREETING, strlen(GREETING)), 0); + }); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + ssize_t n = pipe_sock->read(buffer, sizeof(buffer)); + buffer[n] = 0; + EXPECT_EQ(strlen(GREETING), n); + EXPECT_STREQ(GREETING, buffer); + }); +} + +TEST(coroutine_system, wait_event_writable) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); + sw_tg_buffer()->clear(); + + String str(2 * SW_NUM_MILLION); + str.append_random_bytes(str.size - 1, false); + str.append('\0'); + + test::coroutine::run([&](void *arg) { + Coroutine::create([&](void *) { + System::sleep(0.002); + auto pipe_sock = p.get_socket(true); + + char *ptr = str.value(); + size_t len = str.get_length(); + + while (len > 0) { + ssize_t retval = pipe_sock->write(ptr, len > 8192 ? 8192 : len); + if (retval > 0) { + ptr += retval; + len -= retval; + } else if (retval == 0 || (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_WRITE, 1); + } + }); + + auto pipe_sock = p.get_socket(false); + auto tg_buf = sw_tg_buffer(); + + while (tg_buf->length < str.size - 1) { + ssize_t retval = pipe_sock->read(tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length); + if (retval > 0) { + tg_buf->grow(retval); + continue; + } else if (retval == 0 && (retval < 0 && errno != EAGAIN)) { + break; + } + System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 1); + } + tg_buf->append('\0'); + EXPECT_STREQ(sw_tg_buffer()->value(), str.value()); + }); +} + +TEST(coroutine_system, swoole_stream_select) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + test::coroutine::run([&](void *arg) { + // try timeout to trigger socket_poll_timeout function + ASSERT_FALSE(System::socket_poll(fds, 0.5)); + }); + + // start normal process + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + System::sleep(0.05); + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0.5); + ASSERT_TRUE(result); + + char buffer[128]; + auto pipe_sock = p.get_socket(false); + ssize_t retval = pipe_sock->read(buffer, sizeof(buffer)); + buffer[retval] = '\0'; + + ASSERT_EQ(retval, len); + const char *ptr = text.c_str(); + ASSERT_STREQ(ptr, buffer); + }); +} + +TEST(coroutine_system, timeout_is_zero) { + UnixSocket p(true, SOCK_STREAM); + std::unordered_map fds; + fds.emplace(std::make_pair(p.get_socket(false)->fd, swoole::coroutine::PollSocket(SW_EVENT_READ, nullptr))); + + // timeout is 0 + test::coroutine::run([&](void *arg) { + std::string text = "Hello world"; + size_t len = text.length(); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + bool result = System::socket_poll(fds, 0); + ASSERT_TRUE(result); + + // child pipe + Coroutine::create([&](void *) { + auto pipe_sock = p.get_socket(true); + const char *ptr = text.c_str(); + ASSERT_EQ(pipe_sock->write(ptr, len), len); + }); + + // master pipe + auto pipe_sock = p.get_socket(false); + result = System::wait_event(pipe_sock->get_fd(), SW_EVENT_READ, 0); + ASSERT_TRUE(result); + }); +} + +TEST(coroutine_system, exec) { + test::coroutine::run([](void *arg) { + int status; + auto buffer = std::shared_ptr(swoole::make_string(1024)); + ASSERT_TRUE(System::exec("ls /", true, buffer, &status)); + ASSERT_TRUE(buffer->contains(SW_STRL("tmp"))); + }); +} diff --git a/core-tests/src/hashmap.cpp b/core-tests/src/hashmap.cpp deleted file mode 100644 index 2d877643412..00000000000 --- a/core-tests/src/hashmap.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "tests.h" - -#include -#include - -typedef struct -{ - int fd; - int key; -} swFdInfo; - -bool type_eof() -{ - char eof[] = SW_DATA_EOF; - printf("SW_DATA_STREAM_EOF = %s\n", eof); - return 0; -} - -TEST(hashmap, string) -{ - swHashMap *hm = swHashMap_new(16, NULL); - swHashMap_add(hm, (char *) SW_STRL("hello"), (void *) 199); - swHashMap_add(hm, (char *) SW_STRL("swoole22"), (void *) 8877); - swHashMap_add(hm, (char *) SW_STRL("hello2"), (void *) 200); - swHashMap_add(hm, (char *) SW_STRL("willdel"), (void *) 888); - swHashMap_add(hm, (char *) SW_STRL("willupadte"), (void *) 9999); - swHashMap_add(hm, (char *) SW_STRL("hello3"), (void *) 78978); - - swHashMap_del(hm, (char *) SW_STRL("willdel")); - swHashMap_update(hm, (char *) SW_STRL("willupadte"), (void *) (9999 * 5555)); - - int ret1 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("hello")); - ASSERT_GT(ret1, 0); - - int ret2 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("hello2")); - ASSERT_GT(ret2, 0); - - int ret3 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("notfound")); - ASSERT_EQ(ret3, 0); - - char *key; - int data; - - while (1) - { - data = (int) (long) swHashMap_each(hm, &key); - if (!data) - { - break; - } - } - swHashMap_free(hm); -} - -#define BUFSIZE 128 -#define MAP_SIZE 32 -char data[BUFSIZE]; - -TEST(hashmap, integer) -{ - swHashMap *ht = swHashMap_new(16, free); - swFdInfo *pkt, *tmp; - int i; - swFdInfo *lists[MAP_SIZE]; - - for (i = 0; i < MAP_SIZE; i++) - { - pkt = (swFdInfo *) malloc(sizeof(swFdInfo)); - pkt->key = i; - pkt->fd = i * 37; - swHashMap_add_int(ht, pkt->fd, pkt); - lists[i] = pkt; - } - - tmp = (swFdInfo *) swHashMap_find_int(ht, 37 * 8); - ASSERT_NE((void* )tmp, nullptr); - - tmp = (swFdInfo *) swHashMap_find_int(ht, 37 * 3); - ASSERT_NE((void* )tmp, nullptr); - - tmp = (swFdInfo *) swHashMap_find_int(ht, 36 * 3); - ASSERT_EQ((void* )tmp, nullptr); - - for (i = 0; i < 10; i++) - { - free(lists[i]); - } -} diff --git a/core-tests/src/heap.cpp b/core-tests/src/heap.cpp deleted file mode 100644 index 3e927af78a8..00000000000 --- a/core-tests/src/heap.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "swoole.h" -#include "heap.h" -#include -#include - -typedef struct node_t -{ - int pri; - int val; -} node_t; - -#define SIZE 100 - -TEST(heap, random) -{ - swHeap *pq; - node_t *ns; - node_t *n; - - pq = swHeap_new(SIZE, SW_MAX_HEAP); - ASSERT_NE(pq, nullptr); - - std::map _map; - - int i; - for (i = 0; i < SIZE - 1; i++) - { - int pri = swoole_system_random(10000, 99999); - ns = (node_t*) malloc(sizeof(node_t)); - ns->val = i; - ns->pri = pri; - swHeap_push(pq, pri, ns); - _map[i] = pri; - } - - while ((n = (node_t*) swHeap_pop(pq))) - { - ASSERT_EQ(_map[n->val], n->pri); - free(n); - } - - swHeap_free(pq); -} diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp new file mode 100644 index 00000000000..a2da9992ede --- /dev/null +++ b/core-tests/src/lock/lock.cpp @@ -0,0 +1,217 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_coroutine.h" +#include "swoole_lock.h" +#include "swoole_util.h" + +#include + +using swLock = swoole::Lock; + +using swoole::RWLock; +#ifdef HAVE_SPINLOCK +using swoole::SpinLock; +#endif +using swoole::Mutex; +using swoole::CoroutineLock; +using swoole::Coroutine; +using swoole::test::coroutine; +using swoole::coroutine::System; + +static void test_func(swLock &lock) { + int count = 0; + const int N = 100000; + + auto fn = [&]() { + for (int i = 0; i < N; i++) { + ASSERT_EQ(lock.lock(), 0); + count++; + ASSERT_EQ(lock.unlock(), 0); + } + }; + + std::thread t1(fn); + std::thread t2(fn); + + t1.join(); + t2.join(); + + ASSERT_EQ(count, N * 2); +} + +static void test_lock_rd_func(swLock &lock) { + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock_rd(), 0); + usleep(2000); // wait + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GE(lock.trylock_rd(), 0); + }); + + t1.join(); + t2.join(); +} + +static void test_share_lock_fun(swLock &lock) { + lock.lock(); + const int sleep_us = 10000; + int magic_num = swoole_rand(100000, 9999999); + int *_num = (int *) sw_mem_pool()->alloc(sizeof(int)); + *_num = 0; + + pid_t pid = fork(); + + if (pid == 0) { + lock.lock(); + *_num = magic_num; + usleep(1); + exit(0); + } else { + usleep(sleep_us); + lock.unlock(); + int status; + pid_t _pid = waitpid(pid, &status, 0); + if (_pid != pid) { + swoole_warning("error pid=%d", _pid); + } + ASSERT_EQ(*_num, magic_num); + } +} + +TEST(lock, mutex) { + Mutex lock(0); + test_func(reinterpret_cast(lock)); +} + +TEST(lock, lockwait) { + Mutex lock(0); + + lock.lock(); + + std::thread t1([&lock]() { + long ms1 = swoole::time(); + const int TIMEOUT_1 = 2; + ASSERT_EQ(lock.lock_wait(TIMEOUT_1), ETIMEDOUT); + long ms2 = swoole::time(); + + ASSERT_GE(ms2 - ms1, TIMEOUT_1); + + const int TIMEOUT_2 = 10; + ASSERT_EQ(lock.lock_wait(TIMEOUT_2), 0); + long ms3 = swoole::time(); + + ASSERT_LE(ms3 - ms2, TIMEOUT_2); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + lock.unlock(); + + t1.join(); +} + +TEST(lock, shared) { + Mutex lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + +TEST(lock, try_rd) { + Mutex lock(0); + test_lock_rd_func(lock); +} + +TEST(lock, coroutine_lock) { + CoroutineLock *lock = new CoroutineLock(false); + ASSERT_EQ(lock->lock(), SW_ERROR_CO_OUT_OF_COROUTINE); + auto callback = [lock]() { + coroutine::run([lock](void *arg) { + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { + ASSERT_EQ(lock->lock(), 0); + System::sleep(1); + ASSERT_EQ(lock->unlock(), 0); + }); + + Coroutine::create([lock](void *) { ASSERT_EQ(lock->trylock(), EBUSY); }); + }); + }; + + std::thread t1(callback); + t1.join(); + delete lock; +} + +#ifdef HAVE_RWLOCK +TEST(lock, rwlock_shared) { + RWLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + +TEST(lock, rwlock) { + RWLock lock(false); + test_func(lock); +} + +TEST(lock, rwlock_try_rd) { + RWLock lock(false); + test_lock_rd_func(lock); +} + +TEST(lock, rw_try_wr) { + RWLock lock(false); + std::thread t1([&lock]() { + ASSERT_EQ(lock.lock(), 0); + usleep(2000); + lock.unlock(); + }); + + std::thread t2([&lock]() { + usleep(1000); + ASSERT_GT(lock.trylock(), 0); + }); + t1.join(); + t2.join(); +} +#endif + +#ifdef HAVE_SPINLOCK +TEST(lock, spinlock_shared) { + SpinLock lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + +TEST(lock, spinlock) { + SpinLock lock(false); + test_func(lock); +} + +TEST(lock, spinlock_try_rd) { + SpinLock lock(false); + test_lock_rd_func(lock); +} +#endif diff --git a/core-tests/src/main.cpp b/core-tests/src/main.cpp old mode 100755 new mode 100644 index a6fd1e0dc5e..05f8a5c6b36 --- a/core-tests/src/main.cpp +++ b/core-tests/src/main.cpp @@ -1,26 +1,90 @@ -#include "tests.h" -#include "swoole_api.h" +#include "test_core.h" -static pid_t create_server() -{ - pid_t pid; - swoole_shell_exec("php server/tcp.php", &pid, 1); - sleep(1); // wait 1s - return pid; -} +#include "swoole_proxy.h" + +using namespace swoole; +using namespace std; + +static string root_path; -int main(int argc, char **argv) -{ +static void init_root_path(const char *); + +int main(int argc, char **argv) { swoole_init(); + init_root_path(argv[0]); - pid_t server_pid = create_server(); + if (getenv("DISPLAY_BACKTRACE") != nullptr) { + sw_logger()->display_backtrace(); + } ::testing::InitGoogleTest(&argc, argv); int retval = RUN_ALL_TESTS(); - kill(server_pid, SIGTERM); - int status = 0; - wait(&status); + swoole_clean(); return retval; } + +static void init_root_path(const char *_exec_file) { + char buf[PATH_MAX]; + string file; + if (_exec_file[0] == '/') { + file = _exec_file; + } else { + char *dir = getcwd(buf, sizeof(buf)); + file = string(dir) + "/" + _exec_file; + } + string relative_root_path = file.substr(0, file.rfind('/')) + "/../../"; + char *_realpath = realpath(relative_root_path.c_str(), buf); + if (_realpath == nullptr) { + root_path = relative_root_path; + } else { + root_path = string(_realpath); + } +} + +namespace swoole { +namespace test { + +const string &get_root_path() { + return root_path; +} + +string get_jpg_file() { + return root_path + TEST_JPG_FILE; +} + +bool is_github_ci() { + return getenv("GITHUB_ACTIONS") != nullptr; +} + +Socks5Proxy *create_socks5_proxy() { + auto socks5_proxy = new Socks5Proxy(); + socks5_proxy->host = std::string(TEST_SOCKS5_PROXY_HOST); + socks5_proxy->port = TEST_SOCKS5_PROXY_PORT; + socks5_proxy->dns_tunnel = 1; + if (is_github_ci()) { + socks5_proxy->method = SW_SOCKS5_METHOD_AUTH; + socks5_proxy->username = std::string(TEST_SOCKS5_PROXY_USER); + socks5_proxy->password = std::string(TEST_SOCKS5_PROXY_PASSWORD); + } + return socks5_proxy; +} + +HttpProxy *create_http_proxy() { + auto http_proxy = new HttpProxy(); + http_proxy->proxy_host = std::string(TEST_HTTP_PROXY_HOST); + http_proxy->proxy_port = TEST_HTTP_PROXY_PORT; + if (is_github_ci()) { + http_proxy->username = std::string(TEST_HTTP_PROXY_USER); + http_proxy->password = std::string(TEST_HTTP_PROXY_PASSWORD); + } + return http_proxy; +} + +int get_random_port() { + return TEST_PORT + swoole_system_random(1, 10000); +} + +} // namespace test +} // namespace swoole diff --git a/core-tests/src/memory/buffer.cpp b/core-tests/src/memory/buffer.cpp new file mode 100644 index 00000000000..101c7f6f86d --- /dev/null +++ b/core-tests/src/memory/buffer.cpp @@ -0,0 +1,81 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_memory.h" +#include "swoole_buffer.h" + +using namespace std; +using namespace swoole; + +TEST(buffer, append_iov) { + Buffer buf(1024); + Buffer buf_for_offset(1024); + + int iovcnt = 4; + iovec v[iovcnt]; + size_t total_len = 0; + + SW_LOOP_N (iovcnt) { + v[i].iov_len = swoole_rand(99, 4095); + total_len += v[i].iov_len; + } + + unique_ptr s1(new char[v[0].iov_len]); + unique_ptr s2(new char[v[1].iov_len]); + unique_ptr s3(new char[v[2].iov_len]); + unique_ptr s4(new char[v[3].iov_len]); + + v[0].iov_base = s1.get(); + v[1].iov_base = s2.get(); + v[2].iov_base = s3.get(); + v[3].iov_base = s4.get(); + + memset(v[0].iov_base, 'A', v[0].iov_len); + memset(v[1].iov_base, 'B', v[1].iov_len); + memset(v[2].iov_base, 'C', v[2].iov_len); + memset(v[3].iov_base, 'D', v[3].iov_len); + + buf.append(v, iovcnt, 0); + ASSERT_EQ(buf.length(), total_len); + + size_t offset = swoole_rand(v[0].iov_len + 1, total_len - 1); + buf_for_offset.append(v, iovcnt, offset); + ASSERT_EQ(buf_for_offset.length(), total_len - offset); + + String str(buf_for_offset.length()); + + while (!buf_for_offset.empty()) { + auto chunk = buf_for_offset.front(); + str.append(chunk->value.str, chunk->length); + buf_for_offset.pop(); + } + + size_t indent = 0; + SW_LOOP_N (iovcnt) { + if (offset >= v[i].iov_len) { + offset -= v[i].iov_len; + continue; + } + + ASSERT_EQ(memcmp(str.str + indent, (char *) v[i].iov_base + offset, v[i].iov_len - offset), 0); + indent += v[i].iov_len - offset; + offset = 0; + } +} diff --git a/core-tests/src/memory/fixed_pool.cpp b/core-tests/src/memory/fixed_pool.cpp new file mode 100644 index 00000000000..2a4a21cb8e5 --- /dev/null +++ b/core-tests/src/memory/fixed_pool.cpp @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_memory.h" +#include "swoole_util.h" + +using namespace std; + +TEST(fixed_pool, alloc) { + auto *pool = new swoole::FixedPool(1024, 256, false); + + list alloc_list; + ASSERT_EQ(pool->get_slice_size(), 256); + + for (int i = 0; i < 1200; i++) { + int j = rand(); + void *mem; + + if (j % 4 < 3) { + mem = pool->alloc(0); + ASSERT_TRUE(mem); + alloc_list.push_back(mem); + } else if (!alloc_list.empty()) { + if (j % 2 == 1) { + mem = alloc_list.front(); + alloc_list.pop_front(); + } else { + mem = alloc_list.back(); + alloc_list.pop_back(); + } + pool->free(mem); + } + } + pool->debug(1); + delete pool; +} + +TEST(fixed_pool, realloc) { + void *memory = sw_shm_malloc(1024); + void *new_memory = sw_shm_realloc(memory, 2048); + ON_SCOPE_EXIT { + sw_shm_free(new_memory); + }; + ASSERT_NE(new_memory, nullptr); + +} diff --git a/core-tests/src/memory/global_memory.cpp b/core-tests/src/memory/global_memory.cpp new file mode 100644 index 00000000000..d573caba3a6 --- /dev/null +++ b/core-tests/src/memory/global_memory.cpp @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_memory.h" + +TEST(global_memory, alloc) { + auto pool = new swoole::GlobalMemory(2 * 1024 * 1024, false); + + char *ptr1 = (char *) pool->alloc(199); + pool->free(ptr1); + strcpy(ptr1, "hello, world, #1"); + + char *ptr2 = (char *) pool->alloc(17); + strcpy(ptr2, "hello, world, #2"); + pool->free(ptr2); + + char *ptr3 = (char *) pool->alloc(113); + strcpy(ptr3, "hello, world, #3"); + + ASSERT_TRUE(ptr1); + ASSERT_TRUE(ptr2); + ASSERT_TRUE(ptr3); + + ASSERT_GT(pool->capacity(), 2 * 1024 * 1024 - 512); + ASSERT_GT(pool->get_memory_size(), 0); + + ASSERT_STREQ(ptr1, "hello, world, #1"); + ASSERT_STREQ(ptr2, "hello, world, #2"); + ASSERT_STREQ(ptr3, "hello, world, #3"); + + pool->destroy(); + delete pool; +} diff --git a/core-tests/src/lru_cache.cpp b/core-tests/src/memory/lru_cache.cpp similarity index 86% rename from core-tests/src/lru_cache.cpp rename to core-tests/src/memory/lru_cache.cpp index d91e36e03c0..f569e3a0979 100644 --- a/core-tests/src/lru_cache.cpp +++ b/core-tests/src/memory/lru_cache.cpp @@ -1,5 +1,5 @@ -#include "tests.h" -#include "lru_cache.h" +#include "test_core.h" +#include "swoole_lru_cache.h" using namespace swoole; using namespace std; @@ -7,22 +7,16 @@ using namespace std; LRUCache cache(2); int dtor_num = 0; -class lru_cache_test_class -{ -public: - lru_cache_test_class() - { +class lru_cache_test_class { + public: + lru_cache_test_class() {} - } - - ~lru_cache_test_class() - { + ~lru_cache_test_class() { ++dtor_num; } }; -TEST(lru_cache, basic) -{ +TEST(lru_cache, basic) { shared_ptr val = make_shared("hello"); shared_ptr val1 = make_shared("hello1"); @@ -42,8 +36,7 @@ TEST(lru_cache, basic) ASSERT_EQ(cache.get("test"), nullptr); } -TEST(lru_cache, memory_free) -{ +TEST(lru_cache, memory_free) { shared_ptr val = make_shared(); cache.set("test", val); ASSERT_EQ(cache.get("test").get(), val.get()); @@ -53,8 +46,7 @@ TEST(lru_cache, memory_free) ASSERT_EQ(dtor_num, 1); } -TEST(lru_cache, lru_kick) -{ +TEST(lru_cache, lru_kick) { dtor_num = 0; shared_ptr val = make_shared(); shared_ptr val1 = make_shared(); @@ -78,7 +70,7 @@ TEST(lru_cache, lru_kick) ASSERT_EQ(cache.get("test"), nullptr); shared_ptr val_str = make_shared("hello"); - cache.set("test1", val_str); // update test1 and will del test2 + cache.set("test1", val_str); // update test1 and will del test2 ASSERT_EQ(cache.get("test1").get(), val_str.get()); ASSERT_EQ(dtor_num, 2); @@ -90,4 +82,4 @@ TEST(lru_cache, lru_kick) cache.clear(); ASSERT_EQ(dtor_num, 4); -} \ No newline at end of file +} diff --git a/core-tests/src/memory/ringbuffer.cpp b/core-tests/src/memory/ringbuffer.cpp new file mode 100644 index 00000000000..00f7d327f21 --- /dev/null +++ b/core-tests/src/memory/ringbuffer.cpp @@ -0,0 +1,127 @@ +#include "test_core.h" +#include "swoole_memory.h" +#include "swoole_pipe.h" + +using namespace swoole; + +#include + +#define READ_THREAD_N 4 +#define WRITE_N 10000 +#define PACKET_LEN 90000 +//#define PRINT_SERNUM_N 10 + +static MemoryPool *pool = NULL; + +typedef struct { + uint32_t id; + uint32_t size; + uint32_t serial_num; + void *ptr; +} pkg; + +typedef struct { + std::thread *thread; + UnixSocket *pipe; +} ThreadObject; + +static void thread_read(int i); +static void thread_write(void); +static ThreadObject threads[READ_THREAD_N]; + +TEST(ringbuffer, thread) { + int i; + pool = new RingBuffer(1024 * 1024 * 4, true); + ASSERT_NE(nullptr, pool); + + for (i = 0; i < READ_THREAD_N; i++) { + threads[i].pipe = new UnixSocket(true, SOCK_DGRAM); + ASSERT_TRUE(threads[i].pipe->ready()); + threads[i].thread = new std::thread(thread_read, i); + } + + sleep(1); + srand((unsigned int) time(NULL)); + thread_write(); + + for (i = 0; i < READ_THREAD_N; i++) { + threads[i].thread->join(); + delete threads[i].pipe; + delete threads[i].thread; + } +} + +static void thread_write(void) { + uint32_t size, yield_count = 0, yield_total_count = 0; + void *ptr; + pkg send_pkg; + sw_memset_zero(&send_pkg, sizeof(send_pkg)); + + int i; + for (i = 0; i < WRITE_N; i++) { + size = 10000 + rand() % PACKET_LEN; + // printf("[ < %d] alloc size=%d\n", i, size); + + yield_count = 0; + do { + ptr = pool->alloc(size); + if (ptr) { + break; + } else { + yield_count++; + yield_total_count++; + usleep(10); + } + } while (yield_count < 100); + + if (!ptr) { + swoole_warning("alloc failed. index=%d, break", i); + } + ASSERT_NE(ptr, nullptr); + + send_pkg.id = i; + send_pkg.ptr = ptr; + send_pkg.size = size; + send_pkg.serial_num = rand(); + + //保存长度值 + memcpy(ptr, &size, sizeof(size)); + //在指针末尾保存一个串号 + memcpy((char *) ptr + size - 4, &(send_pkg.serial_num), sizeof(send_pkg.serial_num)); + + ASSERT_FALSE( + threads[i % READ_THREAD_N].pipe->write(&send_pkg, sizeof(send_pkg)) < 0); + } + + // printf("yield_total_count=%d\n", yield_total_count); +} + +static void thread_read(int i) { + pkg recv_pkg; + uint32_t tmp; + int ret; + uint32_t recv_count = 0; + int j = 0; + UnixSocket *sock = threads[i].pipe; + int task_n = WRITE_N / READ_THREAD_N; + + for (j = 0; j < task_n; j++) { + ret = sock->read(&recv_pkg, sizeof(recv_pkg)); + ASSERT_FALSE(ret < 0); + + memcpy(&tmp, recv_pkg.ptr, sizeof(tmp)); + ASSERT_EQ(tmp, recv_pkg.size); + + memcpy(&tmp, (char *) recv_pkg.ptr + recv_pkg.size - 4, sizeof(tmp)); + ASSERT_EQ(tmp, recv_pkg.serial_num); + +#ifdef PRINT_SERNUM_N + if (j % PRINT_SERNUM_N == 0) { + printf("[ > %d] recv. recv_count=%d, serial_num=%d\n", recv_pkg.id, recv_count, tmp); + } +#endif + + pool->free(recv_pkg.ptr); + recv_count++; + } +} diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp new file mode 100644 index 00000000000..d58ca0b0ac2 --- /dev/null +++ b/core-tests/src/memory/table.cpp @@ -0,0 +1,332 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_table.h" + +using namespace swoole; + +#include +#include + +struct exception_t : public std::exception { + int code; + std::string msg; + exception_t(std::string _msg, int _code) : std::exception() { + msg = _msg; + code = _code; + } + const char *what() const throw() { + return msg.c_str(); + } +}; + +struct row_t { + std::string name; + long id; + double score; +}; + +class table_t { + private: + TableColumn *column_id; + TableColumn *column_name; + TableColumn *column_score; + + Table *table; + + public: + table_t(uint32_t rows_size, float conflict_proportion = 0.2) { + table = Table::make(rows_size, conflict_proportion); + if (!table) { + throw exception_t("alloc failed", swoole_get_last_error()); + } + + table->add_column("id", TableColumn::TYPE_INT, 0); + table->add_column("name", TableColumn::TYPE_STRING, 32); + table->add_column("score", TableColumn::TYPE_FLOAT, 0); + + if (!table->create()) { + throw exception_t("create failed", swoole_get_last_error()); + } + column_id = table->get_column("id"); + column_name = table->get_column("name"); + column_score = table->get_column("score"); + } + + bool set(const std::string &key, const row_t &value) { + TableRow *_rowlock = nullptr; + TableRow *row = table->set(key.c_str(), key.length(), &_rowlock, nullptr); + if (!row) { + _rowlock->unlock(); + return false; + } + + row->set_value(column_id, (void *) &value.id, sizeof(value.id)); + row->set_value(column_name, (void *) value.name.c_str(), value.name.length()); + row->set_value(column_score, (void *) &value.score, sizeof(value.score)); + + _rowlock->unlock(); + + return true; + } + + row_t get(const std::string &key) { + row_t result; + TableRow *_rowlock = nullptr; + TableRow *row = table->get(key.c_str(), key.length(), &_rowlock); + if (row) { + memcpy(&result.id, row->data + column_id->index, sizeof(result.id)); + memcpy(&result.score, row->data + column_score->index, sizeof(result.score)); + + TableStringLength l; + memcpy(&l, row->data + column_name->index, sizeof(l)); + result.name = std::string(row->data + column_name->index + sizeof(l), l); + } + _rowlock->unlock(); + + return result; + } + + bool del(const std::string &key) { + return table->del(key.c_str(), key.length()); + } + + bool exists(const std::string &key) { + TableRow *_rowlock = nullptr; + TableRow *row = table->get(key.c_str(), key.length(), &_rowlock); + _rowlock->unlock(); + + return row != nullptr; + } + + size_t count() { + return table->count(); + } + + Table *ptr() { + return table; + } + + ~table_t() { + if (table) { + table->destroy(); + } + } +}; + +TEST(table, create) { + table_t table(1024); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + ASSERT_EQ(table.count(), 3); + + row_t r1 = table.get("java"); + ASSERT_EQ(r1.id, 2); + ASSERT_EQ(r1.score, 3.1415926); + ASSERT_EQ(r1.name, std::string("java")); + + ASSERT_TRUE(table.exists("php")); + ASSERT_TRUE(table.del("php")); + ASSERT_FALSE(table.exists("php")); +} + +void start_iterator(Table *_ptr) { + _ptr->rewind(); + auto count = 0; + while (true) { + _ptr->forward(); + auto row = _ptr->current(); + if (row->key_len == 0) { + break; + } + ASSERT_TRUE(_ptr->exists(row->key, row->key_len)); + count++; + } + ASSERT_EQ(count, _ptr->count()); +} + +TEST(table, iterator) { + table_t table(1024); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + auto _ptr = table.ptr(); + start_iterator(_ptr); +} + +TEST(table, iterator_2) { + table_t table(1024); + auto _ptr = table.ptr(); + _ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + table.set("php", {"php", 1, 1.245}); + table.set("java", {"java", 2, 3.1415926}); + table.set("c++", {"c++", 3, 4.888}); + + start_iterator(_ptr); +} + +static int test_table_size = 128; + +static void create_table(table_t &table) { + auto ptr = table.ptr(); + ptr->set_hash_func([](const char *key, size_t len) -> uint64_t { return 1; }); + + ASSERT_TRUE(table.set("php", {"php", 1, 1.245})); + ASSERT_TRUE(table.set("java", {"java", 2, 3.1415926})); + ASSERT_TRUE(table.set("c++", {"c++", 3, 4.888})); + ASSERT_TRUE(table.set("js", {"js", 9, 6565})); + ASSERT_TRUE(table.set("golang", {"golang", 4, 9.888})); +} + +TEST(table, conflict1) { + table_t table(test_table_size); + ASSERT_FALSE(table.exists("swift")); + + create_table(table); + auto ptr = table.ptr(); + + ASSERT_FALSE(table.exists("kotlin")); + + ASSERT_TRUE(table.del("php")); + ASSERT_FALSE(table.exists("php")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict2) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("java")); + ASSERT_FALSE(table.exists("java")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_FALSE(table.exists("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict3) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, conflict4) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + ASSERT_TRUE(table.del("c++")); + ASSERT_TRUE(table.set("rust", {"rust", 5, 9.888})); + + ASSERT_TRUE(table.del("golang")); + ASSERT_TRUE(table.set("erlang", {"erlang", 6, 12.888})); + + ASSERT_TRUE(table.del("java")); + + ASSERT_EQ(ptr->get_total_slice_num() - ptr->get_available_slice_num(), table.count() - 1); +} + +TEST(table, get_value) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + _rowlock->unlock(); + TableColumn *column_id = ptr->get_column("id"); + TableColumn *column_name = ptr->get_column("name"); + TableColumn *column_score = ptr->get_column("score"); + + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + double dval = 0; + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 1.245); + + long lval = 0; + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 1); + + column_id->clear(row); + column_name->clear(row); + column_score->clear(row); + + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + + row->get_value(column_score, &dval); + ASSERT_EQ(dval, 0); + + row->get_value(column_id, &lval); + ASSERT_EQ(lval, 0); +} + +TEST(table, lock) { + table_t table(test_table_size); + create_table(table); + auto ptr = table.ptr(); + + std::string key("php"); + TableRow *_rowlock = nullptr; + + for (int i = 0; i <= 3; i++) { + std::thread t([&]() { + TableRow *row = ptr->get(key.c_str(), key.length(), &_rowlock); + TableColumn *column_name = ptr->get_column("name"); + char *str = nullptr; + TableStringLength len = 0; + row->get_value(column_name, &str, &len); + ASSERT_STREQ(str, "php"); + }); + t.join(); + } + _rowlock->unlock(); +} diff --git a/core-tests/src/network/aio_thread.cpp b/core-tests/src/network/aio_thread.cpp deleted file mode 100644 index f43044d3246..00000000000 --- a/core-tests/src/network/aio_thread.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "tests.h" -#include "async.h" - -#include - -using namespace std; - -static int callback_count; - -static void aio_callback(swAio_event *event) -{ - callback_count++; -} - -TEST(aio_thread, dispatch) -{ - atomic handle_count(0); - swAio_event event; - event.object = &handle_count; - event.canceled = 0; - event.callback = aio_callback; - - callback_count = 0; - - event.handler = [](swAio_event *event) - { - (*(atomic *) event->object)++; - }; - - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - - for (int i = 0; i < 1000; ++i) - { - auto ret = swAio_dispatch2(&event); - ASSERT_EQ(ret->object, event.object); - ASSERT_NE(ret->task_id, event.task_id); - } - - swoole_event_wait(); - - ASSERT_EQ(handle_count, 1000); - ASSERT_EQ(callback_count, 1000); -} diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp new file mode 100644 index 00000000000..acbee375417 --- /dev/null +++ b/core-tests/src/network/client.cpp @@ -0,0 +1,357 @@ +#include "test_core.h" +#include "test_server.h" +#include "test_process.h" + +#define GREETER "Hello Swoole" +#define GREETER_SIZE sizeof(GREETER) + +using swoole::HttpProxy; +using swoole::Pipe; +using swoole::Socks5Proxy; +using swoole::String; +using swoole::network::AsyncClient; +using swoole::network::Client; +using swoole::test::create_http_proxy; +using swoole::test::create_socks5_proxy; +using swoole::test::Process; +using swoole::test::Server; + +TEST(client, tcp) { + int ret; + char buf[128]; + + pid_t pid; + int port = swoole::test::get_random_port(); + + Process proc([port](Process *proc) { + on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { + SERVER_THIS->send(req->info.fd, req->data, req->info.len); + }; + + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + serv.on("onReceive", (void *) receive_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + Client cli(SW_SOCK_TCP, false); + ASSERT_NE(cli.socket, nullptr); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); + ASSERT_EQ(ret, 0); + ret = cli.send(&cli, SW_STRS(GREETER), 0); + ASSERT_GT(ret, 0); + ret = cli.recv(&cli, buf, 128, 0); + ASSERT_EQ(ret, GREETER_SIZE); + ASSERT_STREQ(GREETER, buf); + + kill(pid, SIGTERM); + int status; + wait(&status); +} + +TEST(client, udp) { + int ret; + char buf[128]; + int port = swoole::test::get_random_port(); + pid_t pid; + + Process proc([port](Process *proc) { + on_packet_lambda_type packet_fn = [](ON_PACKET_PARAMS) { + swoole::DgramPacket *packet = (swoole::DgramPacket *) req->data; + SERVER_THIS->sendto(packet->socket_addr, packet->data, packet->length, req->info.server_fd); + }; + + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_UDP); + serv.on("onPacket", (void *) packet_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + Client cli(SW_SOCK_UDP, false); + ASSERT_NE(cli.socket, nullptr); + ret = cli.connect(&cli, TEST_HOST, port, -1, 0); + ASSERT_EQ(ret, 0); + ret = cli.send(&cli, SW_STRS(GREETER), 0); + ASSERT_GT(ret, 0); + ret = cli.recv(&cli, buf, 128, 0); + ASSERT_EQ(ret, GREETER_SIZE); + ASSERT_STREQ(GREETER, buf); + + kill(pid, SIGTERM); + int status; + wait(&status); +} + +static void test_async_client_tcp(const char *host, int port) { + pid_t pid; + + Pipe p(true); + ASSERT_TRUE(p.ready()); + + Process proc([&p, port](Process *proc) { + on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) { + SERVER_THIS->send(req->info.fd, req->data, req->info.len); + }; + + Server serv(TEST_HOST, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + + serv.set_private_data("pipe", &p); + + serv.on("onReceive", (void *) receive_fn); + + on_workerstart_lambda_type worker_start_fn = [](ON_WORKERSTART_PARAMS) { + Pipe *p = (Pipe *) SERVER_THIS->get_private_data("pipe"); + int64_t value = 1; + p->write(&value, sizeof(value)); + }; + + serv.on("onWorkerStart", (void *) worker_start_fn); + + serv.start(); + }); + + pid = proc.start(); + int64_t value; + p.set_timeout(10); + p.read(&value, sizeof(value)); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + AsyncClient ac(SW_SOCK_TCP); + + ac.on_connect([](AsyncClient *ac) { ac->send(SW_STRS(GREETER)); }); + + ac.on_close([](AsyncClient *ac) { + + }); + ac.on_error([](AsyncClient *ac) { + + }); + + ac.on_receive([](AsyncClient *ac, const char *data, size_t len) { + ASSERT_EQ(len, GREETER_SIZE); + ASSERT_STREQ(GREETER, data); + ac->close(); + }); + + bool retval = ac.connect(host, port); + EXPECT_TRUE(retval); + + swoole_event_wait(); + + kill(pid, SIGTERM); + int status; + wait(&status); +} + +TEST(client, async_tcp) { + test_async_client_tcp(TEST_HOST, swoole::test::get_random_port()); +} + +TEST(client, async_tcp_dns) { + test_async_client_tcp("localhost", swoole::test::get_random_port()); +} + +TEST(client, connect_refuse) { + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, TEST_HOST, swoole::test::get_random_port(), -1, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED); +} + +TEST(client, connect_timeout) { + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "19.168.0.99", swoole::test::get_random_port(), 0.2, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(swoole_get_last_error(), ETIMEDOUT); +} + +TEST(client, shutdown_write) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + cli.shutdown(SHUT_WR); + ssize_t retval = cli.send(&cli, SW_STRL("hello world"), 0); + ASSERT_EQ(retval, -1); + ASSERT_EQ(swoole_get_last_error(), EPIPE); +} + +TEST(client, shutdown_read) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + + cli.shutdown(SHUT_RD); + ssize_t retval = cli.send(&cli, SW_STRL("hello world\r\n\r\n"), 0); + ASSERT_GT(retval, 0); + + char buf[1024]; + retval = cli.recv(&cli, buf, sizeof(buf), 0); + ASSERT_EQ(retval, 0); +} + +TEST(client, shutdown_all) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + + cli.shutdown(SHUT_RDWR); + + ssize_t retval = cli.send(&cli, SW_STRL("hello world\r\n\r\n"), 0); + ASSERT_EQ(retval, -1); + ASSERT_EQ(swoole_get_last_error(), EPIPE); + + char buf[1024]; + retval = cli.recv(&cli, buf, sizeof(buf), 0); + ASSERT_EQ(retval, 0); +} + +#ifdef SW_USE_OPENSSL + +TEST(client, ssl_1) { + bool connected = false; + bool closed = false; + String buf(65536); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + Client client(SW_SOCK_TCP, true); + client.enable_ssl_encrypt(); + client.onConnect = [&connected](Client *cli) { + connected = true; + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); + }; + + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, 443, -1, 0), 0); + + swoole_event_wait(); + + ASSERT_TRUE(connected); + ASSERT_TRUE(closed); + ASSERT_TRUE(buf.contains("Baidu")); +} + +static void proxy_async_test(Client &client, bool https) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + bool connected = false; + bool closed = false; + String buf(65536); + + if (https) { + client.enable_ssl_encrypt(); + } + + client.onConnect = [&connected](Client *cli) { + connected = true; + cli->send(cli, SW_STRL(TEST_REQUEST_BAIDU), 0); + }; + + client.onError = [](Client *cli) {}; + client.onClose = [&closed](Client *cli) { closed = true; }; + client.onReceive = [&buf](Client *cli, const char *data, size_t length) { buf.append(data, length); }; + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); + + swoole_event_wait(); + + ASSERT_TRUE(connected); + ASSERT_TRUE(closed); + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +static void proxy_sync_test(Client &client, bool https) { + String buf(65536); + if (https) { + client.enable_ssl_encrypt(); + } + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, https ? 443 : 80, -1, 0), 0); + ASSERT_GT(client.send(&client, SW_STRL(TEST_REQUEST_BAIDU), 0), 0); + + while (true) { + char rbuf[4096]; + auto nr = client.recv(&client, rbuf, sizeof(rbuf), 0); + if (nr <= 0) { + break; + } + buf.append(rbuf, nr); + } + + ASSERT_TRUE(buf.contains("www.baidu.com")); +} + +static void proxy_set_socks5_proxy(Client &client) { + client.socks5_proxy = create_socks5_proxy(); +} + +static void proxy_set_http_proxy(Client &client) { + client.http_proxy = create_http_proxy(); +} + +TEST(client, https_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, true); +} + +TEST(client, https_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, true); +} + +TEST(client, https_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, https_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, true); +} + +TEST(client, http_get_async_with_http_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_http_proxy(client); + proxy_async_test(client, false); +} + +TEST(client, http_get_async_with_socks5_proxy) { + Client client(SW_SOCK_TCP, true); + proxy_set_socks5_proxy(client); + proxy_async_test(client, false); +} + +TEST(client, http_get_sync_with_http_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_http_proxy(client); + proxy_sync_test(client, false); +} + +TEST(client, http_get_sync_with_socks5_proxy) { + Client client(SW_SOCK_TCP, false); + proxy_set_socks5_proxy(client); + proxy_sync_test(client, false); +} +#endif diff --git a/core-tests/src/network/dns.cpp b/core-tests/src/network/dns.cpp new file mode 100644 index 00000000000..f89746600c8 --- /dev/null +++ b/core-tests/src/network/dns.cpp @@ -0,0 +1,212 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_coroutine.h" + +#include "swoole_socket.h" + +#include "swoole_util.h" + +using namespace swoole; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using namespace swoole::test; +using namespace std; + +TEST(dns, lookup1) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.baidu.com", AF_INET, 10); + ASSERT_GE(list.size(), 1); + }); +} + +TEST(dns, lookup_ipv6) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.google.com", AF_INET6, 2); + ASSERT_GE(list.size(), 1); + }); +} + +TEST(dns, domain_not_found) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.baidu.com-not-found", AF_INET, 2); + ASSERT_EQ(list.size(), 0); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + }); +} + +TEST(dns, bad_family) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.google.com", 9999, 2); + ASSERT_GE(list.size(), 1); + }); +} + +TEST(dns, cancel) { + // swoole_set_trace_flags(SW_TRACE_CARES); + // swoole_set_log_level(SW_LOG_TRACE); + test::coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + Coroutine::create([co](void *) { + System::sleep(0.002); + co->cancel(); + }); + auto list1 = swoole::coroutine::dns_lookup("www.baidu-not-found-for-cancel.com", AF_INET, 2); + ASSERT_EQ(list1.size(), 0); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_CO_CANCELED); + }); +} + +TEST(dns, getaddrinfo) { + swoole::GetaddrinfoRequest req("www.baidu.com", AF_INET, SOCK_STREAM, 0, ""); + ASSERT_EQ(swoole::network::getaddrinfo(&req), 0); + ASSERT_GT(req.count, 0); + + vector ip_list; + req.parse_result(ip_list); + + for (auto &ip : ip_list) { + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); + } +} + +TEST(dns, load_resolv_conf) { + // reset + SwooleG.dns_server_host = ""; + SwooleG.dns_server_port = 0; + int port = swoole::test::get_random_port(); + + auto dns_server = swoole_get_dns_server(); + ASSERT_TRUE(dns_server.first.empty()); + ASSERT_EQ(dns_server.second, 0); + + // with port + std::string test_server = "127.0.0.1:" + std::to_string(port); // fake dns server + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_STREQ(dns_server.first.c_str(), "127.0.0.1"); + ASSERT_EQ(dns_server.second, port); + + // invalid port + test_server = "127.0.0.1:808088"; + swoole_set_dns_server(test_server); + dns_server = swoole_get_dns_server(); + ASSERT_EQ(dns_server.second, SW_DNS_SERVER_PORT); + + ASSERT_TRUE(swoole_load_resolv_conf()); + dns_server = swoole_get_dns_server(); + ASSERT_FALSE(dns_server.first.empty()); + ASSERT_NE(dns_server.second, 0); +} + +TEST(dns, gethosts) { + char hosts_file[] = "/tmp/swoole_hosts"; + ofstream file(hosts_file); + if (!file.is_open()) { + std::cout << std::string("file open failed: ") + std::string(strerror(errno)) << std::endl; + throw strerror(errno); + } + + ON_SCOPE_EXIT { + unlink(hosts_file); + }; + + file << "\n"; + file << "127.0.0.1\n"; + file << "127.0.0.1 localhost\n"; + file << "# 127.0.0.1 aaa.com\n"; + file << " 127.0.0.1 bbb.com ccc.com #ddd.com\n"; + file.close(); + + swoole_set_hosts_path(hosts_file); + + std::string ip = swoole::coroutine::get_ip_by_hosts("localhost"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("aaa.com"); + ASSERT_EQ(ip, ""); + + ip = swoole::coroutine::get_ip_by_hosts("bbb.com"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("ccc.com"); + ASSERT_EQ(ip, "127.0.0.1"); + + ip = swoole::coroutine::get_ip_by_hosts("ddd.com"); + ASSERT_EQ(ip, ""); + + ip = swoole::coroutine::get_ip_by_hosts("non.exist.com"); + ASSERT_EQ(ip, ""); +} + +void name_resolver_test_fn_1() { + NameResolver::Context ctx{}; + ctx.type = AF_INET; + ctx.timeout = 1; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); +} + +void name_resolver_test_fn_2() { + NameResolver::Context ctx; + std::string domain = "non.exist.com"; + NameResolver nr{[](const std::string &domain, NameResolver::Context *ctx, void *) -> std::string { + if (domain == "name1") { + return "127.0.0.2"; + } else if (domain == "www.baidu.com") { + ctx->final_ = true; + return ""; + } + return ""; + }, + nullptr, + NameResolver::TYPE_USER}; + + swoole_name_resolver_add(nr); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.2", swoole_name_resolver_lookup("name1", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("", swoole_name_resolver_lookup("www.baidu.com", &ctx)); + + ctx = {AF_INET}; + ASSERT_EQ("127.0.0.1", swoole_name_resolver_lookup("localhost", &ctx)); + + swoole_name_resolver_each([](const std::list::iterator &iter) -> swTraverseOperation { + if (iter->type == NameResolver::TYPE_USER) { + return SW_TRAVERSE_REMOVE; + } else { + return SW_TRAVERSE_KEEP; + } + }); + + ctx = {AF_INET}; + auto ip = swoole_name_resolver_lookup("www.baidu.com", &ctx); + ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); +} + +TEST(dns, name_resolver_1) { + name_resolver_test_fn_1(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_1(); }); +} + +TEST(dns, name_resolver_2) { + name_resolver_test_fn_2(); + test::coroutine::run([](void *arg) { name_resolver_test_fn_2(); }); +} diff --git a/core-tests/src/network/socket.cpp b/core-tests/src/network/socket.cpp new file mode 100644 index 00000000000..f4b656eb96e --- /dev/null +++ b/core-tests/src/network/socket.cpp @@ -0,0 +1,382 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_file.h" + +using namespace std; +using namespace swoole; + +const char test_data[] = "hello swoole, hello world, php is best"; + +TEST(socket, sendto) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + char sock2_path[] = "/tmp/udp_unix2.sock"; + + unlink(sock1_path); + unlink(sock2_path); + + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + + ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + network::Address sa; + sa.type = SW_SOCK_UNIX_DGRAM; + ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0); + ASSERT_STREQ(test_data, buf); + ASSERT_STREQ(sa.get_ip(), sock1_path); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +static void test_sendto(enum swSocketType sock_type) { + int port1 = 0, port2 = 0; + const char *ip = sock_type == SW_SOCK_UDP ? "127.0.0.1" : "::1"; + + auto sock1 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0); + sock1->bind(ip, &port1); + + auto sock2 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0); + sock2->bind(ip, &port2); + + ASSERT_GT(sock1->sendto(ip, port2, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + network::Address sa; + sa.type = sock_type; + ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0); + + ASSERT_STREQ(test_data, buf); + ASSERT_EQ(sa.get_port(), port1); + ASSERT_STREQ(sa.get_ip(), ip); + + sock1->free(); + sock2->free(); +} + +TEST(socket, sendto_ipv4) { + test_sendto(SW_SOCK_UDP); +} + +TEST(socket, sendto_ipv6) { + test_sendto(SW_SOCK_UDP6); +} + +TEST(socket, recv) { + mutex m; + m.lock(); + int port = swoole::test::get_random_port(); + + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); + char buf[1024] = {}; + svr->set_block(); + m.unlock(); + + auto client_sock = svr->accept(); + client_sock->recv(buf, sizeof(buf), 0); + + ASSERT_STREQ(test_data, buf); + svr->free(); + }); + + thread t2([&m, port]() { + m.lock(); + auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); + ASSERT_EQ(cli->connect(TEST_HOST, port), SW_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + cli->send(test_data, sizeof(test_data), 0); + cli->free(); + }); + + t1.join(); + t2.join(); +} + +TEST(socket, recvfrom_blocking) { + mutex m; + m.lock(); + int port = swoole::test::get_random_port(); + + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_UDP, TEST_HOST, port); + network::Address addr; + char buf[1024] = {}; + svr->set_nonblock(); + m.unlock(); + svr->recvfrom_blocking(buf, sizeof(buf), 0, &addr); + ASSERT_STREQ(test_data, buf); + svr->free(); + }); + + thread t2([&m, port]() { + m.lock(); + auto cli = make_socket(SW_SOCK_UDP, SW_FD_STREAM_CLIENT, 0); + network::Address addr; + addr.assign(SW_SOCK_TCP, TEST_HOST, port); + ASSERT_EQ(cli->connect(addr), SW_OK); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + cli->send(test_data, sizeof(test_data), 0); + cli->free(); + }); + + t1.join(); + t2.join(); +} + +TEST(socket, sendfile_blocking) { + string file = test::get_root_path() + "/examples/test.jpg"; + mutex m; + int port = swoole::test::get_random_port(); + m.lock(); + + auto str = file_get_contents(file); + + thread t1([&m, &str, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); + m.unlock(); + auto cli = svr->accept(); + int len; + cli->recv_blocking(&len, sizeof(len), MSG_WAITALL); + int _len = ntohl(len); + ASSERT_EQ(_len, str->get_length()); + ASSERT_LT(_len, 1024 * 1024); + std::unique_ptr data(new char[_len]); + cli->recv_blocking(data.get(), _len, MSG_WAITALL); + ASSERT_STREQ(data.get(), str->value()); + cli->free(); + svr->free(); + }); + + thread t2([&m, &file, &str, port]() { + m.lock(); + auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); + network::Address addr; + addr.assign(SW_SOCK_TCP, TEST_HOST, port); + ASSERT_EQ(cli->connect(addr), SW_OK); + int len = htonl(str->get_length()); + cli->send(&len, sizeof(len), 0); + ASSERT_EQ(cli->sendfile_blocking(file.c_str(), 0, 0, -1), SW_OK); + cli->free(); + }); + + t1.join(); + t2.join(); +} + +TEST(socket, peek) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + char sock2_path[] = "/tmp/udp_unix2.sock"; + + unlink(sock1_path); + unlink(sock2_path); + + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + + ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + ASSERT_GT(sock2->peek(buf, sizeof(buf), 0), 0); + ASSERT_STREQ(test_data, buf); + + sw_memset_zero(buf, sizeof(buf)); + ASSERT_GT(sock2->recv(buf, sizeof(buf), 0), 0); + ASSERT_STREQ(test_data, buf); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, sendto_blocking) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + unlink(sock1_path); + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + sock1->info.assign(SW_SOCK_UNIX_DGRAM, sock1_path, 0); + + char sock2_path[] = "/tmp/udp_unix2.sock"; + unlink(sock2_path); + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + sock2->info.assign(SW_SOCK_UNIX_DGRAM, sock2_path, 0); + + char sendbuf[65536] = {}; + swoole_random_string(sendbuf, sizeof(sendbuf) - 1); + + thread t1([sock2, sendbuf]() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + char recvbuf[65536] = {}; + while (1) { + auto retval = sock2->recv(recvbuf, sizeof(recvbuf) - 1, 0); + recvbuf[retval] = 0; + if (retval == 3) { + ASSERT_STREQ(recvbuf, "end"); + break; + } else { + ASSERT_STREQ(sendbuf, recvbuf); + } + } + }); + + for (int i = 0; i < 10; i++) { + ASSERT_GT(sock1->sendto_blocking(sock2->info, sendbuf, strlen(sendbuf)), 0); + } + ASSERT_GT(sock1->sendto_blocking(sock2->info, "end", 3), 0); + + t1.join(); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, clean) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + unlink(sock1_path); + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + sock1->info.assign(SW_SOCK_UNIX_DGRAM, sock1_path, 0); + + char sock2_path[] = "/tmp/udp_unix2.sock"; + unlink(sock2_path); + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + sock2->info.assign(SW_SOCK_UNIX_DGRAM, sock2_path, 0); + + char sendbuf[65536] = {}; + swoole_random_string(sendbuf, sizeof(sendbuf) - 1); + + for (int i = 0; i < 3; i++) { + ASSERT_GT(sock1->sendto_blocking(sock2->info, sendbuf, strlen(sendbuf)), 0); + } + + sock2->clean(); + char recvbuf[1024]; + auto retval = sock2->peek(recvbuf, sizeof(recvbuf), MSG_DONTWAIT); + ASSERT_EQ(retval, -1); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, check_liveness) { + mutex m; + int svr_port = swoole::test::get_random_port(); + m.lock(); + + thread t1([&m, svr_port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, svr_port); + m.unlock(); + + auto cli = svr->accept(); + ASSERT_TRUE(cli); + + char buf[1024] = {}; + cli->recv(buf, sizeof(buf), 0); + ASSERT_STREQ(test_data, buf); + + ssize_t n = cli->recv(buf, sizeof(buf), 0); + buf[n] = 0; + ASSERT_STREQ("close", buf); + cli->shutdown(SHUT_RDWR); + cli->free(); + + svr->free(); + }); + + thread t2([&m, svr_port]() { + m.lock(); + + auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); + ASSERT_EQ(cli->connect(TEST_HOST, svr_port), SW_OK); + + cli->send(test_data, sizeof(test_data), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ASSERT_TRUE(cli->check_liveness()); + + cli->send(SW_STRL("close"), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + ASSERT_FALSE(cli->check_liveness()); + + cli->free(); + }); + + t1.join(); + t2.join(); +} + +#define CRLF "\r\n" + +TEST(socket, sync) { + auto sock = make_socket(SW_SOCK_TCP, SW_FD_STREAM, 0); + + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://httpbin.org:80")); + + ASSERT_EQ(sock->connect(addr), 0); + + const char *req = "GET / HTTP/1.1" CRLF \ + "Host: httpbin.org" CRLF \ + "User-Agent: curl/7.81.0" CRLF \ + "Accept: */*" CRLF \ + "Connection: close" CRLF \ + CRLF CRLF; + ssize_t n = strlen(req); + ASSERT_EQ(sock->write_sync(req, n), n); + + string resp; + SW_LOOP { + char buf[1024]; + n = sock->read_sync(buf, sizeof(buf)); + if (n == 0) { + break; + } + ASSERT_GT(n, 0); + resp.append(buf, n); + } + + ASSERT_GT(resp.length(), 4096); + sock->free(); +} + +TEST(socket, ipv6_addr) { + auto sock = make_socket(SW_SOCK_TCP6, SW_FD_STREAM, 0); + swoole::network::Address addr; + ASSERT_TRUE(addr.assign("tcp://[::1]:12345")); + ASSERT_EQ(sock->connect(addr), SW_ERR); + ASSERT_EQ(errno, ECONNREFUSED); + sock->free(); +} diff --git a/core-tests/src/network/stream.cpp b/core-tests/src/network/stream.cpp new file mode 100644 index 00000000000..5dd870eefa0 --- /dev/null +++ b/core-tests/src/network/stream.cpp @@ -0,0 +1,112 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_server.h" + +using namespace std; +using namespace swoole; +using namespace swoole::network; + +TEST(stream, send) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + int svr_port = swoole::test::get_random_port(); + int ori_log_level = sw_logger()->get_level(); + sw_logger()->set_level(SW_LOG_ERROR); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, svr_port); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_length_check = true; + Stream::set_protocol(&port->protocol); + + mutex lock; + lock.lock(); + + char buf[65536]; + ASSERT_EQ(swoole_random_bytes(buf, sizeof(buf)), sizeof(buf)); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + // bad request + auto stream0 = Stream::create(TEST_TMP_FILE, 0, SW_SOCK_UNIX_STREAM); + ASSERT_EQ(stream0, nullptr); + + // bad request + auto stream1 = Stream::create(TEST_HOST, 39999, SW_SOCK_TCP); + ASSERT_TRUE(stream1); + stream1->response = [](Stream *stream, const char *data, uint32_t length) { + EXPECT_EQ(data, nullptr); + EXPECT_EQ(stream->errCode, ECONNREFUSED); + }; + ASSERT_EQ(stream1->send(buf, sizeof(buf)), SW_OK); + + // success requset + auto stream2 = Stream::create(TEST_HOST, svr_port, SW_SOCK_TCP); + ASSERT_TRUE(stream2); + stream2->private_data = new string(buf, sizeof(buf)); + stream2->response = [](Stream *stream, const char *data, uint32_t length) { + string *buf = (string *) stream->private_data; + string pkt = string("Server: ") + *buf; + EXPECT_EQ(string(data, length), pkt); + delete buf; + }; + ASSERT_EQ(stream2->send(buf, sizeof(buf)), SW_OK); + + swoole_event_wait(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [&buf](Server *serv, RecvData *req) -> int { + string req_body(req->data + 4, req->info.len - 4); + + EXPECT_EQ(string(buf, sizeof(buf)), req_body); + + string pkt = string("Server: ") + req_body; + int packed_len = htonl(pkt.length()); + + EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len))); + EXPECT_TRUE(serv->send(req->info.fd, pkt.c_str(), pkt.length())); + + // end stream + packed_len = htonl(0); + EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len))); + + return SW_OK; + }; + + serv.start(); + t1.join(); + + sw_logger()->set_level(ori_log_level); +} diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp new file mode 100644 index 00000000000..8a122069db7 --- /dev/null +++ b/core-tests/src/os/async.cpp @@ -0,0 +1,99 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_socket.h" +#include "swoole_async.h" + +#include + +using namespace swoole; + +static int callback_count; + +TEST(async, dispatch) { + callback_count = 0; + std::atomic handle_count(0); + AsyncEvent event = {}; + event.object = &handle_count; + event.callback = [](AsyncEvent *event) { callback_count++; }; + event.handler = [](AsyncEvent *event) { (*(std::atomic *) event->object)++; }; + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + for (int i = 0; i < 1000; ++i) { + auto ret = swoole::async::dispatch(&event); + EXPECT_EQ(ret->object, event.object); + } + + swoole_event_wait(); + + ASSERT_EQ(handle_count, 1000); + ASSERT_EQ(callback_count, 1000); +} + +TEST(async, schedule) { + callback_count = 0; + std::atomic handle_count(0); + + int N = 1000; + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + AsyncEvent event{}; + event.object = &handle_count; + event.callback = [](AsyncEvent *event) { callback_count++; }; + event.handler = [](AsyncEvent *event) { + usleep(swoole_rand(50000, 100000)); + (*(std::atomic *) event->object)++; + }; + + SwooleG.aio_core_worker_num = 4; + SwooleG.aio_worker_num = 128; + SwooleG.aio_max_wait_time = 0.05; + SwooleG.aio_max_idle_time = 0.5; + + int count = N; + swoole_timer_tick(2, [&count, &event, N](Timer *, TimerNode *timer) { + SW_LOOP_N(swoole_rand(5, 15)) { + auto ret = swoole::async::dispatch(&event); + EXPECT_EQ(ret->object, event.object); + count--; + if (count == 0) { + swoole_timer_del(timer); + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 16); + ASSERT_GT(SwooleTG.async_threads->get_queue_size(), 100); + ASSERT_GT(SwooleTG.async_threads->get_task_num(), 100); + break; + } else if (count == N - 1) { + ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 4); + ASSERT_LE(SwooleTG.async_threads->get_queue_size(), 1); + ASSERT_EQ(SwooleTG.async_threads->get_task_num(), 1); + } else if (count < N / 2) { + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 4); + } + } + }); + + swoole_event_wait(); + + ASSERT_EQ(handle_count, N); + ASSERT_EQ(callback_count, N); +} diff --git a/core-tests/src/os/file.cpp b/core-tests/src/os/file.cpp new file mode 100644 index 00000000000..1b85b01cbc4 --- /dev/null +++ b/core-tests/src/os/file.cpp @@ -0,0 +1,47 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_file.h" + +using namespace swoole; + +TEST(file, read_line) { + std::string filename = test::get_root_path() + "/tests/include/bootstrap.php"; + File file(filename, File::READ); + FILE *stdc_file = fopen(filename.c_str(), "r"); + ASSERT_NE(stdc_file, nullptr); + char buf1[1024]; + char buf2[1024]; + + size_t size = file.get_size(); + size_t total = 0; + + while(true) { + auto retval = file.read_line(buf1, sizeof(buf1)); + if (retval == 0) { + break; + } + total += retval; + ASSERT_NE(fgets(buf2, sizeof(buf2), stdc_file), nullptr); + ASSERT_STREQ(buf1, buf2); + } + ASSERT_EQ(total, size); +} diff --git a/core-tests/src/os/msg_queue.cpp b/core-tests/src/os/msg_queue.cpp new file mode 100644 index 00000000000..31a7a72f755 --- /dev/null +++ b/core-tests/src/os/msg_queue.cpp @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_msg_queue.h" + +using swoole::MsgQueue; +using swoole::QueueNode; + +TEST(msg_queue, rbac) { + MsgQueue q(0x950001); + ASSERT_TRUE(q.ready()); + QueueNode in; + in.mtype = 999; + strcpy(in.mdata, "hello world"); + + if (!swoole::test::is_github_ci()) { + ASSERT_TRUE(q.set_capacity(8192)); + } + + // input data + ASSERT_TRUE(q.push(&in, strlen(in.mdata))); + + size_t queue_num, queue_bytes; + ASSERT_TRUE(q.stat(&queue_num, &queue_bytes)); + ASSERT_EQ(queue_num, 1); + ASSERT_GT(queue_bytes, 10); + + // output data + QueueNode out{}; + ASSERT_GT(q.pop(&out, sizeof(out.mdata)), 1); + + ASSERT_TRUE(q.stat(&queue_num, &queue_bytes)); + ASSERT_EQ(queue_num, 0); + ASSERT_EQ(queue_bytes, 0); + + ASSERT_EQ(out.mtype, in.mtype); + ASSERT_STREQ(out.mdata, in.mdata); + + ASSERT_TRUE(q.destroy()); +} diff --git a/core-tests/src/os/pipe.cpp b/core-tests/src/os/pipe.cpp new file mode 100644 index 00000000000..f966e0cb2ef --- /dev/null +++ b/core-tests/src/os/pipe.cpp @@ -0,0 +1,53 @@ +#include "test_core.h" +#include "swoole_pipe.h" + +using namespace swoole; + +TEST(pipe, unixsock) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + char buf[1024]; + + int ret = p.write((void *) SW_STRS("hello world1")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRS("hello world2")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRS("hello world3")); + ASSERT_GT(ret, 0); + + // 1 + ret = p.read(buf, sizeof(buf)); + if (ret < 0) { + swoole_sys_warning("read() failed."); + } + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world1", buf), 0); + // 2 + ret = p.read(buf, sizeof(buf)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world2", buf), 0); + // 3 + ret = p.read(buf, sizeof(buf)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world3", buf), 0); +} + +TEST(pipe, base) { + int ret; + char data[256]; + + Pipe p(true); + ASSERT_TRUE(p.ready()); + + + ret = p.write((void *) SW_STRL("hello world\n")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRL("你好中国。\n")); + ASSERT_GT(ret, 0); + + sw_memset_zero(data, 256); + ret = p.read(data, 255); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world\n你好中国。\n", data), 0); +} diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp new file mode 100644 index 00000000000..5f043f7a431 --- /dev/null +++ b/core-tests/src/os/process_pool.cpp @@ -0,0 +1,230 @@ +#include "test_core.h" +#include "swoole_process_pool.h" + +#include + +#ifdef __MACH__ +#define sysv_signal signal +#endif + +#include "swoole_signal.h" + +using namespace swoole; + +static void test_func(ProcessPool &pool) { + EventData data{}; + size_t size = swoole_system_random(1024, 4096); + String rmem(size); + rmem.append_random_bytes(size - 1); + rmem.append("\0"); + + data.info.len = size; + memcpy(data.data, rmem.value(), size); + + int worker_id = -1; + ASSERT_EQ(pool.dispatch_blocking(&data, &worker_id), SW_OK); + + pool.running = true; + pool.ptr = &rmem; + pool.main_loop(&pool, pool.get_worker(0)); + pool.destroy(); +} + +static void test_func_task_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_TASK); + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { + pool->running = false; + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, task->data, task->len()); + return 0; + }; + test_func(pool); +} + +static void test_func_message_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_MESSAGE); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); + }; + test_func(pool); +} + +static void test_func_stream_protocol(ProcessPool &pool) { + pool.set_protocol(SW_PROTOCOL_STREAM); + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) { + pool->running = false; + String *_data = (String *) pool->ptr; + EventData *msg = (EventData *) rdata->data; + usleep(10000); + EXPECT_MEMEQ(_data->str, msg->data, msg->len()); + }; + test_func(pool); +} + +TEST(process_pool, tcp) { + ProcessPool pool{}; + int svr_port = swoole::test::get_random_port(); + ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); + + test_func_task_protocol(pool); +} + +TEST(process_pool, unix_sock) { + ProcessPool pool{}; + signal(SIGPIPE, SIG_IGN); + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_task_protocol(pool); +} + +TEST(process_pool, tcp_raw) { + ProcessPool pool{}; + constexpr int size = 2 * 1024 * 1024; + int svr_port = swoole::test::get_random_port(); + ASSERT_EQ(pool.create(1, 0, SW_IPC_SOCKET), SW_OK); + ASSERT_EQ(pool.listen(TEST_HOST, svr_port, 128), SW_OK); + pool.set_max_packet_size(size); + pool.set_protocol(SW_PROTOCOL_STREAM); + + String data(size); + data.append_random_bytes(size - 1); + data.append("\0"); + + ASSERT_EQ(pool.dispatch_blocking(data.str, data.length), SW_OK); + + pool.running = true; + pool.ptr = &data; + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) -> void { + pool->running = false; + String *_data = (String *) pool->ptr; + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); + }; + pool.main_loop(&pool, pool.get_worker(0)); + pool.destroy(); +} + +TEST(process_pool, msgqueue) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); + + test_func_task_protocol(pool); +} + +TEST(process_pool, message_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_message_protocol(pool); +} + +TEST(process_pool, stream_protocol) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_stream_protocol(pool); +} + +constexpr int magic_number = 99900011; +static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; + +TEST(process_pool, shutdown) { + ProcessPool pool{}; + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); + + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); + pool.ptr = shm_value; + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + int *shm_value = (int *) pool->ptr; + *shm_value = magic_number; + usleep(1); + }; + + pool.onTask = [](ProcessPool *pool, Worker *worker, EventData *task) -> int { + kill(pool->master_pid, SIGTERM); + + return 0; + }; + + current_pool = &pool; + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + + // start + ASSERT_EQ(pool.start(), SW_OK); + + EventData msg{}; + msg.info.len = 128; + swoole_random_string(msg.data, msg.info.len); + int worker_id = -1; + pool.dispatch_blocking(&msg, &worker_id); + + // wait + ASSERT_EQ(pool.wait(), SW_OK); + + // shutdown + pool.shutdown(); + pool.destroy(); + + ASSERT_EQ(*shm_value, magic_number); +} + +TEST(process_pool, async) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + pool.ptr = shm_value; + pool.async = true; + + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + int *shm_value = (int *) pool->ptr; + *shm_value = magic_number; + current_worker = worker; + + swoole_signal_set(SIGTERM, [](int sig) { + int *shm_value = (int *) current_pool->ptr; + (*shm_value)++; + current_pool->stop(current_worker); + }); + + usleep(10); + }; + + pool.onMessage = [](ProcessPool *pool, RecvData *msg) { + int *shm_value = (int *) pool->ptr; + (*shm_value)++; + kill(pool->master_pid, SIGTERM); + }; + + current_pool = &pool; + sysv_signal(SIGTERM, [](int sig) { current_pool->running = false; }); + + // start + ASSERT_EQ(pool.start(), SW_OK); + + EventData msg{}; + msg.info.len = 128; + swoole_random_string(msg.data, msg.info.len); + int worker_id = -1; + pool.dispatch_blocking(&msg, &worker_id); + + // wait + ASSERT_EQ(pool.wait(), SW_OK); + + // shutdown + pool.shutdown(); + pool.destroy(); + + ASSERT_EQ(*shm_value, magic_number + 2); +} diff --git a/core-tests/src/os/signal.cpp b/core-tests/src/os/signal.cpp index c2dea989f34..a2f2527a14a 100644 --- a/core-tests/src/os/signal.cpp +++ b/core-tests/src/os/signal.cpp @@ -1,40 +1,34 @@ -#include "tests.h" +#include "test_core.h" +#include "swoole_signal.h" #ifdef HAVE_SIGNALFD -static void sig_usr1(int signo ){ +static void sig_usr1(int signo) {} -} - -TEST(os_signal, swSignalfd_set) -{ +TEST(os_signal, signalfd) { int ret; sigset_t curset; - SwooleG.use_signalfd = 1; - - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); - ret = sigismember(&curset,SIGUSR1); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 0); - swSignalfd_init(); - swSignal_add(SIGUSR1,sig_usr1); - swSignalfd_setup(SwooleTG.reactor); + swoole_signalfd_init(); + swoole_signal_set(SIGUSR1, sig_usr1); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); - ret = sigismember(&curset,SIGUSR1); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 1); - swSignal_add(SIGUSR1, NULL); - swSignal_clear(); + swoole_signal_set(SIGUSR1, NULL); + swoole_signal_clear(); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); - ret = sigismember(&curset,SIGUSR1); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 0); swoole_event_wait(); diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp new file mode 100644 index 00000000000..98dfdef8d80 --- /dev/null +++ b/core-tests/src/os/timer.cpp @@ -0,0 +1,152 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_util.h" +#include "swoole_timer.h" + +using swoole::Timer; +using swoole::TimerNode; + +TEST(timer, sys) { + int timer1_count = 0; + int timer2_count = 0; + int timer_running = true; + + uint64_t ms1 = swoole::time(); + + swoole_timer_add( + 20L, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); + + swoole_timer_add( + 100L, + true, + [&](Timer *, TimerNode *tnode) { + timer2_count++; + if (timer2_count == 5) { + swoole_timer_del(tnode); + timer_running = false; + } + }, + nullptr); + + while (1) { + sleep(10); + if (SwooleG.signal_alarm) { + swoole_timer_select(); + if (!timer_running) { + break; + } + } + } + + uint64_t ms2 = swoole::time(); + + swoole_timer_free(); + + ASSERT_LE(ms2 - ms1, 510); + ASSERT_EQ(timer1_count, 1); + ASSERT_EQ(timer2_count, 5); +} + +TEST(timer, async) { + int timer1_count = 0; + int timer2_count = 0; + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + uint64_t ms1 = swoole::time(); + swoole_timer_after( + 20, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); + + swoole_timer_tick( + 100, + [&](Timer *, TimerNode *tnode) { + timer2_count++; + if (timer2_count == 5) { + swoole_timer_del(tnode); + } + }, + nullptr); + + swoole_event_wait(); + uint64_t ms2 = swoole::time(); + ASSERT_LE(ms2 - ms1, 510); + ASSERT_EQ(timer1_count, 1); + ASSERT_EQ(timer2_count, 5); +} + +TEST(timer, exists) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + ASSERT_TRUE(swoole_timer_exists(timer_id)); +} + +TEST(timer, clear) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + swoole_timer_clear(timer_id); + ASSERT_FALSE(swoole_timer_exists(timer_id)); +} + +TEST(timer, get) { + long timer_id = swoole_timer_tick( + 100, [&](Timer *, TimerNode *tnode) {}, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + ASSERT_EQ(timerNode->id, timer_id); +} + +TEST(timer, delay) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + uint64_t ms1 = swoole::time(); + uint64_t ms2 = 0; + long timer_id = swoole_timer_after( + 100, [&](Timer *, TimerNode *tnode) { ms2 = swoole::time(); }, nullptr); + + TimerNode *timerNode = swoole_timer_get(timer_id); + swoole_timer_delay(timerNode, 100); + swoole_event_wait(); + ASSERT_GE(ms2 - ms1, 100); +} + +TEST(timer, error) { + Timer *tmp = SwooleTG.timer; + SwooleTG.timer = nullptr; + + swoole_timer_free(); + ASSERT_EQ(swoole_timer_select(), SW_ERR); + ASSERT_EQ(swoole_timer_get(1), nullptr); + ASSERT_FALSE(swoole_timer_clear(1)); + ASSERT_FALSE(swoole_timer_exists(1)); + + long timer_id = swoole_timer_tick( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + timer_id = swoole_timer_after( + 0, [&](Timer *, TimerNode *tnode) {}, nullptr); + ASSERT_EQ(timer_id, SW_ERR); + + swoole_timer_delay(nullptr, 100); + ASSERT_FALSE(swoole_timer_del(nullptr)); + SwooleTG.timer = tmp; +} diff --git a/core-tests/src/os/wait.cpp b/core-tests/src/os/wait.cpp index 50bbc1c27f7..84e94689b8a 100644 --- a/core-tests/src/os/wait.cpp +++ b/core-tests/src/os/wait.cpp @@ -1,23 +1,35 @@ -#include "tests.h" -#include "coroutine_c_api.h" +#include "test_coroutine.h" using namespace swoole; +using namespace swoole::test; +using swoole::coroutine::System; -TEST(os_wait, waitpid_before_child_exit) -{ - coro_test([](void *arg) - { - swoole_coroutine_signal_init(); +static pid_t fork_child() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); - pid_t pid = fork(); - ASSERT_NE(pid, -1); + if (pid == 0) { + usleep(100000); + exit(0); + } + return pid; +} - if (pid == 0) - { - usleep(100000); - exit(0); - } +static pid_t fork_child2() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); + + if (pid == 0) { + exit(0); + } + usleep(100000); + return pid; +} + +TEST(os_wait, waitpid_before_child_exit) { + test::coroutine::run([](void *arg) { + auto pid = fork_child(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -25,21 +37,9 @@ TEST(os_wait, waitpid_before_child_exit) }); } -TEST(os_wait, waitpid_after_child_exit) -{ - coro_test([](void *arg) - { - swoole_coroutine_signal_init(); - - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - exit(0); - } - - usleep(100000); +TEST(os_wait, waitpid_after_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -47,29 +47,15 @@ TEST(os_wait, waitpid_after_child_exit) }); } -TEST(os_wait, wait_before_child_exit) -{ - coro_test([](void *arg) - { - swoole_coroutine_signal_init(); - - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - usleep(100000); - exit(0); - } - +TEST(os_wait, wait_before_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child(); int status = -1; pid_t pid2 = -1; - for (;;) - { + for (;;) { pid2 = swoole_coroutine_wait(&status); - if (pid2 == pid) - { + if (pid2 == pid) { break; } } @@ -78,29 +64,15 @@ TEST(os_wait, wait_before_child_exit) }); } -TEST(os_wait, wait_after_child_exit) -{ - coro_test([](void *arg) - { - swoole_coroutine_signal_init(); - - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - exit(0); - } - - usleep(100000); +TEST(os_wait, wait_after_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = -1; - for (;;) - { + for (;;) { pid2 = swoole_coroutine_wait(&status); - if (pid2 == pid) - { + if (pid2 == pid) { break; } } @@ -108,3 +80,14 @@ TEST(os_wait, wait_after_child_exit) ASSERT_EQ(WEXITSTATUS(status), 0); }); } + +TEST(os_wait, waitpid_safe) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); + int status = -1; + + pid_t pid2 = System::waitpid_safe(pid, &status, 0); + ASSERT_EQ(pid2, pid); + ASSERT_EQ(WEXITSTATUS(status), 0); + }); +} diff --git a/core-tests/src/pipe.cpp b/core-tests/src/pipe.cpp deleted file mode 100644 index 172640efa76..00000000000 --- a/core-tests/src/pipe.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "tests.h" - -TEST(pipe, unixsock) -{ - swPipe p; - char buf[1024]; - bzero(&p, sizeof(p)); - int ret = swPipeUnsock_create(&p, 1, SOCK_DGRAM); - ASSERT_EQ(ret, 0); - - ret = p.write(&p, (void*) SW_STRS("hello world1")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void*) SW_STRS("hello world2")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void*) SW_STRS("hello world3")); - ASSERT_GT(ret, 0); - - //1 - ret = p.read(&p, buf, sizeof(buf)); - if (ret < 0) - { - swSysWarn("read() failed."); - } - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world1", buf), 0); - //2 - ret = p.read(&p, buf, sizeof(buf)); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world2", buf), 0); - //3 - ret = p.read(&p, buf, sizeof(buf)); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world3", buf), 0); -} - -TEST(pipe, base) -{ - swPipe p; - int ret; - char data[256]; - - ret = swPipeBase_create(&p, 1); - ASSERT_EQ(ret, 0); - ret = p.write(&p, (void *) SW_STRL("hello world\n")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void *) SW_STRL("你好中国。\n")); - ASSERT_GT(ret, 0); - - bzero(data, 256); - ret = p.read(&p, data, 255); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world\n你好中国。\n", data), 0); -} diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp new file mode 100644 index 00000000000..d815e87b551 --- /dev/null +++ b/core-tests/src/protocol/base.cpp @@ -0,0 +1,87 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_coroutine.h" +#include "redis_client.h" +#include "swoole_redis.h" + +using namespace swoole; +using namespace std; + +constexpr int PKG_N = 32; +constexpr int MAX_SIZE = 128000; +constexpr int MIN_SIZE = 512; + +TEST(protocol, eof) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + String pkgs[PKG_N]; + + for (int i = 0; i < PKG_N; i++) { + pkgs[i].append_random_bytes(swoole_rand(MIN_SIZE, MAX_SIZE), true); + pkgs[i].append("\r\n"); + } + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + port->set_eof_protocol("\r\n", false); + + mutex lock; + lock.lock(); + serv.create(); + + thread t1([&]() { + lock.lock(); + + network::Client cli(SW_SOCK_TCP, false); + EXPECT_EQ(cli.connect(&cli, TEST_HOST, port->port, 1, 0), 0); + + for (int i = 0; i < PKG_N; i++) { + EXPECT_EQ(cli.send(&cli, pkgs[i].str, pkgs[i].length, 0), pkgs[i].length); + } + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + int recv_count = 0; + + serv.onReceive = [&](Server *serv, RecvData *req) -> int { + // printf("[1]LEN=%d, count=%d\n%s\n---------------------------------\n", req->info.len, recv_count, + // req->data); printf("[2]LEN=%d\n%s\n---------------------------------\n", pkgs[recv_count].length, + // pkgs[recv_count].str); + + EXPECT_EQ(memcmp(req->data, pkgs[recv_count].str, req->info.len), 0); + + recv_count++; + + if (recv_count == PKG_N) { + kill(serv->get_master_pid(), SIGTERM); + } + + return SW_OK; + }; + + serv.start(); + + t1.join(); +} diff --git a/core-tests/src/protocol/base64.cpp b/core-tests/src/protocol/base64.cpp new file mode 100644 index 00000000000..3328a3e66b1 --- /dev/null +++ b/core-tests/src/protocol/base64.cpp @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_base64.h" + +TEST(base64, encode) { + char inbuf[1024]; + char outbuf[2048]; + + auto n = swoole_random_bytes(inbuf, sizeof(inbuf) - 1); + auto n2 = swoole::base64_encode((uchar *) inbuf, n, outbuf); + ASSERT_GT(n2, n); +} + +TEST(base64, decode) { + const char *inbuf = "aGVsbG8gd29ybGQ="; + char outbuf[2048]; + + auto n2 = swoole::base64_decode(inbuf, strlen(inbuf), outbuf); + ASSERT_EQ(std::string(outbuf, n2), "hello world"); +} diff --git a/core-tests/src/protocol/http2.cpp b/core-tests/src/protocol/http2.cpp new file mode 100644 index 00000000000..e1efda249fe --- /dev/null +++ b/core-tests/src/protocol/http2.cpp @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_coroutine.h" +#include "redis_client.h" +#include "swoole_http2.h" + +using namespace swoole; +using namespace std; + +const std::string REDIS_TEST_KEY = "key-swoole"; +const std::string REDIS_TEST_VALUE = "value-swoole"; + +TEST(http2, default_settings) { + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), SW_HTTP2_DEFAULT_ENABLE_PUSH); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), + SW_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), SW_HTTP2_DEFAULT_INIT_WINDOW_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), SW_HTTP2_DEFAULT_MAX_FRAME_SIZE); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + + http2::Settings _settings = { + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + (uint32_t) swoole_rand(1, 100000), + }; + + http2::put_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE, _settings.header_table_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH, _settings.enable_push); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, _settings.max_concurrent_streams); + http2::put_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE, _settings.init_window_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE, _settings.max_frame_size); + http2::put_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, _settings.max_header_list_size); + + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTING_HEADER_TABLE_SIZE), _settings.header_table_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_ENABLE_PUSH), _settings.enable_push); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS), _settings.max_concurrent_streams); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE), _settings.init_window_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_FRAME_SIZE), _settings.max_frame_size); + ASSERT_EQ(http2::get_default_setting(SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE), _settings.max_header_list_size); +} diff --git a/core-tests/src/protocol/mime_type.cpp b/core-tests/src/protocol/mime_type.cpp new file mode 100644 index 00000000000..eabd700f828 --- /dev/null +++ b/core-tests/src/protocol/mime_type.cpp @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_mime_type.h" + +using namespace swoole; + +TEST(mime_type, get) { + auto result = mime_type::get("test.html.json"); + ASSERT_EQ(result, "application/json"); +} + +TEST(mime_type, exists) { + ASSERT_TRUE(mime_type::exists("test.html.json")); +} + +TEST(mime_type, set) { + std::string test_mime_type("application/swoole-core-test"); + mime_type::set("swoole_test", test_mime_type); + + auto result = mime_type::get("test.swoole_test"); + ASSERT_EQ(result, test_mime_type); +} + +TEST(mime_type, add) { + std::string test_mime_type("application/swoole-core-test2"); + ASSERT_TRUE(mime_type::add("swoole_test2", test_mime_type)); + ASSERT_FALSE(mime_type::add("swoole_test2", test_mime_type)); + + auto result = mime_type::get("test.swoole_test2"); + ASSERT_EQ(result, test_mime_type); +} + +TEST(mime_type, del) { + ASSERT_TRUE(mime_type::del("json")); + ASSERT_FALSE(mime_type::exists("test.html.json")); +} diff --git a/core-tests/src/protocol/redis.cpp b/core-tests/src/protocol/redis.cpp new file mode 100644 index 00000000000..0ab9f144480 --- /dev/null +++ b/core-tests/src/protocol/redis.cpp @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "test_coroutine.h" +#include "redis_client.h" +#include "swoole_redis.h" + +using namespace swoole; +using namespace std; + +const std::string REDIS_TEST_KEY = "key-swoole"; +const std::string REDIS_TEST_VALUE = "value-swoole"; + +TEST(redis, get) { + test::coroutine::run([](void *arg) { + RedisClient redis; + ASSERT_TRUE(redis.Connect("127.0.0.1", 6379)); + ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE)); + ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE); + }); +} + +TEST(redis, server) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.enable_static_handler = true; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + port->open_redis_protocol = true; + + serv.create(); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id != 0) { + return; + } + swoole::Coroutine::create( + [](void *arg) { + Server *serv = reinterpret_cast(arg); + RedisClient redis; + ASSERT_TRUE(redis.Connect("127.0.0.1", serv->get_primary_port()->port)); + ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE)); + ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE); + kill(serv->gs->master_pid, SIGTERM); + }, + serv); + }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + int session_id = req->info.fd; + auto list = redis::parse(req->data, req->info.len); + + String *buffer = sw_tg_buffer(); + buffer->clear(); + + if (strcasecmp(list[0].c_str(), "GET") == 0) { + redis::format(buffer, redis::REPLY_STRING, REDIS_TEST_VALUE); + serv->send(session_id, buffer->str, buffer->length); + } else if (strcasecmp(list[0].c_str(), "SET") == 0) { + redis::format(buffer, redis::REPLY_STATUS, "OK"); + serv->send(session_id, buffer->str, buffer->length); + } + + return SW_OK; + }; + + serv.start(); +} diff --git a/core-tests/src/rbtree.cpp b/core-tests/src/rbtree.cpp deleted file mode 100644 index 12aa92bab64..00000000000 --- a/core-tests/src/rbtree.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "tests.h" -#include "rbtree.h" -#include - -TEST(rbtree, insert) -{ - swRbtree *tree = swRbtree_new(); - int i; - std::set lists; - - for (i = 1; i < 20000; i++) - { - uint32_t key = i * 37; - swRbtree_insert(tree, key, (void *) (long) (i * 8)); - } - - for (i = 1; i < 1024; i++) - { - uint32_t key = ((rand() % 19999) + 1) * 37; - int ret = (int) (long) swRbtree_find(tree, key); - ASSERT_GT(ret, 0); - lists.insert(key); - } - - for (i = 1; i < 1024; i++) - { - uint32_t key = (rand() % (20000 * 37)); - if (key % 37 == 0) - { - continue; - } - int ret = (int) (long) swRbtree_find(tree, key); - ASSERT_EQ(ret, 0); - } - - for (auto i = lists.begin(); i != lists.end(); i++) - { - int ret = swRbtree_delete(tree, *i); - ASSERT_EQ(ret, 0); - } - - for (auto i = lists.begin(); i != lists.end(); i++) - { - int ret = (int) (long) swRbtree_find(tree, *i); - ASSERT_EQ(ret, 0); - } -} diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp new file mode 100644 index 00000000000..aa018aee9d7 --- /dev/null +++ b/core-tests/src/reactor/base.cpp @@ -0,0 +1,375 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_pipe.h" +#include "swoole_util.h" + +using namespace std; +using namespace swoole; + +TEST(reactor, create) { + swoole_event_init(0); + + Reactor *reactor = SwooleTG.reactor; + + ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS); + + ASSERT_TRUE(reactor->running); + ASSERT_NE(reactor->write, nullptr); + ASSERT_NE(reactor->close, nullptr); + ASSERT_EQ(reactor->defer_tasks, nullptr); + ASSERT_NE(reactor->default_write_handler, nullptr); + + /** + * coroutine socket reactor + */ + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_SOCKET | SW_EVENT_ERROR)], nullptr); + + /** + * system reactor + */ + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_POLL | SW_EVENT_ERROR)], nullptr); + + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_READ)], nullptr); + ASSERT_NE(reactor->write_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_WRITE)], nullptr); + ASSERT_NE(reactor->error_handler[Reactor::get_fd_type(SW_FD_CO_EVENT | SW_EVENT_ERROR)], nullptr); + + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_AIO | SW_EVENT_READ)], nullptr); + + swoole_event_free(); +} + +TEST(reactor, set_handler) { + Reactor reactor; + + reactor.set_handler(SW_EVENT_READ, (ReactorHandler) 0x1); + ASSERT_EQ(reactor.read_handler[Reactor::get_fd_type(SW_EVENT_READ)], (ReactorHandler) 0x1); + + reactor.set_handler(SW_EVENT_WRITE, (ReactorHandler) 0x2); + ASSERT_EQ(reactor.write_handler[Reactor::get_fd_type(SW_EVENT_WRITE)], (ReactorHandler) 0x2); + + reactor.set_handler(SW_EVENT_ERROR, (ReactorHandler) 0x3); + ASSERT_EQ(reactor.error_handler[Reactor::get_fd_type(SW_EVENT_ERROR)], (ReactorHandler) 0x3); +} + +TEST(reactor, wait) { + int ret; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + char buffer[16]; + + ssize_t n = read(ev->fd, buffer, sizeof(buffer)); + EXPECT_EQ(sizeof("hello world"), n); + EXPECT_STREQ("hello world", buffer); + reactor->del(ev->socket); + + return SW_OK; + }); + + ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(swoole_event_get_socket(p.get_socket(false)->get_fd()), p.get_socket(false)); + ASSERT_EQ(ret, SW_OK); + + ret = p.write((void *) SW_STRS("hello world")); + ASSERT_EQ(ret, sizeof("hello world")); + + ret = swoole_event_wait(); + ASSERT_EQ(ret, SW_OK); + ASSERT_EQ(SwooleTG.reactor, nullptr); +} + +TEST(reactor, write) { + int ret; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + char buffer[16]; + + ssize_t n = read(ev->fd, buffer, sizeof(buffer)); + EXPECT_EQ(sizeof("hello world"), n); + EXPECT_STREQ("hello world", buffer); + reactor->del(ev->socket); + + return SW_OK; + }); + + ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(ret, SW_OK); + + auto sock = p.get_socket(true); + + auto n = swoole_event_write(sock, (void *) SW_STRS("hello world")); + ASSERT_EQ(n, sizeof("hello world")); + + ret = swoole_event_wait(); + ASSERT_EQ(ret, SW_OK); + ASSERT_EQ(SwooleTG.reactor, nullptr); +} + +constexpr int DATA_SIZE = 2 * SW_NUM_MILLION; + +TEST(reactor, write_2m) { + int ret; + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + auto tg_buf = sw_tg_buffer(); + ssize_t n = read(ev->fd, tg_buf->str + tg_buf->length, tg_buf->size - tg_buf->length); + if (n <= 0) { + return SW_ERR; + } + tg_buf->grow(n); + if (tg_buf->length == DATA_SIZE) { + tg_buf->append('\0'); + reactor->del(ev->socket); + } + return SW_OK; + }); + + p.set_blocking(false); + p.set_buffer_size(65536); + + ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(ret, SW_OK); + + String str(DATA_SIZE); + str.append_random_bytes(str.size - 1, false); + str.append('\0'); + + sw_tg_buffer()->clear(); + + auto n = swoole_event_write(p.get_socket(true), str.value(), str.get_length()); + ASSERT_EQ(n, str.get_length()); + ASSERT_GT(p.get_socket(true)->out_buffer->length(), 1024); + + ret = swoole_event_wait(); + ASSERT_EQ(ret, SW_OK); + ASSERT_FALSE(swoole_event_is_available()); + ASSERT_STREQ(sw_tg_buffer()->value(), str.value()); +} + +TEST(reactor, bad_fd) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + auto sock = make_socket(999999, SW_FD_STREAM_CLIENT); + sock->nonblock = 1; + auto n = swoole_event_write(sock, SW_STRL("hello world")); + ASSERT_EQ(n, SW_ERR); + ASSERT_EQ(swoole_get_last_error(), EBADF); + swoole_event_free(); + sock->move_fd(); + sock->free(); +} + +static const char *pkt = "hello world\r\n"; + +static void reactor_test_func(Reactor *reactor) { + Pipe p(true); + ASSERT_TRUE(p.ready()); + + reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *event) -> int { + char buf[1024]; + size_t l = strlen(pkt); + size_t n = read(event->fd, buf, sizeof(buf)); + EXPECT_EQ(n, l); + buf[n] = 0; + EXPECT_EQ(std::string(buf, n), std::string(pkt)); + reactor->del(event->socket); + + return SW_OK; + }); + reactor->set_handler(SW_FD_PIPE | SW_EVENT_WRITE, [](Reactor *reactor, Event *event) -> int { + size_t l = strlen(pkt); + EXPECT_EQ(write(event->fd, pkt, l), l); + reactor->del(event->socket); + + return SW_OK; + }); + reactor->add(p.get_socket(false), SW_EVENT_READ); + reactor->add(p.get_socket(true), SW_EVENT_WRITE); + reactor->wait(nullptr); +} + +TEST(reactor, poll) { + Reactor reactor(1024, Reactor::TYPE_POLL); + reactor.wait_exit = true; + reactor_test_func(&reactor); +} + +TEST(reactor, select) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor_test_func(&reactor); +} + +TEST(reactor, add_or_update) { + int ret; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(ret, SW_OK); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ); + + ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_WRITE); + ASSERT_EQ(ret, SW_OK); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_WRITE); + + swoole_event_free(); +} + +TEST(reactor, defer_task) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Reactor *reactor = sw_reactor(); + ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS); + + int count = 0; + reactor->defer([&count](void *) { count++; }); + swoole_event_wait(); + ASSERT_EQ(count, 1); + swoole_event_free(); +} + +static void event_end_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, cycle) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.future_task.callback = event_end_callback; + reactor.future_task.data = (void *) test; + reactor_test_func(&reactor); +} + +static void event_idle_callback(void *data) { + ASSERT_STREQ((char *) data, "hello world"); +} + +TEST(reactor, priority_idle_task) { + Reactor reactor(1024, Reactor::TYPE_SELECT); + reactor.wait_exit = true; + reactor.activate_future_task(); + + const char *test = "hello world"; + reactor.idle_task.callback = event_idle_callback; + reactor.idle_task.data = (void *) test; + reactor_test_func(&reactor); +} + +TEST(reactor, hook) { + Reactor *reactor = new Reactor(1024, Reactor::TYPE_SELECT); + reactor->wait_exit = true; + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_CREATE, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + swoole_add_hook( + SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, + [](void *data) -> void { + Reactor *reactor = (Reactor *) data; + ASSERT_EQ(Reactor::TYPE_SELECT, reactor->type_); + }, + 1); + + ON_SCOPE_EXIT { + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE] = nullptr; + SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_DESTROY] = nullptr; + }; + + reactor_test_func(reactor); + delete reactor; +} + +TEST(reactor, set_fd) { + UnixSocket p(true, SOCK_DGRAM); + Reactor *reactor = new Reactor(1024, Reactor::TYPE_EPOLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_POLL); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; + + reactor = new Reactor(1024, Reactor::TYPE_SELECT); + ASSERT_EQ(reactor->add(p.get_socket(false), SW_EVENT_READ), SW_OK); + ASSERT_EQ(reactor->set(p.get_socket(false), SW_EVENT_WRITE), SW_OK); + delete reactor; +} + +static void error_event(Reactor::Type type) { + Pipe p(true); + ASSERT_TRUE(p.ready()); + + Reactor *reactor = new Reactor(1024, type); + + reactor->set_handler(SW_FD_PIPE | SW_EVENT_ERROR, [](Reactor *reactor, Event *event) -> int { + EXPECT_EQ(reactor->del(event->socket), SW_OK); + reactor->running = false; + return SW_OK; + }); + + reactor->add(p.get_socket(true), SW_EVENT_ERROR); + reactor->add(p.get_socket(false), SW_EVENT_ERROR); + + p.close(SW_PIPE_CLOSE_WORKER); + reactor->wait(nullptr); + delete reactor; +} +TEST(reactor, error_ev) { + error_event(Reactor::TYPE_EPOLL); + error_event(Reactor::TYPE_POLL); +} diff --git a/core-tests/src/ringbuffer.cpp b/core-tests/src/ringbuffer.cpp deleted file mode 100644 index 8f2c5f34217..00000000000 --- a/core-tests/src/ringbuffer.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "tests.h" - -#include - -#define READ_THREAD_N 4 -#define WRITE_N 10000 -#define PACKET_LEN 90000 -//#define PRINT_SERNUM_N 10 - -static swMemoryPool *pool = NULL; - -typedef struct -{ - uint32_t id; - uint32_t size; - uint32_t serial_num; - void* ptr; -} pkg; - -typedef struct -{ - std::thread *thread; - swPipe pipe; -} ThreadObject; - -static void thread_read(int i); -static void thread_write(void); -static ThreadObject threads[READ_THREAD_N]; - -TEST(ringbuffer, thread) -{ - int i; - pool = swRingBuffer_new(1024 * 1024 * 4, 1); - ASSERT_NE(pool, nullptr); - - for (i = 0; i < READ_THREAD_N; i++) - { - int ret = swPipeUnsock_create(&threads[i].pipe, 1, SOCK_DGRAM); - ASSERT_EQ(ret, 0); - threads[i].thread = new std::thread(thread_read, i); - } - - sleep(1); - srand((unsigned int) time(NULL)); - thread_write(); - - for (i = 0; i < READ_THREAD_N; i++) - { - threads[i].thread->join(); - threads[i].pipe.close(&threads[i].pipe); - delete threads[i].thread; - } -} - -static void thread_write(void) -{ - uint32_t size, yield_count = 0, yield_total_count = 0; - void *ptr; - pkg send_pkg; - bzero(&send_pkg, sizeof(send_pkg)); - - int i; - for (i = 0; i < WRITE_N; i++) - { - size = 10000 + rand() % PACKET_LEN; - //printf("[ < %d] alloc size=%d\n", i, size); - - yield_count = 0; - do - { - ptr = pool->alloc(pool, size); - if (ptr) - { - break; - } - else - { - yield_count++; - yield_total_count++; - usleep(10); - } - } while (yield_count < 100); - - if (!ptr) - { - printf("alloc failed. index=%d, break\n", i); - break; - } - - ASSERT_NE(ptr, nullptr); - - send_pkg.id = i; - send_pkg.ptr = ptr; - send_pkg.size = size; - send_pkg.serial_num = rand(); - - //保存长度值 - memcpy(ptr, &size, sizeof(size)); - //在指针末尾保存一个串号 - memcpy((char*) ptr + size - 4, &(send_pkg.serial_num), sizeof(send_pkg.serial_num)); - - ASSERT_FALSE(threads[i % READ_THREAD_N].pipe.write(&threads[i % READ_THREAD_N].pipe, &send_pkg, sizeof(send_pkg)) < 0); - } - -// printf("yield_total_count=%d\n", yield_total_count); -} - -static void thread_read(int i) -{ - pkg recv_pkg; - uint32_t tmp; - int ret; - uint32_t recv_count = 0; - int j = 0; - swPipe *sock = &threads[i].pipe; - int task_n = WRITE_N / READ_THREAD_N; - - for (j = 0; j < task_n; j++) - { - ret = sock->read(sock, &recv_pkg, sizeof(recv_pkg)); - ASSERT_FALSE(ret < 0); - - memcpy(&tmp, recv_pkg.ptr, sizeof(tmp)); - ASSERT_EQ(tmp, recv_pkg.size); - - memcpy(&tmp, (char*) recv_pkg.ptr + recv_pkg.size - 4, sizeof(tmp)); - ASSERT_EQ(tmp, recv_pkg.serial_num); - -#ifdef PRINT_SERNUM_N - if (j % PRINT_SERNUM_N == 0) - { - printf("[ > %d] recv. recv_count=%d, serial_num=%d\n", recv_pkg.id, recv_count, tmp); - } -#endif - - pool->free(pool, recv_pkg.ptr); - recv_count++; - } -} diff --git a/core-tests/src/server.cpp b/core-tests/src/server.cpp deleted file mode 100755 index 8d7589071ab..00000000000 --- a/core-tests/src/server.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "swoole.h" -#include "server.h" - -namespace swoole_test -{ -static int my_onPacket(swServer *serv, swEventData *req); -static int my_onReceive(swServer *serv, swEventData *req); -static void my_onStart(swServer *serv); -static void my_onShutdown(swServer *serv); -static void my_onConnect(swServer *serv, swDataHead *info); -static void my_onClose(swServer *serv, swDataHead *info); -static void my_onWorkerStart(swServer *serv, int worker_id); -static void my_onWorkerStop(swServer *serv, int worker_id); - -static int g_receive_count = 0; - -int server_test() -{ - int ret; - swServer serv; - swServer_init(&serv); - - serv.reactor_num = 4; - serv.worker_num = 2; - - serv.factory_mode = SW_MODE_BASE; - //serv.factory_mode = SW_MODE_SINGLE; //SW_MODE_PROCESS/SW_MODE_THREAD/SW_MODE_BASE/SW_MODE_SINGLE - serv.max_connection = 10000; - //serv.open_cpu_affinity = 1; - //serv.open_tcp_nodelay = 1; - //serv.daemonize = 1; -// memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); //日志 - - serv.dispatch_mode = 2; -// serv.open_tcp_keepalive = 1; - -#ifdef HAVE_OPENSSL - //serv.ssl_cert_file = "tests/ssl/ssl.crt"; - //serv.ssl_key_file = "tests/ssl/ssl.key"; - //serv.open_ssl = 1; -#endif - - serv.onStart = my_onStart; - serv.onShutdown = my_onShutdown; - serv.onConnect = my_onConnect; - serv.onReceive = my_onReceive; - serv.onPacket = my_onPacket; - serv.onClose = my_onClose; - serv.onWorkerStart = my_onWorkerStart; - serv.onWorkerStop = my_onWorkerStop; - -// swSignal_add(SIGINT, user_signal); - - //create Server - ret = swServer_create(&serv); - if (ret < 0) - { - swTrace("create server fail[error=%d].\n", ret); - exit(0); - } - - swListenPort *port = swServer_add_port(&serv, SW_SOCK_TCP, "127.0.0.1", 9501); - port->open_eof_check = 0; - //config - port->backlog = 128; - memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); //开启eof检测,启用buffer区 - - swServer_add_port(&serv, SW_SOCK_UDP, "0.0.0.0", 9502); - swServer_add_port(&serv, SW_SOCK_TCP6, "::", 9503); - swServer_add_port(&serv, SW_SOCK_UDP6, "::", 9504); - - ret = swServer_start(&serv); - if (ret < 0) - { - swTrace("start server fail[error=%d].\n", ret); - exit(0); - } - return 0; -} - -void my_onWorkerStart(swServer *serv, int worker_id) -{ - printf("WorkerStart[%d]PID=%d\n", worker_id, getpid()); -} - -void my_onWorkerStop(swServer *serv, int worker_id) -{ - printf("WorkerStop[%d]PID=%d\n", worker_id, getpid()); -} - -int my_onReceive(swServer *serv, swEventData *req) -{ - int ret; - char resp_data[SW_IPC_BUFFER_SIZE]; - - g_receive_count++; - - swConnection *conn = swWorker_get_connection(serv, req->info.fd); - swoole_rtrim(req->data, req->info.len); - printf("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d\n", g_receive_count, - swConnection_get_ip(conn->socket_type, &conn->info), - swConnection_get_port(conn->socket_type, &conn->info), - req->data, req->info.len); - - int n = snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", req->info.len, req->data); - ret = serv->send(serv, req->info.fd, resp_data, n); - if (ret < 0) - { - printf("send to client fail. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } - return SW_OK; -} - -int my_onPacket(swServer *serv, swEventData *req) -{ - int serv_sock = req->info.server_fd; - char *data; - swWorker_get_data(serv, req, &data); - swDgramPacket *packet = (swDgramPacket *) data; - - int length; - char address[256]; - int port = 0; - int ret; - - if (packet->socket_type == SW_SOCK_UDP) - { - inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); - data = packet->data; - length = packet->length; - port = ntohs(packet->socket_addr.addr.inet_v4.sin_port); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address)); - data = packet->data; - length = packet->length; - port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - strcpy(address, packet->socket_addr.addr.un.sun_path); - data = packet->data; - length = packet->length; - } - - printf("Packet[client=%s:%d, %d bytes]: data=%.*s\n", address, port, length, length, data); - - char resp_data[SW_IPC_BUFFER_SIZE]; - int n = snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s", length, data); - - if (packet->socket_type == SW_SOCK_UDP) - { - ret = swSocket_udp_sendto(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - ret = swSocket_udp_sendto6(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - ret = swSocket_unix_sendto(serv_sock, address, resp_data, n); - } - - if (ret < 0) - { - printf("send to client fail. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } - - return SW_OK; -} - -void my_onStart(swServer *serv) -{ - swNotice("Server is running"); -} - -void my_onShutdown(swServer *serv) -{ - swNotice("Server is shutdown"); -} - -void my_onConnect(swServer *serv, swDataHead *info) -{ - printf("PID=%d\tConnect fd=%d|reactor_id=%d\n", getpid(), info->fd, info->reactor_id); -} - -void my_onClose(swServer *serv, swDataHead *info) -{ - printf("PID=%d\tClose fd=%d|reactor_id=%d\n", getpid(), info->fd, info->reactor_id); -} - -} diff --git a/core-tests/src/server/buffer.cpp b/core-tests/src/server/buffer.cpp new file mode 100644 index 00000000000..9c0df8f346f --- /dev/null +++ b/core-tests/src/server/buffer.cpp @@ -0,0 +1,83 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" +#include "swoole_server.h" + +using namespace std; +using namespace swoole; + +static const char *packet = "hello world\n"; + +TEST(server, send_buffer) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + ASSERT_EQ(serv.create(), SW_OK); + + mutex lock; + lock.lock(); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + swoole::network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[4096]; + + while (1) { + ssize_t retval = c.recv(buf, sizeof(buf)); + if (retval <= 0) { + break; + } + usleep(100); + } + + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + swString resp(1024 * 1024 * 16); + resp.repeat("A", 1, resp.capacity()); + EXPECT_TRUE(serv->send(req->info.fd, resp.value(), resp.get_length())); + EXPECT_TRUE(serv->close(req->info.fd, 0)); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp new file mode 100644 index 00000000000..1fc7058478e --- /dev/null +++ b/core-tests/src/server/http.cpp @@ -0,0 +1,1242 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "httplib_client.h" +#include "llhttp.h" +#include "swoole_server.h" +#include "swoole_file.h" +#include "swoole_http.h" +#include "swoole_util.h" + +using namespace swoole; +using namespace std; +using http_server::Context; +using network::Client; +using network::SyncClient; + +SessionId session_id = 0; +Connection *conn = nullptr; +Session *session = nullptr; + +struct http_context { + unordered_map headers; + unordered_map response_headers; + string url; + string current_key; + Server *server; + int fd; + bool completed; + + void setHeader(string key, string value) { + response_headers[key] = value; + } + + void response(enum swHttpStatusCode code, string body) { + response_headers["Content-Length"] = to_string(body.length()); + response(code); + server->send(fd, body.c_str(), body.length()); + } + + void response(int code) { + String *buf = make_string(1024); + buf->length = sw_snprintf(buf->str, buf->size, "HTTP/1.1 %s\r\n", http_server::get_status_message(code)); + for (auto &kv : response_headers) { + buf->append(kv.first.c_str(), kv.first.length()); + buf->append(SW_STRL(": ")); + buf->append(kv.second.c_str(), kv.second.length()); + buf->append(SW_STRL("\r\n")); + } + buf->append(SW_STRL("\r\n")); + server->send(fd, buf->str, buf->length); + delete buf; + } +}; + +static int handle_on_message_complete(llhttp_t *parser) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->completed = true; + return 0; +} + +static int handle_on_header_field(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->current_key = string(at, length); + return 0; +} + +static int handle_on_header_value(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->headers[ctx->current_key] = string(at, length); + return 0; +} + +static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->url = std::string(at, length); + return 0; +} + +static void test_base_server(function fn) { + thread child_thread; + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.enable_reuse_port = true; + serv.heartbeat_check_interval = 1; + serv.private_data_2 = (void *) &fn; + + serv.enable_static_handler = true; + serv.set_document_root(test::get_root_path()); + serv.add_static_handler_location("/examples"); + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + + serv.create(); + + serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) { + function fn = *(function *) serv->private_data_2; + child_thread = thread(fn, serv); + }; + + serv.onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + EXPECT_EQ(info->reactor_id, -1); + } else { + EXPECT_GE(info->reactor_id, 0); + } + } + }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); + + if (conn->websocket_status == websocket::STATUS_ACTIVE) { + sw_tg_buffer()->clear(); + std::string resp = "Swoole: " + string(req->data, req->info.len); + websocket::encode(sw_tg_buffer(), + resp.c_str(), + resp.length(), + websocket::OPCODE_TEXT, + websocket::FLAG_FIN); + serv->send(session_id, sw_tg_buffer()->str, sw_tg_buffer()->length); + return SW_OK; + } + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = serv; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + + if (err == HPE_PAUSED_UPGRADE) { + ctx.setHeader("Connection", "Upgrade"); + ctx.setHeader("Sec-WebSocket-Accept", "IIRiohCjop4iJrmvySrFcwcXpHo="); + ctx.setHeader("Sec-WebSocket-Version", "13"); + ctx.setHeader("Upgrade", "websocket"); + ctx.setHeader("Content-Length", "0"); + + ctx.response(SW_HTTP_SWITCHING_PROTOCOLS); + + conn->websocket_status = websocket::STATUS_ACTIVE; + + return SW_OK; + } + + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + EXPECT_EQ(err, HPE_OK); + + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + serv->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } + + EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); + + return SW_OK; + }; + + serv.start(); + child_thread.join(); +} + +static Server *test_process_server(Server::DispatchMode dispatch_mode = Server::DISPATCH_FDMOD, bool is_ssl = false) { + Server *server = new Server(Server::MODE_PROCESS); + server->user_ = std::string("root"); + server->group_ = std::string("root"); + server->chroot_ = std::string("/"); + server->worker_num = 2; + server->dispatch_mode = dispatch_mode; + server->open_cpu_affinity = true; + sw_logger()->set_level(SW_LOG_WARNING); + + conn = nullptr; + session = nullptr; + ListenPort *port = is_ssl ? server->add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0) + : server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + port->open_tcp_keepalive = 1; + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->create(); + + server->onClose = [](Server *serv, DataHead *info) -> void { + if (conn) { + if (conn->close_actively) { + ASSERT_EQ(info->reactor_id, -1); + } else { + ASSERT_GE(info->reactor_id, 0); + } + } + }; + + server->onReceive = [&](Server *serv, RecvData *req) -> int { + session_id = req->info.fd; + conn = serv->get_connection_by_session_id(session_id); + session = serv->get_session(session_id); + + EXPECT_LE(serv->get_idle_worker_num(), serv->worker_num); + EXPECT_TRUE(serv->is_healthy_connection(microtime(), conn)); + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = serv; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/overflow") { + conn->overflow = 1; + } + + if (ctx.url == "/pause") { + serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV); + } + + EXPECT_EQ(err, HPE_OK); + ctx.response(SW_HTTP_OK, "hello world"); + + return SW_OK; + }; + + return server; +} + +static Server *test_proxy_server() { + Server *server = new Server(Server::MODE_BASE); + server->worker_num = 1; + + ListenPort *port = server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + port->kernel_socket_send_buffer_size = INT_MAX; + port->kernel_socket_recv_buffer_size = INT_MAX; + port->open_tcp_nodelay = true; + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + server->enable_static_handler = true; + server->set_document_root(test::get_root_path()); + server->add_static_handler_location("/examples"); + + server->get_primary_port()->set_package_max_length(64 * 1024); + port->open_http_protocol = 1; + port->open_websocket_protocol = 1; + + server->create(); + + server->onReceive = [&](Server *server, RecvData *req) -> int { + session_id = req->info.fd; + conn = server->get_connection_by_session_id(session_id); + + SwooleG.process_id = server->worker_num; + + llhttp_t parser = {}; + llhttp_settings_t settings = {}; + llhttp_init(&parser, HTTP_REQUEST, &settings); + + http_context ctx = {}; + parser.data = &ctx; + ctx.server = server; + ctx.fd = session_id; + + settings.on_url = handle_on_url; + settings.on_header_field = handle_on_header_field; + settings.on_header_value = handle_on_header_value; + settings.on_message_complete = handle_on_message_complete; + + enum llhttp_errno err = llhttp_execute(&parser, req->data, req->info.len); + + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + + if (ctx.url == "/just/get/file") { + std::string filename = test::get_root_path() + "/examples/test.jpg"; + server->sendfile(session_id, filename.c_str(), filename.length(), 0, 0); + } else { + ctx.response(SW_HTTP_OK, "hello world"); + } + + EXPECT_EQ(err, HPE_OK); + EXPECT_EQ(ctx.headers["User-Agent"], httplib::USER_AGENT); + return SW_OK; + }; + + return server; +} + +TEST(http_server, get) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, heartbeat_check_interval) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + sleep(3); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, not_active) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + + conn->active = 0; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, has_closed) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + cli.Get("/index.html"); + + conn->closed = 1; + cli.set_read_timeout(0, 100); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, idle_time) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + port->max_idle_time = 1; + + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + EXPECT_EQ(resp->status, 200); + + sleep(2); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, post) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + httplib::Params params; + params.emplace("name", "john"); + params.emplace("note", "coder"); + auto resp = cli.Post("/index.html", params); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, static_get) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/examples/test.jpg"); + EXPECT_EQ(resp->status, 200); + + string file = test::get_root_path() + "/examples/test.jpg"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + + auto str = fp.read_content(); + + EXPECT_EQ(resp->body, str->to_std_string()); + + resp = cli.Get("/just/get/file"); + EXPECT_EQ(resp, nullptr); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, static_files) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + std::string::size_type postion = resp->body.find("Index of"); + EXPECT_TRUE(postion != std::string::npos); + + // directory not exists + resp = cli.Get("/test/../"); + EXPECT_EQ(resp->status, 404); + + // must be document_root + resp = cli.Get("//tests/../"); + EXPECT_EQ(resp->status, 200); + + resp = cli.Get("/tests/../README.md"); + EXPECT_EQ(resp->status, 200); + + // file not exists + resp = cli.Get("/not-exists.jpg"); + EXPECT_EQ(resp->status, 404); + + // try again + serv->add_static_handler_index_files("README.md"); + resp = cli.Get("/"); + postion = resp->body.find("

"); + EXPECT_TRUE(postion != std::string::npos); + + kill(getpid(), SIGTERM); + }); +} + +static void request_with_header(const char *date_format, httplib::Client *cli) { + char temp[128] = {0}; + time_t raw_time = time(NULL) + 7 * 24 * 60 * 60; + tm *time_info = gmtime(&raw_time); + + strftime(temp, sizeof(temp), date_format, time_info); + httplib::Headers headers = {{"If-Modified-Since", temp}}; + auto resp = cli->Get("/", headers); + EXPECT_EQ(resp, nullptr); +} + +TEST(http_server, not_modify) { + test_base_server([](Server *serv) { + serv->http_autoindex = true; + serv->add_static_handler_location(""); + + swoole_signal_block_all(); + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + serv->add_static_handler_index_files("swoole-logo.svg"); + auto resp = cli.Get("/"); + EXPECT_EQ(resp->status, 200); + + // 304 not modified + cli.set_read_timeout(0, 100); + request_with_header(SW_HTTP_RFC1123_DATE_GMT, &cli); + request_with_header(SW_HTTP_RFC1123_DATE_UTC, &cli); + request_with_header(SW_HTTP_RFC850_DATE, &cli); + request_with_header(SW_HTTP_ASCTIME_DATE, &cli); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, proxy_file) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + + auto resp = cli.Get("/just/get/file"); + ASSERT_EQ(resp, nullptr); + } +} + +// need fix +TEST(http_server, proxy_response) { + Server *server = test_proxy_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + // ASSERT_EQ(resp->body, string("hello world")); + } +} + +static void websocket_test(int server_port, const char *data, size_t length) { + httplib::Client cli(TEST_HOST, server_port); + + httplib::Headers headers; + EXPECT_TRUE(cli.Upgrade("/websocket", headers)); + EXPECT_TRUE(cli.Push(data, length)); + + auto msg = cli.Recv(); + EXPECT_EQ(string(msg->payload, msg->payload_length), string("Swoole: ") + string(data, length)); +} + +TEST(http_server, websocket_small) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + websocket_test(serv->get_primary_port()->get_port(), SW_STRL("hello world, swoole is best!")); + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, websocket_medium) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + String str(8192); + str.repeat("A", 1, 8192); + websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, websocket_big) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + String str(128 * 1024); + str.repeat("A", 1, str.capacity() - 1); + websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, parser1) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req1.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, parser2) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->get_primary_port()->set_package_max_length(64 * 1024); + server->upload_max_filesize = 1024 * 1024; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req2.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, heartbeat) { + Server *server = test_process_server(); + server->heartbeat_check_interval = 0; + auto port = server->get_primary_port(); + port->set_package_max_length(1024); + port->heartbeat_idle_time = 2; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + sleep(10); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, overflow) { + Server *server = test_process_server(); + auto port = server->get_primary_port(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + resp = cli.Get("/overflow"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, process) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + } +} + +TEST(http_server, process1) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + sleep(1); + resp = cli.Get("/examples/test.jpg"); + ASSERT_EQ(resp->status, 200); + } +} + +TEST(http_server, redundant_callback) { + Server *server = test_process_server(Server::DISPATCH_IDLE_WORKER); + server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onClose = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferEmpty = [](Server *serv, DataHead *info) -> int { return 0; }; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + ASSERT_EQ(server->onConnect, nullptr); + ASSERT_EQ(server->onClose, nullptr); + ASSERT_EQ(server->onBufferFull, nullptr); + ASSERT_EQ(server->onBufferEmpty, nullptr); + exit(0); + } + + if (pid > 0) { + sleep(2); + kill(server->get_master_pid(), SIGTERM); + } +} + +TEST(http_server, pause) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/pause"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, sni) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + SSLContext *context = new SSLContext(); + *context = *port->ssl_context; + context->cert_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_cert.pem"; + context->key_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_key.pem"; + port->ssl_add_sni_cert("localhost", context); + port->ssl_context->protocols = 0; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string command = "curl https://localhost:" + port_num + " -k -vvv --stderr /tmp/wwwsnitestcom.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream wwwsnitestcom; + wwwsnitestcom.open("/tmp/wwwsnitestcom.txt"); + ASSERT_TRUE(wwwsnitestcom.is_open()); + buffer << wwwsnitestcom.rdbuf(); + wwwsnitestcom.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("CN=cs.php.net") != string::npos); + + string command2 = "curl https://127.0.0.1:" + port_num + " -k -vvv --stderr /tmp/wwwsnitest2com.txt"; + swoole_shell_exec(command2.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer2; + ifstream wwwsnitest2com; + wwwsnitest2com.open("/tmp/wwwsnitest2com.txt"); + ASSERT_TRUE(wwwsnitest2com.is_open()); + buffer2 << wwwsnitest2com.rdbuf(); + string response2(buffer2.str()); + wwwsnitest2com.close(); + ASSERT_TRUE(response2.find("CN=127.0.0.1") != string::npos); + } +} + +TEST(http_server, bad_request) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string str_1 = "curl -X UNKNOWN http://"; + string str_2 = ":"; + string str_3 = " -k -vvv --stderr /tmp/bad_request.txt"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port + str_3; + + pid_t pid2; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream bad_request; + bad_request.open("/tmp/bad_request.txt"); + ASSERT_TRUE(bad_request.is_open()); + buffer << bad_request.rdbuf(); + string response(buffer.str()); + bad_request.close(); + ASSERT_TRUE(response.find("400 Bad Request") != string::npos); + } +} + +TEST(http_server, chunked) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, max_queued_bytes) { + Server *server = test_process_server(); + server->max_queued_bytes = 100; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, dispatch_func_return_error_worker_id) { + Server *server = test_process_server(); + server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int { + return data->info.fd % 2 == 0 ? Server::DISPATCH_RESULT_DISCARD_PACKET + : Server::DISPATCH_RESULT_CLOSE_CONNECTION; + }; + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + }; + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_read_timeout(1, 0); + auto resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, client_ca) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-cert.pem"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-key.pem"); + port->ssl_context->verify_peer = true; + port->ssl_context->allow_self_signed = true; + port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string client_cert = " --cert " + test::get_root_path() + "/tests/include/api/ssl-ca/client-cert.pem "; + string client_key = "--key " + test::get_root_path() + "/tests/include/api/ssl-ca/client-key.pem"; + string command = "curl https://127.0.0.1:" + port_num + " " + client_cert + client_key + + " -k -vvv --stderr /tmp/client_ca.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream client_ca; + client_ca.open("/tmp/client_ca.txt"); + ASSERT_TRUE(client_ca.is_open()); + buffer << client_ca.rdbuf(); + client_ca.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("200 OK") != response.npos); + } +} + +static bool request_with_if_range_header(const char *date_format, std::string port) { + struct stat file_stat; + std::string file_path = test::get_root_path() + "/docs/swoole-logo.svg"; + stat(file_path.c_str(), &file_stat); + time_t file_mtime = file_stat.st_mtim.tv_sec; + struct tm *time_info = gmtime(&file_mtime); + + char temp[128] = {0}; + strftime(temp, sizeof(temp), date_format, time_info); + + string str_1 = "curl http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=0-500' -H 'If-Range: "; + string command = str_1 + host + str_2 + port + str_3 + headers + string(temp) + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + sleep(2); + + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos && response.find("Content-Length: 501") != string::npos; +} + +TEST(http_server, http_range) { + Server *server = test_process_server(); + server->http_autoindex = true; + server->add_static_handler_location("/docs"); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port = to_string(server->get_primary_port()->port); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_GMT, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_UTC, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC850_DATE, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_ASCTIME_DATE, port)); + } +} + +static bool request_with_diff_range(std::string port, std::string range) { + string str_1 = "curl -X GET http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=" + range; + string command = str_1 + host + str_2 + port + str_3 + headers + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + + sleep(2); + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos; +} + +TEST(http_server, http_range2) { + Server *server = test_process_server(); + server->add_static_handler_location("/docs"); + server->add_static_handler_index_files("swoole-logo.svg"); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + exit(0); + } + + if (pid == 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-15")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "16-31")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "-16")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "128-")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-0,-1")); + + } +} + +// it is always last test +TEST(http_server, abort_connection) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.max_sockets = 2; + serv.set_max_connection(1); + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + port->open_http_protocol = 1; + serv.create(); + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + + if (worker->id == 0) { + sleep(1); + kill(serv->get_master_pid(), SIGTERM); + } + }; + + serv.onReceive = [&](Server *server, RecvData *req) -> int { return SW_OK; }; + serv.start(); +} diff --git a/core-tests/src/server/http_parser.cpp b/core-tests/src/server/http_parser.cpp new file mode 100644 index 00000000000..f7e3b6e0d95 --- /dev/null +++ b/core-tests/src/server/http_parser.cpp @@ -0,0 +1,575 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "swoole_http_parser.h" + +using namespace std; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; +// clang-format on + +typedef struct { + long fd; + uchar completed : 1; + uchar end_ : 1; + uchar send_header_ : 1; + + uchar send_chunked : 1; + uchar recv_chunked : 1; + uchar send_trailer_ : 1; + uchar keepalive : 1; + uchar websocket : 1; + + uchar upgrade : 1; + uchar detached : 1; + uchar parse_cookie : 1; + uchar parse_body : 1; + uchar parse_files : 1; + uchar co_socket : 1; + uchar http2 : 1; + + swoole_http_parser parser; + + uint16_t input_var_num; + char *current_header_name; + size_t current_header_name_len; + char *current_input_name; + size_t current_input_name_len; + char *current_form_data_name; + size_t current_form_data_name_len; + + vector header_fields; + vector header_values; + string query_string; +} HttpContext; + +static swoole_http_parser *swoole_http_parser_create(swoole_http_parser_type type = PHP_HTTP_REQUEST) { + HttpContext *ctx = new HttpContext(); + swoole_http_parser *parser = &ctx->parser; + swoole_http_parser_init(parser, type); + parser->data = ctx; + return parser; +} + +static void swoole_http_destroy_context(swoole_http_parser *parser) { + delete (HttpContext *) parser->data; + return; +} + +static int swoole_http_parser_method(string protocol) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, protocol.c_str(), protocol.length()); + + int ret = parser->method; + swoole_http_destroy_context(parser); + return ret; +} + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->query_string = string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_fields.push_back(string(at, length)); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_values.push_back(string(at, length)); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_message_complete(swoole_http_parser *parser) { + return 0; +} + +static const string request_get = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http2 = "GET /get HTTP/2\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http09 = "GET /index.html\r\n"; + +static const string request_head = "HEAD /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string = "GET /get?a=foo&b=bar&c=456%26789#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string2 = "GET /get? HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string3 = "GET /index.html?a=123\r"; + +static const string request_get_with_query_string4 = "GET /index.html?a=123\n"; + +static const string request_get_with_query_string5 = "GET /get#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_schema = "GET http://127.0.0.1:8081/get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_proxy_connection = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_connection_close = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: close\r\n" + "\r\n"; + +static const string request_get_http10 = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "\r\n"; + +static const string request_get_http10_with_keep_alive = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_post = "POST /api/build/v1/foo HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: 7\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string request_upgrade = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "upgrade: websocket\r\n" + "User-Agent: curl/7.64.1\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + +static const string request_dead = "POST /dead HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: abcd\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string response_200 = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_200_without_ok = "HTTP/1.1 200\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_chunk = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "Transfer-Encoding: chunked\r\n" + "Connection: close\r\n" + "\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "0\r\n" + "\r\n"; + +TEST(http_parser, method_name) { + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_DELETE), "DELETE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_GET), "GET"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_HEAD), "HEAD"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_POST), "POST"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PUT), "PUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PATCH), "PATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CONNECT), "CONNECT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_OPTIONS), "OPTIONS"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_TRACE), "TRACE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_COPY), "COPY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_LOCK), "LOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCOL), "MKCOL"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MOVE), "MOVE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCALENDAR), "MKCALENDAR"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPFIND), "PROPFIND"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPPATCH), "PROPPATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SEARCH), "SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNLOCK), "UNLOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_REPORT), "REPORT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKACTIVITY), "MKACTIVITY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CHECKOUT), "CHECKOUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MERGE), "MERGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MSEARCH), "M-SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOTIFY), "NOTIFY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SUBSCRIBE), "SUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNSUBSCRIBE), "UNSUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PURGE), "PURGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOT_IMPLEMENTED), "NOTIMPLEMENTED"); +} + +TEST(http_parser, http_version) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http2.c_str(), request_get_http2.length()); + ASSERT_TRUE(parser->http_major == 2); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http09.c_str(), request_get_http09.length()); + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, should_keep_alive) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_connection_close.c_str(), + request_get_with_connection_close.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_http10_with_keep_alive.c_str(), + request_get_http10_with_keep_alive.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_schema.c_str(), request_get_with_schema.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_head.c_str(), request_head.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, upgrade) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_upgrade.c_str(), request_upgrade.length()); + ASSERT_TRUE(parser->upgrade == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, dead) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_dead.c_str(), request_dead.length()); + ASSERT_TRUE(parser->state == s_dead); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, zero) { + swoole_http_parser *parser = swoole_http_parser_create(); + int ret = swoole_http_parser_execute(parser, &http_parser_settings, "", 0); + ASSERT_TRUE(ret == 0); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, methods) { + ASSERT_EQ(swoole_http_parser_method("COPY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_COPY); + ASSERT_EQ(swoole_http_parser_method("CHECKOUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_CHECKOUT); + ASSERT_EQ(swoole_http_parser_method("HEAD /get HTTP/1.1\r\n\r\n"), PHP_HTTP_HEAD); + ASSERT_EQ(swoole_http_parser_method("LOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_LOCK); + ASSERT_EQ(swoole_http_parser_method("MOVE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MOVE); + ASSERT_EQ(swoole_http_parser_method("MKCALENDAR /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKCALENDAR); + ASSERT_EQ(swoole_http_parser_method("MKACTIVITY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKACTIVITY); + ASSERT_EQ(swoole_http_parser_method("MERGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MERGE); + ASSERT_EQ(swoole_http_parser_method("M-SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MSEARCH); + ASSERT_EQ(swoole_http_parser_method("NOTIFY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOTIFY); + ASSERT_EQ(swoole_http_parser_method("OPTIONS /get HTTP/1.1\r\n\r\n"), PHP_HTTP_OPTIONS); + ASSERT_EQ(swoole_http_parser_method("REPORT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_REPORT); + ASSERT_EQ(swoole_http_parser_method("SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SEARCH); + ASSERT_EQ(swoole_http_parser_method("SUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("UNSUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNSUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("TRACE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_TRACE); + ASSERT_EQ(swoole_http_parser_method("UNLOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNLOCK); + ASSERT_EQ(swoole_http_parser_method("PURGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PURGE); + ASSERT_EQ(swoole_http_parser_method("POST /get HTTP/1.1\r\n\r\n"), PHP_HTTP_POST); + ASSERT_EQ(swoole_http_parser_method("PROPFIND /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPFIND); + ASSERT_EQ(swoole_http_parser_method("PROPPATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPPATCH); + ASSERT_EQ(swoole_http_parser_method("PUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PUT); + ASSERT_EQ(swoole_http_parser_method("PATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PATCH); + ASSERT_EQ(swoole_http_parser_method("UNKNOWN /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOT_IMPLEMENTED); +} + +TEST(http_parser, proxy_connection) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_proxy_connection.c_str(), + request_get_with_proxy_connection.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[4].c_str(), "Proxy-Connection"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, header_field_and_value) { + string header = "User-Agent: curl/7.64.1\r\n" + "\r\n"; + + swoole_http_parser *parser = swoole_http_parser_create(); + parser->state = s_header_field; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[0].c_str(), "User-Agent"); + swoole_http_destroy_context(parser); + + header = "curl/7.64.1\r\n" + "\r\n"; + parser = swoole_http_parser_create(); + parser->state = s_header_value; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + ASSERT_STREQ(ctx->header_values[0].c_str(), "curl/7.64.1"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, response) { + swoole_http_parser *parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute( + parser, &http_parser_settings, response_200_without_ok.c_str(), response_200_without_ok.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_chunk.c_str(), response_chunk.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, query_string) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string.c_str(), request_get_with_query_string.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=foo&b=bar&c=456%26789"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string2.c_str(), request_get_with_query_string2.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string3.c_str(), request_get_with_query_string3.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string4.c_str(), request_get_with_query_string4.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string5.c_str(), request_get_with_query_string5.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, http09) { + string request_get_with_query_string_http09 = "GET /index.html\r"; + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + request_get_with_query_string_http09 = "GET /index.html\n"; + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); +} diff --git a/core-tests/src/server/message_bus.cpp b/core-tests/src/server/message_bus.cpp new file mode 100644 index 00000000000..52f96e78a4f --- /dev/null +++ b/core-tests/src/server/message_bus.cpp @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_signal.h" +#include "swoole_lock.h" + +using namespace std; +using namespace swoole; + +constexpr int DATA_SIZE = 2 * SW_NUM_MILLION; + +struct TestPacket { + SessionId fd; + std::string data; +}; + +struct TestMB { + std::vector q; + MessageBus mb; + std::function read_func; + + bool send_empty_packet(network::Socket *sock) { + SendData _data4; + _data4.data = "hello world"; + _data4.info.fd = 4; + _data4.info.len = 0; + if (!mb.write(sock, &_data4)) { + return false; + } + + SendData _data5; + _data5.data = nullptr; + _data5.info.fd = 5; + _data5.info.len = 10; + if (!mb.write(sock, &_data5)) { + return false; + } + + return true; + } + + int read(Event *ev) { + auto retval = read_func(ev->socket); + if (retval == 0) { + return SW_OK; + } else if (retval < 0) { + swoole_event_del(ev->socket); + return SW_ERR; + } + + auto packet = mb.get_packet(); + + q.push_back(TestPacket{ + mb.get_buffer()->info.fd, + std::string(packet.data, packet.length), + }); + + if (q.size() == 5) { + swoole_event_del(ev->socket); + } + + return SW_OK; + } +}; + +#define MB_SEND(i, s) \ + String pkt##i(s); \ + pkt##i.append_random_bytes(pkt##i.size - 1, false); \ + pkt##i.append('\0'); \ + \ + SendData _data##i{}; \ + _data##i.data = pkt##i.value(); \ + _data##i.info.fd = i; \ + _data##i.info.len = pkt##i.get_length(); \ + ASSERT_TRUE(tmb.mb.write(p.get_socket(true), &_data##i)); + +#define MB_ASSERT(i) \ + auto r##i = tmb.q.at(i - 1); \ + ASSERT_EQ(r##i.fd, i); \ + ASSERT_STREQ(r##i.data.c_str(), pkt##i.value()); + +TEST(message_bus, read) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} + +TEST(message_bus, read_with_buffer) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read_with_buffer(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} diff --git a/core-tests/src/server/multipart_parser.cpp b/core-tests/src/server/multipart_parser.cpp new file mode 100644 index 00000000000..aac4e30ef2f --- /dev/null +++ b/core-tests/src/server/multipart_parser.cpp @@ -0,0 +1,255 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "multipart_parser.h" + +struct MppResult { + std::string data; + std::string header_field; + std::string header_value; + bool header_complete; + bool body_end; +}; + +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_field: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_field = std::string(at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_value: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_value = std::string(at, length); + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_data: length=%lu", length); + auto res = (MppResult *) p->data; + res->data.append(at, length); + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete"); + auto res = (MppResult *) p->data; + res->header_complete = true; + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end"); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begin"); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + swoole_trace("on_body_end"); + auto res = (MppResult *) p->data; + res->body_end = true; + return 0; +} + +static multipart_parser_settings _settings{ + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + +static const std::string boundary = "--WebKitFormBoundaryeGOz80A8JnaO6kuw"; + +static multipart_parser *create_parser() { + return multipart_parser_init(boundary.c_str(), boundary.length(), &_settings); +} + +static void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) { + size_t length = 1024; + char buf[length]; + + parser->error_reason = error_reason; + int result_len = multipart_parser_error_msg(parser, buf, length); + ASSERT_GT(result_len, 0); + buf[result_len] = '\0'; + + std::string response(buf, result_len); + ASSERT_TRUE(response.find(error) != response.npos); +} + +TEST(multipart_parser, error_message) { + size_t length = 1024; + char buf[length]; + auto parser = create_parser(); + + parser->error_reason = MPPE_OK; + ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0); + + parser->error_expected = '\0'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\r'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\n'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = 'a'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); +} + +TEST(multipart_parser, header_field) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + ASSERT_STREQ(result.header_field.c_str(), "Content-Disposition"); + ASSERT_TRUE(result.header_value.find("test") != result.header_value.npos); + + std::string boundary_str(parser->boundary, parser->boundary_length); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); +} + +TEST(multipart_parser, header_error) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\""); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, -1); + ASSERT_EQ(parser->error_reason, MPPE_HEADER_VALUE_INCOMPLETE); + ASSERT_EQ(parser->error_expected, '\r'); +} + +TEST(multipart_parser, data) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + std::string boundary_str(parser->boundary, parser->boundary_length); + + // data part + swoole::String data(128); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + data.append_random_bytes(swoole_rand(60, 120), true); + ASSERT_EQ(multipart_parser_execute(parser, data.value(), data.get_length()), data.get_length()); + + auto append_data = [&]() { + size_t offset = data.length; + data.append_random_bytes(swoole_rand(60, 120), true); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + }; + + append_data(); + data.append("\r"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r")), 1); + + append_data(); + + data.append("\r\n"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + + append_data(); + + { + size_t offset = data.length; + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + } + + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); + + ASSERT_MEMEQ(data.value(), result.data.c_str(), result.data.length()); + + ASSERT_TRUE(result.header_complete); + ASSERT_TRUE(result.body_end); +} diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp new file mode 100644 index 00000000000..7d75e54d4aa --- /dev/null +++ b/core-tests/src/server/server.cpp @@ -0,0 +1,1229 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_signal.h" +#include "swoole_lock.h" +#include "swoole_util.h" + +using namespace std; +using namespace swoole; + +int beforeReloadPid = 0; + +TEST(server, schedule) { + int ret; + Server serv(Server::MODE_PROCESS); + serv.worker_num = 6; + serv.dispatch_mode = Server::DISPATCH_IDLE_WORKER; + ret = serv.create(); + ASSERT_EQ(SW_OK, ret); + + for (uint32_t i = 0; i < serv.worker_num; i++) { + serv.workers[i].set_status_to_busy(); + } + + std::set _worker_id_set; + + for (uint32_t i = 0; i < serv.worker_num; i++) { + auto worker_id = serv.schedule_worker(i * 13, nullptr); + _worker_id_set.insert(worker_id); + } + ASSERT_EQ(_worker_id_set.size(), serv.worker_num); + + for (uint32_t i = 1; i < serv.worker_num - 1; i++) { + serv.workers[i].set_status_to_idle(); + } + + _worker_id_set.clear(); + for (uint32_t i = 0; i < serv.worker_num; i++) { + auto worker_id = serv.schedule_worker(i * 13, nullptr); + _worker_id_set.insert(worker_id); + } + ASSERT_EQ(_worker_id_set.size(), serv.worker_num - 2); +} + +static const char *packet = "hello world\n"; + +TEST(server, base) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, process) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 1; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); + lock->lock(); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onStart = [&lock](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(serv->gs->master_pid, SIGTERM); + }); + t1.detach(); + }; + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + ASSERT_EQ(serv.start(), 0); + + delete lock; +} + +#ifdef SW_THREAD +TEST(server, thread) { + Server serv(Server::MODE_THREAD); + serv.worker_num = 2; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + serv.shutdown(); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} +#endif + +TEST(server, reload_all_workers) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + serv.task_enable_coroutine = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + std::string filename = "/tmp/worker_1.pid"; + if (worker->id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->manager_pid, SIGUSR2); + sleep(1); + kill(serv->gs->manager_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, reload_all_workers2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + serv.onTask = [](Server *serv, swEventData *task) -> int { return 0; }; + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + std::string filename = "/tmp/worker_2.pid"; + if (worker->id == 1) { + if (access(filename.c_str(), R_OK) == -1) { + ofstream file(filename); + file << getpid(); + file.close(); + kill(serv->gs->master_pid, SIGUSR2); + sleep(1); + kill(serv->gs->master_pid, SIGUSR1); + } else { + char buf[10] = {0}; + ifstream file(filename.c_str()); + file >> buf; + file.close(); + + int oldPid = 0; + stringstream stringPid(buf); + stringPid >> oldPid; + + EXPECT_TRUE(oldPid != getpid()); + + sleep(1); + remove(filename.c_str()); + kill(serv->gs->master_pid, SIGTERM); + } + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](Server *serv, EventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + sleep(1); + kill(serv->get_manager_pid(), SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, kill_user_workers1) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 1; + serv.task_worker_num = 2; + serv.max_wait_time = 1; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + Worker *worker1 = new Worker(); + Worker *worker2 = new Worker(); + ASSERT_EQ(serv.add_worker(worker1), worker1->id); + ASSERT_EQ(serv.add_worker(worker2), worker2->id); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onUserWorkerStart = [&](Server *serv, Worker *worker) { EXPECT_GT(worker->id, 0); }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + while (1) { + } + }; + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +#ifdef SW_USE_OPENSSL +TEST(server, ssl) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 1; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); + lock->lock(); + + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + port->ssl_init(); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onStart = [&lock](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + EXPECT_EQ(port->ssl, 1); + EXPECT_EQ(swoole_ssl_is_thread_safety(), true); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.enable_ssl_encrypt(); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(serv->gs->master_pid, SIGTERM); + }); + t1.detach(); + }; + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + ASSERT_EQ(serv.start(), 0); + + delete lock; +} + +TEST(server, dtls) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); + lock->lock(); + + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + port->ssl_init(); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onStart = [&lock](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + EXPECT_EQ(port->ssl, 1); + + network::SyncClient c(SW_SOCK_UDP); + c.connect(TEST_HOST, port->port); + c.enable_ssl_encrypt(); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(serv->gs->master_pid, SIGTERM); + }); + t1.detach(); + }; + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock->unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + ASSERT_EQ(serv.start(), 0); + + delete lock; +} + +TEST(server, dtls2) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->single_thread = false; + ListenPort *port = server->add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + port->ssl_init(); + + server->create(); + server->onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + + sleep(1); + auto port = server->get_primary_port(); + + network::SyncClient c(SW_SOCK_UDP); + c.connect(TEST_HOST, port->port); + c.enable_ssl_encrypt(); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + } +} +#endif + +TEST(server, task_worker) { + Server serv; + serv.worker_num = 1; + serv.task_worker_num = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onTask = [](Server *serv, EventData *task) -> int { + EXPECT_EQ(serv->get_tasking_num(), 1); + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + serv->gs->task_workers.running = 0; + serv->gs->task_count++; + serv->gs->tasking_num--; + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + thread t1([&serv]() { + serv.gs->task_workers.running = 1; + serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); + EXPECT_EQ(serv.get_tasking_num(), 0); + serv.gs->tasking_num--; + EXPECT_EQ(serv.get_tasking_num(), 0); + EXPECT_EQ(serv.get_idle_task_worker_num(), serv.task_worker_num); + }); + + usleep(10000); + + EventData buf; + memset(&buf.info, 0, sizeof(buf.info)); + + buf.info.ext_flags = SW_TASK_NOREPLY; + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + + int _dst_worker_id = 0; + + ASSERT_TRUE(serv.task(&buf, &_dst_worker_id)); + ASSERT_EQ(serv.gs->task_count, 1); + + t1.join(); + serv.gs->task_workers.destroy(); + + ASSERT_EQ(serv.gs->task_count, 2); +} + +// PHP_METHOD(swoole_server, task) +TEST(server, task_worker2) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker->id; + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_CALLBACK); + ASSERT_EQ(serv->gs->task_workers.dispatch(&buf, &_dst_worker_id), SW_OK); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskCo) +TEST(server, task_worker3) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskwait) +TEST(server, task_worker4) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + EXPECT_TRUE(serv->finish(task->data, task->info.len, 0, task)); + return 0; + }; + + serv.onFinish = [](Server *serv, swEventData *task) -> int { + EXPECT_EQ(string(task->data, task->info.len), string(packet)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(1); + + EventData *task_result = serv->get_task_result(); + sw_memset_zero(task_result, sizeof(*task_result)); + memset(&buf.info, 0, sizeof(buf.info)); + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + buf.info.reactor_id = worker->id; + sw_atomic_fetch_add(&serv->gs->tasking_num, 1); + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sw_atomic_fetch_add(&serv->gs->tasking_num, 0); + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +// static PHP_METHOD(swoole_server, taskWaitMulti) +TEST(server, task_worker5) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + serv.task_worker_num = 3; + serv.task_enable_coroutine = 1; + + char data[SW_IPC_MAX_SIZE * 2] = {}; + swoole_random_string(data, SW_IPC_MAX_SIZE * 2); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + serv.onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + serv.onTask = [&data](Server *serv, swEventData *task) -> int { + PacketTask *pkg = (PacketTask *) task->data; + ifstream ifs; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + + EXPECT_EQ(string(resp), string(data)); + EXPECT_TRUE(serv->finish(resp, SW_IPC_MAX_SIZE * 2, 0, task)); + return 0; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + serv.onWorkerStart = [&data](Server *serv, Worker *worker) { + if (worker->id == 1) { + int _dst_worker_id = 0; + + EventData *task_result = &(serv->task_results[worker->id]); + sw_memset_zero(task_result, sizeof(*task_result)); + + File fp = make_tmpfile(); + std::string file_path = fp.get_path(); + fp.close(); + int *finish_count = (int *) task_result->data; + *finish_count = 0; + + swoole_strlcpy(task_result->data + 4, file_path.c_str(), SW_TASK_TMP_PATH_SIZE); + + EventData buf{}; + memset(&buf.info, 0, sizeof(buf.info)); + Server::task_pack(&buf, data, SW_IPC_MAX_SIZE * 2); + buf.info.ext_flags |= SW_TASK_WAITALL; + buf.info.reactor_id = worker->id; + serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); + sleep(3); + + ifstream ifs; + ifs.open(task_result->data + 4); + char recv[sizeof(EventData)] = {0}; + ifs >> recv; + ifs.close(); + + EventData *task = (EventData *) recv; + PacketTask *pkg = (PacketTask *) task->data; + ifs.open(pkg->tmpfile); + char resp[SW_IPC_MAX_SIZE * 2] = {0}; + ifs >> resp; + ifs.close(); + EXPECT_EQ(string(resp), string(data)); + + kill(serv->gs->master_pid, SIGTERM); + } + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, max_connection) { + Server serv; + + serv.set_max_connection(0); + ASSERT_EQ(serv.get_max_connection(), SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets)); + + serv.set_max_connection(SwooleG.max_sockets + 13); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets); + + serv.set_max_connection(SwooleG.max_sockets - 13); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets - 13); + + uint32_t last_value = serv.get_max_connection(); + + serv.create(); + + serv.set_max_connection(100); + ASSERT_EQ(serv.get_max_connection(), last_value); +} + +TEST(server, min_connection) { + Server serv; + + serv.task_worker_num = 14; + serv.worker_num = 5; + + serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + + serv.set_max_connection(15); + serv.create(); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets); +} + +TEST(server, worker_num) { + Server serv; + + serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99; + serv.task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); + ASSERT_EQ(serv.task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); +} + +TEST(server, reactor_num_base) { + Server serv(Server::MODE_BASE); + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, serv.worker_num); +} + +TEST(server, reactor_num_large) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU); +} + +TEST(server, reactor_num_large2) { + Server serv(Server::MODE_PROCESS); + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, serv.worker_num); +} + +TEST(server, reactor_num_zero) { + Server serv; + serv.reactor_num = 0; + serv.create(); + + ASSERT_EQ(serv.reactor_num, SW_CPU_NUM); +} + +void test_command(enum Server::Mode _mode) { + Server serv(_mode); + serv.worker_num = 4; + serv.task_worker_num = 4; + serv.reactor_num = 2; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + ASSERT_EQ(serv.create(), SW_OK); + + serv.add_command("test", Server::Command::ALL_PROCESS, [](Server *, const std::string &msg) -> std::string { + return std::string("json result, ") + msg; + }); + + serv.onStart = [](Server *serv) { + static Server::Command::Callback fn = [&](Server *serv, const std::string &msg) { + usleep(50000); + if (msg == "json result, hello world [0]") { + if (serv->is_base_mode()) { + goto _send_to_event_worker; + } else { + serv->command(1, Server::Command::REACTOR_THREAD, "test", "hello world [1]", fn); + } + } else if (msg == "json result, hello world [1]") { + _send_to_event_worker: + serv->command(1, Server::Command::EVENT_WORKER, "test", "hello world [2]", fn); + } else if (msg == "json result, hello world [2]") { + serv->command(1, Server::Command::TASK_WORKER, "test", "hello world [3]", fn); + } else if (msg == "json result, hello world [3]") { + serv->command(1, Server::Command::MANAGER, "test", "hello world [4]", fn); + } else if (msg == "json result, hello world [4]") { + swoole_timer_after(50, [serv](Timer *, TimerNode *) { serv->shutdown(); }); + } else { + ASSERT_TRUE(0); + } + }; + serv->command(1, Server::Command::MASTER, "test", "hello world [0]", fn); + }; + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + + }; + + serv.onTask = [](Server *, EventData *) -> int { return SW_OK; }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, command_1) { + test_command(Server::MODE_PROCESS); +} + +TEST(server, command_2) { + test_command(Server::MODE_BASE); +} + +TEST(server, sendwait) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + + mutex lock; + lock.lock(); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, system) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + + mutex lock; + lock.lock(); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + int svr_port = swoole::test::get_random_port(); + struct sockaddr_in serv_addr; + bzero(&serv_addr, sizeof(serv_addr)); + serv_addr.sin_addr.s_addr = inet_addr(TEST_HOST); + serv_addr.sin_port = htons(svr_port); + serv_addr.sin_family = AF_INET; + bind(fd, (struct sockaddr *) &serv_addr, sizeof(struct sockaddr)); + listen(fd, 1024); + + setenv("LISTEN_FDS_START", to_string(fd).c_str(), 1); + setenv("LISTEN_FDS", "1", 1); + setenv("LISTEN_PID", to_string(getpid()).c_str(), 1); + + EXPECT_GT(serv.add_systemd_socket(), 0); + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + lock.lock(); + + network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, svr_port); + c.send(packet, strlen(packet)); + char buf[1024]; + c.recv(buf, sizeof(buf)); + c.close(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + + string resp = string("Server: ") + string(packet); + serv->sendwait(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, reopen_log) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.running = 1; + sw_logger()->set_level(SW_LOG_WARNING); + string filename = "/tmp/swoole.log"; + sw_logger()->open(filename.c_str()); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onWorkerStart = [&filename](Server *serv, Worker *worker) { + if (worker->id != 0) { + return; + } + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + unlink(filename.c_str()); + EXPECT_TRUE(access(filename.c_str(), R_OK) == -1); + kill(serv->gs->master_pid, SIGRTMIN); + sleep(2); + EXPECT_TRUE(access(filename.c_str(), R_OK) != -1); + kill(serv->gs->master_pid, SIGTERM); + }; + + ASSERT_EQ(serv.start(), 0); +} + +TEST(server, udp_packet) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_UDP, TEST_HOST, 0); + + server->create(); + server->onPacket = [](Server *serv, RecvData *req) { + DgramPacket *recv_data = (DgramPacket *) req->data; + EXPECT_EQ(string(recv_data->data, recv_data->length), string(packet)); + network::Socket *server_socket = serv->get_server_socket(req->info.server_fd); + string resp = string(packet); + server_socket->sendto(recv_data->socket_addr, resp.c_str(), resp.length(), 0); + return SW_OK; + }; + + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + } + + if (pid == 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + exit(0); + }; + sleep(1); + auto port = server->get_primary_port(); + + network::Client cli(SW_SOCK_UDP, false); + int ret = cli.connect(&cli, TEST_HOST, port->port, -1, 0); + EXPECT_EQ(ret, 0); + ret = cli.send(&cli, packet, strlen(packet), 0); + EXPECT_GT(ret, 0); + + char buf[1024]; + sleep(1); + cli.recv(&cli, buf, 128, 0); + ASSERT_STREQ(buf, packet); + cli.close(); + } +} + +TEST(server, protocols) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + sw_logger()->set_level(SW_LOG_WARNING); + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + + port->open_eof_check = true; + ASSERT_STREQ(port->get_protocols(), "eof"); + port->open_eof_check = false; + + port->open_length_check = true; + ASSERT_STREQ(port->get_protocols(), "length"); + port->open_length_check = false; + + port->open_http_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http"); + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2|websocket"); + port->open_http2_protocol = false; + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_http2_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|http2"); + port->open_http2_protocol = false; + port->open_http_protocol = false; + + port->open_http_protocol = true; + port->open_websocket_protocol = true; + ASSERT_STREQ(port->get_protocols(), "http|websocket"); + port->open_websocket_protocol = false; + port->open_http_protocol = false; + + port->open_mqtt_protocol = true; + ASSERT_STREQ(port->get_protocols(), "mqtt"); + port->open_mqtt_protocol = false; + + port->open_redis_protocol = true; + ASSERT_STREQ(port->get_protocols(), "redis"); + port->open_redis_protocol = false; + + port->clear_protocol(); + ASSERT_EQ(port->open_eof_check, 0); + ASSERT_EQ(port->open_length_check, 0); + ASSERT_EQ(port->open_http_protocol, 0); + ASSERT_EQ(port->open_websocket_protocol, 0); + ASSERT_EQ(port->open_http2_protocol, 0); + ASSERT_EQ(port->open_mqtt_protocol, 0); + ASSERT_EQ(port->open_redis_protocol, 0); + ASSERT_STREQ(port->get_protocols(), "raw"); +} + +TEST(server, pipe_message) { + Server *server = new Server(Server::MODE_PROCESS); + server->worker_num = 2; + server->add_port(SW_SOCK_TCP, TEST_HOST, 0); + + server->create(); + server->onPipeMessage = [](Server *serv, EventData *req) -> int { + EXPECT_EQ(string(req->data, req->info.len), string(packet)); + return SW_OK; + }; + + server->onReceive = [](Server *server, RecvData *req) -> int { return SW_OK; }; + + server->onWorkerStart = [&](Server *server, Worker *worker) { + if (worker->id == 1) { + EventData buf{}; + string data = string(packet); + + memset(&buf.info, 0, sizeof(buf.info)); + ASSERT_TRUE(Server::task_pack(&buf, data.c_str(), data.length())); + buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; + + Worker *to_worker = server->get_worker(worker->id - 1); + server->send_to_worker_from_worker( + to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK); + sleep(1); + + kill(server->get_master_pid(), SIGTERM); + } + }; + + server->start(); +} diff --git a/core-tests/src/socket.cpp b/core-tests/src/socket.cpp deleted file mode 100644 index 4a82d78316e..00000000000 --- a/core-tests/src/socket.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "tests.h" -#ifndef _WIN32 -TEST(socket, swSocket_unix_sendto) -{ - int fd1,fd2,ret; - struct sockaddr_un un1,un2; - char sock1_path[] = "/tmp/udp_unix1.sock"; - char sock2_path[] = "/tmp/udp_unix2.sock"; - char test_data[] = "swoole"; - - bzero(&un1,sizeof(struct sockaddr_un)); - bzero(&un2,sizeof(struct sockaddr_un)); - - un1.sun_family = AF_UNIX; - un2.sun_family = AF_UNIX; - - unlink(sock1_path); - unlink(sock2_path); - - fd1 = socket(AF_UNIX,SOCK_DGRAM,0); - strncpy(un1.sun_path, sock1_path, sizeof(un1.sun_path) - 1); - bind(fd1,(struct sockaddr *)&un1,sizeof(un1)); - - fd2 = socket(AF_UNIX,SOCK_DGRAM,0); - strncpy(un2.sun_path, sock2_path, sizeof(un2.sun_path) - 1); - bind(fd2,(struct sockaddr *)&un2,sizeof(un2)); - - ret = swSocket_unix_sendto(fd1,sock2_path,test_data,strlen(test_data)); - ASSERT_GT(ret, 0); - - unlink(sock1_path); - unlink(sock2_path); -} -#endif diff --git a/core-tests/src/string.cpp b/core-tests/src/string.cpp deleted file mode 100644 index 9eb4fcc8e01..00000000000 --- a/core-tests/src/string.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "tests.h" - -TEST(string, rtrim) -{ - char buf[1024]; - strcpy(buf, "hello world "); - swoole_rtrim(buf, strlen(buf)); - ASSERT_EQ(strcmp("hello world", buf), 0); - ASSERT_NE(strcmp("hello world ", buf), 0); - - strcpy(buf, " "); - swoole_rtrim(buf, strlen(buf)); - ASSERT_EQ(strlen(buf), 0); -} diff --git a/core-tests/src/thread_pool.cpp b/core-tests/src/thread_pool.cpp deleted file mode 100755 index 59089f80856..00000000000 --- a/core-tests/src/thread_pool.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __MACH__ -#include "tests.h" -#include - -static swThreadPool pool; -static int _pipe; -const static int N = 10000; - -static int thread_onTask(swThreadPool *pool, void *task, int task_len) -{ - sw_atomic_long_t *n = (sw_atomic_long_t *) task; - sw_atomic_fetch_add(n, 1); - if (*n == N - 1) - { - write(_pipe, (void*) n, sizeof(long)); - } - return SW_OK; -} - -TEST(thread_pool, dispatch) -{ - ASSERT_EQ(swThreadPool_create(&pool, 4), SW_OK); - pool.onTask = thread_onTask; - ASSERT_EQ(swThreadPool_run(&pool), SW_OK); - sw_atomic_long_t result = 0; - - _pipe = eventfd(0, 0); - - for (long i = 0; i < N; i++) - { - ASSERT_EQ(swThreadPool_dispatch(&pool, (void*) &result, sizeof(result)), SW_OK); - } - - long buf; - read(_pipe, (void*) &buf, sizeof(buf)); - close(_pipe); - - ASSERT_EQ(swThreadPool_free(&pool), SW_OK); - ASSERT_EQ(result, N); -} -#endif - diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 00000000000..55ef9fd9874 --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,145 @@ +# Swoole Changelog + +## 2024-12-16 v6.0.0 +# ✨ New Feature: +- Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. +- Added a new thread class `Swoole\Thread`. @matyhtf +- Introduced thread lock `Swoole\Thread\Lock`. @matyhtf +- Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf +- Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf +- The file asynchronous operation supports using `io_uring` as the underlying engine for file asynchronous operations. When liburing is installed and Swoole is compiled with the --enable-iouring option, the asynchronous operations of functions such as file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, and filesize will be implemented by io_uring. @matyhtf @NathanFreeman +- Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman +- Added `Swoole\Thread\Map::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::find()` method. @matyhtf +- Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf +- Added `Swoole\Process::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setName()` method. @matyhtf +- Added `Swoole\Thread::setAffinity()` method. @matyhtf +- Added `Swoole\Thread::getAffinity()` method. @matyhtf +- Added `Swoole\Thread::setPriority()` method. @matyhtf +- Added `Swoole\Thread::getPriority()` method. @matyhtf +- Added `Swoole\Thread::gettid()` method. +- The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman +- Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman +- Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman +- Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf +- Added new function and class to set cookies. @matyhtf @NathanFreeman +- Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman +- `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf +- `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf +- Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf +- Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf +- Supports the high-performance compression tool `Zstd`. You only need to add `--enable-zstd` when compiling `Swoole`, and then `zstd` can be used to compress or decode responses between the `http` client and server. @NathanFreeman + +# 🐛 Bug Fixed: +- Fixed the issue where installation via `pecl` was not possible. @remicollet +- Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman +- Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman +- Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf +- Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf +- Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman +- Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward +- Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf +- Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf +- Fixed the occasional unreachable branch issue during file upload. @NathanFreeman +- Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman +- Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk +- Capture exceptions when thread creation fails. @matyhtf +- Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk +- Fixed the fatal compile error with `GCC 14`. @remicollet +- Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng +- Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman +- Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf +- Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf +- Fixed incorrect `content-type` when uploading files. @matyhtf +- Fixed code errors in the `http2` coroutine client. @matyhtf +- Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad +- Fixed errors related to `brotli` in `config.m4`. @fundawang +- Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf +- Fixed compilation errors in the `macos` environment. @matyhtf +- Fixed the issue of threads not being able to exit safely. @matyhtf +- Fixed the issue where the static variable for response time returned by `Swoole\Http\Response` in multi-threaded mode was not generated separately for each thread. @matyhtf @NathanFreeman +- Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf +- Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet +- Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf +- Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf +- Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf +- Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf +- Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf +- Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf +- Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf +- Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + +# ⭐️ Kernel optimization: +- Removed unnecessary checks for `socket structs`. @petk +- Upgraded Swoole Library. @deminy +- Added support for status code 451 in `Swoole\Http\Response`. @abnegate +- Synchronized `file` operation code across different PHP versions. @NathanFreeman +- Synchronized `pdo` operation code across different PHP versions. @NathanFreeman +- Optimized the code for `Socket::ssl_recv()`. @matyhtf +- Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman +- Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman +- Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf +- Optimized some fundamental coroutine logic. @matyhtf +- Upgraded the Oracle database version for CI testing. @gvenzl +- Optimized the underlying logic of `sendfile`. @matyhtf +- Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk +- Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf +- Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf +- Enhanced the HTTP client to accept duplicate request headers. @matyhtf +- Optimized `Swoole\Http\Response::write()`. @matyhtf +- `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf +- Compatible with `PHP 8.4`. @matyhtf @NathanFreeman +- Added the ability for asynchronous writing at the underlying socket level. @matyhtf +- Optimized `Swoole\Http\Response`. @NathanFreeman +- Improved underlying error messages. @matyhtf +- Supported sharing PHP native sockets in multi-threaded mode. @matyhtf +- Optimized static file service and fixed static file path error issues. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf +- Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf +- Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman +- Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman +- Optimized timers so that synchronous processes do not depend on signals. @matyhtf +- Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + +# ❌ Deprecated: +- No longer supports `PHP 8.0`. +- No longer supports `Swoole\Coroutine\MySQL` coroutine client. +- No longer supports `Swoole\Coroutine\Redis` coroutine client. +- No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. +- Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. +## 2024-01-24 v5.1.2 +- Added support for embed sapi @matyhtf +- Fixed compatibility with PHP 8.3 ZEND_CHECK_STACK_LIMIT @Yurunsoft +- Fixed no Content-Range response header when the range request returns all the contents of the file @Yurunsoft +- Optimized HTTP server performance @NathanFreeman +- Fixed truncated cookie @stnguyen90 +- Fixed native-curl crash on PHP 8.3 @NathanFreeman +- Added CLOSE_SERVICE_RESTART, CLOSE_TRY_AGAIN_LATER, CLOSE_BAD_GATEWAY as valid close reasons for websocket @cjavad +- Fixed invalid errno after Server::Manager::wait() @JacobBrownAustin +- Fixed HTTP2 Typo @leocavalcante + +## 2022-07-22 v5.0.0 + +### Added +* Added `max_concurrency` option for `Server` +* Added `max_retries` option for `Coroutine\Http\Client` +* Added `name_resolver` global option +* Added `upload_max_filesize` option for `Server` +* Added `Coroutine::getExecuteTime()` +* Added `SWOOLE_DISPATCH_CONCURRENT_LB` dispatch_mode for `Server` + +### Changed +* Enhanced type system, added types for parameters and return values of all functions +* Optimized error handling, all constructors will throw exceptions when fail +* Adjusted the default mode of Server, the default is `SWOOLE_BASE` mode + +### Removed + +- Removed `PSR-0` style class names +- Removed the automatic addition of `Event::wait()` in shutdown function +- Removed `Server::tick/after/clearTimer/defer` aliases +- Removed `--enable-http`/`--enable-swoole-json`, adjusted to be enable by default + +### Deprecated +- Deprecated `Coroutine\Redis` and `Coroutine\MySQL` diff --git a/docs/CODE-STYLE.md b/docs/CODE-STYLE.md new file mode 100644 index 00000000000..f6eaf6c3a89 --- /dev/null +++ b/docs/CODE-STYLE.md @@ -0,0 +1,6 @@ +Code Style +======= +* Google C++ Style +* Indent adjusted to 4 characters +* Line width adjusted to 120 characters + diff --git a/docs/CPPLINT.cfg b/docs/CPPLINT.cfg new file mode 100644 index 00000000000..e5e00361fef --- /dev/null +++ b/docs/CPPLINT.cfg @@ -0,0 +1,4 @@ +set noparent +filter=-build/include_alpha,-build/include_subdir,-build/include_what_you_use,-legal/copyright,-readability/nolint +braces=4 +linelength=120 diff --git a/docs/SUPPORTED.md b/docs/SUPPORTED.md new file mode 100644 index 00000000000..80266880b63 --- /dev/null +++ b/docs/SUPPORTED.md @@ -0,0 +1,35 @@ +## Supported Versions + +| Branch | PHP Version | Initialization | Active Support Until | Security Support Until | +|-----------------------------------------------------------------|-------------|----------------|----------------------|------------------------| +| [v5.1.x](https://github.com/swoole/swoole-src/tree/5.1.x) | 8.0 - 8.3 | 2023-11-29 | 2024-11-29 | 2025-04-29 | +| [v6.0.x](https://github.com/swoole/swoole-src/tree/master) | 8.1 - 8.4 | 2024-12-31 | 2025-12-31 | 2026-06-31 | + + +- **Active support**: A release that is being actively supported. Reported bugs and security issues are fixed and regular point releases are made. +- **Security fixes only**: A release that is supported for critical security issues only. Releases are only made on an as-needed basis. + +## PHP Version Support + +1. Each branch (`MINOR version`) supports a fixed range of PHP versions. The `RELEASE VERSIONS` for that branch will not increase support for higher PHP versions. +2. The upper limit is four PHP versions; any additional versions will not be supported. For example, version 6.0 only supports PHP 8.1 to 8.4. +3. No support for any DEV or RC stage of PHP + +The pace of PHP version updates is rapid, with each version introducing numerous underlying changes. The developers of Swoole have had to invest significant time and effort to support new releases, and we lack sufficient resources to keep up with PHP updates. Therefore, there will be a delay of one MINOR version before supporting new PHP versions. + + +## Unsupported Branches + +> These releases that are no longer supported. Users of this release should upgrade as soon as possible, as they may be exposed to unpatched security vulnerabilities. + + +| Branch | PHP Version | Duration | +|----------------------------|-------------|----------------------------------| +| `1.x` | 5.4 - 7.2 | 2012-7-1 ~ 2018-05-14 | +| `2.x` | 7.0 - 7.3 | 2016-12-30 ~ 2018-05-23 | +| `4.0.x` ~ `4.3.x` | 7.0 - 7.4 | 2018-06-14 ~ 2019-12-31 | +| `4.4.x` | 7.1 - 7.4 | 2019-04-15 ~ 2022-07-31 | +| `4.5.x`,`4.6.x`, `4.7.x` | 7.1 - 7.4 | 2019-12-20 ~ 2021-12-31 | +| `4.8.x` | 7.3 - 8.2 | 2021-10-14 ~ 2024-06-30 | +| `5.0.x` | 7.4 - 8.3 | 2022-01-20 ~ 2023-07-20 | + diff --git a/docs/TESTS.md b/docs/TESTS.md new file mode 100644 index 00000000000..d88b7595cbc --- /dev/null +++ b/docs/TESTS.md @@ -0,0 +1,73 @@ +## Swoole Tests + +## Core Tests +Used to test the core code in the `src/` directory, only as a C++ library, not related to php. +The core tests depends on the googletest framework, and googletest needs to be installed. +The core test cases must be written with `C++`. + +### Build libswoole.so + +```shell +cd swoole-src +cmake . +make -j$(nproc) +``` + +### Export source directory +```shell +SWOOLE_DIR=/your/path/to/swoole-src +``` + +### Build core-tests +```shell +cd core-tests +cmake . +make -j$(nproc) +``` + +### Run core-tests +```shell +# run all tests +./bin/core-tests +# run some test cases +./bin/core-tests --gtest_filter=server.* +# list all tests +./bin/core_tests --gtest_list_tests +``` + +## PHP Tests +Used to test the php extension code in the `ext-src/` directory. The swoole php test depends on php environment. +The `php-dev` related components must be installed. + +The php test cases must be written with `PHP`. + +### Build ext-swoole +```shell +cd swoole-src +phpize +./configure ${options} +make -j$(nproc) +make install +``` +Need to configure `php.ini`, add `extension=swoole.so` to enable `ext-swoole`. + +### Run tests +```shell +./scripts/route.sh +``` + +The automated test scripts in this directory can not only run on Github Action CI. Powered by docker container technology, it can run on any systems. You only need to run the `route.sh` script to create containers of multiple PHP environments then it will run Swoole's build tests and unit tests on multiple systems automatically. + +### With special branch + +```shell +SWOOLE_BRANCH=alpine ./scripts/route.sh +``` + +### Enter the container + +> You can cancel the unit test by `CTRL+C` + +```shell +docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash +``` diff --git a/docs/google-style.xml b/docs/google-style.xml new file mode 100644 index 00000000000..e0559fa71aa --- /dev/null +++ b/docs/google-style.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/sponsors.md b/docs/sponsors.md new file mode 100644 index 00000000000..27bbfb7c100 --- /dev/null +++ b/docs/sponsors.md @@ -0,0 +1,81 @@ +
+ +# [成为 Swoole 的赞助者](#title1) +--- + +Swoole 是采用 Apache 许可的开源项目,是完全免费的。 + +维护这样一个庞大的生态系统和为项目开发新功能所需的巨大努力,只有在我们的赞助者慷慨的财务支持下才得以持续。 +捐赠募集到的资金将全部用于开源项目的维护和发展。包括但不限于: +- 向核心贡献者支付报酬 +- 服务器租赁费用 +- 网站和文档的维护 +- 举办线下活动 +- 为社区活跃者提供奖励 + +> 您可以扫码添加微信客服或通过邮件 `service@swoole.com` 与我们联系,沟通赞助事宜 + +
+ +## [赞助渠道](#title2) + +--- +来自世界各地的企业可以通过赞助来支持 `Swoole` 项目的开发。 +根据赞助商赞助金额的不同分为顶级赞助商、金牌赞助商,在获得捐赠的第一年内我们将为赞助商做以下品牌展示: + +> 顶级赞助商 +- 官网、文档、问答社区首屏顶部显著位置展示 +- GitHub 仓库首页顶部赞助商标识 +- 官方微信公众号、QQ 群、微博、知乎等官方社交平台赞助商宣传内容推送 + +> 金牌赞助商 +- 官网、文档、问答社区首屏侧边位置展示 + +## 顶级赞助商 + + + + +## 金牌赞助商 + + + + + + + + + +## 银牌赞助商 + + + + + + + + diff --git a/docs/swoole-logo.svg b/docs/swoole-logo.svg new file mode 100644 index 00000000000..6297cc67437 --- /dev/null +++ b/docs/swoole-logo.svg @@ -0,0 +1,15 @@ + + + + Transfon SWOOLE - Registered + + + + \ No newline at end of file diff --git a/examples/atomic/test.php b/examples/atomic/test.php index 10b9bf40f83..6afe709eca7 100644 --- a/examples/atomic/test.php +++ b/examples/atomic/test.php @@ -1,5 +1,5 @@ add(12)."\n"; echo $atomic->sub(11)."\n"; echo $atomic->cmpset(122, 999)."\n"; diff --git a/examples/atomic/wait.php b/examples/atomic/wait.php index a4b4048fc2e..26a56364a07 100644 --- a/examples/atomic/wait.php +++ b/examples/atomic/wait.php @@ -1,5 +1,5 @@ 0) { diff --git a/examples/buffer.php b/examples/buffer.php deleted file mode 100644 index ab820d3ef54..00000000000 --- a/examples/buffer.php +++ /dev/null @@ -1,15 +0,0 @@ -append(str_repeat("A", 10)); -$buffer->append(str_repeat("B", 20)); -$buffer->append(str_repeat("C", 30)); - -var_dump($buffer); -echo $buffer->substr(0, 10, true)."\n"; -echo $buffer->substr(0, 20, true)."\n"; -echo $buffer->substr(0, 30)."\n"; -$buffer->clear(); - -echo $buffer->substr(0, 10, true)."\n"; -var_dump($buffer); -sleep(1); diff --git a/examples/c10k.php b/examples/c10k.php deleted file mode 100644 index 1638ff448c1..00000000000 --- a/examples/c10k.php +++ /dev/null @@ -1,28 +0,0 @@ - 0) - { - continue; - } - else - { - for($i = 0; $i < 9999; $i++){ - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 - $ret = $client->connect('127.0.0.1', 9501, 0.5); - if(!$ret) - { - echo "#$i\tConnect fail. errno=".$client->errCode; - die("\n"); - } - $clients[] = $client; - usleep(10); - } - echo "Worker #".posix_getpid()." connect $i finish\n"; - sleep(1000); - exit; - } -} -sleep(1000); diff --git a/examples/client/async.php b/examples/client/async.php deleted file mode 100644 index 83de9fbd88f..00000000000 --- a/examples/client/async.php +++ /dev/null @@ -1,53 +0,0 @@ -set(array( -// 'socket_buffer_size' => 1024 * 1024 * 2, -// 'open_eof_check' => true, -// 'package_eof' => "\r\n\r\n", -//)); - -$client->_count = 0; -$client->on("connect", function(swoole_client $cli) { - //swoole_timer_clear($cli->timer); - $cli->send("GET / HTTP/1.1\r\n\r\n"); - //$cli->sendfile(__DIR__.'/test.txt'); - //$cli->_count = 0; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "Receive: $data"; - $cli->_count++; - if ($cli->_count > 5) - { - //睡眠模式,不再接收新的数据 - echo "count=10, sleep(5000ms)\n"; - $cli->sleep(); - $cli->_count = 0; - swoole_timer_after(5000, function() use ($cli) { - //唤醒 - $cli->wakeup(); - }); - //$cli->close(); - return; - } - else - { - $cli->send(str_repeat('A', 100)."\n"); - } -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -//$client->timer = swoole_timer_after(1000, function () use ($client) { -// echo "socket timeout\n"; -// $client->close(); -//}); - -//echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/client/c10k.php b/examples/client/c10k.php new file mode 100644 index 00000000000..7f4423bd766 --- /dev/null +++ b/examples/client/c10k.php @@ -0,0 +1,23 @@ + 0) { + continue; + } else { + for ($i = 0; $i < 9999; $i++) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $ret = $client->connect('127.0.0.1', 9501, 0.5); + if (!$ret) { + echo "#$i\tConnect fail. errno=" . $client->errCode; + die("\n"); + } + $clients[] = $client; + usleep(10); + } + echo "Worker #" . posix_getpid() . " connect $i finish\n"; + sleep(1000); + exit; + } +} +sleep(1000); diff --git a/examples/client/get_socket.php b/examples/client/get_socket.php index 9b23d9d7543..4fa0eff243d 100644 --- a/examples/client/get_socket.php +++ b/examples/client/get_socket.php @@ -2,7 +2,7 @@ function getClient() { - $client = new swoole_client(SWOOLE_SOCK_TCP); + $client = new Swoole\Client(SWOOLE_SOCK_TCP); if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); @@ -17,7 +17,7 @@ function getClient() $count = 0; //$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) diff --git a/examples/client/long_tcp.php b/examples/client/long_tcp.php index 69aaaef9683..1667ed0d235 100644 --- a/examples/client/long_tcp.php +++ b/examples/client/long_tcp.php @@ -1,7 +1,7 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/client/recv_1m.php b/examples/client/recv_1m.php new file mode 100644 index 00000000000..82d3f8bded9 --- /dev/null +++ b/examples/client/recv_1m.php @@ -0,0 +1,27 @@ +connect('127.0.0.1', 9509, 60); +$c->send("AAAAAAAAAAAAAAAA"); + +$n_bytes = 0; + +while (true) +{ + $line = $c->recv(); + if ($line === false) + { + echo "recv failed.\n"; + break; + } + elseif (empty($line)) + { + echo "recv $n_bytes bytes\n"; + break; + } + else + { + fwrite($f, $line); + $n_bytes += strlen($line); + } +} diff --git a/examples/client/recv_file.php b/examples/client/recv_file.php new file mode 100644 index 00000000000..1af8d443d57 --- /dev/null +++ b/examples/client/recv_file.php @@ -0,0 +1,36 @@ +connect($server_ip, 9501, 5); +$filesize = intval($cli->recv()); +if ($filesize == 0) { + die("get file size failed.\n"); +} +echo "file_size = $filesize\n"; +$content = ''; +$cli->send("get file"); + +$use_waitall = false; + +if ($use_waitall) { + //waitall,需要一次性分配内存,适合小一点的文件 + $content = $cli->recv($filesize, true); +} else { + //循环接收,适合大型文件 + while (1) { + //超大文件接收,这里需要改成分段写磁盘 + $content .= $cli->recv(); + if (strlen($content) == $filesize) { + break; + } + } +} +file_put_contents(__DIR__ . "/recv_file_" . time() . ".jpg", $content); +echo "recv " . strlen($content) . " byte data\n"; +echo "used " . ((microtime(true) - $start_ms) * 1000) . "ms\n"; +$cli->close(); diff --git a/examples/client/select.php b/examples/client/select.php index be4ce20d92f..98d23a43dd1 100644 --- a/examples/client/select.php +++ b/examples/client/select.php @@ -1,30 +1,23 @@ connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Connect Server fail.errCode=".$client->errCode; - } - else - { - $client->send("HELLO WORLD\n"); - $clients[$client->sock] = $client; + if (!$ret) { + echo "Connect Server fail.errCode=" . $client->errCode; + } else { + $client->send("HELLO WORLD\n"); + $clients[$client->sock] = $client; } } -while (!empty($clients)) -{ +while (!empty($clients)) { $write = $error = array(); $read = array_values($clients); $n = swoole_client_select($read, $write, $error, 0.6); - if ($n > 0) - { - foreach ($read as $index => $c) - { + if ($n > 0) { + foreach ($read as $index => $c) { echo "Recv #{$c->sock}: " . $c->recv() . "\n"; unset($clients[$c->sock]); } diff --git a/examples/client/simple.php b/examples/client/simple.php new file mode 100644 index 00000000000..2756cdc435e --- /dev/null +++ b/examples/client/simple.php @@ -0,0 +1,20 @@ +connect('127.0.0.1', 9501, 0.5, 0); + if (!$ret) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + $clients[] = $client; +} +sleep(1); +while (1) { + foreach ($clients as $client) { + $client->send("sss"); + $data = $client->recv(); + var_dump($data); + } + sleep(1); +} diff --git a/examples/client/sync.php b/examples/client/sync.php index f251eb1357a..d93713d21fc 100644 --- a/examples/client/sync.php +++ b/examples/client/sync.php @@ -1,14 +1,13 @@ set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); -//$client = new swoole_client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 +//$client = new Swoole\Client(SWOOLE_SOCK_UNIX_DGRAM, SWOOLE_SOCK_SYNC); //同步阻塞 //if (!$client->connect(dirname(__DIR__).'/server/svr.sock', 0, -1, 1)) do_connect: -if (!$client->connect('127.0.0.1', 9501, -1)) -{ +if (!$client->connect('127.0.0.1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } @@ -23,7 +22,6 @@ $client->close(); $count++; -if ($count < 20) -{ +if ($count < 20) { goto do_connect; } diff --git a/examples/client/udp_async.php b/examples/client/udp_async.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/client/udp_async.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/client/udp_sync.php b/examples/client/udp_sync.php index 03c2847fcfb..55eb8507fc1 100644 --- a/examples/client/udp_sync.php +++ b/examples/client/udp_sync.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9502); for ($i = 0; $i < 100; $i++) diff --git a/examples/client2.php b/examples/client2.php deleted file mode 100644 index e7671133774..00000000000 --- a/examples/client2.php +++ /dev/null @@ -1,21 +0,0 @@ -connect('127.0.0.1', 9501, 0.5, 0); - if(!$ret) - { - echo "Over flow. errno=".$client->errCode; - die("\n"); - } - $clients[] = $client; -} -sleep(1); -while (1) { - foreach ($clients as $client) { - $client->send("sss"); - $data = $client->recv(); - var_dump($data); - } - sleep(1); -} diff --git a/examples/coroutine/TestHttpServ.php b/examples/coroutine/TestHttpServ.php deleted file mode 100644 index d2f796b7d6e..00000000000 --- a/examples/coroutine/TestHttpServ.php +++ /dev/null @@ -1,117 +0,0 @@ -setting = $setting; - } - - /** - * [init description] - * @return [type] [description] - */ - public function init(){ - - if (!isset($this ->setting['host'])) { - $this ->setting['host'] = '0.0.0.0'; - } - if (!isset($this ->setting['port'])) { - $this ->setting['port'] = '9999'; - } - - $this ->http = new swoole_http_server($this ->setting['host'], $this ->setting['port']); - $this ->http ->set($this ->setting); - - $this ->http ->on('request', array($this, 'onRequest')); - $this ->http ->on('close', array($this, 'onClose')); - } - - /** - * [onRequest description] - * @param [type] $request [description] - * @param [type] $response [description] - * @return [type] [description] - */ - public function onRequest($request, $response){ - - // $udp = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_ASYNC); - // $udp->on("connect", function(swoole_client $cli) { - // $cli->send("udp test"); - // }); - // $udp->on("receive", function(swoole_client $cli, $data)use($response){ - - $tcp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $tcp->on("connect", function(swoole_client $cli) { - $cli->send("tcp test"); - }); - $tcp->on("receive", function(swoole_client $cli, $data)use($response){ - $response ->end("

swoole response

"); - }); - $tcp->on("close", function(swoole_client $cli){ - }); - $tcp->on("error", function(swoole_client $cli){ - }); - $tcp->connect('10.100.64.151', 9805); - - // }); - // $udp->on("close", function(swoole_client $cli){ - // }); - // $udp->connect('10.100.65.222', 9906); - - } - - /** - * [onClose description] - * @param [type] $server [description] - * @param [type] $fd [description] - * @param [type] $reactor_id [description] - * @return [type] [description] - */ - public function onClose($server, $fd, $reactor_id){ - - //echo " on close fd = $fd reactor_id = $reactor_id \n"; - } - - /** - * [start description] - * @return [type] [description] - */ - public function start(){ - - $this ->init(); - $this ->http ->start(); - } -} - -$setting = array( - 'host' => '0.0.0.0', - 'port' => 10005, - 'worker_num' => 4, - 'dispatch_mode' => 2, //固定分配请求到worker - 'reactor_num' => 4, //亲核 - 'daemonize' => 1, //守护进程 - 'backlog' => 128, - 'log_file' => '/data/log/test_http_server.log', -); -$th = new TestHttpServer(); -$th ->set($setting); -$th ->start(); diff --git a/examples/coroutine/before_server_start.php b/examples/coroutine/before_server_start.php index 5cac5fab427..4c9b5743ec6 100644 --- a/examples/coroutine/before_server_start.php +++ b/examples/coroutine/before_server_start.php @@ -3,7 +3,7 @@ { co::sleep(1); - $http = new swoole_http_server("127.0.0.1", 9501, SWOOLE_BASE); + $http = new Swoole\Http\Server("127.0.0.1", 9501, SWOOLE_BASE); $http->on("start", function ($server) { diff --git a/examples/coroutine/behavior/for.php b/examples/coroutine/behavior/for.php index 9a62a196634..b7b77b0737b 100644 --- a/examples/coroutine/behavior/for.php +++ b/examples/coroutine/behavior/for.php @@ -1,5 +1,5 @@ true]); $start = microtime(1); echo "start\n"; $flag = 1; diff --git a/examples/coroutine/behavior/preemptive_timer.php b/examples/coroutine/behavior/preemptive_timer.php new file mode 100644 index 00000000000..0f48afb7e1e --- /dev/null +++ b/examples/coroutine/behavior/preemptive_timer.php @@ -0,0 +1,24 @@ + true]); +go(function (){ + $exit = false; + while (true){ + $res = Swoole\Coroutine::stats(); + $num = $res['coroutine_num']; + if ($num < 10){ + go(function () use(&$exit){ + echo "cid:".Swoole\Coroutine::getCid()." start\n"; + Swoole\Coroutine::sleep(1); + echo "cid ".Swoole\Coroutine::getCid()." end\n"; + $exit = true; + }); + } + if ($exit) { + echo "cid ".Swoole\Coroutine::getCid()." break\n"; + break; + } + } + echo "cid ".Swoole\Coroutine::getCid()." exit\n"; +}); +echo "main end\n"; + diff --git a/examples/coroutine/channel/test.php b/examples/coroutine/channel/test.php index 0ea427374c2..582fb191bea 100644 --- a/examples/coroutine/channel/test.php +++ b/examples/coroutine/channel/test.php @@ -15,7 +15,7 @@ function BatchExecMethodByCo() $list[$key] = $channel->pop(); } }); - swoole_event_wait(); + Swoole\Event::wait(); return $list; } function test($value='') @@ -29,4 +29,4 @@ function test2($value='') return "test2 ".rand(1,10)."\n"; } $r = BatchExecMethodByCo("test","test2","test"); -var_dump($r); \ No newline at end of file +var_dump($r); diff --git a/examples/coroutine/client_send_yield.php b/examples/coroutine/client_send_yield.php index 87e955c2f13..edad3ee8b4d 100644 --- a/examples/coroutine/client_send_yield.php +++ b/examples/coroutine/client_send_yield.php @@ -24,4 +24,4 @@ var_dump($client->errCode); }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/coroutine/coro_channel.php b/examples/coroutine/coro_channel.php index 18938ce5983..16052d13491 100644 --- a/examples/coroutine/coro_channel.php +++ b/examples/coroutine/coro_channel.php @@ -1,14 +1,14 @@ set(array( 'log_file' => '/dev/null' )); use Swoole\Coroutine as co; -// $http->on("WorkerStart", function (\swoole_server $serv) +// $http->on("WorkerStart", function (\Swoole\Server $serv) // { // // }); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $ch = new co\Channel(1); $out = new co\Channel(1); diff --git a/examples/coroutine/coro_empty.php b/examples/coroutine/coro_empty.php index 2a869b09f22..5b57ae8cbf7 100644 --- a/examples/coroutine/coro_empty.php +++ b/examples/coroutine/coro_empty.php @@ -1,8 +1,6 @@ 1]); -var_dump(SWOOLE_CORO_SCHEDULE); co::create(function () { echo "no coro exit\n"; }); diff --git a/examples/coroutine/coro_sleep.php b/examples/coroutine/coro_sleep.php index 50468f7e791..e89af375ba1 100644 --- a/examples/coroutine/coro_sleep.php +++ b/examples/coroutine/coro_sleep.php @@ -7,3 +7,4 @@ echo "OK\n"; }); echo "11\n"; +Swoole\Event::wait(); diff --git a/examples/coroutine/csp.php b/examples/coroutine/csp.php index 464b7e4e4dd..015f0f9198e 100644 --- a/examples/coroutine/csp.php +++ b/examples/coroutine/csp.php @@ -1,5 +1,5 @@ on('request', function ($req, $resp) { $chan = new chan(2); diff --git a/examples/coroutine/enable_coroutine.php b/examples/coroutine/enable_coroutine.php index 4fb53f46f37..17e24077e69 100644 --- a/examples/coroutine/enable_coroutine.php +++ b/examples/coroutine/enable_coroutine.php @@ -3,7 +3,7 @@ use Swoole\Http\Request; use Swoole\Http\Response; -$http = new swoole_http_server('127.0.0.1', 9501); +$http = new Swoole\Http\Server('127.0.0.1', 9501); $http->set([ 'enable_coroutine' => false, // close build-in coroutine diff --git a/examples/coroutine/exit_with_status.php b/examples/coroutine/exit_with_status.php index df77d724d30..ae723bbcb08 100644 --- a/examples/coroutine/exit_with_status.php +++ b/examples/coroutine/exit_with_status.php @@ -8,5 +8,5 @@ $exit_status = $e->getStatus(); } }); -swoole_event_wait(); +Swoole\Event::wait(); exit($exit_status); diff --git a/examples/coroutine/http/server.php b/examples/coroutine/http/server.php index 0d3bcb757b8..05928dafdd5 100644 --- a/examples/coroutine/http/server.php +++ b/examples/coroutine/http/server.php @@ -1,6 +1,10 @@ SWOOLE_TRACE_HTTP2, + 'log_level' => 0, +]); go(function () { - $server = new Co\Http\Server("127.0.0.1", 9501, $ssl); + $server = new Co\Http\Server("127.0.0.1", 9501, false); /** * 静态文件处理器 */ @@ -33,6 +37,7 @@ * Http应用 */ $server->handle('/', function ($request, $response) { + var_dump($request); var_dump($request->get); $response->end("

hello world

"); }); @@ -45,4 +50,4 @@ $server->start(); }); -swoole_event_wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/coroutine/http/write_func.php b/examples/coroutine/http/write_func.php new file mode 100644 index 00000000000..e8beabc6e49 --- /dev/null +++ b/examples/coroutine/http/write_func.php @@ -0,0 +1,14 @@ + SWOOLE_TRACE_HTTP2, + 'log_level' => 0, +]); +Co\run(function () { + $client = new Swoole\Coroutine\Http\Client('www.jd.com', 443, true); + $client->set(['write_func' => function($client, $data) { + var_dump(strlen($data)); + }]); + $client->get('/'); + var_dump(strlen($client->getBody())); + return 0; +}); diff --git a/examples/coroutine/http_download.php b/examples/coroutine/http_download.php index d5d3d217936..391d57608fc 100644 --- a/examples/coroutine/http_download.php +++ b/examples/coroutine/http_download.php @@ -1,7 +1,10 @@ set(['timeout' => -1]); $cli->setHeaders([ 'Host' => $host, @@ -9,5 +12,5 @@ 'Accept' => '*', 'Accept-Encoding' => 'gzip' ]); - $cli->download('/static/files/swoole-logo.svg', __DIR__ . '/logo.svg'); + $cli->download('/dist/skin1/images/logo-white.png', '/tmp/logo.png'); }); diff --git a/examples/coroutine/join.php b/examples/coroutine/join.php new file mode 100644 index 00000000000..4431e7e6af0 --- /dev/null +++ b/examples/coroutine/join.php @@ -0,0 +1,36 @@ + true) + ); + echo "connected\n"; + sleep(30); + echo "sleep 30\n"; + $pdo->exec("SELECT sleep(1)"); +}); + +echo "DONE\n"; diff --git a/examples/coroutine/proc_open.php b/examples/coroutine/proc_open.php new file mode 100644 index 00000000000..f98bd81bad2 --- /dev/null +++ b/examples/coroutine/proc_open.php @@ -0,0 +1,20 @@ + array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w"), + ); + + $process = proc_open('unknown', $descriptorspec, $pipes); + + var_dump($pipes); + + var_dump(fread($pipes[2], 8192)); + + $return_value = proc_close($process); + + echo "command returned $return_value\n"; +}); \ No newline at end of file diff --git a/examples/coroutine/send_yield_client.php b/examples/coroutine/send_yield_client.php index 006634ace7d..f8caa3e34af 100644 --- a/examples/coroutine/send_yield_client.php +++ b/examples/coroutine/send_yield_client.php @@ -1,5 +1,5 @@ set(array( 'kernel_socket_buffer_size' => 65536, )); diff --git a/examples/coroutine/server/tcp.php b/examples/coroutine/server/tcp.php index 41af52d1a6c..f22e44cc753 100644 --- a/examples/coroutine/server/tcp.php +++ b/examples/coroutine/server/tcp.php @@ -21,4 +21,4 @@ $server->start(); }); -swoole_event::wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/coroutine/task_co.php b/examples/coroutine/task_co.php index ad5a4e21a55..cd1e5c25fbe 100644 --- a/examples/coroutine/task_co.php +++ b/examples/coroutine/task_co.php @@ -6,7 +6,7 @@ 'task_worker_num' => 2, ]); -$server->on('Task', function (swoole_server $serv, $task_id, $worker_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $worker_id, $data) { echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; if ($serv->worker_id == 1) { sleep(1); @@ -14,7 +14,7 @@ return $data; }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/coroutine/tcp_echo.php b/examples/coroutine/tcp_echo.php index 02da4c53b65..805d6f7b861 100644 --- a/examples/coroutine/tcp_echo.php +++ b/examples/coroutine/tcp_echo.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd]: Connect.\n"; }); @@ -8,7 +8,7 @@ )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "{$data}\n") == false) { diff --git a/examples/coroutine/timer_test.php b/examples/coroutine/timer_test.php index 472c44022d3..9d8a1bb93fc 100755 --- a/examples/coroutine/timer_test.php +++ b/examples/coroutine/timer_test.php @@ -6,11 +6,11 @@ * @Last Modified time: 2016-06-26 16:41:46 */ -swoole_timer_after(1000, function(){ +Swoole\Timer::after(1000, function(){ echo " timer after timeout\n"; }); -swoole_timer_tick(1000, function(){ +Swoole\Timer::tick(1000, function(){ echo "timer tick timeout\n"; }); ?> diff --git a/examples/coroutine/udp_client.php b/examples/coroutine/udp_client.php index 87dc798f4eb..eff7508b268 100755 --- a/examples/coroutine/udp_client.php +++ b/examples/coroutine/udp_client.php @@ -15,7 +15,7 @@ public function sendRequest() public function send() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send($this->data); $ret = $cli->recv(); @@ -24,15 +24,15 @@ public function send() public function moreThanOneRecv() { - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli->connect($this->ip, self::PORT); $cli->send("sent by cli"); - $cli2 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli2 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli2->connect($this->ip, self::PORT); $cli2->send("sent by cli2"); - $cli3 = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli3 = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $ret = $cli3->connect($this->ip, self::PORT); $cli3->send("sent by cli3"); @@ -50,7 +50,7 @@ class Server public function run() { - $this->server = new swoole_http_server("127.0.0.1", 9502); + $this->server = new Swoole\Http\Server("127.0.0.1", 9502); $this->server->set([ 'worker_num' => 1, 'daemonize' => true, @@ -63,7 +63,7 @@ public function run() public static function onRequest($request, $response) { self::staticFunc(); - $cli = new swoole_client_coro(SWOOLE_SOCK_UDP); + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); $client = new Client(); $ret = $client->sendRequest(); $response->end($ret); diff --git a/examples/coroutine/websocket/client.php b/examples/coroutine/websocket/client.php index 47fc094f5cf..2221f9e3fd6 100644 --- a/examples/coroutine/websocket/client.php +++ b/examples/coroutine/websocket/client.php @@ -10,9 +10,8 @@ // co::sleep(0.1); // } // } - // }); -go(function () { +Co\Run(function () { $cli = new Co\http\Client("127.0.0.1", 9501); $cli->set([ 'timeout' => 1 diff --git a/examples/coroutine/websocket/server.php b/examples/coroutine/websocket/server.php index 8c245d2b23c..ec5fbd43142 100644 --- a/examples/coroutine/websocket/server.php +++ b/examples/coroutine/websocket/server.php @@ -1,13 +1,13 @@ set(array( 'log_file' => '/dev/null' )); -$ws->on("WorkerStart", function (\swoole_server $serv) { +$ws->on("WorkerStart", function (\Swoole\Server $serv) { }); -$ws->on('open', function ($serv, swoole_http_request $request) { +$ws->on('open', function ($serv, Swoole\Http\Request $request) { //$ip = co::gethostbyname('www.baidu.com'); if (1) { $serv->push($request->fd, "start\n"); diff --git a/examples/cpp/Makefile b/examples/cpp/Makefile new file mode 100644 index 00000000000..b3bc0fab6ce --- /dev/null +++ b/examples/cpp/Makefile @@ -0,0 +1,10 @@ +all: co repeat test_server + +co: co.cc + g++ co.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o co -g +repeat: repeat.cc + g++ repeat.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o repeat -g +test_server: test_server.cc + g++ test_server.cc -I../.. -I../../include -DHAVE_CONFIG_H -L../../lib -lswoole -o test_server -g +clean: + rm -f co repeat test_server diff --git a/examples/cpp/co.cc b/examples/cpp/co.cc index 6dde6b2bf67..ffd18c82b05 100644 --- a/examples/cpp/co.cc +++ b/examples/cpp/co.cc @@ -1,31 +1,111 @@ #include -#include "coroutine.h" -#include "coroutine_system.h" +#include +#include +#include + +#include "swoole_coroutine.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" using swoole::Coroutine; +using swoole::coroutine::Socket; using swoole::coroutine::System; using namespace std; -int main(int argc, char **argv) -{ - cout << "event init" << endl; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; +list q; +list slaves; +size_t qs; + +int main(int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + + Coroutine::run([](void *arg) { + Coroutine::create([](void *arg) { + System::sleep(2.0); + cout << "CO-1, sleep 2\n"; + }); + + Coroutine::create([](void *arg) { + System::sleep(1); + cout << "CO-2, sleep 1\n"; + }); + + Coroutine::create([](void *arg) { + cout << "CO-3, listen tcp:0.0.0.0:9001\n"; + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9001); + s.listen(); + + while (1) { + Socket *_client = s.accept(); + Coroutine::create( + [](void *arg) { + Socket *client = (Socket *) arg; + while (1) { + char buf[1024]; + auto retval = client->recv(buf, sizeof(buf)); + if (retval == 0) { + cout << "connection close\n"; + break; + } else { + if (strncasecmp("push", buf, 4) == 0) { + q.push_back(string(buf + 5, retval - 5)); + qs += retval - 5; + string resp("OK\n"); + client->send(resp.c_str(), resp.length()); + + for (auto it = slaves.begin(); it != slaves.end();) { + auto sc = *it; + auto n = sc->send(buf, retval); + if (n <= 0) { + it = slaves.erase(it); + delete sc; + } else { + it++; + } + } + } else if (strncasecmp("pop", buf, 3) == 0) { + if (q.empty()) { + string resp("EMPTY\n"); + client->send(resp.c_str(), resp.length()); + } else { + auto data = q.front(); + q.pop_front(); + qs -= data.length(); + client->send(data.c_str(), data.length()); + } + } else if (strncasecmp("stat", buf, 4) == 0) { + char stat_buf[64]; + int n = snprintf(stat_buf, sizeof(stat_buf), "count=%ld,bytes=%ld\n", q.size(), qs); + client->send(stat_buf, n); + } else { + string resp("ERROR\n"); + client->send(resp.c_str(), resp.length()); + } + } + } + delete client; + }, + _client); + } + }); - cout << "create coroutine" << endl; - Coroutine::create([](void *arg) { - cout << "co1 sleep begin" << endl; Coroutine::create([](void *arg) { - cout << "co2 sleep begin" << endl; - System::sleep(0.5); - cout << "co2 sleep end" << endl; + Socket s(SW_SOCK_TCP); + s.bind("0.0.0.0", 9002); + s.listen(); + while (1) { + Socket *_client = s.accept(); + if (_client == nullptr) { + break; + } + for (auto data : q) { + _client->send(data.c_str(), data.length()); + } + slaves.push_back(_client); + } }); - System::sleep(1); - cout << "co1 sleep end" << endl; }); - cout << "event wait" << endl; - swoole_event_wait(); - cout << "event free" << endl; return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/mt_co.cc b/examples/cpp/mt_co.cc deleted file mode 100644 index adffb81628f..00000000000 --- a/examples/cpp/mt_co.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include "coroutine.h" -#include "coroutine_socket.h" -#include "coroutine_system.h" - -using swoole::Coroutine; -using swoole::coroutine::System; -using swoole::coroutine::Socket; - -#include -#include - -struct co_param -{ - int port; -}; - -const int THREAD_N = 4; - -void co_thread(int i) -{ - swoole_event_init(); - - co_param param = {9901 + i}; - - Coroutine::create([](void *param) - { - //read file test - System::read_file("/tmp/test.txt"); - - co_param *_param = ( co_param *)param; - int port = _param->port; - - Socket sock(SW_SOCK_TCP); - bool retval = sock.bind("127.0.0.1", port); - sock.listen(128); - - while(true) - { - Socket *conn = sock.accept(); - if (!conn) - { - if (sock.errCode == ECANCELED) - { - break; - } - System::sleep(1); - printf("accept error, errno=%d\n", sock.errCode); - continue; - } - printf("accept new connection\n"); - Coroutine::create([](void *_sock) - { - printf("new coroutine\n"); - Socket *conn = (Socket *) _sock; - while (true) - { - char buf[1204]; - printf("recv \n"); - ssize_t retval = conn->recv(buf, sizeof(buf) -1); - if (retval <= 0) - { - printf("recv error, errno=%d\n", conn->errCode); - break; - } - else - { - System::sleep(1); - size_t n = sw_snprintf(buf, sizeof(buf), "hello, cid=%d, tid=%ld\n", Coroutine::get_current_cid(), pthread_self()); - conn->send(buf, n); - } - } - delete conn; - }, conn); - } - }, ¶m); - - swoole_event_wait(); -} - -int main(int argc, char **argv) -{ - SwooleG.aio_worker_num = SwooleG.aio_core_worker_num = 2; - - std::thread threads[THREAD_N]; - for (int i = 0; i < THREAD_N; ++i) - { - threads[i] = std::thread(co_thread, i); - } - - std::cout << "Done spawning threads. Now waiting for them to join:\n"; - for (int i = 0; i < THREAD_N; ++i) - { - threads[i].join(); - } - - std::cout << "All threads joined!\n"; - return 0; -} diff --git a/examples/cpp/repeat.cc b/examples/cpp/repeat.cc new file mode 100644 index 00000000000..6d79537be48 --- /dev/null +++ b/examples/cpp/repeat.cc @@ -0,0 +1,66 @@ +#include "swoole_server.h" +using namespace swoole; + +int main(int argc, char **argv) { + swoole_init(); + + enum Server::Mode factory_mode; + if (argc > 1) { + factory_mode = Server::MODE_PROCESS; + } else { + factory_mode = Server::MODE_BASE; + } + + for (int i = 0; i < 2; i++) { + Server serv(factory_mode); + + serv.reactor_num = 1; + serv.worker_num = 1; + + serv.onReceive = [](Server *serv, RecvData *req) { return SW_OK; }; + + serv.onPacket = [](Server *serv, RecvData *req) { return SW_OK; }; + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d, serv=%p,", worker->id, getpid(), serv); + swoole_timer_after( + 1000, + [serv](Timer *, TimerNode *tnode) { + printf("timer=%p\n", tnode); + if (serv->is_base_mode()) { + kill(getpid(), SIGTERM); + } else { + kill(serv->gs->master_pid, SIGTERM); + } + }, + nullptr); + }; + + serv.add_port(SW_SOCK_UDP, "0.0.0.0", 9502); + serv.add_port(SW_SOCK_TCP6, "::", 9503); + serv.add_port(SW_SOCK_UDP6, "::", 9504); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_eof_check = 0; + // config + port->backlog = 128; + memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); + + if (serv.create()) { + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); + exit(1); + } + + if (serv.start() < 0) { + swoole_warning("start server fail[error=%d]", swoole_get_last_error()); + exit(3); + } + } + + return 0; +} diff --git a/examples/cpp/test_server.c b/examples/cpp/test_server.c deleted file mode 100644 index 88ab1ba451d..00000000000 --- a/examples/cpp/test_server.c +++ /dev/null @@ -1,214 +0,0 @@ -/** - * cmake . - * make test_server - * ./bin/test_server - */ -#include "server.h" - -int my_onPacket(swServer *serv, swEventData *req); -int my_onReceive(swServer *serv, swEventData *req); -void my_onStart(swServer *serv); -void my_onShutdown(swServer *serv); -void my_onConnect(swServer *serv, swDataHead *info); -void my_onClose(swServer *serv, swDataHead *info); -void my_onWorkerStart(swServer *serv, int worker_id); -void my_onWorkerStop(swServer *serv, int worker_id); - -static int g_receive_count = 0; - -int main(int argc, char **argv) -{ - int ret; - swServer serv; - swServer_init(&serv); - - serv.reactor_num = 4; - serv.worker_num = 2; - - serv.factory_mode = SW_MODE_BASE; - serv.max_connection = 10000; - //serv.open_cpu_affinity = 1; - //serv.open_tcp_nodelay = 1; - //serv.daemonize = 1; - //memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); - - serv.dispatch_mode = 2; - //serv.open_tcp_keepalive = 1; - -#ifdef HAVE_OPENSSL - //serv.ssl_cert_file = "tests/ssl/ssl.crt"; - //serv.ssl_key_file = "tests/ssl/ssl.key"; - //serv.open_ssl = 1; -#endif - - serv.onStart = my_onStart; - serv.onShutdown = my_onShutdown; - serv.onConnect = my_onConnect; - serv.onReceive = my_onReceive; - serv.onPacket = my_onPacket; - serv.onClose = my_onClose; - serv.onWorkerStart = my_onWorkerStart; - serv.onWorkerStop = my_onWorkerStop; - - // swSignal_add(SIGINT, user_signal); - - //create Server - ret = swServer_create(&serv); - if (ret < 0) - { - swWarn("create server fail[error=%d]", SwooleG.error); - exit(1); - } - - swListenPort *port = swServer_add_port(&serv, SW_SOCK_TCP, "127.0.0.1", 9501); - if (!port) - { - swWarn("listen failed, [error=%d]", SwooleG.error); - exit(2); - } - - port->open_eof_check = 0; - //config - port->backlog = 128; - memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); - - swServer_add_port(&serv, SW_SOCK_UDP, "0.0.0.0", 9502); - swServer_add_port(&serv, SW_SOCK_TCP6, "::", 9503); - swServer_add_port(&serv, SW_SOCK_UDP6, "::", 9504); - - ret = swServer_start(&serv); - if (ret < 0) - { - swWarn("start server fail[error=%d]", ret); - exit(3); - } - return 0; -} - -void my_onWorkerStart(swServer *serv, int worker_id) -{ - swNotice("WorkerStart[%d]PID=%d", worker_id, getpid()); -} - -void my_onWorkerStop(swServer *serv, int worker_id) -{ - swNotice("WorkerStop[%d]PID=%d", worker_id, getpid()); -} - -int my_onReceive(swServer *serv, swEventData *req) -{ - int ret; - char resp_data[SW_IPC_BUFFER_SIZE]; - - g_receive_count++; - - swPacket_ptr *req_pkg = (swPacket_ptr *)req; - swConnection *conn = swWorker_get_connection(serv, req_pkg->info.fd); - - swoole_rtrim(req_pkg->data.str, req_pkg->data.length); - swNotice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", g_receive_count, - swConnection_get_ip(conn->socket_type, &conn->info), - swConnection_get_port(conn->socket_type, &conn->info), - req_pkg->data.str, req_pkg->data.length); - - int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", - req_pkg->data.length, req_pkg->data.str); - - ret = serv->send(serv, req->info.fd, resp_data, n); - if (ret < 0) - { - swNotice("send to client fail. errno=%d", errno); - } - else - { - swNotice("send %d bytes to client success. data=%s", n, resp_data); - } - return SW_OK; -} - -int my_onPacket(swServer *serv, swEventData *req) -{ - char *data; - int length; - char address[256]; - int port = 0; - int ret; - - swDgramPacket *packet; - - swWorker_get_data(serv, req, &data); - packet = (swDgramPacket*) data; - - int serv_sock = req->info.server_fd; - - if (packet->socket_type == SW_SOCK_UDP) - { - inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); - port = ntohs(packet->socket_addr.addr.inet_v4.sin_port); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address)); - port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - strcpy(address, packet->socket_addr.addr.un.sun_path); - } - else - { - abort(); - } - - data = packet->data; - length = packet->length; - - swNotice("Packet[client=%s:%d, %d bytes]: data=%.*s", address, port, length, length, data); - - char resp_data[SW_IPC_BUFFER_SIZE]; - int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s", length, data); - - if (packet->socket_type == SW_SOCK_UDP) - { - ret = swSocket_udp_sendto(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - ret = swSocket_udp_sendto6(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - ret = swSocket_unix_sendto(serv_sock, address, resp_data, n); - } - - if (ret < 0) - { - swNotice("send to client fail. errno=%d", errno); - } - else - { - swNotice("send %d bytes to client success. data=%s", n, resp_data); - } - - return SW_OK; -} - -void my_onStart(swServer *serv) -{ - swNotice("Server is running"); -} - -void my_onShutdown(swServer *serv) -{ - swNotice("Server is shutdown\n"); -} - -void my_onConnect(swServer *serv, swDataHead *info) -{ - swNotice("PID=%d\tConnect fd=%d|reactor_id=%d", getpid(), info->fd, info->reactor_id); -} - -void my_onClose(swServer *serv, swDataHead *info) -{ - swNotice("PID=%d\tClose fd=%d|reactor_id=%d", getpid(), info->fd, info->reactor_id); -} diff --git a/examples/cpp/test_server.cc b/examples/cpp/test_server.cc new file mode 100644 index 00000000000..32e44d2c826 --- /dev/null +++ b/examples/cpp/test_server.cc @@ -0,0 +1,175 @@ +/** + * cmake . + * make test_server + * ./bin/test_server + */ +#include "swoole_server.h" +#include "swoole_util.h" + +using namespace swoole; + +int my_onPacket(Server *serv, RecvData *req); +int my_onReceive(Server *serv, RecvData *req); +void my_onStart(Server *serv); +void my_onShutdown(Server *serv); +void my_onConnect(Server *serv, DataHead *info); +void my_onClose(Server *serv, DataHead *info); +void my_onWorkerStart(Server *serv, Worker *worker); +void my_onWorkerStop(Server *serv, Worker *worker); + +static int g_receive_count = 0; + +int main(int argc, char **argv) { + swoole_init(); + + sw_logger()->set_date_format("%F %T"); + sw_logger()->set_date_with_microseconds(true); + + Server serv(Server::MODE_BASE); + + serv.reactor_num = 4; + serv.worker_num = 1; + + serv.set_max_connection(10000); + // serv.open_cpu_affinity = 1; + // serv.open_tcp_nodelay = 1; + // serv.daemonize = 1; + // memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); + + serv.dispatch_mode = 2; + // serv.open_tcp_keepalive = 1; + +#ifdef HAVE_OPENSSL + // serv.ssl_cert_file = "tests/ssl/ssl.crt"; + // serv.ssl_key_file = "tests/ssl/ssl.key"; + // serv.open_ssl = 1; +#endif + + serv.onStart = my_onStart; + serv.onShutdown = my_onShutdown; + serv.onConnect = my_onConnect; + serv.onReceive = my_onReceive; + serv.onPacket = my_onPacket; + serv.onClose = my_onClose; + serv.onWorkerStart = my_onWorkerStart; + serv.onWorkerStop = my_onWorkerStop; + + // swSignal_set(SIGINT, user_signal); + + serv.add_port(SW_SOCK_UDP, "0.0.0.0", 9502); + serv.add_port(SW_SOCK_TCP6, "::", 9503); + serv.add_port(SW_SOCK_UDP6, "::", 9504); + + swListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_eof_check = 0; + // config + port->backlog = 128; + memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); + + if (serv.create()) { + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); + exit(1); + } + + if (serv.start() < 0) { + swoole_warning("start server fail[error=%d]", swoole_get_last_error()); + exit(3); + } + return 0; +} + +void my_onWorkerStart(Server *serv, Worker *worker) { + swoole_notice("WorkerStart[%d]PID=%d", worker->id, worker->pid); +} + +void my_onWorkerStop(Server *serv, Worker *worker) { + swoole_notice("WorkerStop[%d]PID=%d", worker->id, worker->pid); +} + +int my_onReceive(Server *serv, RecvData *req) { + char req_data[SW_IPC_BUFFER_SIZE]; + char resp_data[SW_IPC_BUFFER_SIZE]; + + g_receive_count++; + + Connection *conn = serv->get_connection_by_session_id(req->info.fd); + + memcpy(req_data, req->data, req->info.len); + swoole::rtrim(req_data, req->info.len); + swoole_notice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", + g_receive_count, + conn->info.get_ip(), + conn->info.get_port(), + req_data, + req->info.len); + + int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", req->info.len, req_data); + + if (!serv->send(req->info.fd, resp_data, n)) { + swoole_notice("send to client fail. errno=%d", errno); + } else { + swoole_notice("send %d bytes to client success. data=%s", n, resp_data); + } + return SW_OK; +} + +int my_onPacket(Server *serv, RecvData *req) { + char address[256]; + int port = 0; + int ret = 0; + + DgramPacket *packet = (DgramPacket *) req->data; + + auto serv_socket = serv->get_server_socket(req->info.server_fd); + + if (packet->socket_type == SW_SOCK_UDP) { + inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); + port = ntohs(packet->socket_addr.addr.inet_v4.sin_port); + } else if (packet->socket_type == SW_SOCK_UDP6) { + inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address)); + port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); + } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) { + strcpy(address, packet->socket_addr.addr.un.sun_path); + } else { + abort(); + } + + char *data = packet->data; + uint32_t length = packet->length; + + swoole_notice("Packet[client=%s:%d, %d bytes]: data=%.*s", address, port, length, length, data); + + char resp_data[SW_IPC_BUFFER_SIZE]; + int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s", length, data); + + ret = serv_socket->sendto(address, port, resp_data, n); + + if (ret < 0) { + swoole_notice("send to client fail. errno=%d", errno); + } else { + swoole_notice("send %d bytes to client success. data=%s", n, resp_data); + } + + return SW_OK; +} + +void my_onStart(Server *serv) { + swoole_notice("Server is running"); +} + +void my_onShutdown(Server *serv) { + swoole_notice("Server is shutdown"); +} + +void my_onConnect(Server *serv, DataHead *info) { + swoole_notice("PID=%d\tConnect fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); +} + +void my_onClose(Server *serv, DataHead *info) { + swoole_notice("PID=%d\tClose fd=%ld|reactor_id=%d", getpid(), info->fd, info->reactor_id); +} diff --git a/examples/curl/hook.php b/examples/curl/hook.php new file mode 100644 index 00000000000..0a492b42e9f --- /dev/null +++ b/examples/curl/hook.php @@ -0,0 +1,34 @@ + SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL, ]); +//Co::set(['hook_flags' => SWOOLE_HOOK_ALL, ]); + +Co\run(function () { + $n = 3; + while($n--) { + go('test'); + } +}); + +function test() { + echo "curl init\n"; + $ch = curl_init(); +// $url = 'https://www.baidu.com/'; + $url = "http://127.0.0.1:9801/"; + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { + //var_dump($ch, $strHeader); + return strlen($strHeader); + }); + + $output = curl_exec($ch); + var_dump($output); + var_dump(strlen($output)); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } +// var_dump($output); + curl_close($ch); +} diff --git a/examples/curl/multi.php b/examples/curl/multi.php new file mode 100644 index 00000000000..b53dda2f2f0 --- /dev/null +++ b/examples/curl/multi.php @@ -0,0 +1,62 @@ +set(['worker_num' => 8, ]); + +$http->on("start", function ($server) { + echo "Swoole http server is started at http://127.0.0.1:9501\n"; +}); + +$http->on("request", function ($request, $response) { + sleep(1); + $response->end("Hello World\n"); +}); + +$http->start(); diff --git a/examples/dtls/client.php b/examples/dtls/client.php new file mode 100644 index 00000000000..0e82611a309 --- /dev/null +++ b/examples/dtls/client.php @@ -0,0 +1,14 @@ +connect("127.0.0.1", 9905); + echo "connect OK\n"; + $client->send("hello world"); + echo $client->recv(); + $client->close(); + echo "END\n"; + } +); \ No newline at end of file diff --git a/examples/dtls/server.php b/examples/dtls/server.php new file mode 100644 index 00000000000..d6b8e4132aa --- /dev/null +++ b/examples/dtls/server.php @@ -0,0 +1,23 @@ +set( + [ + 'ssl_cert_file' => __DIR__ . '/../ssl/ssl.crt', + 'ssl_key_file' => __DIR__ . '/../ssl/ssl.key', + //'ssl_method' => SWOOLE_TLSv1_2_SERVER_METHOD, + 'worker_num' => 1, + //'ssl_client_cert_file' => __DIR__ . '/ca.crt', + //'ssl_verify_depth' => 10, + ] +); + +$server->on('Receive', function (Swoole\Server $serv, $fd, $tid, $data) +{ + var_dump($fd, $data, $serv->getClientInfo($fd)); + $serv->send($fd, "Swoole: $data\n"); + //echo "close dtls session\n"; + //$serv->close($fd); +}); + +$server->start(); diff --git a/examples/eof/async_client.php b/examples/eof/async_client.php deleted file mode 100644 index e935cb86d18..00000000000 --- a/examples/eof/async_client.php +++ /dev/null @@ -1,35 +0,0 @@ -send($_send); - echo "send ".strlen($_send)." bytes\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 -$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - static $i = 0; - if ($i % 100 == 1) - { - echo "received " . strlen($data) . " bytes\n"; - } - $i ++; - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/eof/client.php b/examples/eof/client.php index cad67406525..a013c99a279 100644 --- a/examples/eof/client.php +++ b/examples/eof/client.php @@ -2,33 +2,28 @@ /** * 分段发送数据 * - * @param swoole_client $client - * @param string $data - * @param int $chunk_size + * @param Swoole\Client $client + * @param string $data + * @param int $chunk_size */ -function send_chunk(swoole_client $client, $data, $chunk_size = 1024) +function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) { - $len = strlen($data); - $chunk_num = intval($len / $chunk_size) + 1; - for ($i = 0; $i < $chunk_num; $i++) - { - if ($len < ($i + 1) * $chunk_size) - { - $sendn = $len - ($i * $chunk_size); - } - else - { - $sendn = $chunk_size; - } - $client->send(substr($data, $i * $chunk_size, $sendn)); - } + $len = strlen($data); + $chunk_num = intval($len / $chunk_size) + 1; + for ($i = 0; $i < $chunk_num; $i++) { + if ($len < ($i + 1) * $chunk_size) { + $sendn = $len - ($i * $chunk_size); + } else { + $sendn = $chunk_size; + } + $client->send(substr($data, $i * $chunk_size, $sendn)); + } } -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 -if(!$client->connect('127.0.0.1', 9501, 0.5, 0)) -{ - echo "Over flow. errno=".$client->errCode; - die("\n"); +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 +if (!$client->connect('127.0.0.1', 9501, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); } //for ($i = 0; $i < 10; $i++) @@ -39,21 +34,20 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //exit; $data = array( - 'name' => __FILE__, - 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K + 'name' => __FILE__, + 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K ); $_serialize_data = serialize($data); -$_send = $_serialize_data."__doit__"; +$_send = $_serialize_data . "__doit__"; -echo "serialize_data length=".strlen($_serialize_data)."send length=".strlen($_send)."\n"; +echo "serialize_data length=" . strlen($_serialize_data) . "send length=" . strlen($_send) . "\n"; //send_chunk($client, $_send); // -if(!$client->send($_send)) -{ - die("send failed.\n"); +if (!$client->send($_send)) { + die("send failed.\n"); } //$client->send("\r\n".substr($_serialize_data, 0, 8000)); @@ -65,9 +59,8 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //usleep(500000); -if (!$client->send("\r\n\r\n")) -{ - die("send failed.\n"); +if (!$client->send("\r\n\r\n")) { + die("send failed.\n"); } echo $client->recv(); diff --git a/examples/eof/server.php b/examples/eof/server.php index a9951f231be..bbf1ab21eef 100644 --- a/examples/eof/server.php +++ b/examples/eof/server.php @@ -1,5 +1,5 @@ set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -11,21 +11,18 @@ //$serv->on('connect', function ($serv, $fd) { // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; //}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) -{ +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo '#' . $serv->worker_id . " recv: " . strlen($data) . "\n"; - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $resp = str_repeat('A', rand(10000, 50000)) . "\r\n\r\n"; $serv->send($fd, $resp); - if ($i % 100 == 1) - { + if ($i % 100 == 1) { sleep(1); - echo "send ".strlen($resp)." bytes\n"; + echo "send " . strlen($resp) . " bytes\n"; } } }); //$serv->on('close', function ($serv, $fd) { - //echo "[#" . posix_getpid() . "]\tClient: Close.\n"; +//echo "[#" . posix_getpid() . "]\tClient: Close.\n"; //}); $serv->start(); diff --git a/examples/event/cycle.php b/examples/event/cycle.php index 4282a6f2dd9..bc4a20da260 100644 --- a/examples/event/cycle.php +++ b/examples/event/cycle.php @@ -11,3 +11,5 @@ Swoole\Event::cycle(null); }); }); + +Swoole\Event::wait(); diff --git a/examples/event/inotify.php b/examples/event/inotify.php index 683b101e245..89d96619844 100644 --- a/examples/event/inotify.php +++ b/examples/event/inotify.php @@ -5,7 +5,7 @@ //监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS $watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY); -swoole_event_add($fd, function ($fd) { +Swoole\Event::add($fd, function ($fd) { $events = inotify_read($fd); if ($events) { foreach ($events as $event) { @@ -13,3 +13,5 @@ } } }); + +Swoole\Event::wait(); diff --git a/examples/event/sockets.php b/examples/event/sockets.php index b791c9ac092..65bf34583ad 100644 --- a/examples/event/sockets.php +++ b/examples/event/sockets.php @@ -3,51 +3,48 @@ * require ./configure --enable-sockets */ +use Swoole\Event; + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); function socket_onRead($socket) { - static $i = 0; - - echo socket_read($socket, 8192)."\n"; - $i ++; - if ($i > 10) - { - echo "finish\n"; - swoole_event_del($socket); - socket_close($socket); - } - else - { - sleep(1); - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); - } + static $i = 0; + + echo socket_read($socket, 8192) . "\n"; + $i++; + if ($i > 10) { + echo "finish\n"; + Event::del($socket); + socket_close($socket); + } else { + sleep(1); + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } } function socket_onWrite($socket) { - socket_write($socket, "hi swoole"); - swoole_event_set($socket, null, null, SWOOLE_EVENT_READ); + socket_write($socket, "hi swoole"); + Event::set($socket, null, null, SWOOLE_EVENT_READ); } function socket_onConnect($socket) { - $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); - if ($err == 0) - { - echo "connect server success\n"; - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); - socket_write($socket, "first package\n"); - } - else - { - echo "connect server failed\n"; - swoole_event_del($socket); - socket_close($socket); - } + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "connect server success\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, "first package\n"); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } } -swoole_event_add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); -@socket_connect($socket, '127.0.0.1', 9501); +Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); +socket_connect($socket, '127.0.0.1', 9501); +Event::wait(); diff --git a/examples/event/stdin.php b/examples/event/stdin.php index 6ecd4d8077e..bbf93cd9b24 100644 --- a/examples/event/stdin.php +++ b/examples/event/stdin.php @@ -1,4 +1,5 @@ setData(http_build_query(['a'=>123,'b'=>"哈哈"])); -//$cli->set(['timeout' => -1]); -//$cli->setHeaders(['Host' => 'www.baidu.com']); -//$cli->set(['http_proxy_host' => '127.0.0.1', 'http_proxy_port' => 8888,]); - -$cli->setHeaders(['User-Agent' => "swoole"]); - -$cli->get('/index.php', function ($cli) -{ - var_dump($cli); -}); - -//$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { -// echo $cli->body; -// $cli->get('/index.php', function ($cli) { -// file_put_contents(__DIR__.'/t.html', $cli->body); -// $cli->download('/index.php', __DIR__.'/phpinfo.html', function ($cli) -// { -// var_dump($cli->downloadFile); -// }); -// }); -//}); diff --git a/examples/http/async_websocket.php b/examples/http/async_websocket.php deleted file mode 100644 index be19dff7dde..00000000000 --- a/examples/http/async_websocket.php +++ /dev/null @@ -1,11 +0,0 @@ -on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/http/client.php b/examples/http/client.php index d3f3ada40eb..cdb6dbd5309 100644 --- a/examples/http/client.php +++ b/examples/http/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501); //$type = 'GET'; diff --git a/examples/http/detach.php b/examples/http/detach.php index a12ff8ccd2f..ec929b4c770 100644 --- a/examples/http/detach.php +++ b/examples/http/detach.php @@ -1,5 +1,5 @@ set(['task_worker_num' => 1, 'worker_num' => 1]); diff --git a/examples/http/empty.txt b/examples/http/empty.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/http/event-stream.php b/examples/http/event-stream.php new file mode 100644 index 00000000000..45ab4a7ecc0 --- /dev/null +++ b/examples/http/event-stream.php @@ -0,0 +1,40 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/stream') { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + go(function () use ($resp, $http) { + while (true) { + Co::sleep(1); + $http->send($resp->fd, 'data: ' . base64_encode(random_bytes(random_int(16, 128))). "\n\n"); + } + }); + } elseif ($req->server['request_uri'] == '/') { + $resp->end(<< + + +HTML + ); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/http_proxy.php b/examples/http/http_proxy.php deleted file mode 100644 index e4642f1b635..00000000000 --- a/examples/http/http_proxy.php +++ /dev/null @@ -1,25 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; -$cli->close(); - // echo $cli->body; - }); -}); - - - - -?> diff --git a/examples/http/http_proxy_auth.php b/examples/http/http_proxy_auth.php deleted file mode 100644 index e4a1c40e445..00000000000 --- a/examples/http/http_proxy_auth.php +++ /dev/null @@ -1,18 +0,0 @@ -set(array( - 'http_proxy_host' => "127.0.0.1", - 'http_proxy_port' => 33080, - 'http_proxy_user' => 'test', - 'http_proxy_password' => 'test', -)); -$cli->setHeaders([ - 'Host' => "localhost", - "User-Agent" => 'Chrome/49.0.2587.3', -]); -$cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . ", statusCode=".$cli->statusCode."\n"; - $cli->close(); - echo $cli->body; -}); diff --git a/examples/http/https_proxy.php b/examples/http/https_proxy.php deleted file mode 100644 index eabd3758ec5..00000000000 --- a/examples/http/https_proxy.php +++ /dev/null @@ -1,21 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; - echo $cli->body; - }); -}); - - -?> diff --git a/examples/http/moc.moc b/examples/http/moc.moc new file mode 100644 index 00000000000..db35d33f047 --- /dev/null +++ b/examples/http/moc.moc @@ -0,0 +1 @@ +this is moc.moc diff --git a/examples/http/no-compression.php b/examples/http/no-compression.php new file mode 100644 index 00000000000..b1228aa80b5 --- /dev/null +++ b/examples/http/no-compression.php @@ -0,0 +1,17 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/') { + $resp->header('Content-Encoding', ''); + $resp->end(str_repeat('A', 1024)); + } elseif ($req->server['request_uri'] == '/gzip') { + $resp->end(str_repeat('A', 1024)); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/redirect.php b/examples/http/redirect.php index 0536e3d8453..c06e6d0a167 100644 --- a/examples/http/redirect.php +++ b/examples/http/redirect.php @@ -1,5 +1,5 @@ on('request', function ($req, Swoole\Http\Response $resp) { $resp->redirect("http://www.baidu.com/", 301); diff --git a/examples/http/server.php b/examples/http/server.php index e44e59365d1..ac9dcaf2e3f 100644 --- a/examples/http/server.php +++ b/examples/http/server.php @@ -1,14 +1,16 @@ setGlobal(HTTP_GLOBAL_ALL, HTTP_GLOBAL_GET|HTTP_GLOBAL_POST|HTTP_GLOBAL_COOKIE); $http->set([ // 'daemonize' => 1, @@ -17,7 +19,10 @@ function dump($var) //'open_cpu_affinity' => 1, //'task_worker_num' => 100, //'enable_port_reuse' => true, + // 'http_compression' => false, 'worker_num' => 1, + 'upload_max_filesize' => 1 * 1024 * 1024 * 1024, + 'package_max_length' => 1 * 1024 * 1024, //'log_file' => __DIR__.'/swoole.log', // 'reactor_num' => 24, //'dispatch_mode' => 3, @@ -36,7 +41,7 @@ function dump($var) $http->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); -function chunk(swoole_http_request $request, swoole_http_response $response) +function chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->write("

hello world1

"); //sleep(1); @@ -45,7 +50,7 @@ function chunk(swoole_http_request $request, swoole_http_response $response) $response->end(); } -function no_chunk(swoole_http_request $request, swoole_http_response $response) +function no_chunk(Swoole\Http\Request $request, Swoole\Http\Response $response) { /** * Cookie Test @@ -71,6 +76,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) if (substr($request->server['request_uri'], -8, 8) == 'test.jpg') { $response->header('Content-Type', 'image/jpeg'); $response->sendfile(dirname(__DIR__) . '/test.jpg'); + return; } elseif ($request->server['request_uri'] == '/test.txt') { $last_modified_time = filemtime(__DIR__ . '/test.txt'); $etag = md5_file(__DIR__ . '/test.txt'); @@ -83,9 +89,14 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) } else { $response->sendfile(__DIR__ . '/test.txt'); } + return; } else if ($request->server['request_uri'] == '/favicon.ico') { $response->status(404); $response->end(); + return; + } else if ($request->server['request_uri'] == '/big_response') { + var_dump($response->end(str_repeat('A', 16 * 1024 * 1024))); + return; } else if ($request->server['request_uri'] == '/code') { $response->sendfile(__FILE__); return; @@ -110,7 +121,11 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) $output .= "

HEADER:

" . dump($request->header); $output .= "

SERVER:

" . dump($request->server); if (!empty($request->files)) { - $output .= "

FILE:

" . dump($request->files); + $files = $request->files; + foreach ($files as &$f) { + $f['md5'] = md5_file($f['tmp_name']); + } + $output .= "

FILE:

" . dump($files); } if (!empty($request->cookie)) { $output .= "

COOKIES:

" . dump($request->cookie); @@ -124,8 +139,9 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) var_dump($request->post); //$response->header('X-Server', 'Swoole'); //unset($request, $response); -// swoole_timer_after(2000, function() use ( $response) { +// Swoole\Timer::after(2000, function() use ( $response) { $response->end("

Hello Swoole.

" . $output); + return; // }); } //var_dump($request); @@ -146,15 +162,11 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) //global $http; //$http->task("hello world"); $file = realpath(__DIR__ . '/../' . $request->server['request_uri']); - if (is_file($file)) - { + if (is_file($file)) { echo "http get file=$file\n"; - if (substr($file, -4) == '.php') - { + if (substr($file, -4) == '.php') { $response->gzip(); - } - else - { + } else { $response->header('Content-Type', 'image/jpeg'); } $content = file_get_contents($file); @@ -164,9 +176,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) // $response->end(); $response->end($content); - } - else - { + } else { $response->end("

Hello Swoole.

"); } } @@ -174,23 +184,20 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) $http->on('request', function ($req, $resp) { $uri = $req->server['request_uri']; if ($uri == '/favicon.ico') { - $resp->status(404); + $resp->status(404); $resp->end(); - } - elseif ($uri == '/chunk') { - chunk($req, $resp); + } elseif ($uri == '/chunk') { + chunk($req, $resp); } else { - no_chunk($req, $resp); + no_chunk($req, $resp); } }); -$http->on('finish', function () -{ +$http->on('finish', function () { echo "task finish"; }); -$http->on('task', function () -{ +$http->on('task', function () { echo "async task\n"; }); @@ -199,8 +206,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) //}); -$http->on('workerStart', function ($serv, $id) -{ +$http->on('workerStart', function ($serv, $id) { //var_dump($serv); }); diff --git a/examples/http/static_handler.php b/examples/http/static_handler.php new file mode 100644 index 00000000000..e135f1685fd --- /dev/null +++ b/examples/http/static_handler.php @@ -0,0 +1,14 @@ +set([ + 'enable_static_handler' => true, + 'http_autoindex' => true, + 'document_root' => realpath(__DIR__.'/../www/'), +]); + +$http->on('request', function ($req, $resp) { + $resp->end("hello world\n"); +}); + +$http->start(); diff --git a/examples/http/upload_file.php b/examples/http/upload_file.php deleted file mode 100644 index d5bab206c5e..00000000000 --- a/examples/http/upload_file.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['User-Agent' => "swoole"]); -$cli->addFile(__DIR__.'/post.data', 'post'); -$cli->addFile(dirname(__DIR__).'/test.jpg', 'debug'); - -$cli->post('/dump2.php', array("xxx" => 'abc', 'x2' => 'rango'), function ($cli) { - echo $cli->body; - $cli->close(); -}); diff --git a/examples/http2/server.php b/examples/http2/server.php index d4cbeb2c646..781ae316a97 100644 --- a/examples/http2/server.php +++ b/examples/http2/server.php @@ -1,14 +1,27 @@ SWOOLE_TRACE_HTTP2, + 'log_level' => 0, +]); +$key_dir = __DIR__ . '/../ssl/'; +$http = new Swoole\Http\Server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); $http->set([ 'open_http2_protocol' => 1, - 'ssl_cert_file' => $key_dir.'/ssl.crt', - 'ssl_key_file' => $key_dir.'/ssl.key', + 'enable_static_handler' => TRUE, + 'document_root' => dirname(__DIR__), + 'ssl_cert_file' => $key_dir . '/ssl.crt', + 'ssl_key_file' => $key_dir . '/ssl.key', ]); -$http->on('request', function (swoole_http_request $request, swoole_http_response $response) { +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->header('Test-Value', [ + "a\r\n", + 'd5678', + "e \n ", + null, + 5678, + 3.1415926, + ]); $response->end("

Hello Swoole.

"); }); diff --git a/examples/http2/streaming.php b/examples/http2/streaming.php new file mode 100644 index 00000000000..f0379f80cf4 --- /dev/null +++ b/examples/http2/streaming.php @@ -0,0 +1,21 @@ +set([ + 'open_http2_protocol' => 1, +]); + +/** + * nghttp -v http://localhost:9501 + */ +$http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $n = 5; + while ($n--) { + $response->write("hello world, #$n
\n"); + Co\System::sleep(1); + } + $response->end("hello world"); +}); + +$http->start(); + diff --git a/examples/http2/test.html b/examples/http2/test.html new file mode 100644 index 00000000000..e58375965de --- /dev/null +++ b/examples/http2/test.html @@ -0,0 +1 @@ +

Test

diff --git a/examples/ipv6/tcp_client.php b/examples/ipv6/tcp_client.php index aae16a87dad..b757f5caa83 100644 --- a/examples/ipv6/tcp_client.php +++ b/examples/ipv6/tcp_client.php @@ -1,5 +1,5 @@ connect('::1', 9501, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/ipv6/tcp_server.php b/examples/ipv6/tcp_server.php index e428476fdf9..80d4b0d374c 100644 --- a/examples/ipv6/tcp_server.php +++ b/examples/ipv6/tcp_server.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/ipv6/udp_client.php b/examples/ipv6/udp_client.php index 1d99d7a1231..a1033cdb24a 100644 --- a/examples/ipv6/udp_client.php +++ b/examples/ipv6/udp_client.php @@ -1,5 +1,5 @@ connect('::1', 9502); $client->send("admin"); echo $client->recv()."\n"; diff --git a/examples/ipv6/udp_server.php b/examples/ipv6/udp_server.php index d312cf0c356..725fc26b687 100644 --- a/examples/ipv6/udp_server.php +++ b/examples/ipv6/udp_server.php @@ -1,9 +1,9 @@ set(array( 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; var_dump($serv->connection_info($fd, $reactor_id)); $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); diff --git a/examples/length/async_client.php b/examples/length/async_client.php deleted file mode 100644 index 379267b1729..00000000000 --- a/examples/length/async_client.php +++ /dev/null @@ -1,49 +0,0 @@ - str_repeat('A', rand(100000, 900000)), - 'str2' => str_repeat('B', rand(100000, 900000)), - 'str3' => str_repeat('C', rand(10000, 90000)), - ); - - $data['int1'] = rand(100000, 999999); - - $sendStr = serialize($data); - $sendData = pack('N', strlen($sendStr)) . $sendStr; - $cli->send($sendData); - echo "send length=" . strlen($sendData) . ", SerId={$data['int1']}\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 - -$client->set(array( - 'open_length_check' => 1, - 'package_length_type' => 'N', - 'package_length_offset' => 0, //第N个字节是包长度的值 - 'package_body_offset' => 4, //第几个字节开始计算长度 - 'package_max_length' => 2000000, //协议最大长度 -)); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - $resp = unserialize(substr($data, 4)); - echo "recv length=" . strlen($data) . ", SerId={$resp['int1']}\n".str_repeat('-', 60)."\n"; - $cli->close(); -// sleep(1); - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/length/client.php b/examples/length/client.php index bba05fe4f7a..2a6c7f38ed0 100644 --- a/examples/length/client.php +++ b/examples/length/client.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, diff --git a/examples/length/func.php b/examples/length/func.php index f37ebd7efef..2846cbeb34c 100644 --- a/examples/length/func.php +++ b/examples/length/func.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -17,7 +17,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/length/server.php b/examples/length/server.php index 420db2c8ba6..2f5b21d4ead 100644 --- a/examples/length/server.php +++ b/examples/length/server.php @@ -1,5 +1,5 @@ set(array( 'open_length_check' => true, @@ -11,7 +11,7 @@ 'package_max_length' => 2000000, //协议最大长度 )); -function send(swoole_server $serv, $fd, $data) +function send(Swoole\Server $serv, $fd, $data) { $serv->send($fd, $data); echo "#send =" . strlen($data) . " bytes\n"; @@ -21,7 +21,7 @@ function send(swoole_server $serv, $fd, $data) echo "Client:Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $req = unserialize(substr($data, 4)); echo "#{$serv->worker_id}>> received length=" . strlen($data) . ", SerId: {$req['int1']}\n"; send($serv, $fd, $data); diff --git a/examples/lock/lock.php b/examples/lock/lock.php index 6a699d7dd76..3254889512e 100644 --- a/examples/lock/lock.php +++ b/examples/lock/lock.php @@ -7,7 +7,7 @@ * SWOOLE_RWLOCK 读写锁 */ -$lock = new swoole_lock(SWOOLE_MUTEX); +$lock = new Swoole\Lock(SWOOLE_MUTEX); echo "[Master]create lock\n"; $lock->lock(); if (pcntl_fork() > 0) diff --git a/examples/logo.svg b/examples/logo.svg deleted file mode 100644 index 3e06c3fdc4f..00000000000 --- a/examples/logo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/examples/get_local_ip.php b/examples/misc/get_local_ip.php similarity index 100% rename from examples/get_local_ip.php rename to examples/misc/get_local_ip.php diff --git a/examples/version.php b/examples/misc/version.php similarity index 100% rename from examples/version.php rename to examples/misc/version.php diff --git a/examples/multi_port_server.php b/examples/multi_port_server.php deleted file mode 100644 index eaaa4d4c5f2..00000000000 --- a/examples/multi_port_server.php +++ /dev/null @@ -1,44 +0,0 @@ -addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); - -$port->on('packet', function($serv, $data, $client) { - var_dump($data, $client); - $serv->sendto($client['address'], $client['port'], "welcome admin\n"); -}); -$serv->on('connect', function ($serv, $fd) { - echo "Client:Connect.\n"; -}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - $info = $serv->connection_info($fd, $reactor_id); - //来自9502的内网管理端口 - if($info['server_port'] == 9502) { - $serv->send($fd, "welcome admin\n"); - $start_fd = 0; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - var_dump($conn_list); - - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $serv->send($conn, "hello from $fd\n"); - } - } - } - //来自外网 - else { - $serv->send($fd, 'Swoole: '.$data); - } -}); -$serv->on('close', function ($serv, $fd) { - echo "Client: Close.\n"; -}); -$serv->start(); diff --git a/examples/multicast/client.php b/examples/multicast/client.php index bbee8016948..91a46c5cf63 100644 --- a/examples/multicast/client.php +++ b/examples/multicast/client.php @@ -1,5 +1,5 @@ connect('224.10.20.30', 9905); $client->send("hello world"); echo $client->recv() . "\n"; diff --git a/examples/multicast/server.php b/examples/multicast/server.php index 20b68d92ee9..7e6655032fa 100644 --- a/examples/multicast/server.php +++ b/examples/multicast/server.php @@ -1,5 +1,5 @@ set(['worker_num' => 1]); $socket = $server->getSocket(); @@ -10,15 +10,13 @@ array('group' => '224.10.20.30', 'interface' => 0) ); -if ($ret === false) -{ +if ($ret === false) { throw new RuntimeException('Unable to join multicast group'); } -$server->on('Packet', function (swoole_server $serv, $data, $addr) -{ +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data"); - var_dump( $addr, strlen($data)); + var_dump($addr, strlen($data)); }); $server->start(); diff --git a/examples/mysql_proxy_server.php b/examples/mysql_proxy_server.php deleted file mode 100644 index 0471b0705f9..00000000000 --- a/examples/mysql_proxy_server.php +++ /dev/null @@ -1,126 +0,0 @@ -set(array( - 'worker_num' => 1, - 'max_request' => 0, - )); - - $serv->on('WorkerStart', array($this, 'onStart')); - //$serv->on('Connect', array($this, 'onConnect')); - $serv->on('Receive', array($this, 'onReceive')); - //$serv->on('Close', array($this, 'onClose')); - $serv->start(); - } - - function onStart($serv) - { - $this->serv = $serv; - for ($i = 0; $i < $this->pool_size; $i++) { - $db = new mysqli; - $db->connect('127.0.0.1', 'root', 'root', 'www4swoole'); - $db_sock = swoole_get_mysqli_sock($db); - swoole_event_add($db_sock, array($this, 'onSQLReady')); - $this->idle_pool[] = array( - 'mysqli' => $db, - 'db_sock' => $db_sock, - 'fd' => 0, - ); - } - echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; - } - - function onSQLReady($db_sock) - { - $db_res = $this->busy_pool[$db_sock]; - $mysqli = $db_res['mysqli']; - $fd = $db_res['fd']; - - echo __METHOD__ . ": client_sock=$fd|db_sock=$db_sock\n"; - - if ($result = $mysqli->reap_async_query()) { - $ret = var_export($result->fetch_all(MYSQLI_ASSOC), true) . "\n"; - $this->serv->send($fd, $ret); - if (is_object($result)) { - mysqli_free_result($result); - } - } else { - $this->serv->send($fd, sprintf("MySQLi Error: %s\n", mysqli_error($mysqli))); - } - //release mysqli object - $this->idle_pool[] = $db_res; - unset($this->busy_pool[$db_sock]); - - //这里可以取出一个等待请求 - if (count($this->wait_queue) > 0) { - $idle_n = count($this->idle_pool); - for ($i = 0; $i < $idle_n; $i++) { - $req = array_shift($this->wait_queue); - $this->doQuery($req['fd'], $req['sql']); - } - } - } - - function onReceive($serv, $fd, $reactor_id, $data) - { - echo "Received: $data\n"; - //没有空闲的数据库连接 - - if (count($this->idle_pool) == 0) { - //等待队列未满 - if (count($this->wait_queue) < $this->wait_queue_max) { - $this->wait_queue[] = array( - 'fd' => $fd, - 'sql' => $data, - ); - } else { - $this->serv->send($fd, "request too many, Please try again later."); - } - } else { - $this->doQuery($fd, $data); - } - } - - function doQuery($fd, $sql) - { - //从空闲池中移除 - $db = array_pop($this->idle_pool); - /** - * @var mysqli - */ - $mysqli = $db['mysqli']; - - for ($i = 0; $i < 2; $i++) { - $result = $mysqli->query($sql, MYSQLI_ASYNC); - if ($result === false) { - if ($mysqli->errno == 2013 or $mysqli->errno == 2006) { - $mysqli->close(); - $r = $mysqli->connect(); - if ($r === true) continue; - } - } - break; - } - - $db['fd'] = $fd; - //加入工作池中 - $this->busy_pool[$db['db_sock']] = $db; - } -} - -$server = new DBServer(); -$server->run(); diff --git a/examples/namespace/README.md b/examples/namespace/README.md deleted file mode 100644 index a396a3f5c5c..00000000000 --- a/examples/namespace/README.md +++ /dev/null @@ -1,24 +0,0 @@ -Enable Namespace Class ---------- -modify your `php.ini` file. - -```shell -swoole.use_namespace = on -``` - - -```php -use Swoole\Http\Server; -use Swoole\Http\Request; -use Swoole\Http\Response; - -$serv = new Server('127.0.0.1', 9501); - -$serv->on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); - -``` diff --git a/examples/namespace/atomic.php b/examples/namespace/atomic.php deleted file mode 100644 index 523e333f2d3..00000000000 --- a/examples/namespace/atomic.php +++ /dev/null @@ -1,5 +0,0 @@ -add(12); -echo $an->get()."\n"; diff --git a/examples/namespace/http_server.php b/examples/namespace/http_server.php deleted file mode 100644 index 6560089186b..00000000000 --- a/examples/namespace/http_server.php +++ /dev/null @@ -1,13 +0,0 @@ -on('Request', function(Request $req, Response $resp) { - var_dump($req->header, get_class($req)); - $resp->end("

Hello Swoole

"); -}); - -$serv->start(); diff --git a/examples/namespace/server.php b/examples/namespace/server.php deleted file mode 100644 index 661751d5f08..00000000000 --- a/examples/namespace/server.php +++ /dev/null @@ -1,14 +0,0 @@ -on('receive', function (Server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; - if ($serv->send($fd, "hello\n") == false) - { - echo "error\n"; - } -}); - -$serv->start(); diff --git a/examples/namespace/timer.php b/examples/namespace/timer.php deleted file mode 100644 index e9f551745c2..00000000000 --- a/examples/namespace/timer.php +++ /dev/null @@ -1,4 +0,0 @@ -connect('127.0.0.1', 'root', 'root', 'test'); - -$db->query("show databases", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); - -$db->query("show tables", MYSQLI_ASYNC); -sleep(1); -if ($result = $db->reap_async_query()) -{ - print_r($result->fetch_row()); - if(is_object($result)) - { - mysqli_free_result($result); - } -} -else die(sprintf("MySQLi Error: %s", mysqli_error($link))); diff --git a/examples/process/alarm.php b/examples/process/alarm.php index 859d1ab0d81..32d85db7f75 100644 --- a/examples/process/alarm.php +++ b/examples/process/alarm.php @@ -1,13 +1,13 @@ 20) { - swoole_process::alarm(-1); + Swoole\Process::alarm(-1); } }); -swoole_process::alarm(100 * 1000); +Swoole\Process::alarm(100 * 1000); diff --git a/examples/process/async_master.php b/examples/process/async_master.php index c6b8ae8c8e7..14b63d0ab24 100644 --- a/examples/process/async_master.php +++ b/examples/process/async_master.php @@ -2,7 +2,7 @@ $workers = []; $worker_num = 10; -//swoole_process::daemon(0, 1); +//Swoole\Process::daemon(0, 1); function onReceive($pipe) { global $workers; @@ -14,7 +14,7 @@ function onReceive($pipe) { //循环创建进程 for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process(function(swoole_process $process) { + $process = new Swoole\Process(function(Swoole\Process $process) { $i = 1; while($i++) { @@ -28,14 +28,14 @@ function onReceive($pipe) { $workers[$process->pipe] = $process; } -swoole_process::signal(SIGCHLD, function(){ +Swoole\Process::signal(SIGCHLD, function(){ //表示子进程已关闭,回收它 - $status = swoole_process::wait(); + $status = Swoole\Process::wait(); echo "Worker#{$status['pid']} exit\n"; }); //将子进程的管道加入EventLoop foreach($workers as $process) { - swoole_event_add($process->pipe, 'onReceive'); + Swoole\Event::add($process->pipe, 'onReceive'); } diff --git a/examples/process/client.php b/examples/process/client.php index 2dd195e2a37..181792038a4 100644 --- a/examples/process/client.php +++ b/examples/process/client.php @@ -1,12 +1,12 @@ connect('127.0.0.1', 8089, -1)) { exit("connect failed. Error: {$client->errCode}\n"); } -function _send(swoole_client $client, $data) +function _send(Swoole\Client $client, $data) { return $client->send(pack('N', strlen($data)) . $data); } diff --git a/examples/process/close.php b/examples/process/close.php index 5b00c27fd32..df52e7f5d04 100644 --- a/examples/process/close.php +++ b/examples/process/close.php @@ -1,9 +1,9 @@ pid . "\n"; sleep(2); - $worker->close(swoole_process::PIPE_READ); + $worker->close(Swoole\Process::PIPE_READ); $worker->write("hello master\n"); $worker->exit(0); }, false); diff --git a/examples/process/exec.php b/examples/process/exec.php index 7c7fd95bd9e..5236d423359 100644 --- a/examples/process/exec.php +++ b/examples/process/exec.php @@ -1,8 +1,8 @@ start(); -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { $worker->exec('/usr/local/bin/php', array(__DIR__.'/stdin_stdout.php')); } @@ -11,5 +11,5 @@ function callback_function(swoole_process $worker) $process->write("hello worker\n"); echo "From Worker: ".$process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/msgqueue.php b/examples/process/msgqueue.php index f88c45b0399..03a5b0ccfd9 100644 --- a/examples/process/msgqueue.php +++ b/examples/process/msgqueue.php @@ -4,14 +4,14 @@ for($i = 0; $i < $worker_num; $i++) { - $process = new swoole_process('callback_function', false, false); + $process = new Swoole\Process('callback_function', false, false); $process->useQueue(); $pid = $process->start(); $workers[$pid] = $process; //echo "Master: new worker, PID=".$pid."\n"; } -function callback_function(swoole_process $worker) +function callback_function(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -28,7 +28,7 @@ function callback_function(swoole_process $worker) while(true) { /** - * @var $process swoole_process + * @var $process Swoole\Process */ $pid = array_rand($workers); $process = $workers[$pid]; @@ -38,7 +38,7 @@ function callback_function(swoole_process $worker) for($i = 0; $i < $worker_num; $i++) { - $ret = swoole_process::wait(); + $ret = Swoole\Process::wait(); $pid = $ret['pid']; unset($workers[$pid]); echo "Worker Exit, PID=".$pid.PHP_EOL; diff --git a/examples/process/msgqueue2.php b/examples/process/msgqueue2.php index b3d7172867b..dfa53c2920b 100644 --- a/examples/process/msgqueue2.php +++ b/examples/process/msgqueue2.php @@ -1,5 +1,5 @@ pid."\n"; //recv data from master @@ -13,8 +13,8 @@ function callback_function(swoole_process $worker) $worker->exit(0); } -$process = new swoole_process('callback_function', false, false); -$process->useQueue(ftok(__FILE__, 1), 2 | swoole_process::IPC_NOWAIT); +$process = new Swoole\Process('callback_function', false, false); +$process->useQueue(ftok(__FILE__, 1), 2 | Swoole\Process::IPC_NOWAIT); $send_bytes = 0; foreach(range(1, 10) as $i) diff --git a/examples/process/python.php b/examples/process/python.php index b66450b010c..8efe55f4fba 100644 --- a/examples/process/python.php +++ b/examples/process/python.php @@ -1,8 +1,8 @@ start(); -function pyhon_process(swoole_process $worker) +function pyhon_process(Swoole\Process $worker) { $worker->exec('/usr/bin/python', array("echo.py")); } @@ -10,5 +10,5 @@ function pyhon_process(swoole_process $worker) $process->write("hello world\n"); echo $process->read(); -$ret = swoole_process::wait(); +$ret = Swoole\Process::wait(); var_dump($ret); diff --git a/examples/process/select.php b/examples/process/select.php index 60174f33d13..2b2745c5a39 100644 --- a/examples/process/select.php +++ b/examples/process/select.php @@ -1,5 +1,5 @@ pid . "\n"; sleep(2); diff --git a/examples/process/set_cpu_affinity.php b/examples/process/set_cpu_affinity.php new file mode 100644 index 00000000000..f454327f2e9 --- /dev/null +++ b/examples/process/set_cpu_affinity.php @@ -0,0 +1,3 @@ +id = $i; $pid = $process->start(); $workers[$pid] = $process; @@ -19,10 +19,10 @@ //异步主进程 function master_async($workers) { - swoole_process::signal(SIGCHLD, function ($signo) use (&$workers) { + Swoole\Process::signal(SIGCHLD, function ($signo) use (&$workers) { while(1) { - $ret = swoole_process::wait(false); + $ret = Swoole\Process::wait(false); if ($ret) { $pid = $ret['pid']; @@ -41,11 +41,11 @@ function master_async($workers) }); /** - * @var $process swoole_process + * @var $process Swoole\Process */ foreach($workers as $pid => $process) { - swoole_event_add($process->pipe, function($pipe) use ($process) { + Swoole\Event::add($process->pipe, function($pipe) use ($process) { $recv = $process->read(); if ($recv) echo "From Worker: " . $recv; $process->write("HELLO worker {$process->pid}\n"); @@ -64,7 +64,7 @@ function master_sync($workers) } } -function child_sync(swoole_process $worker) +function child_sync(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -79,7 +79,7 @@ function child_sync(swoole_process $worker) $worker->exit(0); } -function child_async(swoole_process $worker) +function child_async(Swoole\Process $worker) { //echo "Worker: start. PID=".$worker->pid."\n"; //recv data from master @@ -87,18 +87,18 @@ function child_async(swoole_process $worker) global $argv; $worker->name("{$argv[0]}: worker #".$worker->id); - swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { + Swoole\Process::signal(SIGTERM, function($signal_num) use ($worker) { echo "signal call = $signal_num, #{$worker->pid}\n"; }); -// swoole_timer_tick(2000, function () use ($worker) +// Swoole\Timer::tick(2000, function () use ($worker) // { // if (rand(1, 3) % 2) { // $worker->write("hello master {$worker->pid}\n"); // } // }); - swoole_event_add($worker->pipe, function($pipe) use($worker) { + Swoole\Event::add($worker->pipe, function($pipe) use($worker) { $recv = $worker->read(); echo "From Master: $recv\n"; //$worker->write("hello master\n"); diff --git a/examples/process_pool/detach.php b/examples/process_pool/detach.php new file mode 100644 index 00000000000..7a613698fa2 --- /dev/null +++ b/examples/process_pool/detach.php @@ -0,0 +1,30 @@ +on('WorkerStart', function (Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStart\n"); + if ($workerId == 1) { + + } +}); + +$pool->on('WorkerStop', function (\Swoole\Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStop\n"); +}); + +$pool->on('Message', function ($pool, $msg) { + var_dump($msg); + $pool->detach(); + + while(1) { + sleep(1); + echo "pid=".posix_getpid()."\n"; + }; +}); + +$pool->listen('127.0.0.1', 8089); + +$pool->start(); diff --git a/examples/process_pool/send.php b/examples/process_pool/send.php new file mode 100644 index 00000000000..b9ae046e911 --- /dev/null +++ b/examples/process_pool/send.php @@ -0,0 +1,5 @@ + 'hello', 'uid' => 1991]); +fwrite($fp, pack('N', strlen($msg)) . $msg); +sleep(1); diff --git a/examples/recv_1m_client.php b/examples/recv_1m_client.php deleted file mode 100644 index 9d8a960d82d..00000000000 --- a/examples/recv_1m_client.php +++ /dev/null @@ -1,27 +0,0 @@ -connect('127.0.0.1', 9509, 60); -$c->send("AAAAAAAAAAAAAAAA"); - -$n_bytes = 0; - -while (true) -{ - $line = $c->recv(); - if ($line === false) - { - echo "recv failed.\n"; - break; - } - elseif (empty($line)) - { - echo "recv $n_bytes bytes\n"; - break; - } - else - { - fwrite($f, $line); - $n_bytes += strlen($line); - } -} diff --git a/examples/recv_file.php b/examples/recv_file.php deleted file mode 100644 index 717ba2a4e50..00000000000 --- a/examples/recv_file.php +++ /dev/null @@ -1,45 +0,0 @@ -connect($server_ip, 9501, 5); -$filesize = intval($cli->recv()); -if ($filesize == 0) -{ - die("get file size failed.\n"); -} -echo "file_size = $filesize\n"; -$content = ''; -$cli->send("get file"); - -$use_waitall = false; - -if ($use_waitall) -{ - //waitall,需要一次性分配内存,适合小一点的文件 - $content = $cli->recv($filesize, true); -} -else -{ - //循环接收,适合大型文件 - while(1) - { - //超大文件接收,这里需要改成分段写磁盘 - $content .= $cli->recv(); - if (strlen($content) == $filesize) - { - break; - } - } -} -file_put_contents(__DIR__."/recv_file_".time().".jpg", $content); -echo "recv ".strlen($content)." byte data\n"; -echo "used ".((microtime(true) - $start_ms)*1000)."ms\n"; -$cli->close(); diff --git a/examples/reflection_test.php b/examples/reflection_test.php deleted file mode 100644 index 5a1c5cebd66..00000000000 --- a/examples/reflection_test.php +++ /dev/null @@ -1,12 +0,0 @@ -getMethods(); -foreach($methods as $method) { - echo "----------------------------------------" .PHP_EOL; - echo "method name : " . $method->name . PHP_EOL; - echo "----------------------------------------" . PHP_EOL; - $method = $Ref_swoole_server->getMethod($method->name); - $params = $method->getParameters(); - print_r($params); -} diff --git a/examples/runtime/curl.php b/examples/runtime/curl.php index 2d586e270a0..b6c250231e8 100644 --- a/examples/runtime/curl.php +++ b/examples/runtime/curl.php @@ -16,4 +16,4 @@ echo strlen($output) . "bytes\n"; }); -swoole_event_wait(); \ No newline at end of file +Swoole\Event::wait(); diff --git a/examples/runtime/file.php b/examples/runtime/file.php index 84eadc606df..317735d69cb 100644 --- a/examples/runtime/file.php +++ b/examples/runtime/file.php @@ -1,7 +1,7 @@ exec('create table test (id int)'); + $dbh->exec('insert into test values(1)'); + $dbh->exec('insert into test values(2)'); + $res = $dbh->query("select * from test"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['hook_flags' => SWOOLE_HOOK_PDO_ORACLE]); + +Co\run(function () { + test(); +}); diff --git a/examples/runtime/odbc.php b/examples/runtime/odbc.php new file mode 100644 index 00000000000..678c2e25bbe --- /dev/null +++ b/examples/runtime/odbc.php @@ -0,0 +1,22 @@ +query("select sleep(1) s"); + var_dump($res->fetchAll()); + $dbh = null; + } catch (PDOException $exception) { + echo $exception->getMessage(); + exit; + } +} + +Co::set(['trace_flags' => SWOOLE_TRACE_CO_ODBC, 'log_level' => SWOOLE_LOG_DEBUG]); + +Co\run(function () { + test(); +}); diff --git a/examples/runtime/select.php b/examples/runtime/select.php index 45f7367bf91..d73353dad02 100644 --- a/examples/runtime/select.php +++ b/examples/runtime/select.php @@ -21,4 +21,4 @@ } }); -swoole_event_wait(); +Swoole\Event::wait(); diff --git a/examples/runtime/sqlite.php b/examples/runtime/sqlite.php new file mode 100644 index 00000000000..7f31bd71105 --- /dev/null +++ b/examples/runtime/sqlite.php @@ -0,0 +1,15 @@ + SWOOLE_HOOK_PDO_SQLITE]); + +run(function() { + $db = new PDO('sqlite::memory:'); + for ($i = 0; $i < 10; $i++) { + go(function() use($i, $db) { + $db->query('select randomblob(99999999)'); + var_dump($i); + }); + } +}); diff --git a/examples/runtime/strictmode.php b/examples/runtime/strictmode.php deleted file mode 100644 index 1f5f5449582..00000000000 --- a/examples/runtime/strictmode.php +++ /dev/null @@ -1,6 +0,0 @@ -set(array('worker_num' => 1)); -$serv->on('workerStart', function($serv, $worker_id) { - //if($worker_id == 0) $serv->addtimer(500); -}); -$serv->on('connect', function ($serv, $fd, $reactor_id){ - $serv->array['fd'] = &strval($fd); - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; -}); -$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { - //echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; - $array = array('A', 'B', 'C', 'D', 'E', 'F', 'G'); - $data = ''; - $n_bytes = 0; - for ($i = 0; $i < 10; $i++) - { - $_str = str_repeat($array[$i % 7], 4030) . "\n"; - //$serv->send($fd, $_str); - $n_bytes += strlen($_str); - $data .= $_str; - } - echo "send " . $n_bytes . " bytes\n"; - $serv->send( $serv->array['fd'], $data); - $serv->close($fd); -}); -$serv->on('close', function ($serv, $fd, $reactor_id) { - echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; -}); -$serv->start(); diff --git a/examples/sendfile_server.php b/examples/sendfile_server.php deleted file mode 100644 index c5ccdaf2d1b..00000000000 --- a/examples/sendfile_server.php +++ /dev/null @@ -1,24 +0,0 @@ -set(array( - 'worker_num' => 1, -)); -$serv->on('timer', function($serv, $interval) { - echo "onTimer: $interval\n"; -}); -$serv->on('workerStart', function($serv, $worker_id) { - //if($worker_id == 0) $serv->addtimer(300); -}); -$serv->on('connect', function (swoole_server $serv, $fd){ - $serv->send($fd, filesize(__DIR__.'/test.jpg')); - //echo "Client:Connect.\n"; -}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - echo "Client[$fd]: $data\n"; - $serv->sendfile($fd, __DIR__.'/test.jpg'); - //$serv->close($fd); -}); -$serv->on('close', function ($serv, $fd) { - //echo "Client: Close.\n"; -}); -$serv->start(); diff --git a/examples/serialize.php b/examples/serialize.php deleted file mode 100644 index b3e067a0cd3..00000000000 --- a/examples/serialize.php +++ /dev/null @@ -1,17 +0,0 @@ -pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); - -?> diff --git a/examples/serialize2.php b/examples/serialize2.php deleted file mode 100644 index 3ee95c247d2..00000000000 --- a/examples/serialize2.php +++ /dev/null @@ -1,30 +0,0 @@ -sub = new mySubObject(); -$arr->sub->default = new stdclass(); -$obj = new \Swoole\Serialize(); -$ser = $obj->pack($arr); - - -$ser2 = $obj->pack($arr,SWOOLE_FAST_PACK); - -var_dump($obj->unpack($ser)); -var_dump($obj->unpack($ser2)); -var_dump($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump($obj->unpack($ser2, UNSERIALIZE_OBJECT_TO_STDCLASS)); -var_dump(UNSERIALIZE_OBJECT_TO_ARRAY); -var_dump(UNSERIALIZE_OBJECT_TO_STDCLASS); -var_dump(get_class($obj->unpack($ser, UNSERIALIZE_OBJECT_TO_STDCLASS))); - -?> diff --git a/examples/server.php b/examples/server.php deleted file mode 100644 index e51fe19030c..00000000000 --- a/examples/server.php +++ /dev/null @@ -1,567 +0,0 @@ - 16, // 线程数. 一般设置为CPU核数的1-4倍 - 'worker_num' => 8, // 工作进程数量. 设置为CPU的1-4倍最合理 - 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) - 'max_conn' => 10000, // 最大连接数 - 'task_worker_num' => 1, // 任务工作进程数量 -// 'task_ipc_mode' => 2, // 设置 Task 进程与 Worker 进程之间通信的方式。 - 'task_max_request' => 0, // 防止 PHP 内存溢出 - //'task_tmpdir' => '/tmp', - //'message_queue_key' => ftok(SYS_ROOT . 'queue.msg', 1), - 'dispatch_mode' => 2, - //'daemonize' => 1, // 设置守护进程模式 - 'backlog' => 128, - //'log_file' => '/data/logs/swoole.log', - 'heartbeat_check_interval' => 10, // 心跳检测间隔时长(秒) - 'heartbeat_idle_time' => 20, // 连接最大允许空闲的时间 - //'open_eof_check' => 1, - //'open_eof_split' => 1, - //'package_eof' => "\r\r\n", - //'open_cpu_affinity' => 1, - 'socket_buffer_size' => 1024 * 1024 * 128, - 'buffer_output_size' => 1024 * 1024 * 2, - 'enable_delay_receive' => true, - //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 - ); - - private static $buffers = array(); - - /** - * @param $fd - * @return swoole_buffer - */ - static function getBuffer($fd, $create = true) - { - if (!isset(self::$buffers[$fd])) - { - if (!$create) - { - return false; - } - self::$buffers[$fd] = new swoole_buffer(1024 * 128); - } - return self::$buffers[$fd]; - } -} - -if (isset($argv[1]) and $argv[1] == 'daemon') { - G::$config['daemonize'] = true; -} else { - G::$config['daemonize'] = false; -} - -//$mode = SWOOLE_BASE; -$mode = SWOOLE_PROCESS; - -$serv = new swoole_server("0.0.0.0", 9501, $mode, SWOOLE_SOCK_TCP); -$serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); -$serv->listen('::', 9503, SWOOLE_SOCK_TCP6); -$serv->listen('::', 9504, SWOOLE_SOCK_UDP6); -$process1 = new swoole_process(function ($worker) use ($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: my_process1"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { - echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx - foreach ($serv->connections as $conn) - { - $serv->send($conn, "heartbeat\n"); - } - }); - swoole_timer_tick(5000, function () use ($serv) - { - $serv->sendMessage("hello event worker", 0); - $serv->sendMessage("hello task worker", 4); - }); -}, false); - -//$serv->addprocess($process1); - -$process2 = new swoole_process(function ($worker) use ($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: my_process2"); - swoole_timer_tick(2000, function ($interval) use ($worker, $serv) { - echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx - }); -}, false); - -//$serv->addprocess($process2); - -$serv->set(G::$config); -$serv->set(['reactor_num' => 4]); - -/** - * 使用类的静态属性,可以直接访问 - */ -G::$serv = $serv; - -function my_onStart(swoole_server $serv) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: master"); - my_log("Server: start.Swoole version is [".SWOOLE_VERSION."]"); - my_log("MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}"); -} - -function my_log($msg) -{ - global $serv; - if (empty($serv->worker_pid)) - { - $serv->worker_pid = posix_getpid(); - } - echo "#".$serv->worker_pid."\t[".date('H:i:s')."]\t".$msg.PHP_EOL; -} - -function forkChildInWorker() { - global $serv; - echo "on worker start\n"; - $process = new swoole_process( function (swoole_process $worker) use ($serv) { -// $serv = new swoole_server( "0.0.0.0", 9503 ); -// $serv->set(array( -// 'worker_num' => 1 -// )); -// $serv->on ( 'receive', function (swoole_server $serv, $fd, $reactor_id, $data) { -// $serv->send ( $fd, "Swoole: " . $data ); -// $serv->close ( $fd ); -// }); -// $serv->start (); -// swoole_event_add ($worker->pipe, function ($pipe) use ($worker) { -// echo $worker->read()."\n"; -// }); - }); - - $pid = $process->start(); - echo "Fork child process success. pid={$pid}\n"; - //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 - $serv->childprocess = $process; -} - -function processRename(swoole_server $serv, $worker_id) { - - global $argv; - if ( $serv->taskworker) - { - swoole_set_process_name("php {$argv[0]}: task"); - } - else - { - swoole_set_process_name("php {$argv[0]}: worker"); - } -// if ($worker_id == 0) -// { -// var_dump($serv->setting); -// } - my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); -} - -function setTimerInWorker(swoole_server $serv, $worker_id) { - - if ($worker_id == 0) { - echo "Start: ".microtime(true)."\n"; - //$serv->addtimer(3000); -// $serv->addtimer(7000); - //var_dump($serv->gettimer()); - } -// $serv->after(2000, function(){ -// echo "Timeout: ".microtime(true)."\n"; -// }); -// $serv->after(5000, function(){ -// echo "Timeout: ".microtime(true)."\n"; -// global $serv; -// $serv->deltimer(3000); -// }); -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onClose(swoole_server $serv, $fd, $reactor_id) -{ - my_log("Client[$fd@$reactor_id]: fd=$fd is closed"); - $buffer = G::getBuffer($fd); - if ($buffer) - { - $buffer->clear(); - } - //var_dump($serv->getClientInfo($fd)); -} - -function my_onConnect(swoole_server $serv, $fd, $reactor_id) -{ - //throw new Exception("hello world"); -// var_dump($serv->connection_info($fd)); - //var_dump($serv, $fd, $reactor_id); -// echo "Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\n"; - $serv->after(2000, function() use ($serv, $fd) { - $serv->confirm($fd); - }); - my_log("Client: Connect --- {$fd}"); -} - -function timer_show($id) -{ - my_log("Timer#$id"); -} - -function my_onWorkerExit(swoole_server $serv, $worker_id) { - global $argv; -} - -function my_onWorkerStart(swoole_server $serv, $worker_id) -{ - processRename($serv, $worker_id); - - if (!$serv->taskworker) - { - swoole_process::signal(SIGUSR2, function($signo){ - echo "SIGNAL: $signo\n"; - }); - $serv->defer(function(){ - echo "defer call\n"; - }); -// $serv->tick(2000, function() use ($serv) { -// echo "Worker-{$serv->worker_id} tick-2000\n"; -// }); - } - else - { -// swoole_timer_after(2000, function() { -// echo "after 2 secends.\n"; -// }); -// $serv->tick(1000, function ($id) use ($serv) { -// if (G::$index > 10) { -// $serv->after(2500, 'timer_show', 2); -// G::$index = 0; -// } else { -// G::$index++; -// } -// timer_show($id); -// }); - } - //forkChildInWorker(); -// setTimerInWorker($serv, $worker_id); -} - -function my_onWorkerStop($serv, $worker_id) -{ - echo "WorkerStop[$worker_id]|pid=".$serv->worker_pid.".\n"; -} - -function my_onPacket($serv, $data, $clientInfo) -{ - $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data); - var_dump($clientInfo); -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); - $cmd = trim($data); - if($cmd == "reload") - { - $serv->reload(); - } - elseif($cmd == "task") - { - $task_id = $serv->task("task ".$fd); - echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif ($cmd == "taskclose") - { - $serv->task("close " . $fd); - echo "close the connection in taskworker\n"; - } - elseif ($cmd == "tasksend") - { - $serv->task("send " . $fd); - } - elseif ($cmd == "bigtask") - { - $serv->task(str_repeat('A', 8192*5)); - } - elseif($cmd == "taskwait") - { - $result = $serv->taskwait("taskwait"); - if ($result) { - $serv->send($fd, "taskwaitok"); - } - echo "SyncTask: result=".var_export($result, true)."\n"; - } - elseif($cmd == "taskWaitMulti") - { - $result = $serv->taskWaitMulti(array( - str_repeat('A', 8192 * 5), - str_repeat('B', 8192 * 6), - str_repeat('C', 8192 * 8) - )); - if ($result) - { - $resp = "taskWaitMulti ok\n"; - foreach($result as $k => $v) - { - $resp .= "result[$k] length=".strlen($v)."\n"; - } - $serv->send($fd, $resp); - } - else - { - $serv->send($fd, "taskWaitMulti error\n"); - } - } - elseif ($cmd == "hellotask") - { - $serv->task("hellotask"); - } - elseif ($cmd == "taskcallback") - { - $serv->task("taskcallback", -1, function (swoole_server $serv, $task_id, $data) - { - echo "Task Callback: "; - var_dump($task_id, $data); - }); - } - elseif ($cmd == "sendto") - { - $serv->sendto("127.0.0.1", 9999, "hello world"); - } - elseif($cmd == "close") - { - $serv->send($fd, "close connection\n"); - $result = $serv->close($fd); - } - elseif($cmd == "info") - { - $info = $serv->connection_info(strval($fd), $reactor_id); - var_dump($info["remote_ip"]); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif ($cmd == 'proxy') - { - $serv->send(1, "hello world\n"); - } - elseif ($cmd == 'sleep') - { - sleep(10); - } - elseif ($cmd == 'foreach') - { - foreach($serv->connections as $fd) - { - echo "conn : $fd\n"; - } - return; - } - elseif ($cmd == 'tick') - { - $serv->tick(2000, function ($id) { - echo "tick #$id\n"; - }); - } - elseif ($cmd == 'addtimer') - { - $serv->addtimer(3000); - } - elseif($cmd == "list") - { - $start_fd = 0; - echo "broadcast\n"; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if (empty($conn_list)) - { - echo "iterates finished\n"; - break; - } - $start_fd = end($conn_list); - var_dump($conn_list); - } - } - elseif($cmd == "list2") - { - foreach($serv->connections as $con) - { - var_dump($serv->connection_info($con)); - } - } - elseif($cmd == "stats") - { - $serv_stats = $serv->stats(); - $serv->send($fd, 'Stats: '.var_export($serv_stats, true)."\ncount=".count($serv->connections).PHP_EOL); - } - elseif($cmd == "broadcast") - { - broadcast($serv, $fd, "hello from $fd\n"); - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { - hello_no_exists(); - } - elseif($cmd == "exit") - { - exit("worker php exit.\n"); - } - //关闭fd - elseif(substr($cmd, 0, 5) == "close") - { - $close_fd = substr($cmd, 6); - $serv->close($close_fd); - } - elseif($cmd == "shutdown") - { - $serv->shutdown(); - } - elseif($cmd == "fatalerror") - { - require __DIR__.'/php/error.php'; - } - elseif($cmd == 'sendbuffer') - { - $buffer = G::getBuffer($fd); - $buffer->append("hello\n"); - $serv->send($fd, $buffer); - } - elseif($cmd == 'defer') - { - $serv->defer(function() use ($fd, $serv) { - $serv->close($fd); - $serv->defer(function(){ - echo "deferd\n"; - }); - }); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - } - else - { - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - //$serv->close($fd); - } - //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; -// $serv->after( -// 800, function () { -// echo "hello"; -// } -// ); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - if ($data == 'taskwait') - { - $fd = str_replace('task-', '', $data); - $serv->send($fd, "hello world"); - return array("task" => 'wait'); - } - elseif ($data == 'taskcallback') - { - return array("task" => 'callback'); - } - else - { - $cmd = explode(' ', $data); - if ($cmd[0] == 'send') - { - $serv->send($cmd[1], str_repeat('A', 10000)."\n"); - } - elseif ($cmd[0] == 'close') - { - $serv->close($cmd[1]); - } - else - { - echo "bigtask: length=".strlen($data)."\n"; - return $data; - } -// $serv->sendto('127.0.0.1', 9999, "hello world"); - //swoole_timer_after(1000, "test"); -// var_dump($data); -// $serv->send($fd, str_repeat('A', 8192 * 2)); -// $serv->send($fd, str_repeat('B', 8192 * 2)); -// $serv->send($fd, str_repeat('C', 8192 * 2)); -// $serv->send($fd, str_repeat('D', 8192 * 2)); - return; - } - - if ($data == "hellotask") - { - broadcast($serv, 0, "hellotask"); - } - else - { - echo "AsyncTask[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; - //eg: test-18 - return $data; - } -} - -function my_onFinish(swoole_server $serv, $task_id, $data) -{ - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'taskok'); - var_dump($str, $fd); - echo "AsyncTask Finish: result={$data}. PID=".$serv->worker_pid.PHP_EOL; -} - -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code, $signo) -{ - echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code|Signal=$signo\n"; -} - -function broadcast(swoole_server $serv, $fd = 0, $data = "hello") -{ - $start_fd = 0; - echo "broadcast\n"; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $ret1 = $serv->send($conn, $data); - //var_dump($ret1); - //$ret2 = $serv->close($conn); - //var_dump($ret2); - } - } -} - -$serv->on('PipeMessage', function($serv, $src_worker_id, $msg) { - my_log("PipeMessage: Src={$src_worker_id},Msg=".trim($msg)); - if ($serv->taskworker) - { - $serv->sendMessage("hello user process", - $src_worker_id); - } -}); - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Packet', 'my_onPacket'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('WorkerStart', 'my_onWorkerStart'); -$serv->on('WorkerStop', 'my_onWorkerStop'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('WorkerError', 'my_onWorkerError'); -$serv->on('WorkerExit', 'my_onWorkerExit'); -$serv->on('ManagerStart', function($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: manager"); -}); -$serv->start(); diff --git a/examples/db_pool.php b/examples/server/db_pool.php similarity index 96% rename from examples/db_pool.php rename to examples/server/db_pool.php index 9878bf34066..c12ede8875c 100644 --- a/examples/db_pool.php +++ b/examples/server/db_pool.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 100, diff --git a/examples/server/dispatch_func.php b/examples/server/dispatch_func.php index 4db4a52843a..df440a0a6b5 100644 --- a/examples/server/dispatch_func.php +++ b/examples/server/dispatch_func.php @@ -1,5 +1,5 @@ set(array( 'dispatch_func' => function ($serv, $fd, $type, $data) { @@ -8,7 +8,7 @@ }, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/dispatch_stream.php b/examples/server/dispatch_stream.php index 536e431b763..8ae0af553fd 100644 --- a/examples/server/dispatch_stream.php +++ b/examples/server/dispatch_stream.php @@ -1,12 +1,12 @@ set(array( 'dispatch_mode' => 7, 'worker_num' => 2, )); -$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $threadId, $data) { var_dump($data); echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; diff --git a/examples/server/echo.php b/examples/server/echo.php index 303433a5807..cdb1fa25729 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,24 +1,44 @@ on('connect', function ($serv, $fd, $reactor_id){ -// echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; -//}); -$serv->set(array( - 'worker_num' => 1, - -)); - -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { - echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; - if ($serv->send($fd, "hello {$data}\n") == false) - { +//$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_BASE); +// $serv = new Swoole\Server("0.0.0.0", 9501); +$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_THREAD); + +function getpid() +{ + global $serv; + return $serv->mode === SWOOLE_THREAD ? \Swoole\Thread::getId() : posix_getpid(); +} + +$serv->set([ + 'worker_num' => 2, + 'task_worker_num' => 3, +]); + +$serv->on('workerStart', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$serv->on('workerStop', function ($serv, $worker_id) { + echo "[#" . getpid() . "]\tWorker#{$worker_id} is stopped.\n"; +}); + +$serv->on('connect', function ($serv, $fd, $reactor_id) { + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Connect.\n"; +}); + +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + echo "[#" . $serv->worker_id . "]\tClient[$fd] receive data: $data\n"; + if ($serv->send($fd, "hello {$data}\n") == false) { echo "error\n"; } +}); +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "[#" . getpid() . "]\tClient@[$fd:$reactor_id]: Close.\n"; }); -//$serv->on('close', function ($serv, $fd, $reactor_id) { -// echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; -//}); +$serv->on('task', function ($serv, $src_worker_id, $task) { + var_dump($task); +}); $serv->start(); diff --git a/examples/server/eof_client.php b/examples/server/eof_client.php new file mode 100644 index 00000000000..c2768b425f4 --- /dev/null +++ b/examples/server/eof_client.php @@ -0,0 +1,52 @@ +set( + array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'package_max_length' => 8 * 1024 * 1024, + ) +); + +if (!$client->connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + $func($client, $len); + } +} + +function send_test3($client, $len) +{ + $data = str_repeat('A', $len) . "\r\n"; + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } + echo "send : " . strlen($data) . "\n"; +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} + +function send_test2($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(100000, 999999); + $client->send($data); + + $data = $client->recv(); +} + +function send_test1($client, $len) +{ + $client->send(pack('N', $len + 4)); + usleep(10); + $client->send(str_repeat('A', $len) . rand(1000, 9999)); + $data = $client->recv(); +} diff --git a/examples/server/eof_server.php b/examples/server/eof_server.php new file mode 100644 index 00000000000..d901b221d89 --- /dev/null +++ b/examples/server/eof_server.php @@ -0,0 +1,41 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, + 'worker_num' => 1, //how much worker will start + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'package_max_length' => 8 * 1024 * 1024, + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} diff --git a/examples/server/exist.php b/examples/server/exist.php deleted file mode 100755 index a9d4b2a2cf6..00000000000 --- a/examples/server/exist.php +++ /dev/null @@ -1,141 +0,0 @@ - 4, - //'open_eof_check' => true, - //'package_eof' => "\r\n", -// 'task_ipc_mode' => 2, - 'task_worker_num' => 2, - 'user' => 'www', - 'group' => 'www', - 'chroot' => '/opt/tmp', - //'task_ipc_mode' => 1, - //'dispatch_mode' => 1, - //'log_file' => '/tmp/swoole.log', - 'heartbeat_check_interval' => 300, - 'heartbeat_idle_time' => 300, - // open_cpu_affinity => 1, - //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 -); - -if (isset($argv[1]) and $argv[1] == 'daemon') { - $config['daemonize'] = true; -} else { - $config['daemonize'] = false; -} - -//$mode = SWOOLE_BASE; -$mode = SWOOLE_PROCESS; - -$serv = new swoole_server("0.0.0.0", 9501, $mode); -$serv->set($config); -/** - * 使用类的静态属性,可以直接访问 - */ -G::$serv = $serv; - -function my_onStart(swoole_server $serv) -{ - global $argv; - swoole_set_process_name("php {$argv[0]}: master"); - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; -} - -function my_log($msg) -{ - echo $msg.PHP_EOL; -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onClose($serv, $fd, $reactor_id) -{ - my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: fd=$fd is closed"); - $buffer = G::getBuffer($fd); - if ($buffer) - { - $buffer->clear(); - } - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onConnect(swoole_server $serv, $fd, $reactor_id) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - $serv->task($data . '-' . $fd); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - list($str, $fd) = explode('-', $data); - if($serv->exist($fd)) { - echo 'FD[' . $fd . '] exist' . PHP_EOL ; - } else { - echo 'FD[' . $fd . '] not exist' . PHP_EOL; - } - echo "Task[PID=".$serv->worker_pid."]: task_id=$task_id.".PHP_EOL; - return $data; -} - -function my_onFinish(swoole_server $serv, $task_id, $data) -{ - list($str, $fd) = explode('-', $data); - $serv->send($fd, 'Send Data To FD[' . $fd . ']'); - echo "Task Finish: result=" . $data . ". PID=" . $serv->worker_pid.PHP_EOL; -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('ManagerStart', function($serv) { - global $argv; - swoole_set_process_name("php {$argv[0]}: manager"); -}); -$serv->start(); diff --git a/examples/server/fixed_header_client.php b/examples/server/fixed_header_client.php deleted file mode 100644 index c415a384b8c..00000000000 --- a/examples/server/fixed_header_client.php +++ /dev/null @@ -1,54 +0,0 @@ -set(array( - 'open_length_check' => true, - 'package_max_length' => 8 * 1024 * 1024, - 'package_length_type' => 'N', //see php pack() - 'package_length_offset' => 0, - 'package_body_offset' => 4, -)); - -if (!$client->connect('127.0.0.1', 9504)) { - exit("connect failed\n"); -} - -$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); - -for ($l = 0; $l < 1; $l++) { - $data = ''; - for ($i = 0; $i < 10; $i++) { - $len = rand(100000, 200000); - echo "send : " . ($len + 4) . "\n"; - $func($client, $len); - } - sleep(1); -} - -function send_test3($client, $len) -{ - $data = pack('N', $len + 4); - $data .= str_repeat('A', $len) . rand(1000, 9999); - $chunks = str_split($data, 4000); - foreach ($chunks as $ch) { - $client->send($ch); - } - $data = $client->recv(); - echo "recv : " . strlen($data) . "\n"; -} - -function send_test2($client, $len) -{ - $data = pack('N', $len + 4); - $data .= str_repeat('A', $len) . rand(100000, 999999); - $client->send($data); - - $data = $client->recv(); -} - -function send_test1($client, $len) -{ - $client->send(pack('N', $len + 4)); - usleep(10); - $client->send(str_repeat('A', $len) . rand(1000, 9999)); - $data = $client->recv(); -} diff --git a/examples/server/fixed_header_server.php b/examples/server/fixed_header_server.php deleted file mode 100644 index c7e61a4273a..00000000000 --- a/examples/server/fixed_header_server.php +++ /dev/null @@ -1,44 +0,0 @@ -run('0.0.0.0', 9504); - -class SocketServer -{ - protected $serv; //swoole server - - const MAX_PACKAGE_LEN = 8000000; //max data accept - - function run($host, $port) - { - $this->serv = new swoole_server($host, $port); - - $this->serv->set(array( - 'enable_coroutine' => false, -// 'dispatch_mode' => 3, //who come first who is - 'worker_num' => 1, //how much worker will start - 'open_length_check' => true, - 'package_max_length' => 8 * 1024 * 1024, - 'package_length_type' => 'N', //see php pack() - 'package_length_offset' => 0, - 'package_body_offset' => 4, - )); - - $this->serv->on('receive', array($this, 'onReceive')); - $this->serv->start(); - } - - function onReceive($serv, $fd, $tid, $data) - { - echo "recv " . strlen($data) . " bytes\n"; - $packet = substr($data, 4); - $result = array( - "code" => "0", - "msg" => "ok", - "data" => $packet, - ); - $resp = json_encode($result); - $send_data = pack('N', strlen($resp)) . $resp; - echo "send " . strlen($send_data) . " bytes\n"; - $serv->send($fd, $send_data); - } -} diff --git a/examples/server/getReceivedTime.php b/examples/server/getReceivedTime.php index 9e988d3ebae..e1394d55c2f 100644 --- a/examples/server/getReceivedTime.php +++ b/examples/server/getReceivedTime.php @@ -1,5 +1,5 @@ on('connect', function ($serv, $fd, $reactor_id){ // echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; //}); @@ -7,7 +7,7 @@ 'worker_num' => 1, )); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { usleep(rand(100000, 2000000)); var_dump(round($serv->getReceivedTime(), 10)); }); diff --git a/examples/server/host_update.php b/examples/server/host_update.php new file mode 100644 index 00000000000..cda874d010d --- /dev/null +++ b/examples/server/host_update.php @@ -0,0 +1,121 @@ +set(array( + 'worker_num' => 2, + //'open_eof_check' => true, + //'package_eof' => "\r\n", + 'task_worker_num' => 2, + //'dispatch_mode' => 2, + //'daemonize' => 1, + //'heartbeat_idle_time' => 5, + //'heartbeat_check_interval' => 5, +)); +function my_onStart($serv) +{ + echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; + echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n"; +} + +function my_onShutdown($serv) +{ + echo "Server: onShutdown\n"; +} + +function my_onClose($serv, $fd, $reactor_id) +{ + //echo "Client: fd=$fd is closed.\n"; +} + +function my_onConnect($serv, $fd, $reactor_id) +{ + //throw new Exception("hello world"); +// echo "Client:Connect.\n"; +} + + +$class = null; +function my_onWorkerStart($serv, $worker_id) +{ + global $argv; + global $class; + opcache_reset(); + include "hot_update_class.php"; + $class = new HotUpdate(); + if ($worker_id >= $serv->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]} task worker"); + } else { + swoole_set_process_name("php {$argv[0]} event worker"); + } + //echo "WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\n"; +} + +function my_onWorkerStop($serv, $worker_id) +{ + echo "WorkerStop[$worker_id]|pid=" . posix_getpid() . ".\n"; +} + +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) +{ + $cmd = trim($data); + if ($cmd == "reload") { + $serv->reload($serv); + } elseif ($cmd == "task") { + $task_id = $serv->task("hello world", 0); + echo "Dispath AsyncTask: id=$task_id\n"; + } elseif ($cmd == "info") { + $info = $serv->connection_info($fd); + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == "broadcast") { + $start_fd = 0; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if ($conn_list === false) { + break; + } + $start_fd = end($conn_list); + foreach ($conn_list as $conn) { + if ($conn === $fd) { + continue; + } + $serv->send($conn, "hello from $fd\n"); + } + } + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { + hello_no_exists(); + } elseif ($cmd == "shutdown") { + $serv->shutdown(); + } else { + global $class; + $data .= $class->getData(); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + //$serv->close($fd); + } + //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; +} + +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) +{ + echo "AsyncTask[PID=" . posix_getpid() . "]: task_id=$task_id." . PHP_EOL; + $serv->finish("OK"); +} + +function my_onFinish(Swoole\Server $serv, $data) +{ + echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL; +} + +$serv->on('Start', 'my_onStart'); +$serv->on('Connect', 'my_onConnect'); +$serv->on('Receive', 'my_onReceive'); +$serv->on('Close', 'my_onClose'); +$serv->on('Shutdown', 'my_onShutdown'); +$serv->on('WorkerStart', 'my_onWorkerStart'); +$serv->on('WorkerStop', 'my_onWorkerStop'); +$serv->on('Task', 'my_onTask'); +$serv->on('Finish', 'my_onFinish'); +$serv->on('WorkerError', function ($serv, $worker_id, $worker_pid, $exit_code) { + echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; +}); +$serv->start(); diff --git a/examples/hot_update_class.php b/examples/server/hot_update_class.php similarity index 100% rename from examples/hot_update_class.php rename to examples/server/hot_update_class.php diff --git a/examples/server/ip_dispatch.php b/examples/server/ip_dispatch.php index 35cfe7412af..4ca00b743c3 100644 --- a/examples/server/ip_dispatch.php +++ b/examples/server/ip_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->workerid = 0; $serv->set(array( @@ -37,7 +37,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { foreach($serv->fdlist as $_fd=>$val) { $serv->send($_fd, "{$fd} say:".$data.PHP_EOL); diff --git a/examples/server/length_client.php b/examples/server/length_client.php new file mode 100644 index 00000000000..4fbbb1d7618 --- /dev/null +++ b/examples/server/length_client.php @@ -0,0 +1,54 @@ +set(array( + 'open_length_check' => true, + 'package_max_length' => 8 * 1024 * 1024, + 'package_length_type' => 'N', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 4, +)); + +if (!$client->connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + $data = ''; + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + echo "send : " . ($len + 4) . "\n"; + $func($client, $len); + } + sleep(1); +} + +function send_test3($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(1000, 9999); + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} + +function send_test2($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(100000, 999999); + $client->send($data); + + $data = $client->recv(); +} + +function send_test1($client, $len) +{ + $client->send(pack('N', $len + 4)); + usleep(10); + $client->send(str_repeat('A', $len) . rand(1000, 9999)); + $data = $client->recv(); +} diff --git a/examples/server/length_server.php b/examples/server/length_server.php new file mode 100644 index 00000000000..c494a66ad1b --- /dev/null +++ b/examples/server/length_server.php @@ -0,0 +1,44 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, +// 'dispatch_mode' => 3, //who come first who is + 'worker_num' => 1, //how much worker will start + 'open_length_check' => true, + 'package_max_length' => 8 * 1024 * 1024, + 'package_length_type' => 'N', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 4, + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} diff --git a/examples/server/listen_1k_port.php b/examples/server/listen_1k_port.php index e7c6baebfa0..ded093874db 100644 --- a/examples/server/listen_1k_port.php +++ b/examples/server/listen_1k_port.php @@ -1,5 +1,5 @@ on('workerstart', function($server, $id) { global $argv; @@ -7,10 +7,10 @@ $local_listener = stream_socket_server("127.0.0.1", 9999); - swoole_event_add($local_listener, function($server){ + Swoole\Event::add($local_listener, function($server){ $local_client = stream_socket_accept($server); - swoole_event_add($local_client, function($client){ + Swoole\Event::add($local_client, function($client){ echo fread($client, 8192); fwrite($client, "hello"); }); @@ -18,16 +18,16 @@ }); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id) { //echo "connect\n";; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: ".$data); //$serv->close($fd); }); -$serv->on('close', function (swoole_server $serv, $fd, $reactor_id) { +$serv->on('close', function (Swoole\Server $serv, $fd, $reactor_id) { //var_dump($serv->connection_info($fd)); //echo "onClose\n"; }); diff --git a/examples/server/manager_timer.php b/examples/server/manager_timer.php index 2d4e697fdb5..25264525e3e 100644 --- a/examples/server/manager_timer.php +++ b/examples/server/manager_timer.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -10,25 +10,25 @@ // sleep(30); - $id = swoole_timer_tick(3000, function () { + $id = Swoole\Timer::tick(3000, function () { echo "timer 1\n"; }); - swoole_timer_after(9000, function () use ($id) { + Swoole\Timer::after(9000, function () use ($id) { echo "timer 2\n"; - swoole_timer_clear($id); + Swoole\Timer::clear($id); - swoole_timer_tick(2000, function () { + Swoole\Timer::tick(2000, function () { echo "timer 3\n"; }); - swoole_timer_tick(300, function () { + Swoole\Timer::tick(300, function () { echo "timer 4\n"; }); }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd] receive data: $data\n"; if ($serv->send($fd, "hello {$data}\n") == false) { diff --git a/examples/server/mixed.php b/examples/server/mixed.php new file mode 100644 index 00000000000..5408899ac09 --- /dev/null +++ b/examples/server/mixed.php @@ -0,0 +1,452 @@ + 16, // 线程数. 一般设置为CPU核数的1-4倍 + 'worker_num' => 2, // 工作进程数量. 设置为CPU的1-4倍最合理 + 'max_request' => 1000, // 防止 PHP 内存溢出, 一个工作进程处理 X 次任务后自动重启 (注: 0,不自动重启) + 'max_conn' => 10000, // 最大连接数 + 'task_worker_num' => 1, // 任务工作进程数量 +// 'task_ipc_mode' => 2, // 设置 Task 进程与 Worker 进程之间通信的方式。 + 'task_max_request' => 0, // 防止 PHP 内存溢出 + //'task_tmpdir' => '/tmp', + //'message_queue_key' => ftok(SYS_ROOT . 'queue.msg', 1), + 'dispatch_mode' => 2, + //'daemonize' => 1, // 设置守护进程模式 + 'backlog' => 128, + //'log_file' => '/data/logs/swoole.log', + 'heartbeat_check_interval' => 2, // 心跳检测间隔时长(秒) + 'heartbeat_idle_time' => 3, // 连接最大允许空闲的时间 + //'open_eof_check' => 1, + //'open_eof_split' => 1, + //'package_eof' => "\r\r\n", + //'open_cpu_affinity' => 1, + 'socket_buffer_size' => 1024 * 1024 * 128, + 'output_buffer_size' => 1024 * 1024 * 2, + //'enable_delay_receive' => true, + //'cpu_affinity_ignore' =>array(0,1)//如果你的网卡2个队列(或者没有多队列那么默认是cpu0来处理中断),并且绑定了core 0和core 1,那么可以通过这个设置避免swoole的线程或者进程绑定到这2个core,防止cpu0,1被耗光而造成的丢包 + ); +} + +if (isset($argv[1]) and $argv[1] == 'daemon') { + G::$config['daemonize'] = true; +} else { + G::$config['daemonize'] = false; +} + +//$mode = SWOOLE_BASE; +$mode = SWOOLE_PROCESS; + +$serv = new Swoole\Server("0.0.0.0", '9501', $mode, SWOOLE_SOCK_TCP); +$serv->listen('0.0.0.0', 9502, SWOOLE_SOCK_UDP); +$serv->listen('::', 9503, SWOOLE_SOCK_TCP6); +$serv->listen('::', 9504, SWOOLE_SOCK_UDP6); +$process1 = new Swoole\Process(function ($worker) use ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: my_process1"); + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { + echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx + foreach ($serv->connections as $conn) { + $serv->send($conn, "heartbeat\n"); + } + }); + Swoole\Timer::tick(5000, function () use ($serv) { + $serv->sendMessage("hello event worker", 0); + $serv->sendMessage("hello task worker", 4); + }); +}, false); + +//$serv->addprocess($process1); + +$process2 = new Swoole\Process(function ($worker) use ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: my_process2"); + Swoole\Timer::tick(2000, function ($interval) use ($worker, $serv) { + echo "#{$worker->pid} child process timer $interval\n"; // 如果worker中没有定时器,则会输出 process timer xxx + }); +}, false); + +//$serv->addprocess($process2); + +$serv->set(G::$config); +$serv->set(['reactor_num' => 4]); + +/** + * 使用类的静态属性,可以直接访问 + */ +G::$serv = $serv; + +function my_onStart(Swoole\Server $serv) +{ + global $argv; + swoole_set_process_name("php {$argv[0]}: master"); + my_log("Server: start.Swoole version is [" . SWOOLE_VERSION . "]"); + my_log("MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}"); +} + +function my_log($msg) +{ + global $serv; + if (empty($serv->worker_pid)) { + $serv->worker_pid = posix_getpid(); + } + echo "#" . $serv->worker_pid . "\t[" . date('H:i:s') . "]\t" . $msg . PHP_EOL; +} + +function forkChildInWorker() +{ + global $serv; + echo "on worker start\n"; + $process = new Swoole\Process(function (Swoole\Process $worker) use ($serv) { +// $serv = new Swoole\Server( "0.0.0.0", 9503 ); +// $serv->set(array( +// 'worker_num' => 1 +// )); +// $serv->on ( 'receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { +// $serv->send ( $fd, "Swoole: " . $data ); +// $serv->close ( $fd ); +// }); +// $serv->start (); +// Swoole\Event::add ($worker->pipe, function ($pipe) use ($worker) { +// echo $worker->read()."\n"; +// }); + }); + + $pid = $process->start(); + echo "Fork child process success. pid={$pid}\n"; + //保存子进程对象,这里如果不保存,那对象会被销毁,管道也会被关闭 + $serv->childprocess = $process; +} + +function processRename(Swoole\Server $serv, $worker_id) +{ + + global $argv; + if ($serv->taskworker) { + swoole_set_process_name("php {$argv[0]}: task"); + } else { + swoole_set_process_name("php {$argv[0]}: worker"); + } +// if ($worker_id == 0) +// { +// var_dump($serv->setting); +// } + my_log("WorkerStart: MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId={$serv->worker_id}|WorkerPid={$serv->worker_pid}"); +} + +function setTimerInWorker(Swoole\Server $serv, $worker_id) +{ + + if ($worker_id == 0) { + echo "Start: " . microtime(true) . "\n"; + //$serv->addtimer(3000); +// $serv->addtimer(7000); + //var_dump($serv->gettimer()); + } +// $serv->after(2000, function(){ +// echo "Timeout: ".microtime(true)."\n"; +// }); +// $serv->after(5000, function(){ +// echo "Timeout: ".microtime(true)."\n"; +// global $serv; +// $serv->deltimer(3000); +// }); +} + +function my_onShutdown($serv) +{ + echo "Server: onShutdown\n"; +} + +function my_onClose(Swoole\Server $serv, $fd, $reactor_id) +{ + my_log("Client[$fd@$reactor_id]: fd=$fd is closed"); + var_dump($serv->getClientInfo($fd)); +} + +function my_onConnect(Swoole\Server $serv, $fd, $reactor_id) +{ + //throw new Exception("hello world"); +// var_dump($serv->connection_info($fd)); + //var_dump($serv, $fd, $reactor_id); +// echo "Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: Connect.\n"; + $serv->after(2000, function () use ($serv, $fd) { + $serv->confirm($fd); + }); + my_log("Client: Connect --- {$fd}"); +} + +function timer_show($id) +{ + my_log("Timer#$id"); +} + +function my_onWorkerExit(Swoole\Server $serv, $worker_id) +{ + global $argv; +} + +function my_onWorkerStart(Swoole\Server $serv, $worker_id) +{ + processRename($serv, $worker_id); + + if (!$serv->taskworker) { + Swoole\Process::signal(SIGUSR2, function ($signo) { + echo "SIGNAL: $signo\n"; + }); + Swoole\Event::defer(function () { + echo "defer call\n"; + }); +// $serv->tick(2000, function() use ($serv) { +// echo "Worker-{$serv->worker_id} tick-2000\n"; +// }); + } else { +// Swoole\Timer::after(2000, function() { +// echo "after 2 secends.\n"; +// }); +// $serv->tick(1000, function ($id) use ($serv) { +// if (G::$index > 10) { +// $serv->after(2500, 'timer_show', 2); +// G::$index = 0; +// } else { +// G::$index++; +// } +// timer_show($id); +// }); + } + //forkChildInWorker(); +// setTimerInWorker($serv, $worker_id); +} + +function my_onWorkerStop($serv, $worker_id) +{ + echo "WorkerStop[$worker_id]|pid=" . $serv->worker_pid . ".\n"; +} + +function my_onPacket($serv, $data, $clientInfo) +{ + $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server " . $data); + var_dump($clientInfo); +} + +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $data) +{ + my_log("Worker#{$serv->worker_pid} Client[$fd@$reactor_id]: received: $data"); + $cmd = trim($data); + if ($cmd == "reload") { + $serv->reload(); + } elseif ($cmd == "task") { + $task_id = $serv->task("task " . $fd); + echo "Dispath AsyncTask: id=$task_id\n"; + } elseif ($cmd == "taskclose") { + $serv->task("close " . $fd); + echo "close the connection in taskworker\n"; + } elseif ($cmd == "tasksend") { + $serv->task("send " . $fd); + } elseif ($cmd == "bigtask") { + $serv->task(str_repeat('A', 8192 * 5)); + } elseif ($cmd == "taskwait") { + $result = $serv->taskwait("taskwait"); + if ($result) { + $serv->send($fd, "taskwaitok"); + } + echo "SyncTask: result=" . var_export($result, true) . "\n"; + } elseif ($cmd == "taskWaitMulti") { + $result = $serv->taskWaitMulti(array( + str_repeat('A', 8192 * 5), + str_repeat('B', 8192 * 6), + str_repeat('C', 8192 * 8) + )); + if ($result) { + $resp = "taskWaitMulti ok\n"; + foreach ($result as $k => $v) { + $resp .= "result[$k] length=" . strlen($v) . "\n"; + } + $serv->send($fd, $resp); + } else { + $serv->send($fd, "taskWaitMulti error\n"); + } + } elseif ($cmd == "hellotask") { + $serv->task("hellotask"); + } elseif ($cmd == "taskcallback") { + $serv->task("taskcallback", -1, function (Swoole\Server $serv, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); + }); + } elseif ($cmd == "sendto") { + $serv->sendto("127.0.0.1", 9999, "hello world"); + } elseif ($cmd == "close") { + $serv->send($fd, "close connection\n"); + $result = $serv->close($fd); + } elseif ($cmd == "info") { + $info = $serv->connection_info(strval($fd), $reactor_id); + var_dump($info["remote_ip"]); + $serv->send($fd, 'Info: ' . var_export($info, true) . PHP_EOL); + } elseif ($cmd == 'proxy') { + $serv->send(1, "hello world\n"); + } elseif ($cmd == 'sleep') { + sleep(10); + } elseif ($cmd == 'foreach') { + foreach ($serv->connections as $fd) { + echo "conn : $fd\n"; + } + return; + } elseif ($cmd == 'tick') { + $serv->tick(2000, function ($id) { + echo "tick #$id\n"; + }); + } elseif ($cmd == 'addtimer') { + $serv->addtimer(3000); + } elseif ($cmd == "list") { + $start_fd = 0; + echo "broadcast\n"; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if (empty($conn_list)) { + echo "iterates finished\n"; + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + } + } elseif ($cmd == "list2") { + foreach ($serv->connections as $con) { + var_dump($serv->connection_info($con)); + } + } elseif ($cmd == "stats") { + $serv_stats = $serv->stats(); + $serv->send($fd, 'Stats: ' . var_export($serv_stats, true) . "\ncount=" . count($serv->connections) . PHP_EOL); + } elseif ($cmd == "broadcast") { + broadcast($serv, $fd, "hello from $fd\n"); + } //这里故意调用一个不存在的函数 + elseif ($cmd == "error") { + hello_no_exists(); + } elseif ($cmd == "exit") { + exit("worker php exit.\n"); + } //关闭fd + elseif (substr($cmd, 0, 5) == "close") { + $close_fd = substr($cmd, 6); + $serv->close($close_fd); + } elseif ($cmd == "shutdown") { + $serv->shutdown(); + } elseif ($cmd == "fatalerror") { + require __DIR__ . '/php/error.php'; + } elseif ($cmd == 'defer') { + $serv->defer(function () use ($fd, $serv) { + $serv->close($fd); + $serv->defer(function () { + echo "deferd\n"; + }); + }); + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + } else { + $serv->send($fd, 'Swoole: ' . $data, $reactor_id); + //$serv->close($fd); + } + //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; +// $serv->after( +// 800, function () { +// echo "hello"; +// } +// ); + //Swoole\Server_send($serv, $other_fd, "Server: $data", $other_reactor_id); +} + +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) +{ + if ($data == 'taskwait') { + $fd = str_replace('task-', '', $data); + $serv->send($fd, "hello world"); + return array("task" => 'wait'); + } elseif ($data == 'taskcallback') { + return array("task" => 'callback'); + } else { + $cmd = explode(' ', $data); + if ($cmd[0] == 'send') { + $serv->send($cmd[1], str_repeat('A', 10000) . "\n"); + } elseif ($cmd[0] == 'close') { + $serv->close($cmd[1]); + } else { + echo "bigtask: length=" . strlen($data) . "\n"; + return $data; + } +// $serv->sendto('127.0.0.1', 9999, "hello world"); + //Swoole\Timer::after(1000, "test"); +// var_dump($data); +// $serv->send($fd, str_repeat('A', 8192 * 2)); +// $serv->send($fd, str_repeat('B', 8192 * 2)); +// $serv->send($fd, str_repeat('C', 8192 * 2)); +// $serv->send($fd, str_repeat('D', 8192 * 2)); + return; + } + + if ($data == "hellotask") { + broadcast($serv, 0, "hellotask"); + } else { + echo "AsyncTask[PID=" . $serv->worker_pid . "]: task_id=$task_id." . PHP_EOL; + //eg: test-18 + return $data; + } +} + +function my_onFinish(Swoole\Server $serv, $task_id, $data) +{ + list($str, $fd) = explode('-', $data); + $serv->send($fd, 'taskok'); + var_dump($str, $fd); + echo "AsyncTask Finish: result={$data}. PID=" . $serv->worker_pid . PHP_EOL; +} + +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code, $signo) +{ + echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code|Signal=$signo\n"; +} + +function broadcast(Swoole\Server $serv, $fd = 0, $data = "hello") +{ + $start_fd = 0; + echo "broadcast\n"; + while (true) { + $conn_list = $serv->connection_list($start_fd, 10); + if ($conn_list === false) { + break; + } + $start_fd = end($conn_list); + foreach ($conn_list as $conn) { + if ($conn === $fd) continue; + $ret1 = $serv->send($conn, $data); + //var_dump($ret1); + //$ret2 = $serv->close($conn); + //var_dump($ret2); + } + } +} + +$serv->on('PipeMessage', function ($serv, $src_worker_id, $msg) { + my_log("PipeMessage: Src={$src_worker_id},Msg=" . trim($msg)); + if ($serv->taskworker) { + $serv->sendMessage("hello user process", + $src_worker_id); + } +}); + +$serv->on('Start', 'my_onStart'); +$serv->on('Connect', 'my_onConnect'); +$serv->on('Receive', 'my_onReceive'); +$serv->on('Packet', 'my_onPacket'); +$serv->on('Close', 'my_onClose'); +$serv->on('Shutdown', 'my_onShutdown'); +$serv->on('WorkerStart', 'my_onWorkerStart'); +$serv->on('WorkerStop', 'my_onWorkerStop'); +$serv->on('Task', 'my_onTask'); +$serv->on('Finish', 'my_onFinish'); +$serv->on('WorkerError', 'my_onWorkerError'); +$serv->on('WorkerExit', 'my_onWorkerExit'); +$serv->on('ManagerStart', function ($serv) { + global $argv; + swoole_set_process_name("php {$argv[0]}: manager"); +}); +$serv->start(); diff --git a/examples/server/multi_instance.php b/examples/server/multi_instance.php index abd632386cc..2a4318075fa 100644 --- a/examples/server/multi_instance.php +++ b/examples/server/multi_instance.php @@ -1,9 +1,9 @@ on("start", function ($server) use ($port) { echo "Swoole http server is started at http://127.0.0.1:{$port}\n"; diff --git a/examples/server/multi_port.php b/examples/server/multi_port.php new file mode 100644 index 00000000000..4c6c462f768 --- /dev/null +++ b/examples/server/multi_port.php @@ -0,0 +1,44 @@ +addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); + +$port->on('packet', function($serv, $data, $client) { + var_dump($data, $client); + $serv->sendto($client['address'], $client['port'], "welcome admin\n"); +}); +$serv->on('connect', function ($serv, $fd) { + echo "Client:Connect.\n"; +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + $info = $serv->connection_info($fd, $reactor_id); + //来自9502的内网管理端口 + if($info['server_port'] == 9502) { + $serv->send($fd, "welcome admin\n"); + $start_fd = 0; + while(true) + { + $conn_list = $serv->connection_list($start_fd, 10); + if($conn_list === false) + { + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + + foreach($conn_list as $conn) + { + if($conn === $fd) continue; + $serv->send($conn, "hello from $fd\n"); + } + } + } + //来自外网 + else { + $serv->send($fd, 'Swoole: '.$data); + } +}); +$serv->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/pipe_message.php b/examples/server/pipe_message.php index 713c8e885a7..827b372f008 100644 --- a/examples/server/pipe_message.php +++ b/examples/server/pipe_message.php @@ -1,6 +1,6 @@ set(array( 'worker_num' => 2, 'task_worker_num' => 2, @@ -10,7 +10,7 @@ echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; }); -$serv->on('task', function (swoole_server $serv, $task_id, $reactor_id, $data){ +$serv->on('task', function (Swoole\Server $serv, $task_id, $reactor_id, $data){ echo "#{$serv->worker_id} NewTask: $data\n"; $serv->sendMessage($data, 0); //$serv->send($fd, str_repeat('B', 1024*rand(40, 60)).rand(10000, 99999)."\n"); @@ -20,7 +20,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); if($cmd == 'totask') { diff --git a/examples/proxy_sync.php b/examples/server/proxy_sync.php similarity index 93% rename from examples/proxy_sync.php rename to examples/server/proxy_sync.php index 9e2cf5ea611..3ec5440bfa1 100644 --- a/examples/proxy_sync.php +++ b/examples/server/proxy_sync.php @@ -7,7 +7,7 @@ class ProxyServer function run() { - $serv = new swoole_server("127.0.0.1", 9509); + $serv = new Swoole\Server("127.0.0.1", 9509); $serv->set(array( 'worker_num' => 32, //reactor thread num 'backlog' => 128, //listen backlog @@ -47,7 +47,7 @@ function onConnect($serv, $fd, $reactor_id) function onReceive($serv, $fd, $reactor_id, $data) { - $socket = new swoole_client(SWOOLE_SOCK_TCP); + $socket = new Swoole\Client(SWOOLE_SOCK_TCP); if($socket->connect('127.0.0.1', 80, 0.5)) { $socket->send($data); diff --git a/examples/redis_pool.php b/examples/server/redis_pool.php similarity index 98% rename from examples/redis_pool.php rename to examples/server/redis_pool.php index be6b0d98a8f..12e79d6976b 100644 --- a/examples/redis_pool.php +++ b/examples/server/redis_pool.php @@ -18,7 +18,7 @@ throw new Exception("install redis extension, pecl install redis"); } -$serv = new swoole_server("0.0.0.0", 9508); +$serv = new Swoole\Server("0.0.0.0", 9508); $serv->set(array( 'worker_num' => 4,//base on you cpu nums diff --git a/examples/server/reload_aysnc.php b/examples/server/reload_aysnc.php index a66b194c37c..a5ecbd18d5b 100644 --- a/examples/server/reload_aysnc.php +++ b/examples/server/reload_aysnc.php @@ -1,5 +1,5 @@ set([ 'worker_num' => 4, @@ -13,7 +13,7 @@ if ($serv->taskworker) { return; } - swoole_event::add(STDIN, function () use ($wid) { + Swoole\Event::add(STDIN, function () use ($wid) { $data = fread(STDIN, 8192); if ($data) { echo "#{$wid}: $data"; @@ -21,17 +21,17 @@ }); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ $serv->on('WorkerExit', function ($serv, $wid) { echo "WorkerExit, PID=".posix_getpid()."\t$wid\n"; - swoole_event::del(STDIN); + Swoole\Event::del(STDIN); }); $serv->start(); diff --git a/examples/server/reload_force.php b/examples/server/reload_force.php index 03f8536d628..9bd5b3b70e6 100644 --- a/examples/server/reload_force.php +++ b/examples/server/reload_force.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/reload_force2.php b/examples/server/reload_force2.php index 50873d568eb..785b82f5299 100644 --- a/examples/server/reload_force2.php +++ b/examples/server/reload_force2.php @@ -1,11 +1,11 @@ set([ "worker_num" => 4, "max_wait_time" => 1 ]); -$serv->on("WorkerStart", function (\swoole_server $server, $worker_id) { +$serv->on("WorkerStart", function (\Swoole\Server $server, $worker_id) { global $flag; echo "$worker_id [".$server->worker_pid."] start \n"; }); @@ -15,4 +15,4 @@ sleep(100); } }); -$serv->start(); \ No newline at end of file +$serv->start(); diff --git a/examples/server/send_1m.php b/examples/server/send_1m.php new file mode 100644 index 00000000000..4a416f9e3f0 --- /dev/null +++ b/examples/server/send_1m.php @@ -0,0 +1,30 @@ +set(array('worker_num' => 1)); +$serv->on('workerStart', function($serv, $worker_id) { + //if($worker_id == 0) $serv->addtimer(500); +}); +$serv->on('connect', function ($serv, $fd, $reactor_id){ + $serv->array['fd'] = &strval($fd); + echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; +}); +$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { + //echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; + $array = array('A', 'B', 'C', 'D', 'E', 'F', 'G'); + $data = ''; + $n_bytes = 0; + for ($i = 0; $i < 10; $i++) + { + $_str = str_repeat($array[$i % 7], 4030) . "\n"; + //$serv->send($fd, $_str); + $n_bytes += strlen($_str); + $data .= $_str; + } + echo "send " . $n_bytes . " bytes\n"; + $serv->send( $serv->array['fd'], $data); + $serv->close($fd); +}); +$serv->on('close', function ($serv, $fd, $reactor_id) { + echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/sendfile.php b/examples/server/sendfile.php new file mode 100644 index 00000000000..1b87c62155f --- /dev/null +++ b/examples/server/sendfile.php @@ -0,0 +1,24 @@ +set(array( + 'worker_num' => 1, +)); +$serv->on('timer', function($serv, $interval) { + echo "onTimer: $interval\n"; +}); +$serv->on('workerStart', function($serv, $worker_id) { + //if($worker_id == 0) $serv->addtimer(300); +}); +$serv->on('connect', function (Swoole\Server $serv, $fd){ + $serv->send($fd, filesize(__DIR__.'/test.jpg')); + //echo "Client:Connect.\n"; +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { + echo "Client[$fd]: $data\n"; + $serv->sendfile($fd, __DIR__.'/test.jpg'); + //$serv->close($fd); +}); +$serv->on('close', function ($serv, $fd) { + //echo "Client: Close.\n"; +}); +$serv->start(); diff --git a/examples/server/single.php b/examples/server/single.php index 83f9b8e32e1..b1fea489695 100644 --- a/examples/server/single.php +++ b/examples/server/single.php @@ -1,6 +1,6 @@ on('Receive', function($serv, $fd, $reactor_id, $data) { $serv->send($fd, "Swoole: $data"); diff --git a/examples/server/tcp_client.php b/examples/server/tcp_client.php new file mode 100644 index 00000000000..9b0ed877ef4 --- /dev/null +++ b/examples/server/tcp_client.php @@ -0,0 +1,29 @@ +connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + $data = ''; + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + echo "send : " . ($len + 4) . "\n"; + send_test3($client, $len); + } +} + +function send_test3($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(1000, 9999); + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} diff --git a/examples/server/tcp_server.php b/examples/server/tcp_server.php new file mode 100644 index 00000000000..f3e8dc687b6 --- /dev/null +++ b/examples/server/tcp_server.php @@ -0,0 +1,38 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, + 'worker_num' => 1, //how much worker will start + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} diff --git a/examples/server/trace.php b/examples/server/trace.php index c35ab10b03c..a2c923dacdf 100644 --- a/examples/server/trace.php +++ b/examples/server/trace.php @@ -10,7 +10,7 @@ function test_sleep() sleep(5); } -$server = new swoole_server('127.0.0.1', 9501); +$server = new Swoole\Server('127.0.0.1', 9501); $server->set([ 'worker_num' => 1, @@ -29,13 +29,13 @@ function test_sleep() $serv->send($fd, "Swoole: $data"); }); -$server->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$server->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; test(); $serv->send($data, "Swoole: task\n"); }); -$server->on('Finish', function (swoole_server $serv, $task_id, $data) { +$server->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/server/uid_dispatch.php b/examples/server/uid_dispatch.php index cdd82661d2d..c2f5ad94d3d 100644 --- a/examples/server/uid_dispatch.php +++ b/examples/server/uid_dispatch.php @@ -1,5 +1,5 @@ fdlist = []; $serv->set(array( //'tcp_defer_accept' => 5, @@ -36,7 +36,7 @@ }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $conn = $serv->connection_info($fd); print_r($conn); echo "worker_id: " . $serv->worker_id . PHP_EOL; diff --git a/examples/server/unix_stream.php b/examples/server/unix_stream.php index 665259c3e2f..38e532d829e 100644 --- a/examples/server/unix_stream.php +++ b/examples/server/unix_stream.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, )); $serv->on('connect', function ($serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => '1213', "bat" => "ab"))); //$serv->close($fd); diff --git a/examples/server/zmq.php b/examples/server/zmq.php index 3c00e72861a..5455abd8a87 100644 --- a/examples/server/zmq.php +++ b/examples/server/zmq.php @@ -1,5 +1,5 @@ getsockopt(ZMQ::SOCKOPT_FD); - swoole_event_add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); + Swoole\Event::add($rfd, 'onZMQR', NULL , SWOOLE_EVENT_READ); echo "worker start\n"; }); @@ -37,7 +37,7 @@ function onZMQR() echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { $cmd = trim($data); echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; diff --git a/examples/server_hot_update_opcache.php b/examples/server_hot_update_opcache.php deleted file mode 100644 index c5d1e196579..00000000000 --- a/examples/server_hot_update_opcache.php +++ /dev/null @@ -1,144 +0,0 @@ -set(array( - 'worker_num' => 2, - //'open_eof_check' => true, - //'package_eof' => "\r\n", - 'task_worker_num' => 2, - //'dispatch_mode' => 2, - //'daemonize' => 1, - //'heartbeat_idle_time' => 5, - //'heartbeat_check_interval' => 5, -)); -function my_onStart($serv) -{ - echo "MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}\n"; - echo "Server: start.Swoole version is [".SWOOLE_VERSION."]\n"; - //$serv->addtimer(1000); -} - -function my_onShutdown($serv) -{ - echo "Server: onShutdown\n"; -} - -function my_onTimer($serv, $interval) -{ - echo "Server:Timer Call.Interval=$interval\n"; -} - -function my_onClose($serv, $fd, $reactor_id) -{ - //echo "Client: fd=$fd is closed.\n"; -} - -function my_onConnect($serv, $fd, $reactor_id) -{ - //throw new Exception("hello world"); -// echo "Client:Connect.\n"; -} - - -$class = null; -function my_onWorkerStart($serv, $worker_id) -{ - global $argv; - global $class; - opcache_reset(); - include "hot_update_class.php"; - $class = new HotUpdate(); - if($worker_id >= $serv->setting['worker_num']) { - swoole_set_process_name("php {$argv[0]} task worker"); - } else { - swoole_set_process_name("php {$argv[0]} event worker"); - } - //echo "WorkerStart|MasterPid={$serv->master_pid}|Manager_pid={$serv->manager_pid}|WorkerId=$worker_id\n"; - //$serv->addtimer(500); //500ms -} - -function my_onWorkerStop($serv, $worker_id) -{ - echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; -} - -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $data) -{ - $cmd = trim($data); - if($cmd == "reload") - { - $serv->reload($serv); - } - elseif($cmd == "task") - { - $task_id = $serv->task("hello world", 0); - echo "Dispath AsyncTask: id=$task_id\n"; - } - elseif($cmd == "info") - { - $info = $serv->connection_info($fd); - $serv->send($fd, 'Info: '.var_export($info, true).PHP_EOL); - } - elseif($cmd == "broadcast") - { - $start_fd = 0; - while(true) - { - $conn_list = $serv->connection_list($start_fd, 10); - if($conn_list === false) - { - break; - } - $start_fd = end($conn_list); - foreach($conn_list as $conn) - { - if($conn === $fd) continue; - $serv->send($conn, "hello from $fd\n"); - } - } - } - //这里故意调用一个不存在的函数 - elseif($cmd == "error") - { - hello_no_exists(); - } - elseif($cmd == "shutdown") - { - $serv->shutdown(); - } - else - { - global $class; - $data .= $class->getData(); - $serv->send($fd, 'Swoole: '.$data, $reactor_id); - //$serv->close($fd); - } - //echo "Client:Data. fd=$fd|reactor_id=$reactor_id|data=$data"; - //$serv->deltimer(800); - //swoole_server_send($serv, $other_fd, "Server: $data", $other_reactor_id); -} - -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) -{ - echo "AsyncTask[PID=".posix_getpid()."]: task_id=$task_id.".PHP_EOL; - $serv->finish("OK"); -} - -function my_onFinish(swoole_server $serv, $data) -{ - echo "AsyncTask Finish:Connect.PID=".posix_getpid().PHP_EOL; -} - -$serv->on('Start', 'my_onStart'); -$serv->on('Connect', 'my_onConnect'); -$serv->on('Receive', 'my_onReceive'); -$serv->on('Close', 'my_onClose'); -$serv->on('Shutdown', 'my_onShutdown'); -$serv->on('Timer', 'my_onTimer'); -$serv->on('WorkerStart', 'my_onWorkerStart'); -$serv->on('WorkerStop', 'my_onWorkerStop'); -$serv->on('Task', 'my_onTask'); -$serv->on('Finish', 'my_onFinish'); -$serv->on('WorkerError', function($serv, $worker_id, $worker_pid, $exit_code) { - echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; -}); -$serv->start(); diff --git a/examples/set_cpu_affinity.php b/examples/set_cpu_affinity.php deleted file mode 100644 index c44f3adf6bd..00000000000 --- a/examples/set_cpu_affinity.php +++ /dev/null @@ -1,3 +0,0 @@ -on("connect", function (swoole_client $cli) -{ - $cli->send("Hello World\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->close(); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); -echo "connect to 127.0.0.1:9501\n"; diff --git a/examples/ssl/client.c b/examples/ssl/client.c index c263324ebaa..c818f8d54f1 100644 --- a/examples/ssl/client.c +++ b/examples/ssl/client.c @@ -28,7 +28,7 @@ int OpenConnection(const char *hostname, int port) } sd = socket(PF_INET, SOCK_STREAM, 0); - bzero(&addr, sizeof(addr)); + sw_memset_zero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = *(long*) (host->h_addr); diff --git a/examples/ssl/client.php b/examples/ssl/client.php index d4bbf0f9cd3..3fee823cc50 100644 --- a/examples/ssl/client.php +++ b/examples/ssl/client.php @@ -1,5 +1,5 @@ set(array( 'ssl_cert_file' => __DIR__.'/ca/client-cert.pem', diff --git a/examples/ssl/gen_cert.md b/examples/ssl/gen_cert.md new file mode 100644 index 00000000000..2fa84b87958 --- /dev/null +++ b/examples/ssl/gen_cert.md @@ -0,0 +1,5 @@ +```shell +openssl genrsa -out ssl.key 2048 +openssl req -new -key ssl.key -out ssl.csr +openssl x509 -req -days 365 -in ssl.csr -signkey ssl.key -out ssl.crt +``` \ No newline at end of file diff --git a/examples/ssl/passphrase.php b/examples/ssl/passphrase.php index bf62abe0206..ac7b9ab979f 100644 --- a/examples/ssl/passphrase.php +++ b/examples/ssl/passphrase.php @@ -1,5 +1,5 @@ set(array( "ssl_key_file" => __DIR__ . '/ssl.key', "ssl_cert_file" => __DIR__ . '/ssl.crt', diff --git a/examples/ssl/server.php b/examples/ssl/server.php index fdd3cf50a44..d82fc116a71 100644 --- a/examples/ssl/server.php +++ b/examples/ssl/server.php @@ -1,6 +1,6 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_TCP); @@ -18,13 +18,13 @@ 'ssl_verify_depth' => 10, )); -$serv->on('connect', function (swoole_server $serv, $fd, $reactor_id){ +$serv->on('connect', function (Swoole\Server $serv, $fd, $reactor_id){ echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; $info = $serv->getClientInfo($fd); var_dump($info); }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; $serv->send($fd, "Swoole: $data\n"); }); diff --git a/examples/ssl/ssl.crt b/examples/ssl/ssl.crt index 54840643020..ac933537595 100644 --- a/examples/ssl/ssl.crt +++ b/examples/ssl/ssl.crt @@ -1,13 +1,22 @@ -----BEGIN CERTIFICATE----- -MIICAzCCAWwCCQCgnsxsw2bDrDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMCAXDTE0MDcwOTE0MTIxNloYDzIxMTQwNjE1MTQxMjE2WjBFMQsw -CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu -ZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDP -nYsgGr6Q9jqL97xGo2zxrOWGQd96PnItf5uOdvZJjwTLsN8an33UOtFVxwGIaTb8 -MtuXiLZbslKWLIOoqq0Lk92RwF4Zxs02Yt+S1sM/9ST7tiJQKYx1+rfaZIj0Dy9y -VoWHGfRmVZMcWkhslaHR/Yz+Ul4CIqr03/BjYjjQfQIDAQABMA0GCSqGSIb3DQEB -CwUAA4GBAJIjeaUeizJ88G1M9fFUTwf11ywWsrzIQxCaMqmzRyrlIhwuC5qXKsA/ -wHRfj9+KnfJ1LOAguXa/CSRFCogQnYur4+Kzy/PBchMFjIKS9UzmQQWZYsmzBgTX -e6pGJN2fxTmpGKf0lj7//NWxOmFDFWgyUeIR4TaAJ5dWpS8Cr0Gc +MIIDlTCCAn0CFGDgpe31nz4rUDOQKyH6U53AWXHoMA0GCSqGSIb3DQEBCwUAMIGG +MQswCQYDVQQGEwJjbjERMA8GA1UECAwIc2hhbmdoYWkxETAPBgNVBAcMCHNoYW5n +aGFpMQ8wDQYDVQQKDAZzd29vbGUxDzANBgNVBAsMBnN3b29sZTEOMAwGA1UEAwwF +cmFuZ28xHzAdBgkqhkiG9w0BCQEWEHJhbmdvQHN3b29sZS5jb20wHhcNMjAwNDA2 +MTMzMjAwWhcNMjEwNDA2MTMzMjAwWjCBhjELMAkGA1UEBhMCY24xETAPBgNVBAgM +CHNoYW5naGFpMREwDwYDVQQHDAhzaGFuZ2hhaTEPMA0GA1UECgwGc3dvb2xlMQ8w +DQYDVQQLDAZzd29vbGUxDjAMBgNVBAMMBXJhbmdvMR8wHQYJKoZIhvcNAQkBFhBy +YW5nb0Bzd29vbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +s6JM9QuOV4hQ7HlOBCp5aINATTFAYot2n/fwfWnRyrC9riUrLjdIXc3PdjBMZlyr +Mq6ZameM7RDydl0yJTq4mMOMzk8QIydk4/YkWeNRtoKee7lBCS6AUVqh/PwoeuoA +F3f4Mf6jICC1CEF3FECjHNZEBdA46jESAm/XvKjccevduXYUcVRBERRdst7Cd13A +OmtHUE/tgRtTMWh9NxT5vOPOO+H7Ri9g3pEiGofxroOTDqxALxHRcj0k1UkH1J74 +amvw+FY+twx9kBaj+f/JkaTflDjrrf/Stc11AmfjQCdZUnW62Banps9JqyqIsEdI +BcTMYZq5EH0xBSoQDEr4cwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAsk//RTiYF +5Fgfu5F1DTL3lNaG6JI78oKZTFx24sM7CE+wQRLUs5WEiDFkTSCInUbfA2tdRxjB +Y4ye6fKGIL7K9yIN+3Y8ChYZBZ2xHjm4QcAKZ3TFwpxueM74sXZToaDQQooJ/5xD +E1RTcA49waQ6zTFoG0aCeK1hjZ3ZtXaOCPLIP8yqRfkmezdKoU90LBRgXRyVR5jA +mY+0v3Q6irZTpESY+e1RYHK5Yf8TesZ4J4LGRqSjWGP6GidpNc46wko3FOF2/KeB +lN0jjSPx+rXCdA5hrfZIjNFjOLu/CSgOHzOflpSpYh8Jgfe9YBJRFFQI1PlcILGL +3JmkbOAiplbA -----END CERTIFICATE----- diff --git a/examples/ssl/ssl.csr b/examples/ssl/ssl.csr new file mode 100644 index 00000000000..68d763f862a --- /dev/null +++ b/examples/ssl/ssl.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAmNuMREwDwYDVQQIDAhzaGFuZ2hhaTER +MA8GA1UEBwwIc2hhbmdoYWkxDzANBgNVBAoMBnN3b29sZTEPMA0GA1UECwwGc3dv +b2xlMQ4wDAYDVQQDDAVyYW5nbzEfMB0GCSqGSIb3DQEJARYQcmFuZ29Ac3dvb2xl +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALOiTPULjleIUOx5 +TgQqeWiDQE0xQGKLdp/38H1p0cqwva4lKy43SF3Nz3YwTGZcqzKumWpnjO0Q8nZd +MiU6uJjDjM5PECMnZOP2JFnjUbaCnnu5QQkugFFaofz8KHrqABd3+DH+oyAgtQhB +dxRAoxzWRAXQOOoxEgJv17yo3HHr3bl2FHFUQREUXbLewnddwDprR1BP7YEbUzFo +fTcU+bzjzjvh+0YvYN6RIhqH8a6Dkw6sQC8R0XI9JNVJB9Se+Gpr8PhWPrcMfZAW +o/n/yZGk35Q4663/0rXNdQJn40AnWVJ1utgWp6bPSasqiLBHSAXEzGGauRB9MQUq +EAxK+HMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCxXN1YuqL06eAXPEUQNbTb +BlZ/5r7yKpGlx1bOzdZYEveDZYTT3Mmhuy7GaX29j7TD1nxM+oVdMkN7ug/AjjY+ +ybwUjWt3719kWfrIBteB0tZXqk+0pr9NDz6xxDtsDdpRxP4yhfxyZTSWX/1j2HQd +a8W8YY5FRKJJWAYqM9zZY7+LIqBA9Tub0HPQS2O6tRfbiFJiKIiGkgOd7VrUXq+D +B9y172aIRs704wSaBbq4Q4h6xzoRcqyVBp4wHNa3opiDPcJJyiu4zHCV+vV0nSVZ +7v9c5Cu5ttio6bHfOOWhZn64Z3wlYtQVIQ/Cj/fEujaFAvDDpdDU5Br46ZUHOmOP +-----END CERTIFICATE REQUEST----- diff --git a/examples/ssl/ssl.key b/examples/ssl/ssl.key index 7ca76987c82..f95aa54392f 100644 --- a/examples/ssl/ssl.key +++ b/examples/ssl/ssl.key @@ -1,15 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDPnYsgGr6Q9jqL97xGo2zxrOWGQd96PnItf5uOdvZJjwTLsN8a -n33UOtFVxwGIaTb8MtuXiLZbslKWLIOoqq0Lk92RwF4Zxs02Yt+S1sM/9ST7tiJQ -KYx1+rfaZIj0Dy9yVoWHGfRmVZMcWkhslaHR/Yz+Ul4CIqr03/BjYjjQfQIDAQAB -AoGBAL4DtYp7j8BC61ChOvr3pcUG9cbL0UojjwJfUTKxZDXspHn8diT6pgIwltjH -23pKuZ1Wuq3U3PnNmlKBiTo8g0R4OudAWJi1kIyY9AXVm9Ffr1wS0krRrOXXJws/ -P0Y+NtSdpMDEbCiW/GbADPhlWl2JdpOgxdB5mNmCykt4dvRtAkEA7JnxiJWIQg/z -Wpy485hrIFwNNNiRJGKVSySZf9itZd+4fC1F6vTlX+ylDemTbYB427dlJ09nINRz -gXMW3xh8hwJBAOCjNdhTXCESSeJyt8U5cN0PzIJuKq1CvJdLgaNN+RIsC8v9ZTJF -nmMOgjSYNWILRSrHlx0g7VKb2GH6LPsqr9sCQQDKuwGdsdsGGBrB6oYDm/c2zAk4 -3dRH4/zeXSb1x9iT8QVnyXceYubjsaaf7CM58ZodUeBntX69P60VH2Nal+WjAkEA -2r7TziXOjv3KKNLhFRLMTtf1pAU3VaSpFQMX6DgjlIiDrE7CXmPgykD0ldaqFSE4 -Z2IYSusnbswHt9DwQFzfBQJANhJBIvQgj/BcAV+0qV1NgzXbmywtQcwGg+NcimsH -R/tW2SeAXinNkVMjDh5NLzHO5Lz//E0N3lPRljnLfRX69g== +MIIEpAIBAAKCAQEAs6JM9QuOV4hQ7HlOBCp5aINATTFAYot2n/fwfWnRyrC9riUr +LjdIXc3PdjBMZlyrMq6ZameM7RDydl0yJTq4mMOMzk8QIydk4/YkWeNRtoKee7lB +CS6AUVqh/PwoeuoAF3f4Mf6jICC1CEF3FECjHNZEBdA46jESAm/XvKjccevduXYU +cVRBERRdst7Cd13AOmtHUE/tgRtTMWh9NxT5vOPOO+H7Ri9g3pEiGofxroOTDqxA +LxHRcj0k1UkH1J74amvw+FY+twx9kBaj+f/JkaTflDjrrf/Stc11AmfjQCdZUnW6 +2Banps9JqyqIsEdIBcTMYZq5EH0xBSoQDEr4cwIDAQABAoIBAQCfwa8pm65rIJu/ +w55jzBaPoXMVc8DsI1ZLrKkgEHV5CziWYQ4HGzlr23cviILZ4n6LI4bjf9Zwm8rX +A8luHMxBaGCYtu4GJanvp1/q5WUtqPsj070IWmfacn1wTh3+Oo/8T0DXzbvr/cqi +3f32cAngwppahTWo6h03N5qpeZAca4ghy9VOdNEzLmDGkR6lkrgSibpJA92LVpFH +yHsuWvnSm1kOe2GDVQv4GOKIKH5Fo5WEkTZOocXvM1C+NNQcEP+87ZIPjoyBrPwG +Vlb1EkGHhNh0tDsNTIDuYWo/WxPsKqziczS/TB4QgR7cMePsH83e8fn5VrKSRU00 +jqQEt43RAoGBANhCzG0ImvmjYSF8i53bPyuVAJfqpIMQY/BVYG82O1DVNjoK+2pU +PwIEmX0QrltLDULxx9mV3SHzRmxnA280Y+GSIsvYqYBr7doO2cTAEP3Z+MzOiXEm +bPAANIwt+iFp8OeMCbKh12Kr0WRhkF0oKFsLEsFT8/E/7eHwrDIF7A8pAoGBANSk +hZ2K+4GblUEQ2vQgJc/RH9+W8NUMEX+Zv+mtEpCCEoErSjU5StcGrSMIgp2K+IGT +yB/QdH9aYUM1iiBgXPr7IvWNF0IwxwYxJys0kagDoSDH7CdY+pANiEBgWHeHPPQW +R+EBO9eJeEqyp/3nxouwQuhM/1UQXbJhOEmpyOo7AoGAUEqcs26ae0zZZyFihL09 ++uRbUAviAHc5Y6WPJDsyUpSWiX5CUfvtWMZ0ZianVWXMCqrR1Er7yvsTjnPApq0A +2GL1YFGUvQVVTbxtxcq5hEZFRWT5SnVFxOWOnO0FDRrUQmOPxi6oVQ4NtAM1IiEi +uoql7/lHfK6Ll3NPu1LPjeECgYB6mD/QjIhkOObcFHOnu8ERB2hw5/0BV2vfPS0/ +8+B4IAW7ItItzVIwllDLmeR0H+MWEbWXYf9ITTs2HG/+KpBwwsls+GMobibQkxYZ ++rzfOPh0hc7JAKu00Zh0RRe8EeQ6dX4LY0K1CTSpHEduQ2pcrspkU8ABsuXSKerh +zVlgcQKBgQCLCN9jSnIo5dT6N7QABjIEO9sMrnlVfoEzDxn76MwYQ6ZJxf2ONwhi +YoOXguXxMfonleOxwAt8YbfYiT8DypryUM8cuSjB4GPJILn1W1XsNtEWBjFUeiqh +C4MNUZis1dJrUjrieWNWp8rkGV2la1JOJYEq9SSQijeuPNFemdAvNQ== -----END RSA PRIVATE KEY----- diff --git a/examples/ssl/swoole.log b/examples/ssl/swoole.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/ssl/webserver.php b/examples/ssl/webserver.php index a3f794ad86e..9ec72146c31 100644 --- a/examples/ssl/webserver.php +++ b/examples/ssl/webserver.php @@ -1,6 +1,6 @@ set([ 'ssl_cert_file' => __DIR__ . '/ssl.crt', 'ssl_key_file' => __DIR__ . '/ssl.key', @@ -12,7 +12,7 @@ ]); //c158354564362fcc -$serv->on('Request', function(swoole_http_request $request, swoole_http_response $response) { +$serv->on('Request', function(Swoole\Http\Request $request, Swoole\Http\Response $response) { //var_dump($request->get); //var_dump($request->post); //var_dump($request->cookie); diff --git a/examples/ssl/websocket_server.php b/examples/ssl/websocket_server.php index ec191a2eed7..065842d24f9 100644 --- a/examples/ssl/websocket_server.php +++ b/examples/ssl/websocket_server.php @@ -1,7 +1,7 @@ set([ 'ssl_cert_file' => $ssl_dir . '/ssl.crt', 'ssl_key_file' => $ssl_dir . '/ssl.key', @@ -18,7 +18,7 @@ echo "client {$fd} connect\n"; }); -$serv->on('open', function (swoole_websocket_server $_server, swoole_http_request $request) { +$serv->on('open', function (Swoole\WebSocket\Server $_server, Swoole\Http\Request $request) { echo "server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\n"; // var_dump($request); }); @@ -27,7 +27,7 @@ $resp->end(file_get_contents(__DIR__.'/websocket_client.html')); }); -$serv->on('message', function (swoole_websocket_server $_server, $frame) { +$serv->on('message', function (Swoole\WebSocket\Server $_server, $frame) { var_dump($frame->data); echo "received ".strlen($frame->data)." bytes\n"; $_send = str_repeat('B', rand(100, 800)); diff --git a/examples/swoole_http_client.php b/examples/swoole_http_client.php deleted file mode 100644 index 3b21681f990..00000000000 --- a/examples/swoole_http_client.php +++ /dev/null @@ -1,51 +0,0 @@ -set([ - //'worker_num' => 2, -]); -$i = 0; -$http->on('request', function ($request, swoole_http_response $response)use(&$i) { - - $route = $request->server['request_uri']; - if($route == '/info'){ - $response->end(json_encode($request)); - return; - } - - $cli = new swoole_http_client('127.0.0.1', 9501); - $cli->set([ - 'timeout' => 0.3, - 'keep_alive' => 1, - ]); - //post request - $cli->setData(http_build_query(['a'=>123,'b'=>"哈哈"])); - $cli->setHeaders(['User-Agent' => "swoole"]); - $cli->on('close', function($cli)use($response){ - // echo "close\n"; - }); - $cli->on('error', function($cli) use ($response){ - $response->end("error"); - }); - $cli->execute('/info', function($cli)use( $response, &$i){ - $cli->setHeaders(['User-Agent' => "swoole"]); - //get request - $cli->execute('/info', function($cli)use($response, &$i){ - $ret = json_encode($cli->headers) . "\nSERVER RESPONSE: ". $cli->body; - $response->end($ret); - $cli->close(); - }); - }); - - - if($i++ == 1000){ - echo "----->Mem: ", memory_get_usage(), "b\n"; - $i = 0; - } - -}); - -$http->start(); diff --git a/examples/table/array.php b/examples/table/array.php deleted file mode 100644 index 59fb1397616..00000000000 --- a/examples/table/array.php +++ /dev/null @@ -1,18 +0,0 @@ -column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); -$table->create(); - -$table['apple'] = array('id' => 145, 'name' => 'iPhone', 'num' => 3.1415); -$table['google'] = array('id' => 358, 'name' => "AlphaGo", 'num' => 3.1415); - -$table['microsoft']['name'] = "Windows"; -$table['microsoft']['num'] = '1997.03'; - -var_dump($table['apple']); -var_dump($table['microsoft']); - -$table['google']['num'] = 500.90; -var_dump($table['google']); diff --git a/examples/table/deadlock.php b/examples/table/deadlock.php index fb17279d075..89517873811 100644 --- a/examples/table/deadlock.php +++ b/examples/table/deadlock.php @@ -2,7 +2,7 @@ ini_set('memory_limit', '8M'); $table = new Swoole\Table(1024); -$table->column('name', swoole_table::TYPE_STRING, 1024 * 64); +$table->column('name', Swoole\Table::TYPE_STRING, 1024 * 64); $table->create(); $table->set('key1', ['name' => str_repeat('A', 1024 * 64 - 1) . "\n"]); diff --git a/examples/table/iterator.php b/examples/table/iterator.php index 2f77712879a..475fd19d1e6 100644 --- a/examples/table/iterator.php +++ b/examples/table/iterator.php @@ -1,8 +1,8 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); $table->set('tianfenghan@qq.com', array('id' => 145, 'name' => 'rango1', 'num' => 3.1415)); diff --git a/examples/table/server.php b/examples/table/server.php index acb2bfdc7da..4f56336441a 100644 --- a/examples/table/server.php +++ b/examples/table/server.php @@ -1,12 +1,12 @@ column('fd', swoole_table::TYPE_INT); -$table->column('reactor_id', swoole_table::TYPE_INT); -$table->column('data', swoole_table::TYPE_STRING, 64); +$table = new Swoole\Table(1024); +$table->column('fd', Swoole\Table::TYPE_INT); +$table->column('reactor_id', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); $table->create(); -$serv = new swoole_server('127.0.0.1', 9501); -$serv->set(['dispatch_mode' => 1]); +$serv = new Swoole\Server('127.0.0.1', 9501); +$serv->set(['dispatch_mode' => 2]); $serv->table = $table; $serv->on('connect', function($serv, $fd, $reactor_id){ diff --git a/examples/table/set.php b/examples/table/set.php index bea34127aa2..b4ab0b5a252 100644 --- a/examples/table/set.php +++ b/examples/table/set.php @@ -1,11 +1,11 @@ column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('name', swoole_table::TYPE_STRING, 64); -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); -//$worker = new swoole_process('child1', false, false); +//$worker = new Swoole\Process('child1', false, false); //$worker->start(); // //child diff --git a/examples/table/simulation.php b/examples/table/simulation.php index 17b0d942655..770ba67ccb1 100644 --- a/examples/table/simulation.php +++ b/examples/table/simulation.php @@ -1,11 +1,11 @@ column('name', swoole_table::TYPE_STRING, 64); -$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 -$table->column('num', swoole_table::TYPE_FLOAT); +$table = new Swoole\Table(1024); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('id', Swoole\Table::TYPE_INT, 4); //1,2,4,8 +$table->column('num', Swoole\Table::TYPE_FLOAT); $table->create(); while (true) { diff --git a/examples/table/usage.php b/examples/table/usage.php new file mode 100644 index 00000000000..9ca36c7a859 --- /dev/null +++ b/examples/table/usage.php @@ -0,0 +1,13 @@ +column('id', Swoole\Table::TYPE_INT); +$table->column('name', Swoole\Table::TYPE_STRING, 64); +$table->column('num', Swoole\Table::TYPE_FLOAT); +$table->create(); + +$table->set('a', array('id' => 1, 'name' => 'swoole-co-uk', 'num' => 3.1415)); +$table->set('b', array('id' => 2, 'name' => "swoole-uk", 'num' => 3.1415)); +$table->set('hello@swoole.co.uk', array('id' => 3, 'name' => 'swoole', 'num' => 3.1415)); + +var_dump($table->get('a')); +var_dump($table->get('b', 'name')); diff --git a/examples/task/http.php b/examples/task/http.php index affbcf9d71a..83402fd8f6c 100644 --- a/examples/task/http.php +++ b/examples/task/http.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 1, @@ -19,13 +19,13 @@ }); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { //echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; // $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); @@ -41,7 +41,7 @@ } }); -$serv->on('workerStop', function (swoole_server $serv, $id) { +$serv->on('workerStop', function (Swoole\Server $serv, $id) { echo "stop\n"; var_dump($id); }); diff --git a/examples/task/shared_client.php b/examples/task/shared_client.php index b174fb1a283..171b37aaa1c 100644 --- a/examples/task/shared_client.php +++ b/examples/task/shared_client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501)) { exit("connect failed\n"); diff --git a/examples/task/shared_server.php b/examples/task/shared_server.php index 3b276a61eec..9cfb72177bb 100644 --- a/examples/task/shared_server.php +++ b/examples/task/shared_server.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -51,7 +51,7 @@ function my_onWorkerStop($serv, $worker_id) echo "WorkerStop[$worker_id]|pid=".posix_getpid().".\n"; } -function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) +function my_onReceive(Swoole\Server $serv, $fd, $reactor_id, $rdata) { $data = unserialize($rdata); if (isset($data['cmd'])) @@ -79,7 +79,7 @@ function my_onReceive(swoole_server $serv, $fd, $reactor_id, $rdata) } } -function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) +function my_onTask(Swoole\Server $serv, $task_id, $reactor_id, $data) { static $datas = array(); if (isset($data['cmd'])) @@ -112,12 +112,12 @@ function my_onTask(swoole_server $serv, $task_id, $reactor_id, $data) // $serv->finish("OK"); } -function my_onFinish(swoole_server $serv, $task_id, $from_worker_id, $data) +function my_onFinish(Swoole\Server $serv, $task_id, $from_worker_id, $data) { echo "AsyncTask Finish: Connect.PID=" . posix_getpid() . PHP_EOL; } -function my_onWorkerError(swoole_server $serv, $worker_id, $worker_pid, $exit_code) +function my_onWorkerError(Swoole\Server $serv, $worker_id, $worker_pid, $exit_code) { echo "worker abnormal exit. WorkerId=$worker_id|Pid=$worker_pid|ExitCode=$exit_code\n"; } diff --git a/examples/task/task.php b/examples/task/task.php index 8d2d3867134..9435fc693c6 100644 --- a/examples/task/task.php +++ b/examples/task/task.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,7 +9,7 @@ //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -28,13 +28,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); // return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_coro.php b/examples/task/task_coro.php index 3224b8dcb46..79cd927663d 100644 --- a/examples/task/task_coro.php +++ b/examples/task/task_coro.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 4, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); $tasks[] = mt_rand(1000, 9999); @@ -17,7 +17,7 @@ var_dump($results); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [ID={$serv->worker_id}]: task_id=$task_id, data=$data, data_len=".strlen($data).".".PHP_EOL; //测试超时 if ($serv->worker_id % 4 == 3) @@ -35,7 +35,7 @@ return "hello world.[{$data}]"; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_num.php b/examples/task/task_num.php index 599c4a63a0b..7a673c158cc 100644 --- a/examples/task/task_num.php +++ b/examples/task/task_num.php @@ -1,12 +1,12 @@ set(array( 'worker_num' => 1, 'task_worker_num' => 2, //'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = intval($data); for($i=0;$i<$data;$i++) { @@ -16,7 +16,7 @@ } }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "onTask: [PID=".posix_getpid()."]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; sleep(10); //$serv->finish($data); @@ -24,11 +24,11 @@ return; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); -$serv->on('Timer', function(swoole_server $serv, $time) { +$serv->on('Timer', function(Swoole\Server $serv, $time) { echo "{$time} call".PHP_EOL; print_r($serv->stats()); }); diff --git a/examples/task/task_queue.php b/examples/task/task_queue.php index 0e872ec8c6f..1da9257d16e 100644 --- a/examples/task/task_queue.php +++ b/examples/task/task_queue.php @@ -1,5 +1,5 @@ set(array( //'worker_num' => 1, @@ -9,15 +9,15 @@ 'task_tmpdir' => '/data/task/', )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: TASK_ID=$task_id]\n"; var_dump($data); }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/task/task_stream.php b/examples/task/task_stream.php index 90108ea0dcf..10b5d6ff8fe 100644 --- a/examples/task/task_stream.php +++ b/examples/task/task_stream.php @@ -1,5 +1,5 @@ set(array( 'worker_num' => 1, @@ -8,7 +8,7 @@ // 'message_queue_key' => 0x70001001, )); -$serv->on('Receive', function(swoole_server $serv, $fd, $reactor_id, $data) { +$serv->on('Receive', function(Swoole\Server $serv, $fd, $reactor_id, $data) { //AsyncTask $data = trim($data); //$data = str_repeat('A', 8192*100); @@ -27,13 +27,13 @@ } //$serv->send($fd, "OK\n"); }); -$serv->on('Task', function (swoole_server $serv, $task_id, $reactor_id, $data) { +$serv->on('Task', function (Swoole\Server $serv, $task_id, $reactor_id, $data) { echo "#{$serv->worker_id}\tonTask: [PID={$serv->worker_pid}]: task_id=$task_id, data_len=".strlen($data).".".PHP_EOL; $serv->finish($data); return $data; }); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { +$serv->on('Finish', function (Swoole\Server $serv, $task_id, $data) { echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; }); diff --git a/examples/test_buffer.php b/examples/test_buffer.php deleted file mode 100644 index 02f766c6e66..00000000000 --- a/examples/test_buffer.php +++ /dev/null @@ -1,39 +0,0 @@ -connect('127.0.0.1', 9501)) -{ - exit("connect fail\n"); -} - -for($i=0; $i<$loop; $i++) -{ - $client->send(str_repeat("A", 8000).$i."[0]"); - //$client->send(str_repeat("A", 20).$i."[1]"); - //$client->send(str_repeat("A", 30).$i."[2]"); - //$ret = $client->send("GET / HTTP/1.1\r\n"); - //$client->send("Host: localhost\r\n"); - //$client->send("Connection: keep-alive\r\n"); - $client->send("\r\n\r\n"); - - //$data = $client->recv(1024, 0); - //if($data === false) - //{ - // echo "#{$i} recv fail.break\n"; -// break; -// } - //echo "recv[$i]",$data,"\n"; -} - -sleep(1000); -echo "$i: ",$data,"\n"; -echo "test ok. use".((microtime(true) - $_s)*1000)."ms\n"; diff --git a/examples/thread/aio.php b/examples/thread/aio.php new file mode 100644 index 00000000000..e55972f01d0 --- /dev/null +++ b/examples/thread/aio.php @@ -0,0 +1,42 @@ +join(); + } + var_dump($atomic->get()); + sleep(2); + + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); + var_dump($atomic->get()); +} else { + $atomic = $args[1]; + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); +} diff --git a/examples/thread/argv.php b/examples/thread/argv.php new file mode 100644 index 00000000000..a224b95da7f --- /dev/null +++ b/examples/thread/argv.php @@ -0,0 +1,17 @@ +join(); + } +} else { + var_dump($args[0], $args[1], $args[2]); + sleep(1); +} diff --git a/examples/thread/array.php b/examples/thread/array.php new file mode 100644 index 00000000000..1df20fb66c7 --- /dev/null +++ b/examples/thread/array.php @@ -0,0 +1,20 @@ +join(); + } + var_dump($a1->get(), $a2->get()); +} else { + $a1 = $args[1]; + $a2 = $args[2]; + + $a1->add(3); + $a2->add(7); +} diff --git a/examples/thread/benchmark.php b/examples/thread/benchmark.php new file mode 100644 index 00000000000..5a52e6df5a5 --- /dev/null +++ b/examples/thread/benchmark.php @@ -0,0 +1,21 @@ +id); +//var_dump($t2->id); +echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; + +try { + var_dump($list[999]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'out of range')); +} + +try { + unset($list[0]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'unsupported')); +} + +$t1->join(); +$t2->join(); + + diff --git a/examples/thread/exit.php b/examples/thread/exit.php new file mode 100644 index 00000000000..5d1c071f6d8 --- /dev/null +++ b/examples/thread/exit.php @@ -0,0 +1,21 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + sleep(1); + exit(234); +} diff --git a/examples/thread/hook.php b/examples/thread/hook.php new file mode 100644 index 00000000000..de55b63bb2c --- /dev/null +++ b/examples/thread/hook.php @@ -0,0 +1,24 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + echo "main thread\n"; + $lock->unlock(); + $thread->join(); + var_dump($thread->getExitStatus()); +} else { + $lock = $args[0]; + $lock->lock(); + Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + sleep(1); + Swoole\Runtime::enableCoroutine(0); + exit(234); +} diff --git a/examples/thread/lock.php b/examples/thread/lock.php new file mode 100644 index 00000000000..e18674d6314 --- /dev/null +++ b/examples/thread/lock.php @@ -0,0 +1,19 @@ +lock(); + $thread = new Thread(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + $lock = $args[0]; + sleep(1); + $lock->unlock(); +} diff --git a/examples/thread/map.php b/examples/thread/map.php new file mode 100644 index 00000000000..fc38de69ffd --- /dev/null +++ b/examples/thread/map.php @@ -0,0 +1,18 @@ + random_int(1, 999999999999999999), + 'b' => random_bytes(128), + 'c' => uniqid(), + 'd' => time(), + ]; + + $map = new Thread\Map($array); + $thread = new Thread(__FILE__, $map); +} else { + $map = $args[0]; + var_dump($map->toArray()); +} diff --git a/examples/thread/mt.php b/examples/thread/mt.php new file mode 100644 index 00000000000..ca14d152f26 --- /dev/null +++ b/examples/thread/mt.php @@ -0,0 +1,27 @@ +keys()); + +$list[] = uniqid('swoole'); +$list[count($list)] = uniqid('php'); + +var_dump($args); + +echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $list[0] . "\n"; +var_dump(count($list)); + +//if ($args[0] == 'thread-2') { +// $t3 = new Swoole\Thread('mt.php', 'thread-3', PHP_OS); +// $t3->join(); +//} + +//sleep(5); +//echo "end\n"; diff --git a/examples/thread/nested_map.php b/examples/thread/nested_map.php new file mode 100644 index 00000000000..daa0af5f9c2 --- /dev/null +++ b/examples/thread/nested_map.php @@ -0,0 +1,21 @@ + uniqid(), + 'b' => random_int(1000, 9999), +]; + +var_dump($map['map1']['key1']); +var_dump($map['list1'][0]); + +var_dump($map['list1']->toArray()); + +var_dump($map['map2']); diff --git a/examples/thread/pipe.php b/examples/thread/pipe.php new file mode 100644 index 00000000000..2e806cf17a1 --- /dev/null +++ b/examples/thread/pipe.php @@ -0,0 +1,20 @@ +recv(8192), PHP_EOL; + $thread->join(); + }); +} else { + $sockets = $args[0]; + Co\run(function () use ($sockets) { + sleep(1); + $sockets[1]->send(uniqid()); + }); +} diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php new file mode 100644 index 00000000000..85a736214ca --- /dev/null +++ b/examples/thread/run_test.php @@ -0,0 +1,16 @@ +join(); +} + diff --git a/examples/thread/server.php b/examples/thread/server.php new file mode 100644 index 00000000000..79435f73da7 --- /dev/null +++ b/examples/thread/server.php @@ -0,0 +1,25 @@ +join(); + } +} else { + $http = new Swoole\Http\Server("0.0.0.0", 9503); + $http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->end('hello world'); + }); + $http->start(); +} diff --git a/examples/thread/signal.php b/examples/thread/signal.php new file mode 100644 index 00000000000..b41930b693a --- /dev/null +++ b/examples/thread/signal.php @@ -0,0 +1,36 @@ +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + echo "child thread\n"; + $sockets = $args[0]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); +} diff --git a/examples/thread/socket.php b/examples/thread/socket.php new file mode 100644 index 00000000000..05f57cff1d2 --- /dev/null +++ b/examples/thread/socket.php @@ -0,0 +1,21 @@ +join(); +} else { + $map = $args[0]; + $sock = $map['socket']; + $retval = socket_connect($sock, '127.0.0.1', 80); +} diff --git a/examples/thread/test.php b/examples/thread/test.php new file mode 100644 index 00000000000..3c4b8bc1a20 --- /dev/null +++ b/examples/thread/test.php @@ -0,0 +1,17 @@ +uuid = uniqid(); +$map['obj'] = $o; + +var_dump($map['obj']); + +$s = serialize($map); +var_dump(unserialize($s)); + diff --git a/examples/thread/thread_pool.php b/examples/thread/thread_pool.php new file mode 100644 index 00000000000..226f14a5a9e --- /dev/null +++ b/examples/thread/thread_pool.php @@ -0,0 +1,37 @@ +push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php new file mode 100644 index 00000000000..ddb19ec32af --- /dev/null +++ b/examples/thread/thread_server.php @@ -0,0 +1,67 @@ +set([ + 'worker_num' => 2, + 'task_worker_num' => 3, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL, +// 'trace_flags' => SWOOLE_TRACE_SERVER, +// 'log_level' => SWOOLE_LOG_TRACE, + 'init_arguments' => function () use ($http) { + $map = new Swoole\Thread\Map; + return [$map]; + } +]); + +$http->on('Request', function ($req, $resp) use ($http) { +// $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); + if ($req->server['request_uri'] == '/task') { + $http->task(['code' => uniqid()]); + } elseif ($req->server['request_uri'] == '/stop') { + var_dump($http->getWorkerId()); + var_dump($req->get['worker_id']); + $http->stop($req->get['worker_id'] ?? 0); + } elseif ($req->server['request_uri'] == '/msg') { + $dstWorkerId = random_int(0, 4); + if ($dstWorkerId != $http->getWorkerId()) { + $http->sendMessage('hello ' . base64_encode(random_bytes(16)), $dstWorkerId); + echo "[worker#" . $http->getWorkerId() . "]\tsend pipe message to " . $dstWorkerId . "\n"; + } + } + $resp->end('hello world'); +}); + +$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) { + echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; +}); + +$http->addProcess(new \Swoole\Process(function () { + echo "user process, id=" . \Swoole\Thread::getId() . "\n"; + sleep(2); +})); + +$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { + var_dump($taskId, $srcWorkerId, $data); + return ['result' => uniqid()]; +}); + +$http->on('Finish', function ($server, $taskId, $data) { + var_dump($taskId, $data); +}); + +$http->on('workerStart', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is started.\n"; +}); + +$http->on('workerStop', function ($serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is stopped.\n"; +}); + +$http->on('workerExit', function (Server $serv, $worker_id) { + echo "[#" . Swoole\Thread::getId() . "]\tWorker#{$worker_id} is exited, event_num=" . Swoole\Coroutine::stats()['event_num'] . ".\n"; +}); + +$http->start(); diff --git a/examples/timer/after.php b/examples/timer/after.php index eca3d5c3a74..039864df96a 100644 --- a/examples/timer/after.php +++ b/examples/timer/after.php @@ -4,24 +4,24 @@ function timeout($tm) echo time() . ": Timeout #$tm\n"; if ($tm == 5) { - swoole_timer_after(3000, 'timeout', 7); + Swoole\Timer::after(3000, 'timeout', 7); } } -$timer1 = swoole_timer_after(1000, function () { +$timer1 = Swoole\Timer::after(1000, function () { timeout(1); global $timer1, $timer3; - swoole_timer_clear($timer1); - swoole_timer_clear($timer3); + Swoole\Timer::clear($timer1); + Swoole\Timer::clear($timer3); }); -$timer2 = swoole_timer_after(2000, 'timeout', 2); -$timer3 = swoole_timer_after(4000, 'timeout', 3); -$timer4 = swoole_timer_after(8000, 'timeout', 4); -$timer5 = swoole_timer_after(10000, 'timeout', 5); -$timer6 = swoole_timer_after(5000, 'timeout', 6); +$timer2 = Swoole\Timer::after(2000, 'timeout', 2); +$timer3 = Swoole\Timer::after(4000, 'timeout', 3); +$timer4 = Swoole\Timer::after(8000, 'timeout', 4); +$timer5 = Swoole\Timer::after(10000, 'timeout', 5); +$timer6 = Swoole\Timer::after(5000, 'timeout', 6); var_dump($timer1, $timer2, $timer3, $timer4, $timer5, $timer6); -swoole_process::signal(SIGTERM, function() { - swoole_event_exit(); +Swoole\Process::signal(SIGTERM, function() { + Swoole\Event::exit(); }); diff --git a/examples/timer/clear.php b/examples/timer/clear.php index aac2163abea..1a4a51bf8bf 100644 --- a/examples/timer/clear.php +++ b/examples/timer/clear.php @@ -1,10 +1,10 @@ false ]); -swoole_timer_tick(1000, function () { +Swoole\Timer::tick(1000, function () { $uid = Co::getuid(); assert($uid === -1); echo "#{$uid}\n"; diff --git a/examples/timer/tick.php b/examples/timer/tick.php index 430a34b61c3..029dcd77aa1 100644 --- a/examples/timer/tick.php +++ b/examples/timer/tick.php @@ -3,21 +3,21 @@ function timeout($tm) { echo time() . ": Timeout #$tm\n"; } -$timer1 = swoole_timer_tick(1000, 'timeout', 1); -$timer2 = swoole_timer_tick(2000, 'timeout', 2); +$timer1 = Swoole\Timer::tick(1000, 'timeout', 1); +$timer2 = Swoole\Timer::tick(2000, 'timeout', 2); -swoole_timer_tick(3000, function($id) { +Swoole\Timer::tick(3000, function($id) { timeout($id); - //swoole_timer_clear($id); + //Swoole\Timer::clear($id); static $remove = true; if ($remove) { global $timer1; - swoole_timer_clear($timer1); - swoole_timer_tick(7000, 'timeout', 7); + Swoole\Timer::clear($timer1); + Swoole\Timer::tick(7000, 'timeout', 7); $remove = false; } }); -$timer4 = swoole_timer_tick(4000, 'timeout', 4); -$timer5 = swoole_timer_tick(5000, 'timeout', 5); -$timer6 = swoole_timer_tick(6000, 'timeout', 6); +$timer4 = Swoole\Timer::tick(4000, 'timeout', 4); +$timer5 = Swoole\Timer::tick(5000, 'timeout', 5); +$timer6 = Swoole\Timer::tick(6000, 'timeout', 6); diff --git a/examples/udp/async_client.php b/examples/udp/async_client.php deleted file mode 100644 index 33855fea5a4..00000000000 --- a/examples/udp/async_client.php +++ /dev/null @@ -1,23 +0,0 @@ -on("connect", function(swoole_client $cli) { - echo "connected\n"; - $cli->send("hello world\n"); -}); - -$client->on('close', function($cli){ - echo "closed\n"; -}); - -$client->on('error', function($cli){ - echo "error\n"; -}); - -$client->on("receive", function(swoole_client $cli, $data){ - echo "received: $data\n"; - sleep(1); - $cli->send("hello_".rand(1000,9999)); -}); - -$client->connect('127.0.0.1', 9502, 0.5); diff --git a/examples/udp/client.php b/examples/udp/client.php index 7f04980c572..5336a9593ef 100644 --- a/examples/udp/client.php +++ b/examples/udp/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9905); $client->send(serialize(['hello' => str_repeat('A', 600), 'rand' => rand(1, 100)])); echo $client->recv() . "\n"; diff --git a/examples/udp/server.php b/examples/udp/server.php index eb5ca83ec7b..dd2edcb608e 100644 --- a/examples/udp/server.php +++ b/examples/udp/server.php @@ -1,12 +1,12 @@ listen('0.0.0.0', 9906 + $i, SWOOLE_SOCK_UDP); } $server->set(['worker_num' => 4]); -$server->on('Packet', function (swoole_server $serv, $data, $addr) +$server->on('Packet', function (Swoole\Server $serv, $data, $addr) { $serv->sendto($addr['address'], $addr['port'], "Swoole: $data", $addr['server_socket']); }); diff --git a/examples/unixsock/async_client.php b/examples/unixsock/async_client.php deleted file mode 100644 index c1f39559908..00000000000 --- a/examples/unixsock/async_client.php +++ /dev/null @@ -1,28 +0,0 @@ -on("connect", function (swoole_client $cli) -{ - $cli->send("GET / HTTP/1.1\r\n\r\n"); -}); - -$client->on("receive", function (swoole_client $cli, $data) -{ - echo "Receive: $data"; - $cli->send(str_repeat('A', 100) . "\n"); -}); - -$client->on("error", function (swoole_client $cli) -{ - echo "error: [" . $cli->errCode . "] " . socket_strerror($cli->errCode) . "\n"; -}); - -$client->on("close", function (swoole_client $cli) -{ - echo "Connection close\n"; -}); - -$client->connect(__DIR__ . '/svr.sock', 0, -1); - -swoole_event_wait(); -echo "exit\n"; diff --git a/examples/unixsock/dgram_client.php b/examples/unixsock/dgram_client.php index 1e53d33620b..7cf5d535083 100644 --- a/examples/unixsock/dgram_client.php +++ b/examples/unixsock/dgram_client.php @@ -1,5 +1,5 @@ connect(__DIR__ . '/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/dgram_server.php b/examples/unixsock/dgram_server.php index 140e5a04cb7..c3efa5ff682 100644 --- a/examples/unixsock/dgram_server.php +++ b/examples/unixsock/dgram_server.php @@ -1,17 +1,17 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, //'daemonize' => true, //'log_file' => '/tmp/swoole.log' )); -//$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { +//$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { // echo "[#".posix_getpid()."]\tClient[$fd]: $data\n"; // $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)).PHP_EOL); //}); -$serv->on('Packet', function (swoole_server $serv, $data, $addr) { +$serv->on('Packet', function (Swoole\Server $serv, $data, $addr) { //echo "[#".posix_getpid()."]\tClient[{$addr['address']}]: $data\n"; var_dump($addr); $serv->send($addr['address'], json_encode(array("hello" => $data, "addr" => $addr)).PHP_EOL); diff --git a/examples/unixsock/stream_client.php b/examples/unixsock/stream_client.php index a16fcf58d01..bb2b621c2db 100644 --- a/examples/unixsock/stream_client.php +++ b/examples/unixsock/stream_client.php @@ -1,5 +1,5 @@ connect(__DIR__.'/svr.sock', 0, -1)) { exit("connect failed. Error: {$client->errCode}\n"); diff --git a/examples/unixsock/stream_server.php b/examples/unixsock/stream_server.php index 7068c487b7a..36a46019859 100644 --- a/examples/unixsock/stream_server.php +++ b/examples/unixsock/stream_server.php @@ -1,5 +1,5 @@ set(array( //'tcp_defer_accept' => 5, 'worker_num' => 1, @@ -19,7 +19,7 @@ echo "Close, client={$fd}\n"; }); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo "[#" . posix_getpid() . "]\tClient[$fd]: $data\n"; $serv->send($fd, json_encode(array("hello" => $data, "from" => $reactor_id)) . PHP_EOL); diff --git a/examples/weather_server.php b/examples/weather_server.php deleted file mode 100644 index c7ce1a31060..00000000000 --- a/examples/weather_server.php +++ /dev/null @@ -1,201 +0,0 @@ -setting = $setting; - } - - public function init() - { - - $this->http = new swoole_http_server($this->setting['host'], $this->setting['port'], SWOOLE_BASE); - $this->http->set($this->setting); - //register_shutdown_function('handleFatal'); - $this->http->on('request', function ($request, $response) - { - if ($request->server['request_uri'] == '/favicon.ico') - { - $response->status(404); - $response->end('Not Found'); - return; - } - $this->getResult($response); - }); - } - - function getResult2($response) - { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); - $client->on('connect', function ($cli) - { - echo "cli1 connect\n"; - $cli->send("hello world\n"); - }); - - $client->on('Receive', function ($cli, $data) use ($response) - { - echo "cli1 receive\n"; - $response->end($data); - $cli->close(); - }); - - $client->on("error", function ($cli) use ($response) - { - echo "cli1 error\n"; - $response->end("empty\n"); - }); - - $client->on("close", function ($cli) - { - echo "cli1 close\n"; - }); - $client->connect('127.0.0.1', 9501); - } - - function getResult5($response) - { - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($response) - { - $response->header('Content-Type', 'application/json'); - $response->write(json_encode(array($host => $ip))); - $response->end(); - }); - } - - function getResult3($response) - { - $cityId = '01010101'; -// swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) -// { -// if (empty($ip)) -// { -// return $ret; -// } -// else -// { - $ip = '14.18.245.236'; - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - }); -// } -// }); - } - - function getResult($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('test', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $client->close(); - $cityId = '01010101'; - swoole_async_dns_lookup("weather.gtimg.cn", function ($host, $ip) use ($cityId, $response) - { - if (empty($ip)) - { - return false; - } - else - { - $httpcli = new swoole_http_client($ip, 80); - //$httpcli->on("close", function($httpcli){}); - $url = "/qqindex/" . $cityId . ".js?_ref=14"; - - $httpcli->get($url, function ($hcli) use ($response) - { - //echo "get content is" . $hcli->body; - $retWeather = iconv("GBK", 'UTF-8', $hcli->body); - //echo "ret:" . $retWeather; - $hcli->close(); - - $response->header('Content-Type', 'application/json'); - $response->write(json_encode($retWeather)); - $response->end(); - - }); - } - }); - }); - }); - } - - function getResult4($response) - { - $client = new swoole_redis(); - $ip = "127.0.0.1"; - $port = 6379; - - $client->connect($ip, $port, function (swoole_redis $client, $result) use ($response) - { - if ($result === false) - { - echo "connect to redis server failed\n"; - return false; - } - $client->GET('key', function (swoole_redis $client, $result) use ($response) - { - //echo "get result is :" . $result; - $response->header('Content-Type', 'application/json'); - $response->end($result); - }); - }); - } - - public function start() - { - $this->init(); - $this->http->start(); - } -} - - -$setting = array( - - 'host' => '127.0.0.1', - 'port' => 9100, - 'worker_num' => 1, - 'dispatch_mode' => 2, - //'reactor_num' => 4, - 'daemonize' => 0, - //'log_file' => './logs/test_udp_server.log', -); - - -$server = new HttpServ(); -$server->set($setting); -$server->start(); diff --git a/examples/websocket/async_client.php b/examples/websocket/async_client.php deleted file mode 100644 index 2d619873dd3..00000000000 --- a/examples/websocket/async_client.php +++ /dev/null @@ -1,11 +0,0 @@ -setHeaders(['Trace-Id' => md5(time()),]); -$cli->on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/websocket/client.php b/examples/websocket/client.php index 906b0846219..bd6a03170e3 100644 --- a/examples/websocket/client.php +++ b/examples/websocket/client.php @@ -1,5 +1,5 @@ addlistener('0.0.0.0', 9502, SWOOLE_SOCK_UDP); -//$server->set(['worker_num' => 4, -// 'task_worker_num' => 4, -//]); +$server->set([ + // 'worker_num' => 4, + // 'task_worker_num' => 4, + 'websocket_compression' => true, +]); -function user_handshake(swoole_http_request $request, swoole_http_response $response) +function user_handshake(Request $request, Response $response) { //自定定握手规则,没有设置则用系统内置的(只支持version:13的) if (!isset($request->header['sec-websocket-key'])) @@ -50,40 +56,37 @@ function user_handshake(swoole_http_request $request, swoole_http_response $resp } //$server->on('handshake', 'user_handshake'); -$server->on('open', function (swoole_websocket_server $_server, swoole_http_request $request) { +$server->on('open', function (Server $_server, Request $request) { echo "server#{$_server->worker_pid}: handshake success with fd#{$request->fd}\n"; var_dump($_server->exist($request->fd), $_server->getClientInfo($request->fd)); $fd = $request->fd; - $_server->tick(2000, function($id) use ($fd, $_server) { - $_send = str_repeat('B', rand(100, 5000)); - $ret = $_server->push($fd, $_send); - if (!$ret) - { - var_dump($id); - var_dump($_server->clearTimer($id)); - } - }); +// $_server->tick(2000, function($id) use ($fd, $_server) { +// $_send = str_repeat('B', rand(100, 5000)); +// $ret = $_server->push($fd, $_send); +// if (!$ret) +// { +// var_dump($id); +// var_dump($_server->clearTimer($id)); +// } +// }); }); -$server->on('message', function (swoole_websocket_server $_server, $frame) { - var_dump($frame->data); - echo "received ".strlen($frame->data)." bytes\n"; - if ($frame->data == "close") - { +$server->on('message', function (Server $_server, $frame) { + //var_dump($frame->data); + echo "received " . strlen($frame->data) . " bytes\n"; + if ($frame->data == "close") { $_server->close($frame->fd); - } - elseif($frame->data == "task") - { + } elseif ($frame->data == "task") { $_server->task(['go' => 'die']); - } - else - { + } else { //echo "receive from {$frame->fd}:{$frame->data}, opcode:{$frame->opcode}, finish:{$frame->finish}\n"; - // for ($i = 0; $i < 100; $i++) + // for ($i = 0; $i < 100; $i++) { - $_send = str_repeat('B', rand(100, 800)); - $_server->push($frame->fd, $_send); - // echo "#$i\tserver sent " . strlen($_send) . " byte \n"; +// $_send = '' + $_send = base64_encode(random_bytes(rand(100, 1024))); +// $_send = str_repeat('B', rand(100, 800)); + $_server->push($frame->fd, $_send, SWOOLE_WEBSOCKET_OPCODE_TEXT, SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS); + // echo "#$i\tserver sent " . strlen($_send) . " byte \n"; } } }); @@ -108,10 +111,10 @@ function user_handshake(swoole_http_request $request, swoole_http_response $resp var_dump($client); }); -$server->on('request', function (swoole_http_request $request, swoole_http_response $response) { +$server->on('request', function (Request $request, Response $response) { $response->end(<<Swoole WebSocket Server

- +HTML); + }); + $server->on('handshake', function (Request $request, Response $response) { + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + $key = base64_encode(sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + )); + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + $response->status(101); + $response->end(); + return true; + }); + $server->on('message', function ($serv, $frame) { + $serv->push($frame->fd, "hello world"); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/thirdparty/boost/asm/LICENSE b/thirdparty/boost/asm/LICENSE new file mode 100644 index 00000000000..36b7cd93cdf --- /dev/null +++ b/thirdparty/boost/asm/LICENSE @@ -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. diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index cbe77512e37..3aeb528fd97 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -1,22 +1,16 @@ -#ifdef __linux__ - #if defined(__i386__) - #include "make_i386_sysv_elf_gas.S" - #include "jump_i386_sysv_elf_gas.S" - #elif defined(__x86_64__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + #if defined(__x86_64__) #include "make_x86_64_sysv_elf_gas.S" #include "jump_x86_64_sysv_elf_gas.S" - #elif defined(__ppc__) - #include "make_ppc32_sysv_elf_gas.S" - #include "jump_ppc32_sysv_elf_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_elf_gas.S" #include "jump_ppc64_sysv_elf_gas.S" - #elif defined(__arm__) - #include "make_arm_aapcs_elf_gas.S" - #include "jump_arm_aapcs_elf_gas.S" #elif defined(__arm64__) #include "make_arm64_aapcs_elf_gas.S" #include "jump_arm64_aapcs_elf_gas.S" + #elif defined(__loongarch64) + #include "make_loongarch64_sysv_elf_gas.S" + #include "jump_loongarch64_sysv_elf_gas.S" #else #error "No arch's" #endif @@ -25,4 +19,4 @@ #include "jump_combined_sysv_macho_gas.S" #else #error "not supports" -#endif \ No newline at end of file +#endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 09bd7b52259..47282c18e96 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 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) @@ -51,32 +51,21 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "jump_arm64_aapcs_elf_gas.S" .text .align 2 -.global jump_fcontext -.type jump_fcontext, %function -jump_fcontext: +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: # prepare stack for GP + FPU sub sp, sp, #0xb0 -# Because gcc may save integer registers in fp registers across a -# function call we cannot skip saving the fp registers. -# -# Do not reinstate this test unless you fully understand what you -# are doing. -# -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 1f - # save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: # save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -88,17 +77,11 @@ jump_fcontext: # save LR as PC str x30, [sp, #0xa0] - # store RSP (pointing to context-data) in first argument (x0). - # STR cannot have sp as a target register + # store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - - # restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 2f + # restore RSP (pointing to context-data) from X1 + mov sp, x0 # load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -106,7 +89,6 @@ jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: # load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -115,9 +97,10 @@ jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - # use third arg as return value after jump - # and as first arg in context function - mov x0, x2 + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 # load pc ldr x4, [sp, #0xa0] @@ -126,6 +109,6 @@ jump_fcontext: add sp, sp, #0xb0 ret x4 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S index 958178ee719..dc544e026e4 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + 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) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -46,26 +52,18 @@ *******************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .balign 16 -_jump_fcontext: +_swoole_jump_fcontext: ; prepare stack for GP + FPU sub sp, sp, #0xb0 -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 1f - ; save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: -#endif - ; save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -77,18 +75,11 @@ _jump_fcontext: ; save LR as PC str lr, [sp, #0xa0] - ; store RSP (pointing to context-data) in first argument (x0). - ; STR cannot have sp as a target register + ; store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - ; restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 2f + ; restore RSP (pointing to context-data) from X1 + mov sp, x0 ; load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -96,9 +87,6 @@ _jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: -#endif - ; load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -107,9 +95,10 @@ _jump_fcontext: ldp x27, x28, [sp, #0x80] ldp fp, lr, [sp, #0x90] - ; use third arg as return value after jump - ; and as first arg in context function - mov x0, x2 + ; return transfer_t from jump + ; pass transfer_t as first arg in context function + ; X0 == FCTX, X1 == DATA + mov x0, x4 ; load pc ldr x4, [sp, #0xa0] diff --git a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S deleted file mode 100644 index 3019aeb49dc..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,%function -jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} -.size jump_fcontext,.-jump_fcontext - -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S deleted file mode 100644 index 75935347498..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ locate TLS to save/restore SjLj handler - mrc p15, 0, v2, c13, c0, #3 - bic v2, v2, #3 - - @ load TLS[__PTK_LIBC_DYLD_Unwind_SjLj_Key] - ldr v1, [v2, #72] - @ save SjLj handler - push {v1} - - @ prepare stack for FPU - sub sp, sp, #64 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: -#endif - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: -#endif - - @ prepare stack for FPU - add sp, sp, #64 - - @ restore SjLj handler - pop {v1} - @ store SjLj handler in TLS - str v1, [v2, #72] - - @ use third arg as return value after jump - @ and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR,PC - pop {v1-v8,lr,pc} diff --git a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm deleted file mode 100644 index 4e145dfa5bc..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,112 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; 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) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - AREA |.text|, CODE - ALIGN 4 - EXPORT jump_fcontext - -jump_fcontext PROC - @ save LR as PC - push {lr} - @ save V1-V8,LR - push {v1-v8,lr} - - @ prepare stack for FPU - sub sp, sp, #0x4c - - @ test if fpu env should be preserved - cmp a4, #0 - beq 1f - - @ save S16-S31 - vstmia sp, {d8-d15} - -1: - ; load TIB to save/restore thread size and limit. - ; we do not need preserve CPU flag and can use it's arg register - mrc p15, #0, v1, c13, c0, #2 - - ; save current stack base - ldr a5, [v1,#0x04] - str a5, [sp,#0x48] - ; save current stack limit - ldr a5, [v1,#0x08] - str a5, [sp,#0x44] - ; save current deallocation stack - ldr a5, [v1,#0xe0c] - str a5, [sp,#0x40] - - @ store RSP (pointing to context-data) in A1 - str sp, [a1] - - @ restore RSP (pointing to context-data) from A2 - mov sp, a2 - - @ test if fpu env should be preserved - cmp a4, #0 - beq 2f - - @ restore S16-S31 - vldmia sp, {d8-d15} - -2: - ; restore stack base - ldr a5, [sp,#0x48] - str a5, [v1,#0x04] - ; restore stack limit - ldr a5, [sp,#0x44] - str a5, [v1,#0x08] - ; restore deallocation stack - ldr a5, [sp,#0x40] - str a5, [v1,#0xe0c] - - @ prepare stack for FPU - add sp, sp, #0x4c - - ; use third arg as return value after jump - ; and as first arg in context function - mov a1, a3 - - @ restore v1-V8,LR - pop {v1-v8,lr} - pop {pc} - - ENDP - END diff --git a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S index 1d27afad100..773e8345dfe 100644 --- a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S @@ -7,14 +7,12 @@ // Stub file for universal binary -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +#if defined(__x86_64__) #include "jump_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "jump_ppc64_sysv_macho_gas.S" +#elif defined(__arm64__) + #include "jump_arm64_aapcs_macho_gas.S" #else #error "No arch's" #endif diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm b/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm deleted file mode 100644 index b55b77207a4..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_gas.asm +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - 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) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -* *****************************************************************/ - -.file "jump_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _jump_fcontext -.def _jump_fcontext; .scl 2; .type 32; .endef -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* load NT_TIB */ - movl %fs:(0x18), %edx - - /* load current SEH exception list */ - movl (%edx), %eax - push %eax - - /* load current stack base */ - movl 0x04(%edx), %eax - push %eax - - /* load current stack limit */ - movl 0x08(%edx), %eax - push %eax - - /* load current dealloction stack */ - movl 0xe0c(%edx), %eax - push %eax - - /* load fiber local storage */ - movl 0x10(%edx), %eax - push %eax - - /* prepare stack for FPU */ - leal -0x08(%esp), %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 1f - - /* save MMX control word */ - stmxcsr (%esp) - /* save x87 control word */ - fnstcw 0x04(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x30(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x34(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x38(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - testl %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x04(%esp) - -2: - /* prepare stack for FPU */ - leal 0x08(%esp), %esp - - /* load NT_TIB into ECX */ - movl %fs:(0x18), %edx - - /* restore fiber local storage */ - popl %ecx - movl %ecx, 0x10(%edx) - - /* restore current deallocation stack */ - popl %ecx - movl %ecx, 0xe0c(%edx) - - /* restore current stack limit */ - popl %ecx - movl %ecx, 0x08(%edx) - - /* restore current stack base */ - popl %ecx - movl %ecx, 0x04(%edx) - - /* restore current SEH exception list */ - popl %ecx - movl %ecx, (%edx) - - popl %edi /* save EDI */ - popl %esi /* save ESI */ - popl %ebx /* save EBX */ - popl %ebp /* save EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x04(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm b/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm deleted file mode 100644 index d12f7109055..00000000000 --- a/thirdparty/boost/asm/jump_i386_ms_pe_masm.asm +++ /dev/null @@ -1,142 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; 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) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT - ; fourth arg of jump_fcontext() == flag indicating preserving FPU - mov ecx, [esp+010h] - - push ebp ; save EBP - push ebx ; save EBX - push esi ; save ESI - push edi ; save EDI - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; load current SEH exception list - mov eax, [edx] - push eax - - ; load current stack base - mov eax, [edx+04h] - push eax - - ; load current stack limit - mov eax, [edx+08h] - push eax - - ; load current deallocation stack - mov eax, [edx+0e0ch] - push eax - - ; load fiber local storage - mov eax, [edx+010h] - push eax - - ; prepare stack for FPU - lea esp, [esp-08h] - - ; test for flag preserve_fpu - test ecx, ecx - je nxt1 - - ; save MMX control- and status-word - stmxcsr [esp] - ; save x87 control-word - fnstcw [esp+04h] - -nxt1: - ; first arg of jump_fcontext() == context jumping from - mov eax, [esp+030h] - - ; store ESP (pointing to context-data) in EAX - mov [eax], esp - - ; second arg of jump_fcontext() == context jumping to - mov edx, [esp+034h] - - ; third arg of jump_fcontext() == value to be returned after jump - mov eax, [esp+038h] - - ; restore ESP (pointing to context-data) from EDX - mov esp, edx - - ; test for flag preserve_fpu - test ecx, ecx - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [esp] - ; restore x87 control-word - fldcw [esp+04h] - -nxt2: - ; prepare stack for FPU - lea esp, [esp+08h] - - assume fs:nothing - ; load NT_TIB into ECX - mov edx, fs:[018h] - assume fs:error - - ; restore fiber local storage - pop ecx - mov [edx+010h], ecx - - ; restore current deallocation stack - pop ecx - mov [edx+0e0ch], ecx - - ; restore current stack limit - pop ecx - mov [edx+08h], ecx - - ; restore current stack base - pop ecx - mov [edx+04h], ecx - - ; restore current SEH exception list - pop ecx - mov [edx], ecx - - pop edi ; save EDI - pop esi ; save ESI - pop ebx ; save EBX - pop ebp ; save EBP - - ; restore return-address - pop edx - - ; use value in EAX as return-value after jump - ; use value in EAX as first arg in context function - mov [esp+04h], eax - - ; indirect jump to context - jmp edx -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S b/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S deleted file mode 100644 index aa47b0f3967..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_elf_gas.S +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx -.size jump_fcontext,.-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S deleted file mode 100644 index 05d669f56ae..00000000000 --- a/thirdparty/boost/asm/jump_i386_sysv_macho_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - /* fourth arg of jump_fcontext() == flag indicating preserving FPU */ - movl 0x10(%esp), %ecx - - pushl %ebp /* save EBP */ - pushl %ebx /* save EBX */ - pushl %esi /* save ESI */ - pushl %edi /* save EDI */ - - /* prepare stack for FPU */ - leal -0x8(%esp), %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 1f - - /* save MMX control- and status-word */ - stmxcsr (%esp) - /* save x87 control-word */ - fnstcw 0x4(%esp) - -1: - /* first arg of jump_fcontext() == context jumping from */ - movl 0x1c(%esp), %eax - - /* store ESP (pointing to context-data) in EAX */ - movl %esp, (%eax) - - /* second arg of jump_fcontext() == context jumping to */ - movl 0x20(%esp), %edx - - /* third arg of jump_fcontext() == value to be returned after jump */ - movl 0x24(%esp), %eax - - /* restore ESP (pointing to context-data) from EDX */ - movl %edx, %esp - - /* test for flag preserve_fpu */ - test %ecx, %ecx - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr (%esp) - /* restore x87 control-word */ - fldcw 0x4(%esp) -2: - /* prepare stack for FPU */ - leal 0x8(%esp), %esp - - popl %edi /* restore EDI */ - popl %esi /* restore ESI */ - popl %ebx /* restore EBX */ - popl %ebp /* restore EBP */ - - /* restore return-address */ - popl %edx - - /* use value in EAX as return-value after jump */ - /* use value in EAX as first arg in context function */ - movl %eax, 0x4(%esp) - - /* indirect jump to context */ - jmp *%edx diff --git a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index 959ddac16f8..00000000000 --- a/thirdparty/boost/asm/jump_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - 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) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "jump_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "jump_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..89a08821ca6 --- /dev/null +++ b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S @@ -0,0 +1,121 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 2 +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: + # reserve space on stack + addi.d $sp, $sp, -160 + + # save fs0 - fs7 + fst.d $fs0, $sp, 0 + fst.d $fs1, $sp, 8 + fst.d $fs2, $sp, 16 + fst.d $fs3, $sp, 24 + fst.d $fs4, $sp, 32 + fst.d $fs5, $sp, 40 + fst.d $fs6, $sp, 48 + fst.d $fs7, $sp, 56 + + # save s0 - s8, fp, ra + st.d $s0, $sp, 64 + st.d $s1, $sp, 72 + st.d $s2, $sp, 80 + st.d $s3, $sp, 88 + st.d $s4, $sp, 96 + st.d $s5, $sp, 104 + st.d $s6, $sp, 112 + st.d $s7, $sp, 120 + st.d $s8, $sp, 128 + st.d $fp, $sp, 136 + st.d $ra, $sp, 144 + + # save RA as PC + st.d $ra, $sp, 152 + + # store SP (pointing to context-data) in A2 + move $a2, $sp + + # restore SP (pointing to context-data) from A0 + move $sp, $a0 + + # load fs0 - fs7 + fld.d $fs0, $sp, 0 + fld.d $fs1, $sp, 8 + fld.d $fs2, $sp, 16 + fld.d $fs3, $sp, 24 + fld.d $fs4, $sp, 32 + fld.d $fs5, $sp, 40 + fld.d $fs6, $sp, 48 + fld.d $fs7, $sp, 56 + + #load s0 - s7 + ld.d $s0, $sp, 64 + ld.d $s1, $sp, 72 + ld.d $s2, $sp, 80 + ld.d $s3, $sp, 88 + ld.d $s4, $sp, 96 + ld.d $s5, $sp, 104 + ld.d $s6, $sp, 112 + ld.d $s7, $sp, 120 + ld.d $s8, $sp, 128 + ld.d $fp, $sp, 136 + ld.d $ra, $sp, 144 + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + move $a0, $a2 + + # load PC + ld.d $a2, $sp, 152 + + # restore stack + addi.d $sp, $sp, 160 + + # jump to context + jr $a2 +.size swoole_jump_fcontext, .-swoole_jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S b/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S deleted file mode 100644 index 67cc0796a6d..00000000000 --- a/thirdparty/boost/asm/jump_mips32_o32_elf_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -.ent jump_fcontext -jump_fcontext: - # reserve space on stack - addiu $sp, $sp, -92 - - sw $s0, 48($sp) # save S0 - sw $s1, 52($sp) # save S1 - sw $s2, 56($sp) # save S2 - sw $s3, 60($sp) # save S3 - sw $s4, 64($sp) # save S4 - sw $s5, 68($sp) # save S5 - sw $s6, 72($sp) # save S6 - sw $s7, 76($sp) # save S7 - sw $fp, 80($sp) # save FP - sw $ra, 84($sp) # save RA - sw $ra, 88($sp) # save RA as PC - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - - s.d $f20, ($sp) # save F20 - s.d $f22, 8($sp) # save F22 - s.d $f24, 16($sp) # save F24 - s.d $f26, 24($sp) # save F26 - s.d $f28, 32($sp) # save F28 - s.d $f30, 40($sp) # save F30 - -1: -#endif - - # store SP (pointing to context-data) in A0 - sw $sp, ($a0) - - # restore SP (pointing to context-data) from A1 - move $sp, $a1 - - -#if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f20, ($sp) # restore F20 - l.d $f22, 8($sp) # restore F22 - l.d $f24, 16($sp) # restore F24 - l.d $f26, 24($sp) # restore F26 - l.d $f28, 32($sp) # restore F28 - l.d $f30, 40($sp) # restore F30 - -2: -#endif - - lw $s0, 48($sp) # restore S0 - lw $s1, 52($sp) # restore S1 - lw $s2, 56($sp) # restore S2 - lw $s3, 60($sp) # restore S3 - lw $s4, 64($sp) # restore S4 - lw $s5, 68($sp) # restore S5 - lw $s6, 72($sp) # restore S6 - lw $s7, 76($sp) # restore S7 - lw $fp, 80($sp) # restore FP - lw $ra, 84($sp) # restore RA - - # load PC - lw $t9, 88($sp) - - # adjust stack - addiu $sp, $sp, 92 - - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 - - # jump to context - jr $t9 -.end jump_fcontext -.size jump_fcontext, .-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S new file mode 100644 index 00000000000..edff6ec0501 --- /dev/null +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -0,0 +1,124 @@ +/* + Copyright Jiaxun Yang 2018. + 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) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_mips64_n64_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 3 +.type swoole_jump_fcontext,@function +.ent swoole_jump_fcontext +swoole_jump_fcontext: + # reserve space on stack + daddiu $sp, $sp, -160 + + sd $s0, 64($sp) # save S0 + sd $s1, 72($sp) # save S1 + sd $s2, 80($sp) # save S2 + sd $s3, 88($sp) # save S3 + sd $s4, 96($sp) # save S4 + sd $s5, 104($sp) # save S5 + sd $s6, 112($sp) # save S6 + sd $s7, 120($sp) # save S7 + sd $fp, 128($sp) # save FP + sd $ra, 144($sp) # save RA + sd $ra, 152($sp) # save RA as PC + +#if defined(__mips_hard_float) + s.d $f24, 0($sp) # save F24 + s.d $f25, 8($sp) # save F25 + s.d $f26, 16($sp) # save F26 + s.d $f27, 24($sp) # save F27 + s.d $f28, 32($sp) # save F28 + s.d $f29, 40($sp) # save F29 + s.d $f30, 48($sp) # save F30 + s.d $f31, 56($sp) # save F31 +#endif + + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp + + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 + +#if defined(__mips_hard_float) + l.d $f24, 0($sp) # restore F24 + l.d $f25, 8($sp) # restore F25 + l.d $f26, 16($sp) # restore F26 + l.d $f27, 24($sp) # restore F27 + l.d $f28, 32($sp) # restore F28 + l.d $f29, 40($sp) # restore F29 + l.d $f30, 48($sp) # restore F30 + l.d $f31, 56($sp) # restore F31 +#endif + + ld $s0, 64($sp) # restore S0 + ld $s1, 72($sp) # restore S1 + ld $s2, 80($sp) # restore S2 + ld $s3, 88($sp) # restore S3 + ld $s4, 96($sp) # restore S4 + ld $s5, 104($sp) # restore S5 + ld $s6, 112($sp) # restore S6 + ld $s7, 120($sp) # restore S7 + ld $fp, 128($sp) # restore FP + ld $ra, 144($sp) # restore RAa + + # load PC + ld $t9, 152($sp) + + # adjust stack + daddiu $sp, $sp, 160 + + move $a0, $v0 # move old sp from v0 to a0 as param + move $v1, $a1 # move *data from a1 to v1 as return + + # jump to context + jr $t9 +.end swoole_jump_fcontext +.size swoole_jump_fcontext, .-swoole_jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index f175e31233f..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - 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) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "jump_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "jump_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S deleted file mode 100644 index d5380d50f23..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,208 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl jump_fcontext -.align 2 -.type jump_fcontext,@function -jump_fcontext: - # reserve space on stack - subi %r1, %r1, 240 - - stw %r13, 152(%r1) # save R13 - stw %r14, 156(%r1) # save R14 - stw %r15, 160(%r1) # save R15 - stw %r16, 164(%r1) # save R16 - stw %r17, 168(%r1) # save R17 - stw %r18, 172(%r1) # save R18 - stw %r19, 176(%r1) # save R19 - stw %r20, 180(%r1) # save R20 - stw %r21, 184(%r1) # save R21 - stw %r22, 188(%r1) # save R22 - stw %r23, 192(%r1) # save R23 - stw %r24, 196(%r1) # save R24 - stw %r25, 200(%r1) # save R25 - stw %r26, 204(%r1) # save R26 - stw %r27, 208(%r1) # save R27 - stw %r28, 212(%r1) # save R28 - stw %r29, 216(%r1) # save R29 - stw %r30, 220(%r1) # save R30 - stw %r31, 224(%r1) # save R31 - - # save CR - mfcr %r0 - stw %r0, 228(%r1) - # save LR - mflr %r0 - stw %r0, 232(%r1) - # save LR as PC - stw %r0, 236(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR - -1: - # store RSP (pointing to context-data) in R3 - stw %r1, 0(%r3) - - # restore RSP (pointing to context-data) from R4 - mr %r1, %r4 - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: - lwz %r13, 152(%r1) # restore R13 - lwz %r14, 156(%r1) # restore R14 - lwz %r15, 160(%r1) # restore R15 - lwz %r16, 164(%r1) # restore R16 - lwz %r17, 168(%r1) # restore R17 - lwz %r18, 172(%r1) # restore R18 - lwz %r19, 176(%r1) # restore R19 - lwz %r20, 180(%r1) # restore R20 - lwz %r21, 184(%r1) # restore R21 - lwz %r22, 188(%r1) # restore R22 - lwz %r23, 192(%r1) # restore R23 - lwz %r24, 196(%r1) # restore R24 - lwz %r25, 200(%r1) # restore R25 - lwz %r26, 204(%r1) # restore R26 - lwz %r27, 208(%r1) # restore R27 - lwz %r28, 212(%r1) # restore R28 - lwz %r29, 216(%r1) # restore R29 - lwz %r30, 220(%r1) # restore R30 - lwz %r31, 224(%r1) # restore R31 - - # restore CR - lwz %r0, 228(%r1) - mtcr %r0 - # restore LR - lwz %r0, 232(%r1) - mtlr %r0 - - # load PC - lwz %r0, 236(%r1) - # restore CTR - mtctr %r0 - - # adjust stack - addi %r1, %r1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 - - # jump to context - bctr -.size jump_fcontext, .-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S deleted file mode 100644 index 59d8f49746b..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,203 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _jump_fcontext -.align 2 -_jump_fcontext: - ; reserve space on stack - subi r1, r1, 240 - - stw r13, 152(r1) ; save R13 - stw r14, 156(r1) ; save R14 - stw r15, 160(r1) ; save R15 - stw r16, 164(r1) ; save R16 - stw r17, 168(r1) ; save R17 - stw r18, 172(r1) ; save R18 - stw r19, 176(r1) ; save R19 - stw r20, 180(r1) ; save R20 - stw r21, 184(r1) ; save R21 - stw r22, 188(r1) ; save R22 - stw r23, 192(r1) ; save R23 - stw r24, 196(r1) ; save R24 - stw r25, 200(r1) ; save R25 - stw r26, 204(r1) ; save R26 - stw r27, 208(r1) ; save R27 - stw r28, 212(r1) ; save R28 - stw r29, 216(r1) ; save R29 - stw r30, 220(r1) ; save R30 - stw r31, 224(r1) ; save R31 - - ; save CR - mfcr r0 - stw r0, 228(r1) - ; save LR - mflr r0 - stw r0, 232(r1) - ; save LR as PC - stw r0, 236(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) - - ; restore RSP (pointing to context-data) from R4 - mr r1, r4 - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -l2: - lwz r13, 152(r1) ; restore R13 - lwz r14, 156(r1) ; restore R14 - lwz r15, 160(r1) ; restore R15 - lwz r16, 164(r1) ; restore R16 - lwz r17, 168(r1) ; restore R17 - lwz r18, 172(r1) ; restore R18 - lwz r19, 176(r1) ; restore R19 - lwz r20, 180(r1) ; restore R20 - lwz r21, 184(r1) ; restore R21 - lwz r22, 188(r1) ; restore R22 - lwz r23, 192(r1) ; restore R23 - lwz r24, 196(r1) ; restore R24 - lwz r25, 200(r1) ; restore R25 - lwz r26, 204(r1) ; restore R26 - lwz r27, 208(r1) ; restore R27 - lwz r28, 212(r1) ; restore R28 - lwz r29, 216(r1) ; restore R29 - lwz r30, 220(r1) ; restore R30 - lwz r31, 224(r1) ; restore R31 - - ; restore CR - lwz r0, 228(r1) - mtcr r0 - ; restore LR - lwz r0, 232(r1) - mtlr r0 - - ; load PC - lwz r0, 236(r1) - ; restore CTR - mtctr r0 - - ; adjust stack - addi r1, r1, 240 - - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 - - ; jump to context - bctr diff --git a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index 7cdf8a92883..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,138 +0,0 @@ -.globl .jump_fcontext -.globl jump_fcontext[DS] -.align 2 -.csect jump_fcontext[DS] -jump_fcontext: - .long .jump_fcontext -.jump_fcontext: - # reserve space on stack - subi 1, 1, 240 - - stw 13, 152(1) # save R13 - stw 14, 156(1) # save R14 - stw 15, 160(1) # save R15 - stw 16, 164(1) # save R16 - stw 17, 168(1) # save R17 - stw 18, 172(1) # save R18 - stw 19, 176(1) # save R19 - stw 20, 180(1) # save R20 - stw 21, 184(1) # save R21 - stw 22, 188(1) # save R22 - stw 23, 192(1) # save R23 - stw 24, 196(1) # save R24 - stw 25, 200(1) # save R25 - stw 26, 204(1) # save R26 - stw 27, 208(1) # save R27 - stw 28, 212(1) # save R28 - stw 29, 216(1) # save R29 - stw 30, 220(1) # save R30 - stw 31, 224(1) # save R31 - - # save CR - mfcr 0 - stw 0, 228(1) - # save LR - mflr 0 - stw 0, 232(1) - # save LR as PC - stw 0, 236(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) - - # restore RSP (pointing to context-data) from R4 - mr 1, 4 - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - lwz 13, 152(1) # restore R13 - lwz 14, 156(1) # restore R14 - lwz 15, 160(1) # restore R15 - lwz 16, 164(1) # restore R16 - lwz 17, 168(1) # restore R17 - lwz 18, 172(1) # restore R18 - lwz 19, 176(1) # restore R19 - lwz 20, 180(1) # restore R20 - lwz 21, 184(1) # restore R21 - lwz 22, 188(1) # restore R22 - lwz 23, 192(1) # restore R23 - lwz 24, 196(1) # restore R24 - lwz 25, 200(1) # restore R25 - lwz 26, 204(1) # restore R26 - lwz 27, 208(1) # restore R27 - lwz 28, 212(1) # restore R28 - lwz 29, 216(1) # restore R29 - lwz 30, 220(1) # restore R30 - lwz 31, 224(1) # restore R31 - - # restore CR - lwz 0, 228(1) - mtcr 0 - # restore LR - lwz 0, 232(1) - mtlr 0 - - # load PC - lwz 0, 236(1) - # restore CTR - mtctr 0 - - # adjust stack - addi 1, 1, 240 - - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 - - # jump to context - bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S index 46bc3cc2c81..a90ffbe1681 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -12,253 +12,207 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ -.globl jump_fcontext +.file "jump_ppc64_sysv_elf_gas.S" +.globl swoole_jump_fcontext #if _CALL_ELF == 2 .text .align 2 -jump_fcontext: - addis %r2, %r12, .TOC.-jump_fcontext@ha - addi %r2, %r2, .TOC.-jump_fcontext@l - .localentry jump_fcontext, . - jump_fcontext +swoole_jump_fcontext: + addis %r2, %r12, .TOC.-swoole_jump_fcontext@ha + addi %r2, %r2, .TOC.-swoole_jump_fcontext@l + .localentry swoole_jump_fcontext, . - swoole_jump_fcontext #else .section ".opd","aw" .align 3 -jump_fcontext: +swoole_jump_fcontext: # ifdef _CALL_LINUX - .quad .L.jump_fcontext,.TOC.@tocbase,0 - .type jump_fcontext,@function + .quad .L.swoole_jump_fcontext,.TOC.@tocbase,0 + .type swoole_jump_fcontext,@function .text .align 2 -.L.jump_fcontext: +.L.swoole_jump_fcontext: # else - .hidden .jump_fcontext - .globl .jump_fcontext - .quad .jump_fcontext,.TOC.@tocbase,0 - .size jump_fcontext,24 - .type .jump_fcontext,@function + .hidden .swoole_jump_fcontext + .globl .swoole_jump_fcontext + .quad .swoole_jump_fcontext,.TOC.@tocbase,0 + .size swoole_jump_fcontext,24 + .type .swoole_jump_fcontext,@function .text .align 2 -.jump_fcontext: +.swoole_jump_fcontext: # endif #endif # reserve space on stack - subi %r1, %r1, 328 + subi %r1, %r1, 184 #if _CALL_ELF != 2 - std %r2, 152(%r1) # save TOC + std %r2, 0(%r1) # save TOC +#endif + std %r14, 8(%r1) # save R14 + std %r15, 16(%r1) # save R15 + std %r16, 24(%r1) # save R16 + std %r17, 32(%r1) # save R17 + std %r18, 40(%r1) # save R18 + std %r19, 48(%r1) # save R19 + std %r20, 56(%r1) # save R20 + std %r21, 64(%r1) # save R21 + std %r22, 72(%r1) # save R22 + std %r23, 80(%r1) # save R23 + std %r24, 88(%r1) # save R24 + std %r25, 96(%r1) # save R25 + std %r26, 104(%r1) # save R26 + std %r27, 112(%r1) # save R27 + std %r28, 120(%r1) # save R28 + std %r29, 128(%r1) # save R29 + std %r30, 136(%r1) # save R30 + std %r31, 144(%r1) # save R31 +#if _CALL_ELF != 2 + std %r3, 152(%r1) # save hidden #endif - std %r14, 160(%r1) # save R14 - std %r15, 168(%r1) # save R15 - std %r16, 176(%r1) # save R16 - std %r17, 184(%r1) # save R17 - std %r18, 192(%r1) # save R18 - std %r19, 200(%r1) # save R19 - std %r20, 208(%r1) # save R20 - std %r21, 216(%r1) # save R21 - std %r22, 224(%r1) # save R22 - std %r23, 232(%r1) # save R23 - std %r24, 240(%r1) # save R24 - std %r25, 248(%r1) # save R25 - std %r26, 256(%r1) # save R26 - std %r27, 264(%r1) # save R27 - std %r28, 272(%r1) # save R28 - std %r29, 280(%r1) # save R29 - std %r30, 288(%r1) # save R30 - std %r31, 296(%r1) # save R31 # save CR mfcr %r0 - std %r0, 304(%r1) + std %r0, 160(%r1) # save LR mflr %r0 - std %r0, 312(%r1) + std %r0, 168(%r1) # save LR as PC - std %r0, 320(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f + std %r0, 176(%r1) - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR - -1: - # store RSP (pointing to context-data) in R3 - std %r1, 0(%r3) + # store RSP (pointing to context-data) in R6 + mr %r6, %r1 +#if _CALL_ELF == 2 + # restore RSP (pointing to context-data) from R3 + mr %r1, %r3 +#else # restore RSP (pointing to context-data) from R4 mr %r1, %r4 - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: + ld %r2, 0(%r1) # restore TOC +#endif + ld %r14, 8(%r1) # restore R14 + ld %r15, 16(%r1) # restore R15 + ld %r16, 24(%r1) # restore R16 + ld %r17, 32(%r1) # restore R17 + ld %r18, 40(%r1) # restore R18 + ld %r19, 48(%r1) # restore R19 + ld %r20, 56(%r1) # restore R20 + ld %r21, 64(%r1) # restore R21 + ld %r22, 72(%r1) # restore R22 + ld %r23, 80(%r1) # restore R23 + ld %r24, 88(%r1) # restore R24 + ld %r25, 96(%r1) # restore R25 + ld %r26, 104(%r1) # restore R26 + ld %r27, 112(%r1) # restore R27 + ld %r28, 120(%r1) # restore R28 + ld %r29, 128(%r1) # restore R29 + ld %r30, 136(%r1) # restore R30 + ld %r31, 144(%r1) # restore R31 #if _CALL_ELF != 2 - ld %r2, 152(%r1) # restore TOC + ld %r3, 152(%r1) # restore hidden #endif - ld %r14, 160(%r1) # restore R14 - ld %r15, 168(%r1) # restore R15 - ld %r16, 176(%r1) # restore R16 - ld %r17, 184(%r1) # restore R17 - ld %r18, 192(%r1) # restore R18 - ld %r19, 200(%r1) # restore R19 - ld %r20, 208(%r1) # restore R20 - ld %r21, 216(%r1) # restore R21 - ld %r22, 224(%r1) # restore R22 - ld %r23, 232(%r1) # restore R23 - ld %r24, 240(%r1) # restore R24 - ld %r25, 248(%r1) # restore R25 - ld %r26, 256(%r1) # restore R26 - ld %r27, 264(%r1) # restore R27 - ld %r28, 272(%r1) # restore R28 - ld %r29, 280(%r1) # restore R29 - ld %r30, 288(%r1) # restore R30 - ld %r31, 296(%r1) # restore R31 # restore CR - ld %r0, 304(%r1) + ld %r0, 160(%r1) mtcr %r0 # restore LR - ld %r0, 312(%r1) + ld %r0, 168(%r1) mtlr %r0 # load PC - ld %r12, 320(%r1) + ld %r12, 176(%r1) # restore CTR mtctr %r12 # adjust stack - addi %r1, %r1, 328 + addi %r1, %r1, 184 - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 +#if _CALL_ELF == 2 + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + # arg pointer already in %r4 # jump to context bctr -#if _CALL_ELF == 2 - .size jump_fcontext, .-jump_fcontext + .size swoole_jump_fcontext, .-swoole_jump_fcontext #else + # zero in r3 indicates first jump to context-function + cmpdi %r3, 0 + beq use_entry_arg + + # return transfer_t + std %r6, 0(%r3) + std %r5, 8(%r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + mr %r4, %r5 + + # jump to context + bctr # ifdef _CALL_LINUX - .size .jump_fcontext, .-.L.jump_fcontext + .size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext # else - .size .jump_fcontext, .-.jump_fcontext + .size .swoole_jump_fcontext, .-.swoole_jump_fcontext # endif #endif diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S index d94b7f65a0d..abea7940628 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S @@ -12,215 +12,153 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text .align 2 -.globl jump_fcontext +.globl _swoole_jump_fcontext -_jump_fcontext: +_swoole_jump_fcontext: ; reserve space on stack - subi r1, r1, 328 - - std r13, 152(r1) ; save R13 - std r14, 160(r1) ; save R14 - std r15, 168(r1) ; save R15 - std r16, 176(r1) ; save R16 - std r17, 184(r1) ; save R17 - std r18, 192(r1) ; save R18 - std r19, 200(r1) ; save R19 - std r20, 208(r1) ; save R20 - std r21, 216(r1) ; save R21 - std r22, 224(r1) ; save R22 - std r23, 232(r1) ; save R23 - std r24, 240(r1) ; save R24 - std r25, 248(r1) ; save R25 - std r26, 256(r1) ; save R26 - std r27, 264(r1) ; save R27 - std r28, 272(r1) ; save R28 - std r29, 280(r1) ; save R29 - std r30, 288(r1) ; save R30 - std r31, 296(r1) ; save R31 + subi r1, r1, 184 + + std r14, 8(r1) ; save R14 + std r15, 16(r1) ; save R15 + std r16, 24(r1) ; save R16 + std r17, 32(r1) ; save R17 + std r18, 40(r1) ; save R18 + std r19, 48(r1) ; save R19 + std r20, 56(r1) ; save R20 + std r21, 64(r1) ; save R21 + std r22, 72(r1) ; save R22 + std r23, 80(r1) ; save R23 + std r24, 88(r1) ; save R24 + std r25, 96(r1) ; save R25 + std r26, 104(r1) ; save R26 + std r27, 112(r1) ; save R27 + std r28, 120(r1) ; save R28 + std r29, 128(r1) ; save R29 + std r30, 136(r1) ; save R30 + std r31, 144(r1) ; save R31 + std r3, 152(r1) ; save hidden ; save CR mfcr r0 - std r0, 304(r1) + std r0, 160(r1) ; save LR mflr r0 - std r0, 312(r1) + std r0, 168(r1) ; save LR as PC - std r0, 320(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) + std r0, 176(r1) + + ; store RSP (pointing to context-data) in R6 + mr r6, r1 ; restore RSP (pointing to context-data) from R4 mr r1, r4 - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -2: - ld r13, 152(r1) ; restore R13 - ld r14, 160(r1) ; restore R14 - ld r15, 168(r1) ; restore R15 - ld r16, 176(r1) ; restore R16 - ld r17, 184(r1) ; restore R17 - ld r18, 192(r1) ; restore R18 - ld r19, 200(r1) ; restore R19 - ld r20, 208(r1) ; restore R20 - ld r21, 216(r1) ; restore R21 - ld r22, 224(r1) ; restore R22 - ld r23, 232(r1) ; restore R23 - ld r24, 240(r1) ; restore R24 - ld r25, 248(r1) ; restore R25 - ld r26, 256(r1) ; restore R26 - ld r27, 264(r1) ; restore R27 - ld r28, 272(r1) ; restore R28 - ld r29, 280(r1) ; restore R29 - ld r30, 288(r1) ; restore R30 - ld r31, 296(r1) ; restore R31 + ld r14, 8(r1) ; restore R14 + ld r15, 16(r1) ; restore R15 + ld r16, 24(r1) ; restore R16 + ld r17, 32(r1) ; restore R17 + ld r18, 40(r1) ; restore R18 + ld r19, 48(r1) ; restore R19 + ld r20, 56(r1) ; restore R20 + ld r21, 64(r1) ; restore R21 + ld r22, 72(r1) ; restore R22 + ld r23, 80(r1) ; restore R23 + ld r24, 88(r1) ; restore R24 + ld r25, 96(r1) ; restore R25 + ld r26, 104(r1) ; restore R26 + ld r27, 112(r1) ; restore R27 + ld r28, 120(r1) ; restore R28 + ld r29, 128(r1) ; restore R29 + ld r30, 136(r1) ; restore R30 + ld r31, 144(r1) ; restore R31 + ld r3, 152(r1) ; restore hidden ; restore CR - ld r0, 304(r1) + ld r0, 160(r1) mtcr r0 ; restore LR - ld r0, 312(r1) + ld r0, 168(r1) mtlr r0 ; load PC - ld r0, 320(r1) + ld r12, 176(r1) ; restore CTR - mtctr r0 + mtctr r12 ; adjust stack - addi r1, r1, 328 + addi r1, r1, 184 + + ; zero in r3 indicates first jump to context-function + cmpdi r3, 0 + beq use_entry_arg + + ; return transfer_t + std r6, 0(r3) + std r5, 8(r3) + + ; jump to context + bctr - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 +use_entry_arg: + ; copy transfer_t into transfer_fn arg registers + mr r3, r6 + mr r4, r5 ; jump to context bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S index 65d60d4f54b..a125f681b5e 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S @@ -1,134 +1,173 @@ -.align 2 -.globl .jump_fcontext -.jump_fcontext: + +/* + Copyright Oliver Kowalke 2009. + 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) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "jump_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_jump_fcontext[DS] + .globl .swoole_jump_fcontext + .csect swoole_jump_fcontext[DS], 3 +swoole_jump_fcontext: + .llong .swoole_jump_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_jump_fcontext: # reserve space on stack - subi 1, 1, 328 - - std 13, 152(1) # save R13 - std 14, 160(1) # save R14 - std 15, 168(1) # save R15 - std 16, 176(1) # save R16 - std 17, 184(1) # save R17 - std 18, 192(1) # save R18 - std 19, 200(1) # save R19 - std 20, 208(1) # save R20 - std 21, 216(1) # save R21 - std 22, 224(1) # save R22 - std 23, 232(1) # save R23 - std 24, 240(1) # save R24 - std 25, 248(1) # save R25 - std 26, 256(1) # save R26 - std 27, 264(1) # save R27 - std 28, 272(1) # save R28 - std 29, 280(1) # save R29 - std 30, 288(1) # save R30 - std 31, 296(1) # save R31 + subi 1, 1, 184 + + std 2, 0(1) # save TOC + std 14, 8(1) # save R14 + std 15, 16(1) # save R15 + std 16, 24(1) # save R16 + std 17, 32(1) # save R17 + std 18, 40(1) # save R18 + std 19, 48(1) # save R19 + std 20, 56(1) # save R20 + std 21, 64(1) # save R21 + std 22, 72(1) # save R22 + std 23, 80(1) # save R23 + std 24, 88(1) # save R24 + std 25, 96(1) # save R25 + std 26, 104(1) # save R26 + std 27, 112(1) # save R27 + std 28, 120(1) # save R28 + std 29, 128(1) # save R29 + std 30, 136(1) # save R30 + std 31, 144(1) # save R31 + std 3, 152(1) # save hidden # save CR mfcr 0 - std 0, 304(1) + std 0, 160(1) # save LR mflr 0 - std 0, 312(1) + std 0, 168(1) # save LR as PC - std 0, 320(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) + std 0, 176(1) + + # store RSP (pointing to context-data) in R6 + mr 6, 1 # restore RSP (pointing to context-data) from R4 mr 1, 4 - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - ld 13, 152(1) # restore R13 - ld 14, 160(1) # restore R14 - ld 15, 168(1) # restore R15 - ld 16, 176(1) # restore R16 - ld 17, 184(1) # restore R17 - ld 18, 192(1) # restore R18 - ld 19, 200(1) # restore R19 - ld 20, 208(1) # restore R20 - ld 21, 216(1) # restore R21 - ld 22, 224(1) # restore R22 - ld 23, 232(1) # restore R23 - ld 24, 240(1) # restore R24 - ld 25, 248(1) # restore R25 - ld 26, 256(1) # restore R26 - ld 27, 264(1) # restore R27 - ld 28, 272(1) # restore R28 - ld 29, 280(1) # restore R29 - ld 30, 288(1) # restore R30 - ld 31, 296(1) # restore R31 + ld 2, 0(1) # restore TOC + ld 14, 8(1) # restore R14 + ld 15, 16(1) # restore R15 + ld 16, 24(1) # restore R16 + ld 17, 32(1) # restore R17 + ld 18, 40(1) # restore R18 + ld 19, 48(1) # restore R19 + ld 20, 56(1) # restore R20 + ld 21, 64(1) # restore R21 + ld 22, 72(1) # restore R22 + ld 23, 80(1) # restore R23 + ld 24, 88(1) # restore R24 + ld 25, 96(1) # restore R25 + ld 26, 104(1) # restore R26 + ld 27, 112(1) # restore R27 + ld 28, 120(1) # restore R28 + ld 29, 128(1) # restore R29 + ld 30, 136(1) # restore R30 + ld 31, 144(1) # restore R31 + ld 3, 152(1) # restore hidden # restore CR - ld 0, 304(1) + ld 0, 160(1) mtcr 0 # restore LR - ld 0, 312(1) + ld 0, 168(1) mtlr 0 # load PC - ld 0, 320(1) + ld 0, 176(1) # restore CTR mtctr 0 # adjust stack - addi 1, 1, 328 + addi 1, 1, 184 + + # zero in r3 indicates first jump to context-function + cmpdi 3, 0 + beq use_entry_arg + + # return transfer_t + std 6, 0(3) + std 5, 8(3) + + # jump to context + bctr - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr 3, 6 + mr 4, 5 # jump to context bctr diff --git a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S new file mode 100644 index 00000000000..a2f9a2f3bb3 --- /dev/null +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -0,0 +1,150 @@ +/* + 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) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | fs0 | fs1 | fs2 | fs3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | fs4 | fs5 | fs6 | fs7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | fs8 | fs9 | fs10 | fs11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | s0 | s1 | s2 | s3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | s4 | s5 | s6 | s7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| * + * ------------------------------------------------- * + * | s8 | s9 | s10 | s11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | | | | | * + * ------------------------------------------------- * + * | 0xc0| 0xc4| 0xc8| 0xcc| | | | | * + * ------------------------------------------------- * + * | ra | pc | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "jump_riscv64_sysv_elf_gas.S" +.text +.align 1 +.global swoole_jump_fcontext +.type swoole_jump_fcontext, %function +swoole_jump_fcontext: + # prepare stack for GP + FPU + addi sp, sp, -0xd0 + + # save fs0 - fs11 + fsd fs0, 0x00(sp) + fsd fs1, 0x08(sp) + fsd fs2, 0x10(sp) + fsd fs3, 0x18(sp) + fsd fs4, 0x20(sp) + fsd fs5, 0x28(sp) + fsd fs6, 0x30(sp) + fsd fs7, 0x38(sp) + fsd fs8, 0x40(sp) + fsd fs9, 0x48(sp) + fsd fs10, 0x50(sp) + fsd fs11, 0x58(sp) + + # save s0-s11, ra + sd s0, 0x60(sp) + sd s1, 0x68(sp) + sd s2, 0x70(sp) + sd s3, 0x78(sp) + sd s4, 0x80(sp) + sd s5, 0x88(sp) + sd s6, 0x90(sp) + sd s7, 0x98(sp) + sd s8, 0xa0(sp) + sd s9, 0xa8(sp) + sd s10, 0xb0(sp) + sd s11, 0xb8(sp) + sd ra, 0xc0(sp) + + # save RA as PC + sd ra, 0xc8(sp) + + # store SP (pointing to context-data) in A2 + mv a2, sp + + # restore SP (pointing to context-data) from A0 + mv sp, a0 + + # load fs0 - fs11 + fld fs0, 0x00(sp) + fld fs1, 0x08(sp) + fld fs2, 0x10(sp) + fld fs3, 0x18(sp) + fld fs4, 0x20(sp) + fld fs5, 0x28(sp) + fld fs6, 0x30(sp) + fld fs7, 0x38(sp) + fld fs8, 0x40(sp) + fld fs9, 0x48(sp) + fld fs10, 0x50(sp) + fld fs11, 0x58(sp) + + # load s0-s11,ra + ld s0, 0x60(sp) + ld s1, 0x68(sp) + ld s2, 0x70(sp) + ld s3, 0x78(sp) + ld s4, 0x80(sp) + ld s5, 0x88(sp) + ld s6, 0x90(sp) + ld s7, 0x98(sp) + ld s8, 0xa0(sp) + ld s9, 0xa8(sp) + ld s10, 0xb0(sp) + ld s11, 0xb8(sp) + ld ra, 0xc0(sp) + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + mv a0, a2 + + # load pc + ld a2, 0xc8(sp) + + # restore stack from GP + FPU + addi sp, sp, 0xd0 + + jr a2 +.size swoole_jump_fcontext,.-swoole_jump_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S index ec01de8e1ff..14622086294 100644 --- a/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_sparc64_sysv_elf_gas.S @@ -45,13 +45,13 @@ .register %g6,#ignore .text -.globl jump_fcontext +.globl swoole_jump_fcontext .align 4 -.type jump_fcontext,@function +.type swoole_jump_fcontext,@function // intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, +// swoole_jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, // bool preserve_fpu = true); -jump_fcontext: +swoole_jump_fcontext: // %o0 = pointer to old fcontext, save current state here // %o1 = new context to jump to // %o2 = new return value in context %o0 @@ -133,7 +133,7 @@ Lno_fpu: jmp %o4 mov %o2, %o0 // return arg as result -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S deleted file mode 100644 index 5f85c3e57d4..00000000000 --- a/thirdparty/boost/asm/jump_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright Martin Husemann 2013. - 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) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 -#ifdef __NetBSD__ -#define FLUSHW t 0x83; nop // T_FLUSHWIN -#endif - -.text -.globl jump_fcontext -.align 4 -.type jump_fcontext,@function -// intptr_t -// jump_fcontext( fcontext_t * ofc, fcontext_t const* nfc, intptr_t vp, -// bool preserve_fpu = true); -jump_fcontext: - // %o0 = pointer to old fcontext, save current state here - // %o1 = new context to jump to - // %o2 = new return value in context %o0 - // %o3 = preserve fpu registers - // Save current state in %o0 fcontext, then activate %o1. - // If %o3, include fpu registers. - - FLUSHW // make sure all shadow registers are up to date in the current stack - - // save current state to fcontext_t at %o0 - st %sp, [%o0 + FC_GREG + OFF(0)] // current stack pointer - add %o7, 8, %o4 // calculate next instruction past call - st %o4, [%o0 + FC_GREG + OFF(1)] // and store it as %pc in save context - st %o7, [%o0 + FC_GREG + OFF(2)] - st %g1, [%o0 + FC_GREG + OFF(3)] - st %g2, [%o0 + FC_GREG + OFF(4)] - st %g3, [%o0 + FC_GREG + OFF(5)] - st %g6, [%o0 + FC_GREG + OFF(6)] - st %g7, [%o0 + FC_GREG + OFF(7)] - - // do we need to handle fpu? - cmp %o3, 0 - bz Lno_fpu - nop - - add %o0, FC_FPU, %o5 - std %f0, [%o5] - std %f2, [%o5+0x08] - std %f4, [%o5+0x10] - std %f6, [%o5+0x18] - std %f8, [%o5+0x20] - std %f10, [%o5+0x28] - std %f12, [%o5+0x30] - std %f14, [%o5+0x38] - st %fsr, [%o0+FC_FSR] - - add %o1, FC_FPU, %o5 - ldd [%o5], %f0 - ldd [%o5+0x08], %f2 - ldd [%o5+0x10], %f4 - ldd [%o5+0x18], %f6 - ldd [%o5+0x20], %f8 - ldd [%o5+0x28], %f10 - ldd [%o5+0x30], %f12 - ldd [%o5+0x38], %f14 - ld [%o1+FC_FSR], %fsr - -Lno_fpu: - // load new state from %o1 - ld [%o1 + FC_GREG + OFF(1)], %o4 - ld [%o1 + FC_GREG + OFF(2)], %o7 - ld [%o1 + FC_GREG + OFF(3)], %g1 - ld [%o1 + FC_GREG + OFF(4)], %g2 - ld [%o1 + FC_GREG + OFF(5)], %g3 - ld [%o1 + FC_GREG + OFF(6)], %g6 - ld [%o1 + FC_GREG + OFF(7)], %g7 - // switch to new stack - ld [%o1 + FC_GREG + OFF(0)], %sp - // and now reload from this stack the shadow regist bank contents - ld [%sp + OFF(0)], %l0 - ld [%sp + OFF(1)], %l1 - ld [%sp + OFF(2)], %l2 - ld [%sp + OFF(3)], %l3 - ld [%sp + OFF(4)], %l4 - ld [%sp + OFF(5)], %l5 - ld [%sp + OFF(6)], %l6 - ld [%sp + OFF(7)], %l7 - ld [%sp + OFF(8)], %i0 - ld [%sp + OFF(9)], %i1 - ld [%sp + OFF(10)], %i2 - ld [%sp + OFF(11)], %i3 - ld [%sp + OFF(12)], %i4 - ld [%sp + OFF(13)], %i5 - ld [%sp + OFF(14)], %i6 - ld [%sp + OFF(15)], %i7 - - // finally continue execution in new context - jmp %o4 - mov %o2, %o0 // return arg as result - -.size jump_fcontext,.-jump_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm deleted file mode 100644 index c4706ef9913..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "jump_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl jump_fcontext -.def jump_fcontext; .scl 2; .type 32; .endef -.seh_proc jump_fcontext -jump_fcontext: -.seh_endprologue - - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %rsi /* save RSI */ - pushq %rdi /* save RDI */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* save current stack base */ - movq 0x08(%r10), %rax - pushq %rax - /* save current stack limit */ - movq 0x10(%r10), %rax - pushq %rax - /* save current deallocation stack */ - movq 0x1478(%r10), %rax - pushq %rax - /* save fiber local storage */ - movq 0x18(%r10), %rax - pushq %rax - - /* prepare stack for FPU */ - leaq -0xa8(%rsp), %rsp - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 1f - - /* save MMX control- and status-word */ - stmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fnstcw 0xa4(%rsp) - - /* save XMM storage */ - movaps %xmm6, (%rsp) - movaps %xmm7, 0x10(%rsp) - movaps %xmm8, 0x20(%rsp) - movaps %xmm9, 0x30(%rsp) - movaps %xmm10, 0x40(%rsp) - movaps %xmm11, 0x50(%rsp) - movaps %xmm12, 0x60(%rsp) - movaps %xmm13, 0x70(%rsp) - movaps %xmm14, 0x80(%rsp) - movaps %xmm15, 0x90(%rsp) - -1: - /* set R10 to zero */ - xorq %r10, %r10 - /* set indicator */ - pushq %r10 - - /* store RSP (pointing to context-data) in RCX */ - movq %rsp, (%rcx) - - /* restore RSP (pointing to context-data) from RDX */ - movq %rdx, %rsp - - /* load indicator */ - popq %r10 - - /* test for flag preserve_fpu */ - testq %r9, %r9 - je 2f - - /* restore MMX control- and status-word */ - ldmxcsr 0xa0(%rsp) - /* save x87 control-word */ - fldcw 0xa4(%rsp) - - /* restore XMM storage */ - movaps (%rsp), %xmm6 - movaps 0x10(%rsp), %xmm7 - movaps 0x20(%rsp), %xmm8 - movaps 0x30(%rsp), %xmm9 - movaps 0x40(%rsp), %xmm10 - movaps 0x50(%rsp), %xmm11 - movaps 0x60(%rsp), %xmm12 - movaps 0x70(%rsp), %xmm13 - movaps 0x80(%rsp), %xmm14 - movaps 0x90(%rsp), %xmm15 - -2: - /* set offset of stack */ - movq 0xa8, %rcx - - /* test for indicator */ - testq %r10, %r10 - je 3f - - addq 0x8, %rcx - -3: - /* prepare stack for FPU */ - leaq (%rsp,%rcx), %rsp - - /* load NT_TIB */ - movq %gs:(0x30), %r10 - /* restore fiber local storage */ - popq %rax - movq %rax, 0x18(%r10) - /* restore deallocation stack */ - popq %rax - movq %rax, 0x1478(%r10) - /* restore stack limit */ - popq %rax - movq %rax, 0x10(%r10) - /* restore stack base */ - popq %rax - movq %rax, 0x8(%r10) - - popq %r12 /* restore R12 */ - popq %r13 /* restore R13 */ - popq %r14 /* restore R14 */ - popq %r15 /* restore R15 */ - popq %rdi /* restore RDI */ - popq %rsi /* restore RSI */ - popq %rbx /* restore RBX */ - popq %rbp /* restore RBP */ - - /* restore return-address */ - popq %r10 - - /* use third arg as return-value after jump */ - movq %r8, %rax - /* use third arg as first arg in context function */ - movq %r8, %rcx - - /* indirect jump to context */ - jmp *%r10 -.seh_endproc diff --git a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm deleted file mode 100644 index 6950ee7ff6c..00000000000 --- a/thirdparty/boost/asm/jump_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,216 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; 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) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -.code - -jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - .endprolog - - push rbp ; save RBP - push rbx ; save RBX - push rsi ; save RSI - push rdi ; save RDI - push r15 ; save R15 - push r14 ; save R14 - push r13 ; save R13 - push r12 ; save R12 - - ; load NT_TIB - mov r10, gs:[030h] - ; save current stack base - mov rax, [r10+08h] - push rax - ; save current stack limit - mov rax, [r10+010h] - push rax - ; save current deallocation stack - mov rax, [r10+01478h] - push rax - ; save fiber local storage - mov rax, [r10+018h] - push rax - - ; prepare stack for FPU - lea rsp, [rsp-0a8h] - - ; test for flag preserve_fpu - test r9, r9 - je nxt1 - - ; save MMX control- and status-word - stmxcsr [rsp+0a0h] - ; save x87 control-word - fnstcw [rsp+0a4h] - - ; save XMM storage - movaps [rsp], xmm6 - movaps [rsp+010h], xmm7 - movaps [rsp+020h], xmm8 - movaps [rsp+030h], xmm9 - movaps [rsp+040h], xmm10 - movaps [rsp+050h], xmm11 - movaps [rsp+060h], xmm12 - movaps [rsp+070h], xmm13 - movaps [rsp+080h], xmm14 - movaps [rsp+090h], xmm15 - -nxt1: - ; set R10 to zero - xor r10, r10 - ; set indicator - push r10 - - ; store RSP (pointing to context-data) in RCX - mov [rcx], rsp - - ; restore RSP (pointing to context-data) from RDX - mov rsp, rdx - - ; load indicator - pop r10 - - ; test for flag preserve_fpu - test r9, r9 - je nxt2 - - ; restore MMX control- and status-word - ldmxcsr [rsp+0a0h] - ; save x87 control-word - fldcw [rsp+0a4h] - - ; restore XMM storage - movaps xmm6, [rsp] - movaps xmm7, [rsp+010h] - movaps xmm8, [rsp+020h] - movaps xmm9, [rsp+030h] - movaps xmm10, [rsp+040h] - movaps xmm11, [rsp+050h] - movaps xmm12, [rsp+060h] - movaps xmm13, [rsp+070h] - movaps xmm14, [rsp+080h] - movaps xmm15, [rsp+090h] - -nxt2: - ; set offset of stack - mov rcx, 0a8h - - ; test for indicator - test r10, r10 - je nxt3 - - add rcx, 08h - -nxt3: - ; prepare stack for FPU - lea rsp, [rsp+rcx] - - ; load NT_TIB - mov r10, gs:[030h] - ; restore fiber local storage - pop rax - mov [r10+018h], rax - ; restore deallocation stack - pop rax - mov [r10+01478h], rax - ; restore stack limit - pop rax - mov [r10+010h], rax - ; restore stack base - pop rax - mov [r10+08h], rax - - pop r12 ; restore R12 - pop r13 ; restore R13 - pop r14 ; restore R14 - pop r15 ; restore R15 - pop rdi ; restore RDI - pop rsi ; restore RSI - pop rbx ; restore RBX - pop rbp ; restore RBP - - ; restore return-address - pop r10 - - ; use third arg as return-value after jump - mov rax, r8 - ; use third arg as first arg in context function - mov rcx, r8 - - ; indirect jump to context - jmp r10 -jump_fcontext ENDP -END diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index ac555319e93..7b80132b67e 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -12,87 +12,131 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "jump_x86_64_sysv_elf_gas.S" .text -.globl jump_fcontext -.type jump_fcontext,@function +.globl swoole_jump_fcontext +.type swoole_jump_fcontext,@function .align 16 -jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp +swoole_jump_fcontext: + _CET_ENDBR + leaq -0x40(%rsp), %rsp /* prepare stack */ - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x10(%rsp) /* save R12 */ + movq %r13, 0x18(%rsp) /* save R13 */ + movq %r14, 0x20(%rsp) /* save R14 */ + movq %r15, 0x28(%rsp) /* save R15 */ + movq %rbx, 0x30(%rsp) /* save RBX */ + movq %rbp, 0x38(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp +#if SWOOLE_CONTEXT_SHADOW_STACK + /* grow the stack to reserve space for shadow stack pointer(SSP) */ + leaq -0x8(%rsp), %rsp + /* read the current SSP and store it */ + rdsspq %rcx + movq %rcx, (%rsp) +#endif - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp -2: - /* prepare stack for FPU */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* first 8 bytes are SSP */ + movq (%rsp), %rcx leaq 0x8(%rsp), %rsp - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + /* Restore target(new) shadow stack */ + rstorssp -8(%rcx) + /* restore token for previous shadow stack is pushed */ + /* on previous shadow stack after saveprevssp */ + saveprevssp + + /* when return, swoole_jump_fcontext jump to restored return address */ + /* (r8) instead of RET. This miss of RET implies us to unwind */ + /* shadow stack accordingly. Otherwise mismatch occur */ + movq $1, %rcx + incsspq %rcx +#endif + + movq 0x40(%rsp), %r8 /* restore return-address */ + +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq 0x8(%rsp), %rdx /* load stack guard */ + movq %rdx, %fs:0x28 /* restore stack guard to TLS record */ +#endif + + movq 0x10(%rsp), %r12 /* restore R12 */ + movq 0x18(%rsp), %r13 /* restore R13 */ + movq 0x20(%rsp), %r14 /* restore R14 */ + movq 0x28(%rsp), %r15 /* restore R15 */ + movq 0x30(%rsp), %rbx /* restore RBX */ + movq 0x38(%rsp), %rbp /* restore RBP */ - /* restore return-address */ - popq %r8 + leaq 0x48(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ +#if !defined(_ILP32) + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx +#else + /* RAX == data:fctx */ + salq $32, %rsi + orq %rsi, %rax +#endif + /* pass transfer_t as first arg in context function */ +#if !defined(_ILP32) + /* RDI == fctx, RSI == data */ +#else + /* RDI == data:fctx */ +#endif + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 -.size jump_fcontext,.-jump_fcontext +.size swoole_jump_fcontext,.-swoole_jump_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S index 50563c323e4..0bf18bd763f 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S @@ -21,73 +21,55 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ .text -.globl _jump_fcontext +.globl _swoole_jump_fcontext .align 8 -_jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp - - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +_swoole_jump_fcontext: + leaq -0x38(%rsp), %rsp /* prepare stack */ - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + movq 0x38(%rsp), %r8 /* restore return-address */ -2: - /* prepare stack for FPU */ - leaq 0x8(%rsp), %rsp +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ - /* restore return-address */ - popq %r8 + leaq 0x40(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 diff --git a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S index b208ab7f39d..fd98d15984d 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 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) @@ -51,19 +51,19 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "make_arm64_aapcs_elf_gas.S" .text .align 2 -.global make_fcontext -.type make_fcontext, %function -make_fcontext: +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: # shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF # reserve space for context-data on context-stack sub x0, x0, #0xb0 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # store address as a PC to jump in str x2, [x0, #0xa0] @@ -80,6 +80,6 @@ finish: # exit application bl _exit -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index 6b55a08adfc..7977c0ee9bd 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + 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) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -45,28 +51,22 @@ * * *******************************************************/ - .text -.globl _make_fcontext +.globl _swoole_make_fcontext .balign 16 -_make_fcontext: +_swoole_make_fcontext: ; shift address in x0 (allocated stack) to lower 16 byte boundary and x0, x0, ~0xF ; reserve space for context-data on context-stack sub x0, x0, #0xb0 - ; third arg of make_fcontext() == address of context-function + ; third arg of swoole_make_fcontext() == address of context-function ; store address as a PC to jump in str x2, [x0, #0xa0] - ; compute abs address of label finish - ; 0x0c = 3 instructions * size (4) before label 'finish' - - ; TODO: Numeric offset since llvm still does not support labels in ADR. Fix: - ; http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20140407/212336.html - adr x1, 0x0c + adr x1, finish ; save address of finish as return-address for context-function ; will be entered after context-function returns (LR register) diff --git a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S deleted file mode 100644 index 9877655161b..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,71 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| | * - * ------------------------------------------------- * - * | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,%function -make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #104 - - @ third arg of make_fcontext() == address of context-function - str a3, [a1,#100] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#96] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl _exit@PLT -.size make_fcontext,.-make_fcontext - -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S deleted file mode 100644 index 039726f02c9..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * - * ------------------------------------------------- * - * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * - * ------------------------------------------------- * - * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * - * ------------------------------------------------- * - * | sjlj| v1 | v2 | v3 | v4 | v5 | v6 | v7 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | | * - * ------------------------------------------------- * - * | 0x60| 0x64| 0x68| | * - * ------------------------------------------------- * - * | v8 | lr | pc | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - @ shift address in A1 to lower 16 byte boundary - bic a1, a1, #15 - - @ reserve space for context-data on context-stack - sub a1, a1, #108 - - @ third arg of make_fcontext() == address of context-function - str a3, [a1,#104] - - @ compute abs address of label finish - adr a2, finish - @ save address of finish as return-address for context-function - @ will be entered after context-function returns - str a2, [a1,#100] - - bx lr @ return pointer to context-data - -finish: - @ exit code is zero - mov a1, #0 - @ exit application - bl __exit diff --git a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm b/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm deleted file mode 100644 index 5c3d6df2c4d..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_pe_armasm.asm +++ /dev/null @@ -1,86 +0,0 @@ -;/* -; Copyright Oliver Kowalke 2009. -; 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) -;*/ - -; ******************************************************* -; * * -; * ------------------------------------------------- * -; * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * -; * ------------------------------------------------- * -; * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * -; * ------------------------------------------------- * -; * | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * -; * ------------------------------------------------- * -; * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * -; * ------------------------------------------------- * -; * | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * -; * ------------------------------------------------- * -; * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * -; * ------------------------------------------------- * -; * |deall|limit| base| v1 | v2 | v3 | v4 | v5 | * -; * ------------------------------------------------- * -; * ------------------------------------------------- * -; * | 24 | 25 | 26 | 27 | 28 | | * -; * ------------------------------------------------- * -; * | 0x60| 0x64| 0x68| 0x6c| 0x70| | * -; * ------------------------------------------------- * -; * | v6 | v7 | v8 | lr | pc | | * -; * ------------------------------------------------- * -; * * -; ******************************************************* - - - AREA |.text|, CODE - ALIGN 4 - EXPORT make_fcontext - IMPORT _exit - -make_fcontext PROC - ; first arg of make_fcontext() == top of context-stack - ; save top of context-stack (base) A4 - mov a4, a1 - - ; shift address in A1 to lower 16 byte boundary - bic a1, a1, #0x0f - - ; reserve space for context-data on context-stack - sub a1, a1, #0x74 - - ; save top address of context_stack as 'base' - str a4, [a1,#0x48] - ; second arg of make_fcontext() == size of context-stack - ; compute bottom address of context-stack (limit) - sub a4, a4, a2 - ; save bottom address of context-stack as 'limit' - str a4, [a1,#0x44] - ; save bottom address of context-stack as 'dealloction stack' - str a4, [a1,#0x40] - - ; third arg of make_fcontext() == address of context-function - str a3, [a1,#0x70] - - ; compute abs address of label finish - adr a2, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - str a2, [a1,#0x6c] - - bx lr ; return pointer to context-data - -finish - ; exit code is zero - mov a1, #0 - ; exit application - bl _exit - - ENDP - END diff --git a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S index 727e9045fc1..226dd9a16d5 100644 --- a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S @@ -7,14 +7,12 @@ // Stub file for universal binary -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) +#if defined(__x86_64__) #include "make_x86_64_sysv_macho_gas.S" -#elif defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" #elif defined(__ppc64__) #include "make_ppc64_sysv_macho_gas.S" +#elif defined(__arm64__) + #include "make_arm64_aapcs_macho_gas.S" #else #error "No arch's" #endif diff --git a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm b/thirdparty/boost/asm/make_i386_ms_pe_gas.asm deleted file mode 100644 index 881b3c81e95..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_gas.asm +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - 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) -*/ - -/******************************************************************** - --------------------------------------------------------------------------------- - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - --------------------------------------------------------------------------------- - | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | - --------------------------------------------------------------------------------- - | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | - --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- - | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - --------------------------------------------------------------------------------- - | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | - --------------------------------------------------------------------------------- - | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| - --------------------------------------------------------------------------------- -*******************************************************************/ - -.file "make_i386_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl _make_fcontext -.def _make_fcontext; .scl 2; .type 32; .endef -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %eax - - /* reserve space for first argument of context-function */ - /* EAX might already point to a 16byte border */ - leal -0x08(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - /* additional space is required for SEH */ - leal -0x3c(%eax), %eax - - /* first arg of make_fcontext() == top of context-stack */ - movl 0x04(%esp), %ecx - /* save top address of context stack as 'base' */ - movl %ecx, 0x14(%eax) - /* second arg of make_fcontext() == size of context-stack */ - movl 0x08(%esp), %edx - /* negate stack size for LEA instruction (== substraction) */ - negl %edx - /* compute bottom address of context stack (limit) */ - leal (%ecx,%edx), %ecx - /* save bottom address of context-stack as 'limit' */ - movl %ecx, 0x10(%eax) - /* save bottom address of context-stack as 'dealloction stack' */ - movl %ecx, 0xc(%eax) - - /* third arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %ecx - movl %ecx, 0x2c(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x04(%eax) - - /* compute abs address of label finish */ - movl $finish, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x30(%eax) - - /* traverse current seh chain to get the last exception handler installed by Windows */ - /* note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default */ - /* the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler */ - /* at its end by RaiseException all seh andlers are disregarded if not present and the */ - /* program is aborted */ - /* load NT_TIB into ECX */ - movl %fs:(0x0), %ecx - -walk: - /* load 'next' member of current SEH into EDX */ - movl (%ecx), %edx - /* test if 'next' of current SEH is last (== 0xffffffff) */ - incl %edx - jz found - decl %edx - /* exchange content; ECX contains address of next SEH */ - xchgl %ecx, %edx - /* inspect next SEH */ - jmp walk - -found: - /* load 'handler' member of SEH == address of last SEH handler installed by Windows */ - movl 0x04(%ecx), %ecx - /* save address in ECX as SEH handler for context */ - movl %ecx, 0x3c(%eax) - /* set ECX to -1 */ - movl $0xffffffff, %ecx - /* save ECX as next SEH item */ - movl %ecx, 0x38(%eax) - /* load address of next SEH item */ - leal 0x38(%eax), %ecx - /* save next SEH */ - movl %ecx, 0x18(%eax) - - /* return pointer to context-data */ - ret - -finish: - /* ESP points to same address as ESP on entry of context function + 0x4 */ - xorl %eax, %eax - /* exit code is zero */ - movl %eax, (%esp) - /* exit application */ - call __exit - hlt - -.def __exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm b/thirdparty/boost/asm/make_i386_ms_pe_masm.asm deleted file mode 100644 index 35c8b1cf447..00000000000 --- a/thirdparty/boost/asm/make_i386_ms_pe_masm.asm +++ /dev/null @@ -1,122 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; 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) - -; --------------------------------------------------------------------------------- -; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -; --------------------------------------------------------------------------------- -; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch | -; --------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI | -; --------------------------------------------------------------------------------- -; --------------------------------------------------------------------------------- -; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -; --------------------------------------------------------------------------------- -; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch | -; --------------------------------------------------------------------------------- -; | ESI | EBX | EBP | EIP | EXIT | | SEH NXT |SEH HNDLR| -; --------------------------------------------------------------------------------- - -.386 -.XMM -.model flat, c -; standard C library function -_exit PROTO, value:SDWORD -.code - -make_fcontext PROC BOOST_CONTEXT_EXPORT - ; first arg of make_fcontext() == top of context-stack - mov eax, [esp+04h] - - ; reserve space for first argument of context-function - ; EAX might already point to a 16byte border - lea eax, [eax-08h] - - ; shift address in EAX to lower 16 byte boundary - and eax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. EIP + return-address for context-function - ; on context-function entry: (ESP -0x4) % 8 == 0 - ; additional space is required for SEH - lea eax, [eax-03ch] - - ; first arg of make_fcontext() == top of context-stack - mov ecx, [esp+04h] - ; save top address of context stack as 'base' - mov [eax+014h], ecx - ; second arg of make_fcontext() == size of context-stack - mov edx, [esp+08h] - ; negate stack size for LEA instruction (== substraction) - neg edx - ; compute bottom address of context stack (limit) - lea ecx, [ecx+edx] - ; save bottom address of context-stack as 'limit' - mov [eax+010h], ecx - ; save bottom address of context-stack as 'dealloction stack' - mov [eax+0ch], ecx - - ; third arg of make_fcontext() == address of context-function - mov ecx, [esp+0ch] - mov [eax+02ch], ecx - - ; save MMX control- and status-word - stmxcsr [eax] - ; save x87 control-word - fnstcw [eax+04h] - - ; compute abs address of label finish - mov ecx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [eax+030h], ecx - - ; traverse current seh chain to get the last exception handler installed by Windows - ; note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default - ; the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler - ; at its end by RaiseException all seh-handlers are disregarded if not present and the - ; program is aborted - assume fs:nothing - ; load NT_TIB into ECX - mov ecx, fs:[0h] - assume fs:error - -walk: - ; load 'next' member of current SEH into EDX - mov edx, [ecx] - ; test if 'next' of current SEH is last (== 0xffffffff) - inc edx - jz found - dec edx - ; exchange content; ECX contains address of next SEH - xchg edx, ecx - ; inspect next SEH - jmp walk - -found: - ; load 'handler' member of SEH == address of last SEH handler installed by Windows - mov ecx, [ecx+04h] - ; save address in ECX as SEH handler for context - mov [eax+03ch], ecx - ; set ECX to -1 - mov ecx, 0ffffffffh - ; save ECX as next SEH item - mov [eax+038h], ecx - ; load address of next SEH item - lea ecx, [eax+038h] - ; save next SEH - mov [eax+018h], ecx - - ret ; return pointer to context-data - -finish: - ; exit code is zero - xor eax, eax - mov [esp], eax - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S b/thirdparty/boost/asm/make_i386_sysv_elf_gas.S deleted file mode 100644 index 26c94513448..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_elf_gas.S +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* third arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - call 2f - /* address of label 2 */ -2: popl %ebx - /* compute address of GOT and store it in EBX */ - addl $_GLOBAL_OFFSET_TABLE_+[.-2b], %ebx - - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call _exit@PLT - hlt -.size make_fcontext,.-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_sysv_macho_gas.S deleted file mode 100644 index 02ae2230e04..00000000000 --- a/thirdparty/boost/asm/make_i386_sysv_macho_gas.S +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ---------------------------------------------------------------------------------- * - * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * - * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | EXIT | * - * ---------------------------------------------------------------------------------- * - * * - ****************************************************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ - movl 0x4(%esp), %eax - - /* reserve space for first argument of context-function - rax might already point to a 16byte border */ - leal -0x8(%eax), %eax - - /* shift address in EAX to lower 16 byte boundary */ - andl $-16, %eax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. EIP + return-address for context-function */ - /* on context-function entry: (ESP -0x4) % 8 == 0 */ - leal -0x20(%eax), %eax - - /* thrid arg of make_fcontext() == address of context-function */ - movl 0xc(%esp), %edx - movl %edx, 0x18(%eax) - - /* save MMX control- and status-word */ - stmxcsr (%eax) - /* save x87 control-word */ - fnstcw 0x4(%eax) - - /* compute abs address of label finish */ - call 1f - /* address of label 1 */ -1: popl %ecx - /* compute abs address of label finish */ - addl $finish-1b, %ecx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movl %ecx, 0x1c(%eax) - - ret /* return pointer to context-data */ - -finish: - /* exit code is zero */ - xorl %eax, %eax - movl %eax, (%esp) - /* exit application */ - call __exit - hlt diff --git a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S deleted file mode 100644 index e364b2db62f..00000000000 --- a/thirdparty/boost/asm/make_i386_x86_64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - 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) -*/ - -// Stub file for universal binary - -#if defined(__i386__) - #include "make_i386_sysv_macho_gas.S" -#elif defined(__x86_64__) - #include "make_x86_64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..5359e0235e1 --- /dev/null +++ b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S @@ -0,0 +1,72 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 2 +.type swoole_make_fcontext,@function +swoole_make_fcontext: + # shift address in A0 to lower 16 byte boundary + bstrins.d $a0, $zero, 3, 0 + + # reserve space for context-data on context-stack + addi.d $a0, $a0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + st.d $a2, $a0, 152 + + # save address of finish as return-address for context-function + # will be entered after context-function returns + la.local $a4, finish + st.d $a4, $a0, 144 + + # return pointer to context-data + jr $ra + +finish: + # exit code is zero + li.d $a0, 0 + # call _exit(0) + b %plt(_exit) + +.size swoole_make_fcontext, .-swoole_make_fcontext +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S b/thirdparty/boost/asm/make_mips32_o32_elf_gas.S deleted file mode 100644 index c71ad05fbd9..00000000000 --- a/thirdparty/boost/asm/make_mips32_o32_elf_gas.S +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F20 | F22 | F24 | F26 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F28 | F30 | S0 | S1 | S2 | S3 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | | * - * ------------------------------------------------- * - * | S4 | S5 | S6 | S7 | FP | RA | PC | | * - * ------------------------------------------------- * - * * - * *****************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -.ent make_fcontext -make_fcontext: -#ifdef __PIC__ -.set noreorder -.cpload $t9 -.set reorder -#endif - # first arg of make_fcontext() == top address of context-stack - move $v0, $a0 - - # shift address in A0 to lower 16 byte boundary - move $v1, $v0 - li $v0, -16 # 0xfffffffffffffff0 - and $v0, $v1, $v0 - - # reserve space for context-data on context-stack - # including 48 byte of shadow space (sp % 16 == 0) - addiu $v0, $v0, -140 - - # third arg of make_fcontext() == address of context-function - sw $a2, 88($v0) - # save global pointer in context-data - # S0 will contain address of global pointer - sw $gp, 48($v0) - - # compute abs address of label finish - la $t9, finish - # save address of finish as return-address for context-function - # will be entered after context-function returns - sw $t9, 84($v0) - - jr $ra # return pointer to context-data - -finish: - # allocate stack space (contains shadow space for subroutines) - addiu $sp, $sp, -32 - # save return address - sw $ra, 28($sp) - - # restore GP (global pointer) - move $gp, $s0 - # exit code is zero - move $a0, $zero - # address of exit - lw $t9, %call16(_exit)($gp) - # exit application - jalr $t9 -.end make_fcontext -.size make_fcontext, .-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S new file mode 100644 index 00000000000..d3d46313b6b --- /dev/null +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -0,0 +1,96 @@ +/* + Copyright Jiaxun Yang 2018. + 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) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | F24 | F25 | F26 | F27 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | F28 | F29 | F30 | F31 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | FP | GP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_mips64_n64_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 3 +.type swoole_make_fcontext,@function +.ent swoole_make_fcontext +swoole_make_fcontext: +#ifdef __PIC__ +.set noreorder +.cpload $t9 +.set reorder +#endif + # shift address in A0 to lower 16 byte boundary + li $v1, 0xfffffffffffffff0 + and $v0, $v1, $a0 + + # reserve space for context-data on context-stack + daddiu $v0, $v0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + sd $a2, 152($v0) + # save global pointer in context-data + sd $gp, 136($v0) + + # psudo instruction compute abs address of label finish based on GP + dla $t9, finish + + # save address of finish as return-address for context-function + # will be entered after context-function returns + sd $t9, 144($v0) + + jr $ra # return pointer to context-data + +finish: + # reload our gp register (needed for la) + daddiu $t0, $sp, -160 + ld $gp, 136($t0) + + # call _exit(0) + # the previous function should have left the 16 bytes incoming argument + # area on the stack which we reuse for calling _exit + dla $t9, _exit + move $a0, $zero + jr $t9 +.end swoole_make_fcontext +.size swoole_make_fcontext, .-swoole_make_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S deleted file mode 100644 index 52e72209337..00000000000 --- a/thirdparty/boost/asm/make_ppc32_ppc64_sysv_macho_gas.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright Sergue E. Leontiev 2013. - 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) -*/ - -// Stub file for universal binary - -#if defined(__ppc__) - #include "make_ppc32_sysv_macho_gas.S" -#elif defined(__ppc64__) - #include "make_ppc64_sysv_macho_gas.S" -#else - #error "No arch's" -#endif diff --git a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S deleted file mode 100644 index 30ce473c9eb..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,123 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl make_fcontext -.align 2 -.type make_fcontext,@function -make_fcontext: - # save return address into R6 - mflr %r6 - - # first arg of make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi %r3, %r3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 304 - - # third arg of make_fcontext() == address of context-function - stw %r5, 236(%r3) - - # load LR - mflr %r0 - # jump to label 1 - bl 1f -1: - # load LR into R4 - mflr %r4 - # compute abs address of label finish - addi %r4, %r4, finish - 1b - # restore LR - mtlr %r0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw %r4, 232(%r3) - - # restore return address from R6 - mtlr %r6 - - blr # return pointer to context-data - -finish: - # save return address into R0 - mflr %r0 - # save return address on stack, set up stack frame - stw %r0, 4(%r1) - # allocate stack space, R1 % 16 == 0 - stwu %r1, -16(%r1) - - # exit code is zero - li %r3, 0 - # exit application - bl _exit@plt -.size make_fcontext, .-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S deleted file mode 100644 index f99b883f074..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_macho_gas.S +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - 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) -*/ - -/******************************************************* - * * - * ------------------------------------------------- * - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * - * ------------------------------------------------- * - * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * - * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * - * ------------------------------------------------- * - * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * - * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * - * ------------------------------------------------- * - * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * - * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * - * ------------------------------------------------- * - * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * - * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * - * ------------------------------------------------- * - * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * - * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | R14 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * - * ------------------------------------------------- * - * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * - * ------------------------------------------------- * - * | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * - * ------------------------------------------------- * - * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * - * ------------------------------------------------- * - * | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 56 | 57 | 58 | 59 | | * - * ------------------------------------------------- * - * | 224 | 228 | 232 | 236 | | * - * ------------------------------------------------- * - * | R31 | CR | LR | PC | | * - * ------------------------------------------------- * - * * - *******************************************************/ - -.text -.globl _make_fcontext -.align 2 -_make_fcontext: - ; save return address into R6 - mflr r6 - - ; first arg of make_fcontext() == top address of context-function - ; shift address in R3 to lower 16 byte boundary - clrrwi r3, r3, 4 - - ; reserve space for context-data on context-stack - ; including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi r3, r3, 304 - - ; third arg of make_fcontext() == address of context-function - stw r5, 236(r3) - - ; load LR - mflr r0 - ; jump to label 1 - bl l1 -l1: - ; load LR into R4 - mflr r4 - ; compute abs address of label finish - addi r4, r4, lo16((finish - .)+4) - # restore LR - mtlr r0 - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - stw r4, 232(r3) - - ; restore return address from R6 - mtlr r6 - - blr ; return pointer to context-data - -finish: - ; save return address into R0 - mflr r0 - ; save return address on stack, set up stack frame - stw r0, 4(r1) - ; allocate stack space, R1 % 16 == 0 - stwu r1, -16(r1) - - ; exit code is zero - li r3, 0 - ; exit application - bl __exit diff --git a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S deleted file mode 100644 index 5298d494a4b..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_xcoff_gas.S +++ /dev/null @@ -1,55 +0,0 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect make_fcontext[DS] -make_fcontext: - .long .make_fcontext[PR] - .csect .make_fcontext[PR], 3 -#.make_fcontext: - # save return address into R6 - mflr 6 - - # first arg of make_fcontext() == top address of context-function - # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 - - # reserve space for context-data on context-stack - # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 304 - - # third arg of make_fcontext() == address of context-function - stw 5, 236(3) - - # load LR - mflr 0 - # jump to label 1 - bl .Label -.Label: - # load LR into R4 - mflr 4 - # compute abs address of label .L_finish - addi 4, 4, .L_finish - .Label - # restore LR - mtlr 0 - # save address of finish as return-address for context-function - # will be entered after context-function returns - stw 4, 232(3) - - # restore return address from R6 - mtlr 6 - - blr # return pointer to context-data - -.L_finish: - # save return address into R0 - mflr 0 - # save return address on stack, set up stack frame - stw 0, 4(1) - # allocate stack space, R1 % 16 == 0 - stwu 1, -16(1) - - # exit code is zero - li 3, 0 - # exit application - bl ._exit - nop diff --git a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S index b777fe69b9e..59354f8dde5 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S @@ -12,134 +12,122 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ -.globl make_fcontext +.file "make_ppc64_sysv_elf_gas.S" +.globl swoole_make_fcontext #if _CALL_ELF == 2 .text .align 2 -make_fcontext: - addis %r2, %r12, .TOC.-make_fcontext@ha - addi %r2, %r2, .TOC.-make_fcontext@l - .localentry make_fcontext, . - make_fcontext +swoole_make_fcontext: + addis %r2, %r12, .TOC.-swoole_make_fcontext@ha + addi %r2, %r2, .TOC.-swoole_make_fcontext@l + .localentry swoole_make_fcontext, . - swoole_make_fcontext #else .section ".opd","aw" .align 3 -make_fcontext: +swoole_make_fcontext: # ifdef _CALL_LINUX - .quad .L.make_fcontext,.TOC.@tocbase,0 - .type make_fcontext,@function + .quad .L.swoole_make_fcontext,.TOC.@tocbase,0 + .type swoole_make_fcontext,@function .text .align 2 -.L.make_fcontext: +.L.swoole_make_fcontext: # else - .hidden .make_fcontext - .globl .make_fcontext - .quad .make_fcontext,.TOC.@tocbase,0 - .size make_fcontext,24 - .type .make_fcontext,@function + .hidden .swoole_make_fcontext + .globl .swoole_make_fcontext + .quad .swoole_make_fcontext,.TOC.@tocbase,0 + .size swoole_make_fcontext,24 + .type .swoole_make_fcontext,@function .text .align 2 -.make_fcontext: +.swoole_make_fcontext: # endif #endif # save return address into R6 mflr %r6 - # first arg of make_fcontext() == top address of context-stack + # first arg of swoole_make_fcontext() == top address of context-stack # shift address in R3 to lower 16 byte boundary clrrdi %r3, %r3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 392 + subi %r3, %r3, 248 - # third arg of make_fcontext() == address of context-function + # third arg of swoole_make_fcontext() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point - std %r5, 320(%r3) + std %r5, 176(%r3) #else # save address of context-function entry point ld %r4, 0(%r5) - std %r4, 320(%r3) + std %r4, 176(%r3) # save TOC of context-function ld %r4, 8(%r5) - std %r4, 152(%r3) + std %r4, 0(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + +#if _CALL_ELF != 2 + # zero in r3 indicates first jump to context-function + std %r0, 152(%r3) #endif # load LR @@ -155,7 +143,7 @@ make_fcontext: mtlr %r0 # save address of finish as return-address for context-function # will be entered after context-function returns - std %r4, 312(%r3) + std %r4, 168(%r3) # restore return address from R6 mtlr %r6 @@ -176,12 +164,12 @@ finish: bl _exit nop #if _CALL_ELF == 2 - .size make_fcontext, .-make_fcontext + .size swoole_make_fcontext, .-swoole_make_fcontext #else # ifdef _CALL_LINUX - .size .make_fcontext, .-.L.make_fcontext + .size .swoole_make_fcontext, .-.L.swoole_make_fcontext # else - .size .make_fcontext, .-.make_fcontext + .size .swoole_make_fcontext, .-.swoole_make_fcontext # endif #endif diff --git a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S index c4208c550e1..717f3bb2cf2 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S @@ -12,98 +12,85 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text -.globl _make_fcontext -_make_fcontext: +.globl _swoole_make_fcontext +_swoole_make_fcontext: ; save return address into R6 mflr r6 - ; first arg of make_fcontext() == top address of context-function + ; first arg of swoole_make_fcontext() == top address of context-function ; shift address in R3 to lower 16 byte boundary clrrwi r3, r3, 4 ; reserve space for context-data on context-stack ; including 64 byte of linkage + parameter area (R1 16 == 0) - subi r3, r3, 392 + subi r3, r3, 240 + + ; third arg of swoole_make_fcontext() == address of context-function + stw r5, 176(r3) + + ; set back-chain to zero + li r0, 0 + std r0, 184(r3) - ; third arg of make_fcontext() == address of context-function - stw r5, 320(r3) + ; compute address of returned transfer_t + addi r0, r3, 224 + mr r4, r0 + std r4, 152(r3) ; load LR mflr r0 @@ -118,7 +105,7 @@ l1: mtlr r0 ; save address of finish as return-address for context-function ; will be entered after context-function returns - std r4, 312(r3) + std r4, 168(r3) ; restore return address from R6 mtlr r6 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S index 8de630ca83f..58fd12eb916 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S @@ -1,22 +1,106 @@ - .globl make_fcontext[DS] - .globl .make_fcontext[PR] - .align 2 - .csect .make_fcontext[PR], 3 - .globl _make_fcontext -#._make_fcontext: +/* + Copyright Oliver Kowalke 2009. + 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) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "make_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext + .csect swoole_make_fcontext[DS], 3 +swoole_make_fcontext: + .llong .swoole_make_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_make_fcontext: # save return address into R6 mflr 6 - # first arg of make_fcontext() == top address of context-function + # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 + clrrdi 3, 3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 392 + subi 3, 3, 248 + + # third arg of swoole_make_fcontext() == address of context-function descriptor + ld 4, 0(5) + std 4, 176(3) + # save TOC of context-function + ld 4, 8(5) + std 4, 0(3) + + # set back-chain to zero + li 0, 0 + std 0, 184(3) - # third arg of make_fcontext() == address of context-function - stw 5, 320(3) + # zero in r3 indicates first jump to context-function + std 0, 152(3) # load LR mflr 0 @@ -31,7 +115,7 @@ mtlr 0 # save address of finish as return-address for context-function # will be entered after context-function returns - stw 4, 312(3) + std 4, 168(3) # restore return address from R6 mtlr 6 @@ -42,9 +126,9 @@ # save return address into R0 mflr 0 # save return address on stack, set up stack frame - stw 0, 8(1) + std 0, 8(1) # allocate stack space, R1 % 16 == 0 - stwu 1, -32(1) + stdu 1, -32(1) # exit code is zero li 3, 0 diff --git a/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S new file mode 100644 index 00000000000..cd43c775447 --- /dev/null +++ b/thirdparty/boost/asm/make_riscv64_sysv_elf_gas.S @@ -0,0 +1,91 @@ +/* + 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) +*/ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| * + * ------------------------------------------------- * + * | fs0 | fs1 | fs2 | fs3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| * + * ------------------------------------------------- * + * | fs4 | fs5 | fs6 | fs7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| * + * ------------------------------------------------- * + * | fs8 | fs9 | fs10 | fs11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| * + * ------------------------------------------------- * + * | s0 | s1 | s2 | s3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| * + * ------------------------------------------------- * + * | s4 | s5 | s6 | s7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 0xa0| 0xa4| 0xa8| 0xac| 0xb0| 0xb4| 0xb8| 0xbc| * + * ------------------------------------------------- * + * | s8 | s9 | s10 | s11 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | | | | | * + * ------------------------------------------------- * + * | 0xc0| 0xc4| 0xc8| 0xcc| | | | | * + * ------------------------------------------------- * + * | ra | pc | | | * + * ------------------------------------------------- * + * * + *******************************************************/ + +.file "make_riscv64_sysv_elf_gas.S" +.text +.align 1 +.global swoole_make_fcontext +.type swoole_make_fcontext, %function +swoole_make_fcontext: + # shift address in a0 (allocated stack) to lower 16 byte boundary + andi a0, a0, ~0xF + + # reserve space for context-data on context-stack + addi a0, a0, -0xd0 + + # third arg of swoole_make_fcontext() == address of context-function + # store address as a PC to jump in + sd a2, 0xc8(a0) + + # save address of finish as return-address for context-function + # will be entered after context-function returns (RA register) + lla a4, finish + sd a4, 0xc0(a0) + + ret // return pointer to context-data (a0) + +finish: + # exit code is zero + li a0, 0 + # exit application + tail _exit@plt + +.size swoole_make_fcontext,.-swoole_make_fcontext +# Mark that we don't need executable stack. +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S index 113e0f50f9a..1db988d2032 100644 --- a/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_sparc64_sysv_elf_gas.S @@ -45,12 +45,12 @@ .register %g6,#ignore .text -.globl make_fcontext +.globl swoole_make_fcontext .align 4 -.type make_fcontext,@function +.type swoole_make_fcontext,@function // fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: +// swoole_make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) +swoole_make_fcontext: save %sp, -CCFSZ, %sp // %i0 initial stack pointer // %i1 stack size limit @@ -83,7 +83,7 @@ finish: call _exit nop -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S b/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S deleted file mode 100644 index 2633471f2f1..00000000000 --- a/thirdparty/boost/asm/make_sparc_sysv_elf_gas.S +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright Martin Husemann 2013. - 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) -*/ - -/******************************************************************* - * * - * ------------------------------------------------------------- * - * | Offset (in 4 or 8 byte units) | Content | * - * ------------------------------------------------------------- * - * | 0 | %sp | * - * ------------------------------------------------------------- * - * | 1 | %pc | * - * ------------------------------------------------------------- * - * | 2 | %i7 (return address) | * - * ------------------------------------------------------------- * - * | 3 | %g1 | * - * ------------------------------------------------------------- * - * | 4 | %g2 | * - * ------------------------------------------------------------- * - * | 5 | %g3 | * - * ------------------------------------------------------------- * - * | 6 | %g6 | * - * ------------------------------------------------------------- * - * | 7 | %g7 | * - * ------------------------------------------------------------- * - * The local and in registers are stored on the stack. * - *******************************************************************/ - -#define OFF(N) (4*(N)) -#define CCFSZ 96 -#define FC_SZ 176 -#define FC_stK 168 // offsetof(fcontext_t, fc_stack) -#define FC_FPU 0 // offsetof(fcontext_t, fc_fp) -#define FC_FSR 128 // offsetof(fcontext_t, fc_fp.fp_fsr) -#define FC_GREG 136 // offsetof(fcontext_t, fc_greg) -#define BLOCK_SIZE 8 - -.text -.globl make_fcontext -.align 4 -.type make_fcontext,@function -// fcontext_t * -// make_fcontext( void * sp, std::size_t size, void (* fn)( intptr_t) ) -make_fcontext: - save %sp, -CCFSZ, %sp - // %i0 initial stack pointer - // %i1 stack size limit - // %i2 function pointer for context start function - - sub %i0, FC_SZ, %i4 // allocate fcontext_t at on the new stack and keep pointer as return value - andn %i4, BLOCK_SIZE-1, %i5 // force block ops usable alignement and keep pointer to fcontext in %i5 - - st %i0, [%i5+FC_stK+OFF(0)] // save fs_stack.sp - st %i1, [%i5+FC_stK+OFF(1)] // save fs_stack.size - sub %i5, CCFSZ, %o1 // leave space for one register window - st %o1, [%i5+FC_GREG+OFF(0)] // save new stack pointer - st %i2, [%i5+FC_GREG+OFF(1)] // save new %pc (function pointer) - st %g1, [%i5+FC_GREG+OFF(3)] - st %g2, [%i5+FC_GREG+OFF(4)] - st %g3, [%i5+FC_GREG+OFF(5)] - st %g6, [%i5+FC_GREG+OFF(6)] - st %g7, [%i5+FC_GREG+OFF(7)] - - // synthesize "return address": jump to finish - mov %i7, %l0 -2: call 3f - nop -3: add finish-2b-8, %o7, %i4 - st %i4, [%i5+FC_GREG+OFF(2)] - - ret - restore %g0, %i5, %o0 // return fcontext_t - -finish: - mov %g0, %o0 - call _exit - nop - -.size make_fcontext,.-make_fcontext - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm deleted file mode 100644 index 31dd56645d5..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_gas.asm +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright Oliver Kowalke 2009. - Copyright Thomas Sailer 2013. - 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) -*/ - -/**************************************************************************************** - * * - * ---------------------------------------------------------------------------------- - * | 0 | 1 | | - * ---------------------------------------------------------------------------------- - * | 0x0 | 0x4 | | - * ---------------------------------------------------------------------------------- - * | | | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | - * ---------------------------------------------------------------------------------- - * | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - * ---------------------------------------------------------------------------------- - * | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | - * ---------------------------------------------------------------------------------- - * | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | - * ---------------------------------------------------------------------------------- - * | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | - * ---------------------------------------------------------------------------------- - * | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | - * ---------------------------------------------------------------------------------- - * | SEE registers (XMM6-XMM15) | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | - * ---------------------------------------------------------------------------------- - * | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | - * ---------------------------------------------------------------------------------- - * | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - * ---------------------------------------------------------------------------------- - * | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | - * ---------------------------------------------------------------------------------- - * | limit | base | R12 | R13 | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | - * ---------------------------------------------------------------------------------- - * | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | - * ---------------------------------------------------------------------------------- - * | R14 | R15 | RDI | RSI | - * ---------------------------------------------------------------------------------- - * ---------------------------------------------------------------------------------- - * | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | - * ---------------------------------------------------------------------------------- - * | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | - * ---------------------------------------------------------------------------------- - * | RBX | RBP | RIP | EXIT | - * ---------------------------------------------------------------------------------- - * * - * *************************************************************************************/ - -.file "make_x86_64_ms_pe_gas.asm" -.text -.p2align 4,,15 -.globl make_fcontext -.def make_fcontext; .scl 2; .type 32; .endef -.seh_proc make_fcontext -make_fcontext: -.seh_endprologue - - /* first arg of make_fcontext() == top of context-stack */ - movq %rcx, %rax - - /* reserve 32byte shadow-space for context-function */ - leaq -0x28(%rax), %rax - - /* shift address in RAX to lower 16 byte boundary */ - /* == pointer to fcontext_t and address of context stack */ - andq $-16, %rax - - /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ - /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x128(%rax), %rax - - /* third arg of make_fcontext() == address of context-function */ - movq %r8, 0x118(%rax) - - /* first arg of make_fcontext() == top of context-stack */ - /* save top address of context stack as 'base' */ - movq %rcx, 0xd0(%rax) - /* second arg of make_fcontext() == size of context-stack */ - /* negate stack size for LEA instruction (== substraction) */ - negq %rdx - /* compute bottom address of context stack (limit) */ - leaq (%rcx,%rdx), %rcx - /* save bottom address of context stack as 'limit' */ - movq %rcx, 0xc8(%rax) - /* save address of context stack limit as 'dealloction stack' */ - movq %rcx, 0xc0(%rax) - - /* save MMX control- and status-word */ - stmxcsr 0xa8(%rax) - /* save x87 control-word */ - fnstcw 0xac(%rax) - - /* compute abs address of label finish */ - leaq finish(%rip), %rcx - /* save address of finish as return-address for context-function */ - /* will be entered after context-function returns */ - movq %rcx, 0x120(%rax) - - /* set indicator */ - movq 1, %rcx - movq %rcx, (%rax) - - ret /* return pointer to context-data */ - -finish: - /* 32byte shadow-space for _exit() are */ - /* already reserved by make_fcontext() */ - /* exit code is zero */ - xorq %rcx, %rcx - /* exit application */ - call _exit - hlt -.seh_endproc - -.def _exit; .scl 2; .type 32; .endef /* standard C library function */ diff --git a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm b/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm deleted file mode 100644 index a4eb58c021a..00000000000 --- a/thirdparty/boost/asm/make_x86_64_ms_pe_masm.asm +++ /dev/null @@ -1,144 +0,0 @@ - -; Copyright Oliver Kowalke 2009. -; 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) - -; ---------------------------------------------------------------------------------- -; | 0 | 1 | | -; ---------------------------------------------------------------------------------- -; | 0x0 | 0x4 | | -; ---------------------------------------------------------------------------------- -; | | | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -; ---------------------------------------------------------------------------------- -; | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | 0x20 | 0x24 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -; ---------------------------------------------------------------------------------- -; | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | 0x40 | 0x44 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -; ---------------------------------------------------------------------------------- -; | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c | 0x60 | 0x64 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -; ---------------------------------------------------------------------------------- -; | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c | 0x80 | 0x84 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -; ---------------------------------------------------------------------------------- -; | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c | 0xa0 | 0xa4 | -; ---------------------------------------------------------------------------------- -; | SEE registers (XMM6-XMM15) | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -; ---------------------------------------------------------------------------------- -; | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc | 0xc0 | 0xc4 | -; ---------------------------------------------------------------------------------- -; | fc_mxcsr|fc_x87_cw| | fbr_strg | fc_dealloc | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -; ---------------------------------------------------------------------------------- -; | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc | 0xe0 | 0xe4 | -; ---------------------------------------------------------------------------------- -; | limit | base | R12 | R13 | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -; ---------------------------------------------------------------------------------- -; | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc | 0x100 | 0x104 | -; ---------------------------------------------------------------------------------- -; | R14 | R15 | RDI | RSI | -; ---------------------------------------------------------------------------------- -; ---------------------------------------------------------------------------------- -; | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -; ---------------------------------------------------------------------------------- -; | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c | 0x120 | 0x124 | -; ---------------------------------------------------------------------------------- -; | RBX | RBP | RIP | EXIT | -; ---------------------------------------------------------------------------------- - -; standard C library function -EXTERN _exit:PROC -.code - -; generate function table entry in .pdata and unwind information in -make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME - ; .xdata for a function's structured exception handling unwind behavior - .endprolog - - ; first arg of make_fcontext() == top of context-stack - mov rax, rcx - - ; reserve 32byte shadow-space for context-function - sub rax, 028h - - ; shift address in RAX to lower 16 byte boundary - ; == pointer to fcontext_t and address of context stack - and rax, -16 - - ; reserve space for context-data on context-stack - ; size for fc_mxcsr .. RIP + return-address for context-function - ; on context-function entry: (RSP -0x8) % 16 == 0 - sub rax, 0128h - - ; third arg of make_fcontext() == address of context-function - mov [rax+0118h], r8 - - ; first arg of make_fcontext() == top of context-stack - ; save top address of context stack as 'base' - mov [rax+0d0h], rcx - ; second arg of make_fcontext() == size of context-stack - ; negate stack size for LEA instruction (== substraction) - neg rdx - ; compute bottom address of context stack (limit) - lea rcx, [rcx+rdx] - ; save bottom address of context stack as 'limit' - mov [rax+0c8h], rcx - ; save address of context stack limit as 'dealloction stack' - mov [rax+0c0h], rcx - - ; save MMX control- and status-word - stmxcsr [rax+0a8h] - ; save x87 control-word - fnstcw [rax+0ach] - - ; compute abs address of label finish - lea rcx, finish - ; save address of finish as return-address for context-function - ; will be entered after context-function returns - mov [rax+0120h], rcx - - ; set indicator - mov rcx, 1 - mov [rax], rcx - - ret ; return pointer to context-data - -finish: - ; 32byte shadow-space for _exit() are - ; already reserved by make_fcontext() - ; exit code is zero - xor rcx, rcx - ; exit application - call _exit - hlt -make_fcontext ENDP -END diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index b6878795846..00674735f38 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -12,64 +12,136 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "make_x86_64_sysv_elf_gas.S" .text -.globl make_fcontext -.type make_fcontext,@function +.globl swoole_make_fcontext +.type swoole_make_fcontext,@function .align 16 -make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +swoole_make_fcontext: + _CET_ENDBR +#if SWOOLE_CONTEXT_SHADOW_STACK + /* the new shadow stack pointer (SSP) */ + movq -0x8(%rdi), %r9 +#endif + + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* third arg of swoole_make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x30(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + /* save stack guard */ + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x40(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x38(%rax) + +#if SWOOLE_CONTEXT_SHADOW_STACK + /* Populate the shadow stack and normal stack */ + /* get original SSP */ + rdsspq %r8 + /* restore new shadow stack */ + rstorssp -0x8(%r9) + /* save the restore token on the original shadow stack */ + saveprevssp + /* push the address of "jmp trampoline" to the new shadow stack */ + /* as well as the stack */ + call 1f + jmp trampoline +1: + /* save address of "jmp trampoline" as return-address */ + /* for context-function */ + pop 0x38(%rax) + /* Get the new SSP. */ + rdsspq %r9 + /* restore original shadow stack */ + rstorssp -0x8(%r8) + /* save the restore token on the new shadow stack. */ + saveprevssp + + /* reserve space for the new SSP */ + leaq -0x8(%rax), %rax + /* save the new SSP to this fcontext */ + movq %r9, (%rax) +#endif ret /* return pointer to context-data */ +trampoline: + _CET_ENDBR + /* store return address on stack */ + /* fix stack alignment */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* save address of "jmp *%rbp" as return-address */ + /* on stack and shadow stack */ + call 2f + jmp *%rbp +2: +#else + push %rbp +#endif + /* jump to context-function */ + jmp *%rbx + finish: + _CET_ENDBR /* exit code is zero */ xorq %rdi, %rdi /* exit application */ call _exit@PLT hlt -.size make_fcontext,.-make_fcontext +.size swoole_make_fcontext,.-swoole_make_fcontext /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S index 3a030f8420d..46f8bd15cc4 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S @@ -21,48 +21,53 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ .text -.globl _make_fcontext +.globl _swoole_make_fcontext .align 8 -_make_fcontext: - /* first arg of make_fcontext() == top of context-stack */ +_swoole_make_fcontext: + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ - movabs $-16, %r8 - andq %r8, %rax + andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x48(%rax), %rax + leaq -0x40(%rax), %rax - /* third arg of make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* third arg of swoole_make_fcontext() == address of context-function */ + /* stored in RBX */ + movq %rdx, 0x28(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x30(%rax) ret /* return pointer to context-data */ +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + finish: /* exit code is zero */ xorq %rdi, %rdi diff --git a/thirdparty/hiredis/Makefile b/thirdparty/hiredis/Makefile deleted file mode 100644 index 06ca9946832..00000000000 --- a/thirdparty/hiredis/Makefile +++ /dev/null @@ -1,213 +0,0 @@ -# Hiredis Makefile -# Copyright (C) 2010-2011 Salvatore Sanfilippo -# Copyright (C) 2010-2011 Pieter Noordhuis -# This file is released under the BSD license, see the COPYING file - -OBJ=net.o hiredis.o sds.o async.o read.o -EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib -TESTS=hiredis-test -LIBNAME=libhiredis -PKGCONFNAME=hiredis.pc - -HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') -HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') -HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') -HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') - -# Installation related variables and target -PREFIX?=/usr/local -INCLUDE_PATH?=include/hiredis -LIBRARY_PATH?=lib -PKGCONF_PATH?=pkgconfig -INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) -INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) -INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) - -# redis-server configuration used for testing -REDIS_PORT=56379 -REDIS_SERVER=redis-server -define REDIS_TEST_CONFIG - daemonize yes - pidfile /tmp/hiredis-test-redis.pid - port $(REDIS_PORT) - bind 127.0.0.1 - unixsocket /tmp/hiredis-test-redis.sock -endef -export REDIS_TEST_CONFIG - -# Fallback to gcc when $CC is not in $PATH. -CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') -CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') -OPTIMIZATION?=-O3 -WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -DEBUG_FLAGS?= -g -ggdb -REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) -REAL_LDFLAGS=$(LDFLAGS) - -DYLIBSUFFIX=so -STLIBSUFFIX=a -DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) -DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) -DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) -DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) -STLIB_MAKE_CMD=ar rcs $(STLIBNAME) - -# Platform-specific overrides -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -ifeq ($(uname_S),SunOS) - REAL_LDFLAGS+= -ldl -lnsl -lsocket - DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) -endif -ifeq ($(uname_S),Darwin) - DYLIBSUFFIX=dylib - DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) - DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -endif - -all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) - -# Deps (use make dep to generate this) -async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h -dict.o: dict.c fmacros.h dict.h -hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h -net.o: net.c fmacros.h net.h hiredis.h read.h sds.h -read.o: read.c fmacros.h read.h sds.h -sds.o: sds.c sds.h -test.o: test.c fmacros.h hiredis.h read.h sds.h - -$(DYLIBNAME): $(OBJ) - $(DYLIB_MAKE_CMD) $(OBJ) - -$(STLIBNAME): $(OBJ) - $(STLIB_MAKE_CMD) $(OBJ) - -dynamic: $(DYLIBNAME) -static: $(STLIBNAME) - -# Binaries: -hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME) - -hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME) - -hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) - -hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME) - -hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) - -ifndef AE_DIR -hiredis-example-ae: - @echo "Please specify AE_DIR (e.g. /src)" - @false -else -hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME) -endif - -ifndef LIBUV_DIR -hiredis-example-libuv: - @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" - @false -else -hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) -endif - -ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) -hiredis-example-qt: - @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" - @false -else -hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) - $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore -endif - -hiredis-example: examples/example.c $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME) - -examples: $(EXAMPLES) - -hiredis-test: test.o $(STLIBNAME) - -hiredis-%: %.o $(STLIBNAME) - $(CC) $(REAL_CFLAGS) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME) - -test: hiredis-test - ./hiredis-test - -check: hiredis-test - @echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) - - $(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \ - ( kill `cat /tmp/hiredis-test-redis.pid` && false ) - kill `cat /tmp/hiredis-test-redis.pid` - -.c.o: - $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< - -clean: - rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov - -dep: - $(CC) -MM *.c - -INSTALL?= cp -pPR - -$(PKGCONFNAME): hiredis.h - @echo "Generating $@ for pkgconfig..." - @echo prefix=$(PREFIX) > $@ - @echo exec_prefix=\$${prefix} >> $@ - @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ - @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ - @echo >> $@ - @echo Name: hiredis >> $@ - @echo Description: Minimalistic C client library for Redis. >> $@ - @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ - @echo Libs: -L\$${libdir} -lhiredis >> $@ - @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ - -install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) - $(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters - $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) - $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) - mkdir -p $(INSTALL_PKGCONF_PATH) - $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) - -32bit: - @echo "" - @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" - @echo "" - $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" - -32bit-vars: - $(eval CFLAGS=-m32) - $(eval LDFLAGS=-m32) - -gprof: - $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" - -gcov: - $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" - -coverage: gcov - make check - mkdir -p tmp/lcov - lcov -d . -c -o tmp/lcov/hiredis.info - genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info - -noopt: - $(MAKE) OPTIMIZATION="" - -.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt diff --git a/thirdparty/hiredis/adapters/ae.h b/thirdparty/hiredis/adapters/ae.h deleted file mode 100644 index 5c551c2ed74..00000000000 --- a/thirdparty/hiredis/adapters/ae.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __HIREDIS_AE_H__ -#define __HIREDIS_AE_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisAeEvents { - redisAsyncContext *context; - aeEventLoop *loop; - int fd; - int reading, writing; -} redisAeEvents; - -static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleRead(e->context); -} - -static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleWrite(e->context); -} - -static void redisAeAddRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->reading) { - e->reading = 1; - aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); - } -} - -static void redisAeDelRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->reading) { - e->reading = 0; - aeDeleteFileEvent(loop,e->fd,AE_READABLE); - } -} - -static void redisAeAddWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->writing) { - e->writing = 1; - aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); - } -} - -static void redisAeDelWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->writing) { - e->writing = 0; - aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); - } -} - -static void redisAeCleanup(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - redisAeDelRead(privdata); - redisAeDelWrite(privdata); - free(e); -} - -static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisAeEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisAeEvents*)malloc(sizeof(*e)); - e->context = ac; - e->loop = loop; - e->fd = c->fd; - e->reading = e->writing = 0; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisAeAddRead; - ac->ev.delRead = redisAeDelRead; - ac->ev.addWrite = redisAeAddWrite; - ac->ev.delWrite = redisAeDelWrite; - ac->ev.cleanup = redisAeCleanup; - ac->ev.data = e; - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/glib.h b/thirdparty/hiredis/adapters/glib.h deleted file mode 100644 index e0a6411d395..00000000000 --- a/thirdparty/hiredis/adapters/glib.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef __HIREDIS_GLIB_H__ -#define __HIREDIS_GLIB_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct -{ - GSource source; - redisAsyncContext *ac; - GPollFD poll_fd; -} RedisSource; - -static void -redis_source_add_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_add_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_cleanup (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - - g_return_if_fail(source); - - redis_source_del_read(source); - redis_source_del_write(source); - /* - * It is not our responsibility to remove ourself from the - * current main loop. However, we will remove the GPollFD. - */ - if (source->poll_fd.fd >= 0) { - g_source_remove_poll((GSource *)data, &source->poll_fd); - source->poll_fd.fd = -1; - } -} - -static gboolean -redis_source_prepare (GSource *source, - gint *timeout_) -{ - RedisSource *redis = (RedisSource *)source; - *timeout_ = -1; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_check (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - RedisSource *redis = (RedisSource *)source; - - if ((redis->poll_fd.revents & G_IO_OUT)) { - redisAsyncHandleWrite(redis->ac); - redis->poll_fd.revents &= ~G_IO_OUT; - } - - if ((redis->poll_fd.revents & G_IO_IN)) { - redisAsyncHandleRead(redis->ac); - redis->poll_fd.revents &= ~G_IO_IN; - } - - if (callback) { - return callback(user_data); - } - - return TRUE; -} - -static void -redis_source_finalize (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - - if (redis->poll_fd.fd >= 0) { - g_source_remove_poll(source, &redis->poll_fd); - redis->poll_fd.fd = -1; - } -} - -static GSource * -redis_source_new (redisAsyncContext *ac) -{ - static GSourceFuncs source_funcs = { - .prepare = redis_source_prepare, - .check = redis_source_check, - .dispatch = redis_source_dispatch, - .finalize = redis_source_finalize, - }; - redisContext *c = &ac->c; - RedisSource *source; - - g_return_val_if_fail(ac != NULL, NULL); - - source = (RedisSource *)g_source_new(&source_funcs, sizeof *source); - source->ac = ac; - source->poll_fd.fd = c->fd; - source->poll_fd.events = 0; - source->poll_fd.revents = 0; - g_source_add_poll((GSource *)source, &source->poll_fd); - - ac->ev.addRead = redis_source_add_read; - ac->ev.delRead = redis_source_del_read; - ac->ev.addWrite = redis_source_add_write; - ac->ev.delWrite = redis_source_del_write; - ac->ev.cleanup = redis_source_cleanup; - ac->ev.data = source; - - return (GSource *)source; -} - -#endif /* __HIREDIS_GLIB_H__ */ diff --git a/thirdparty/hiredis/adapters/ivykis.h b/thirdparty/hiredis/adapters/ivykis.h deleted file mode 100644 index 6a12a868a35..00000000000 --- a/thirdparty/hiredis/adapters/ivykis.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __HIREDIS_IVYKIS_H__ -#define __HIREDIS_IVYKIS_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisIvykisEvents { - redisAsyncContext *context; - struct iv_fd fd; -} redisIvykisEvents; - -static void redisIvykisReadEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleRead(context); -} - -static void redisIvykisWriteEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleWrite(context); -} - -static void redisIvykisAddRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); -} - -static void redisIvykisDelRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, NULL); -} - -static void redisIvykisAddWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); -} - -static void redisIvykisDelWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, NULL); -} - -static void redisIvykisCleanup(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - - iv_fd_unregister(&e->fd); - free(e); -} - -static int redisIvykisAttach(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisIvykisEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisIvykisEvents*)malloc(sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisIvykisAddRead; - ac->ev.delRead = redisIvykisDelRead; - ac->ev.addWrite = redisIvykisAddWrite; - ac->ev.delWrite = redisIvykisDelWrite; - ac->ev.cleanup = redisIvykisCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - IV_FD_INIT(&e->fd); - e->fd.fd = c->fd; - e->fd.handler_in = redisIvykisReadEvent; - e->fd.handler_out = redisIvykisWriteEvent; - e->fd.handler_err = NULL; - e->fd.cookie = e->context; - - iv_fd_register(&e->fd); - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/libev.h b/thirdparty/hiredis/adapters/libev.h deleted file mode 100644 index 2bf8d521fc2..00000000000 --- a/thirdparty/hiredis/adapters/libev.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __HIREDIS_LIBEV_H__ -#define __HIREDIS_LIBEV_H__ -#include -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibevEvents { - redisAsyncContext *context; - struct ev_loop *loop; - int reading, writing; - ev_io rev, wev; -} redisLibevEvents; - -static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleRead(e->context); -} - -static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleWrite(e->context); -} - -static void redisLibevAddRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->reading) { - e->reading = 1; - ev_io_start(EV_A_ &e->rev); - } -} - -static void redisLibevDelRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->reading) { - e->reading = 0; - ev_io_stop(EV_A_ &e->rev); - } -} - -static void redisLibevAddWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->writing) { - e->writing = 1; - ev_io_start(EV_A_ &e->wev); - } -} - -static void redisLibevDelWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->writing) { - e->writing = 0; - ev_io_stop(EV_A_ &e->wev); - } -} - -static void redisLibevCleanup(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - redisLibevDelRead(privdata); - redisLibevDelWrite(privdata); - free(e); -} - -static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisLibevEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibevEvents*)malloc(sizeof(*e)); - e->context = ac; -#if EV_MULTIPLICITY - e->loop = loop; -#else - e->loop = NULL; -#endif - e->reading = e->writing = 0; - e->rev.data = e; - e->wev.data = e; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibevAddRead; - ac->ev.delRead = redisLibevDelRead; - ac->ev.addWrite = redisLibevAddWrite; - ac->ev.delWrite = redisLibevDelWrite; - ac->ev.cleanup = redisLibevCleanup; - ac->ev.data = e; - - /* Initialize read/write events */ - ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); - ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); - return REDIS_OK; -} - -#endif diff --git a/thirdparty/hiredis/adapters/libevent.h b/thirdparty/hiredis/adapters/libevent.h deleted file mode 100644 index 7d2bef180f6..00000000000 --- a/thirdparty/hiredis/adapters/libevent.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __HIREDIS_LIBEVENT_H__ -#define __HIREDIS_LIBEVENT_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibeventEvents { - redisAsyncContext *context; - struct event *rev, *wev; -} redisLibeventEvents; - -static void redisLibeventReadEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleRead(e->context); -} - -static void redisLibeventWriteEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleWrite(e->context); -} - -static void redisLibeventAddRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->rev,NULL); -} - -static void redisLibeventDelRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->rev); -} - -static void redisLibeventAddWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->wev,NULL); -} - -static void redisLibeventDelWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->wev); -} - -static void redisLibeventCleanup(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_free(e->rev); - event_free(e->wev); - free(e); -} - -static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { - redisContext *c = &(ac->c); - redisLibeventEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibeventEvents*)malloc(sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibeventAddRead; - ac->ev.delRead = redisLibeventDelRead; - ac->ev.addWrite = redisLibeventAddWrite; - ac->ev.delWrite = redisLibeventDelWrite; - ac->ev.cleanup = redisLibeventCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e); - e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e); - event_add(e->rev, NULL); - event_add(e->wev, NULL); - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/libuv.h b/thirdparty/hiredis/adapters/libuv.h deleted file mode 100644 index ff08c25e1ff..00000000000 --- a/thirdparty/hiredis/adapters/libuv.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef __HIREDIS_LIBUV_H__ -#define __HIREDIS_LIBUV_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" -#include - -typedef struct redisLibuvEvents { - redisAsyncContext* context; - uv_poll_t handle; - int events; -} redisLibuvEvents; - - -static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - if (status != 0) { - return; - } - - if (p->context != NULL && (events & UV_READABLE)) { - redisAsyncHandleRead(p->context); - } - if (p->context != NULL && (events & UV_WRITABLE)) { - redisAsyncHandleWrite(p->context); - } -} - - -static void redisLibuvAddRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_READABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_READABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void redisLibuvAddWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_WRITABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_WRITABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void on_close(uv_handle_t* handle) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - free(p); -} - - -static void redisLibuvCleanup(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->context = NULL; // indicate that context might no longer exist - uv_close((uv_handle_t*)&p->handle, on_close); -} - - -static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { - redisContext *c = &(ac->c); - - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - - ac->ev.addRead = redisLibuvAddRead; - ac->ev.delRead = redisLibuvDelRead; - ac->ev.addWrite = redisLibuvAddWrite; - ac->ev.delWrite = redisLibuvDelWrite; - ac->ev.cleanup = redisLibuvCleanup; - - redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p)); - - if (!p) { - return REDIS_ERR; - } - - memset(p, 0, sizeof(*p)); - - if (uv_poll_init(loop, &p->handle, c->fd) != 0) { - return REDIS_ERR; - } - - ac->ev.data = p; - p->handle.data = p; - p->context = ac; - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/macosx.h b/thirdparty/hiredis/adapters/macosx.h deleted file mode 100644 index 72121f606f4..00000000000 --- a/thirdparty/hiredis/adapters/macosx.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#ifndef __HIREDIS_MACOSX_H__ -#define __HIREDIS_MACOSX_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct { - redisAsyncContext *context; - CFSocketRef socketRef; - CFRunLoopSourceRef sourceRef; -} RedisRunLoop; - -static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { - if( redisRunLoop != NULL ) { - if( redisRunLoop->sourceRef != NULL ) { - CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); - CFRelease(redisRunLoop->sourceRef); - } - if( redisRunLoop->socketRef != NULL ) { - CFSocketInvalidate(redisRunLoop->socketRef); - CFRelease(redisRunLoop->socketRef); - } - free(redisRunLoop); - } - return REDIS_ERR; -} - -static void redisMacOSAddRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSDelRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSAddWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSDelWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSCleanup(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - freeRedisRunLoop(redisRunLoop); -} - -static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { - redisAsyncContext* context = (redisAsyncContext*) info; - - switch (callbackType) { - case kCFSocketReadCallBack: - redisAsyncHandleRead(context); - break; - - case kCFSocketWriteCallBack: - redisAsyncHandleWrite(context); - break; - - default: - break; - } -} - -static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { - redisContext *redisCtx = &(redisAsyncCtx->c); - - /* Nothing should be attached when something is already attached */ - if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; - - RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop)); - if( !redisRunLoop ) return REDIS_ERR; - - /* Setup redis stuff */ - redisRunLoop->context = redisAsyncCtx; - - redisAsyncCtx->ev.addRead = redisMacOSAddRead; - redisAsyncCtx->ev.delRead = redisMacOSDelRead; - redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; - redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; - redisAsyncCtx->ev.cleanup = redisMacOSCleanup; - redisAsyncCtx->ev.data = redisRunLoop; - - /* Initialize and install read/write events */ - CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; - - redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - redisMacOSAsyncCallback, - &socketCtx); - if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); - - redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); - if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); - - CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); - - return REDIS_OK; -} - -#endif - diff --git a/thirdparty/hiredis/adapters/qt.h b/thirdparty/hiredis/adapters/qt.h deleted file mode 100644 index 5cc02e6ce5e..00000000000 --- a/thirdparty/hiredis/adapters/qt.h +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * Copyright (C) 2014 Pietro Cerutti - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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. - */ - -#ifndef __HIREDIS_QT_H__ -#define __HIREDIS_QT_H__ -#include -#include "../async.h" - -static void RedisQtAddRead(void *); -static void RedisQtDelRead(void *); -static void RedisQtAddWrite(void *); -static void RedisQtDelWrite(void *); -static void RedisQtCleanup(void *); - -class RedisQtAdapter : public QObject { - - Q_OBJECT - - friend - void RedisQtAddRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addRead(); - } - - friend - void RedisQtDelRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delRead(); - } - - friend - void RedisQtAddWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addWrite(); - } - - friend - void RedisQtDelWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delWrite(); - } - - friend - void RedisQtCleanup(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->cleanup(); - } - - public: - RedisQtAdapter(QObject * parent = 0) - : QObject(parent), m_ctx(0), m_read(0), m_write(0) { } - - ~RedisQtAdapter() { - if (m_ctx != 0) { - m_ctx->ev.data = NULL; - } - } - - int setContext(redisAsyncContext * ac) { - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - m_ctx = ac; - m_ctx->ev.data = this; - m_ctx->ev.addRead = RedisQtAddRead; - m_ctx->ev.delRead = RedisQtDelRead; - m_ctx->ev.addWrite = RedisQtAddWrite; - m_ctx->ev.delWrite = RedisQtDelWrite; - m_ctx->ev.cleanup = RedisQtCleanup; - return REDIS_OK; - } - - private: - void addRead() { - if (m_read) return; - m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); - connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); - } - - void delRead() { - if (!m_read) return; - delete m_read; - m_read = 0; - } - - void addWrite() { - if (m_write) return; - m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); - connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); - } - - void delWrite() { - if (!m_write) return; - delete m_write; - m_write = 0; - } - - void cleanup() { - delRead(); - delWrite(); - } - - private slots: - void read() { redisAsyncHandleRead(m_ctx); } - void write() { redisAsyncHandleWrite(m_ctx); } - - private: - redisAsyncContext * m_ctx; - QSocketNotifier * m_read; - QSocketNotifier * m_write; -}; - -#endif /* !__HIREDIS_QT_H__ */ diff --git a/thirdparty/hiredis/alloc.c b/thirdparty/hiredis/alloc.c new file mode 100644 index 00000000000..7fb6b35e778 --- /dev/null +++ b/thirdparty/hiredis/alloc.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#include "fmacros.h" +#include "alloc.h" +#include +#include + +hiredisAllocFuncs hiredisAllocFns = { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, +}; + +/* Override hiredis' allocators with ones supplied by the user */ +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { + hiredisAllocFuncs orig = hiredisAllocFns; + + hiredisAllocFns = *override; + + return orig; +} + +/* Reset allocators to use libc defaults */ +void hiredisResetAllocators(void) { + hiredisAllocFns = (hiredisAllocFuncs) { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, + }; +} + +#ifdef _WIN32 + +void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#endif diff --git a/thirdparty/hiredis/alloc.h b/thirdparty/hiredis/alloc.h new file mode 100644 index 00000000000..34a05f49f31 --- /dev/null +++ b/thirdparty/hiredis/alloc.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#ifndef HIREDIS_ALLOC_H +#define HIREDIS_ALLOC_H + +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure pointing to our actually configured allocators */ +typedef struct hiredisAllocFuncs { + void *(*mallocFn)(size_t); + void *(*callocFn)(size_t,size_t); + void *(*reallocFn)(void*,size_t); + char *(*strdupFn)(const char*); + void (*freeFn)(void*); +} hiredisAllocFuncs; + +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); +void hiredisResetAllocators(void); + +#ifndef _WIN32 + +/* Hiredis' configured allocator function pointer struct */ +extern hiredisAllocFuncs hiredisAllocFns; + +static inline void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +static inline void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +static inline void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +static inline char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +static inline void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#else + +void *hi_malloc(size_t size); +void *hi_calloc(size_t nmemb, size_t size); +void *hi_realloc(void *ptr, size_t size); +char *hi_strdup(const char *str); +void hi_free(void *ptr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HIREDIS_ALLOC_H */ diff --git a/thirdparty/hiredis/appveyor.yml b/thirdparty/hiredis/appveyor.yml deleted file mode 100644 index 819efbd58b3..00000000000 --- a/thirdparty/hiredis/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) -environment: - matrix: - - CYG_BASH: C:\cygwin64\bin\bash - CC: gcc - - CYG_BASH: C:\cygwin\bin\bash - CC: gcc - TARGET: 32bit - TARGET_VARS: 32bit-vars - -clone_depth: 1 - -# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail -init: - - git config --global core.autocrlf input - -# Install needed build dependencies -install: - - '%CYG_BASH% -lc "cygcheck -dc cygwin"' - -build_script: - - 'echo building...' - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include "async.h" -#include "net.h" -#include "dict.c" -#include "sds.h" - -#define _EL_ADD_READ(ctx) do { \ - if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_READ(ctx) do { \ - if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ - } while(0) -#define _EL_ADD_WRITE(ctx) do { \ - if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_WRITE(ctx) do { \ - if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ - } while(0) -#define _EL_CLEANUP(ctx) do { \ - if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ - } while(0); - -/* Forward declaration of function in hiredis.c */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); - -/* Functions managing dictionary of callbacks for pub/sub. */ -static unsigned int callbackHash(const void *key) { - return dictGenHashFunction((const unsigned char *)key, - sdslen((const sds)key)); -} - -static void *callbackValDup(void *privdata, const void *src) { - ((void) privdata); - redisCallback *dup = malloc(sizeof(*dup)); - memcpy(dup,src,sizeof(*dup)); - return dup; -} - -static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { - int l1, l2; - ((void) privdata); - - l1 = sdslen((const sds)key1); - l2 = sdslen((const sds)key2); - if (l1 != l2) return 0; - return memcmp(key1,key2,l1) == 0; -} - -static void callbackKeyDestructor(void *privdata, void *key) { - ((void) privdata); - sdsfree((sds)key); -} - -static void callbackValDestructor(void *privdata, void *val) { - ((void) privdata); - free(val); -} - -static dictType callbackDict = { - callbackHash, - NULL, - callbackValDup, - callbackKeyCompare, - callbackKeyDestructor, - callbackValDestructor -}; - -static redisAsyncContext *redisAsyncInitialize(redisContext *c) { - redisAsyncContext *ac; - - ac = realloc(c,sizeof(redisAsyncContext)); - if (ac == NULL) - return NULL; - - c = &(ac->c); - - /* The regular connect functions will always set the flag REDIS_CONNECTED. - * For the async API, we want to wait until the first write event is - * received up before setting this flag, so reset it here. */ - c->flags &= ~REDIS_CONNECTED; - - ac->err = 0; - ac->errstr = NULL; - ac->data = NULL; - - ac->ev.data = NULL; - ac->ev.addRead = NULL; - ac->ev.delRead = NULL; - ac->ev.addWrite = NULL; - ac->ev.delWrite = NULL; - ac->ev.cleanup = NULL; - - ac->onConnect = NULL; - ac->onDisconnect = NULL; - - ac->replies.head = NULL; - ac->replies.tail = NULL; - ac->sub.invalid.head = NULL; - ac->sub.invalid.tail = NULL; - ac->sub.channels = dictCreate(&callbackDict,NULL); - ac->sub.patterns = dictCreate(&callbackDict,NULL); - return ac; -} - -/* We want the error field to be accessible directly instead of requiring - * an indirection to the redisContext struct. */ -static void __redisAsyncCopyError(redisAsyncContext *ac) { - if (!ac) - return; - - redisContext *c = &(ac->c); - ac->err = c->err; - ac->errstr = c->errstr; -} - -redisAsyncContext *redisAsyncConnect(const char *ip, int port) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectNonBlock(ip,port); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlock(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectUnix(const char *path) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectUnixNonBlock(path); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { - if (ac->onConnect == NULL) { - ac->onConnect = fn; - - /* The common way to detect an established connection is to wait for - * the first write event to be fired. This assumes the related event - * library functions are already set. */ - _EL_ADD_WRITE(ac); - return REDIS_OK; - } - return REDIS_ERR; -} - -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { - if (ac->onDisconnect == NULL) { - ac->onDisconnect = fn; - return REDIS_OK; - } - return REDIS_ERR; -} - -/* Helper functions to push/shift callbacks */ -static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { - redisCallback *cb; - - /* Copy callback from stack to heap */ - cb = malloc(sizeof(*cb)); - if (cb == NULL) - return REDIS_ERR_OOM; - - if (source != NULL) { - memcpy(cb,source,sizeof(*cb)); - cb->next = NULL; - } - - /* Store callback in list */ - if (list->head == NULL) - list->head = cb; - if (list->tail != NULL) - list->tail->next = cb; - list->tail = cb; - return REDIS_OK; -} - -static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { - redisCallback *cb = list->head; - if (cb != NULL) { - list->head = cb->next; - if (cb == list->tail) - list->tail = NULL; - - /* Copy callback from heap to stack */ - if (target != NULL) - memcpy(target,cb,sizeof(*cb)); - free(cb); - return REDIS_OK; - } - return REDIS_ERR; -} - -static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { - redisContext *c = &(ac->c); - if (cb->fn != NULL) { - c->flags |= REDIS_IN_CALLBACK; - cb->fn(ac,reply,cb->privdata); - c->flags &= ~REDIS_IN_CALLBACK; - } -} - -/* Helper function to free the context. */ -static void __redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb; - dictIterator *it; - dictEntry *de; - - /* Execute pending callbacks with NULL reply. */ - while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Execute callbacks for invalid commands */ - while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Run subscription callbacks callbacks with NULL reply */ - it = dictGetIterator(ac->sub.channels); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.channels); - - it = dictGetIterator(ac->sub.patterns); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.patterns); - - /* Signal event lib to clean up */ - _EL_CLEANUP(ac); - - /* Execute disconnect callback. When redisAsyncFree() initiated destroying - * this context, the status will always be REDIS_OK. */ - if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { - if (c->flags & REDIS_FREEING) { - ac->onDisconnect(ac,REDIS_OK); - } else { - ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); - } - } - - /* Cleanup self */ - redisFree(c); -} - -/* Free the async context. When this function is called from a callback, - * control needs to be returned to redisProcessCallbacks() before actual - * free'ing. To do so, a flag is set on the context which is picked up by - * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ -void redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_FREEING; - if (!(c->flags & REDIS_IN_CALLBACK)) - __redisAsyncFree(ac); -} - -/* Helper function to make the disconnect happen and clean up. */ -static void __redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - /* Make sure error is accessible if there is any */ - __redisAsyncCopyError(ac); - - if (ac->err == 0) { - /* For clean disconnects, there should be no pending callbacks. */ - int ret = __redisShiftCallback(&ac->replies,NULL); - assert(ret == REDIS_ERR); - } else { - /* Disconnection is caused by an error, make sure that pending - * callbacks cannot call new commands. */ - c->flags |= REDIS_DISCONNECTING; - } - - /* For non-clean disconnects, __redisAsyncFree() will execute pending - * callbacks with a NULL-reply. */ - __redisAsyncFree(ac); -} - -/* Tries to do a clean disconnect from Redis, meaning it stops new commands - * from being issued, but tries to flush the output buffer and execute - * callbacks for all remaining replies. When this function is called from a - * callback, there might be more replies and we can safely defer disconnecting - * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately - * when there are no pending callbacks. */ -void redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_DISCONNECTING; - if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) - __redisAsyncDisconnect(ac); -} - -static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { - redisContext *c = &(ac->c); - dict *callbacks; - redisCallback *cb; - dictEntry *de; - int pvariant; - char *stype; - sds sname; - - /* Custom reply functions are not supported for pub/sub. This will fail - * very hard when they are used... */ - if (reply->type == REDIS_REPLY_ARRAY) { - assert(reply->elements >= 2); - assert(reply->element[0]->type == REDIS_REPLY_STRING); - stype = reply->element[0]->str; - pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; - - if (pvariant) - callbacks = ac->sub.patterns; - else - callbacks = ac->sub.channels; - - /* Locate the right callback */ - assert(reply->element[1]->type == REDIS_REPLY_STRING); - sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); - de = dictFind(callbacks,sname); - if (de != NULL) { - cb = dictGetEntryVal(de); - - /* If this is an subscribe reply decrease pending counter. */ - if (strcasecmp(stype+pvariant,"subscribe") == 0) { - cb->pending_subs -= 1; - } - - memcpy(dstcb,cb,sizeof(*dstcb)); - - /* If this is an unsubscribe message, remove it. */ - if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { - if (cb->pending_subs == 0) - dictDelete(callbacks,sname); - - /* If this was the last unsubscribe message, revert to - * non-subscribe mode. */ - assert(reply->element[2]->type == REDIS_REPLY_INTEGER); - - /* Unset subscribed flag only when no pipelined pending subscribe. */ - if (reply->element[2]->integer == 0 - && dictSize(ac->sub.channels) == 0 - && dictSize(ac->sub.patterns) == 0) - c->flags &= ~REDIS_SUBSCRIBED; - } - } - sdsfree(sname); - } else { - /* Shift callback for invalid commands. */ - __redisShiftCallback(&ac->sub.invalid,dstcb); - } - return REDIS_OK; -} - -void redisProcessCallbacks(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb = {NULL, NULL, 0, NULL}; - void *reply = NULL; - int status; - - while((status = redisGetReply(c,&reply)) == REDIS_OK) { - if (reply == NULL) { - /* When the connection is being disconnected and there are - * no more replies, this is the cue to really disconnect. */ - if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0 - && ac->replies.head == NULL) { - __redisAsyncDisconnect(ac); - return; - } - - /* If monitor mode, repush callback */ - if(c->flags & REDIS_MONITORING) { - __redisPushCallback(&ac->replies,&cb); - } - - /* When the connection is not being disconnected, simply stop - * trying to get replies and wait for the next loop tick. */ - break; - } - - /* Even if the context is subscribed, pending regular callbacks will - * get a reply before pub/sub messages arrive. */ - if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { - /* - * A spontaneous reply in a not-subscribed context can be the error - * reply that is sent when a new connection exceeds the maximum - * number of allowed connections on the server side. - * - * This is seen as an error instead of a regular reply because the - * server closes the connection after sending it. - * - * To prevent the error from being overwritten by an EOF error the - * connection is closed here. See issue #43. - * - * Another possibility is that the server is loading its dataset. - * In this case we also want to close the connection, and have the - * user wait until the server is ready to take our request. - */ - if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { - c->err = REDIS_ERR_OTHER; - snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); - c->reader->fn->freeObject(reply); - __redisAsyncDisconnect(ac); - return; - } - /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ - assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); - if(c->flags & REDIS_SUBSCRIBED) - __redisGetSubscribeCallback(ac,reply,&cb); - } - - if (cb.fn != NULL) { - __redisRunCallback(ac,&cb,reply); - c->reader->fn->freeObject(reply); - - /* Proceed with free'ing when redisAsyncFree() was called. */ - if (c->flags & REDIS_FREEING) { - __redisAsyncFree(ac); - return; - } - } else { - /* No callback for this reply. This can either be a NULL callback, - * or there were no callbacks to begin with. Either way, don't - * abort with an error, but simply ignore it because the client - * doesn't know what the server will spit out over the wire. */ - c->reader->fn->freeObject(reply); - } - } - - /* Disconnect when there was an error reading the reply */ - if (status != REDIS_OK) - __redisAsyncDisconnect(ac); -} - -/* Internal helper function to detect socket status the first time a read or - * write event fires. When connecting was not successful, the connect callback - * is called with a REDIS_ERR status and the context is free'd. */ -static int __redisAsyncHandleConnect(redisAsyncContext *ac) { - int completed = 0; - redisContext *c = &(ac->c); - if (redisCheckConnectDone(c, &completed) == REDIS_ERR) { - /* Error! */ - redisCheckSocketError(c); - if (ac->onConnect) ac->onConnect(ac, REDIS_ERR); - __redisAsyncDisconnect(ac); - return REDIS_ERR; - } else if (completed == 1) { - /* connected! */ - if (ac->onConnect) ac->onConnect(ac, REDIS_OK); - c->flags |= REDIS_CONNECTED; - return REDIS_OK; - } else { - return REDIS_OK; - } -} - -/* This function should be called when the socket is readable. - * It processes all replies that can be read and executes their callbacks. - */ -void redisAsyncHandleRead(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferRead(c) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Always re-schedule reads */ - _EL_ADD_READ(ac); - redisProcessCallbacks(ac); - } -} - -void redisAsyncHandleWrite(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - int done = 0; - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferWrite(c,&done) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Continue writing when not done, stop writing otherwise */ - if (!done) - _EL_ADD_WRITE(ac); - else - _EL_DEL_WRITE(ac); - - /* Always schedule reads after writes */ - _EL_ADD_READ(ac); - } -} - -/* Sets a pointer to the first argument and its length starting at p. Returns - * the number of bytes to skip to get to the following argument. */ -static const char *nextArgument(const char *start, const char **str, size_t *len) { - const char *p = start; - if (p[0] != '$') { - p = strchr(p,'$'); - if (p == NULL) return NULL; - } - - *len = (int)strtol(p+1,NULL,10); - p = strchr(p,'\r'); - assert(p); - *str = p+2; - return p+2+(*len)+2; -} - -/* Helper function for the redisAsyncCommand* family of functions. Writes a - * formatted command to the output buffer and registers the provided callback - * function with the context. */ -static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - redisContext *c = &(ac->c); - redisCallback cb; - struct dict *cbdict; - dictEntry *de; - redisCallback *existcb; - int pvariant, hasnext; - const char *cstr, *astr; - size_t clen, alen; - const char *p; - sds sname; - int ret; - - /* Don't accept new commands when the connection is about to be closed. */ - if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; - - /* Setup callback */ - cb.fn = fn; - cb.privdata = privdata; - cb.pending_subs = 1; - - /* Find out which command will be appended. */ - p = nextArgument(cmd,&cstr,&clen); - assert(p != NULL); - hasnext = (p[0] == '$'); - pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; - cstr += pvariant; - clen -= pvariant; - - if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { - c->flags |= REDIS_SUBSCRIBED; - - /* Add every channel/pattern to the list of subscription callbacks. */ - while ((p = nextArgument(p,&astr,&alen)) != NULL) { - sname = sdsnewlen(astr,alen); - if (pvariant) - cbdict = ac->sub.patterns; - else - cbdict = ac->sub.channels; - - de = dictFind(cbdict,sname); - - if (de != NULL) { - existcb = dictGetEntryVal(de); - cb.pending_subs = existcb->pending_subs + 1; - } - - ret = dictReplace(cbdict,sname,&cb); - - if (ret == 0) sdsfree(sname); - } - } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { - /* It is only useful to call (P)UNSUBSCRIBE when the context is - * subscribed to one or more channels or patterns. */ - if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; - - /* (P)UNSUBSCRIBE does not have its own response: every channel or - * pattern that is unsubscribed will receive a message. This means we - * should not append a callback function for this command. */ - } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { - /* Set monitor flag and push callback */ - c->flags |= REDIS_MONITORING; - __redisPushCallback(&ac->replies,&cb); - } else { - if (c->flags & REDIS_SUBSCRIBED) - /* This will likely result in an error reply, but it needs to be - * received and passed to the callback. */ - __redisPushCallback(&ac->sub.invalid,&cb); - else - __redisPushCallback(&ac->replies,&cb); - } - - __redisAppendCommand(c,cmd,len); - - /* Always schedule a write when the write buffer is non-empty */ - _EL_ADD_WRITE(ac); - - return REDIS_OK; -} - -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { - char *cmd; - int len; - int status; - len = redisvFormatCommand(&cmd,format,ap); - - /* We don't want to pass -1 or -2 to future functions as a length. */ - if (len < 0) - return REDIS_ERR; - - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - free(cmd); - return status; -} - -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { - va_list ap; - int status; - va_start(ap,format); - status = redisvAsyncCommand(ac,fn,privdata,format,ap); - va_end(ap); - return status; -} - -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - int status; - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len < 0) - return REDIS_ERR; - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - sdsfree(cmd); - return status; -} - -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - int status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - return status; -} diff --git a/thirdparty/hiredis/async.h b/thirdparty/hiredis/async.h deleted file mode 100644 index 740555c24ca..00000000000 --- a/thirdparty/hiredis/async.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __HIREDIS_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Address used for connect() */ - struct sockaddr *saddr; - size_t addrlen; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/thirdparty/hiredis/dict.c b/thirdparty/hiredis/dict.c deleted file mode 100644 index e17a625461a..00000000000 --- a/thirdparty/hiredis/dict.c +++ /dev/null @@ -1,338 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#include "fmacros.h" -#include -#include -#include -#include "dict.h" - -/* -------------------------- private prototypes ---------------------------- */ - -static int _dictExpandIfNeeded(dict *ht); -static unsigned long _dictNextPower(unsigned long size); -static int _dictKeyIndex(dict *ht, const void *key); -static int _dictInit(dict *ht, dictType *type, void *privDataPtr); - -/* -------------------------- hash functions -------------------------------- */ - -/* Generic hash function (a popular one from Bernstein). - * I tested a few and this was the best. */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { - unsigned int hash = 5381; - - while (len--) - hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ - return hash; -} - -/* ----------------------------- API implementation ------------------------- */ - -/* Reset an hashtable already initialized with ht_init(). - * NOTE: This function should only called by ht_destroy(). */ -static void _dictReset(dict *ht) { - ht->table = NULL; - ht->size = 0; - ht->sizemask = 0; - ht->used = 0; -} - -/* Create a new hash table */ -static dict *dictCreate(dictType *type, void *privDataPtr) { - dict *ht = malloc(sizeof(*ht)); - _dictInit(ht,type,privDataPtr); - return ht; -} - -/* Initialize the hash table */ -static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { - _dictReset(ht); - ht->type = type; - ht->privdata = privDataPtr; - return DICT_OK; -} - -/* Expand or create the hashtable */ -static int dictExpand(dict *ht, unsigned long size) { - dict n; /* the new hashtable */ - unsigned long realsize = _dictNextPower(size), i; - - /* the size is invalid if it is smaller than the number of - * elements already inside the hashtable */ - if (ht->used > size) - return DICT_ERR; - - _dictInit(&n, ht->type, ht->privdata); - n.size = realsize; - n.sizemask = realsize-1; - n.table = calloc(realsize,sizeof(dictEntry*)); - - /* Copy all the elements from the old to the new table: - * note that if the old hash table is empty ht->size is zero, - * so dictExpand just creates an hash table. */ - n.used = ht->used; - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if (ht->table[i] == NULL) continue; - - /* For each hash entry on this slot... */ - he = ht->table[i]; - while(he) { - unsigned int h; - - nextHe = he->next; - /* Get the new element index */ - h = dictHashKey(ht, he->key) & n.sizemask; - he->next = n.table[h]; - n.table[h] = he; - ht->used--; - /* Pass to the next element */ - he = nextHe; - } - } - assert(ht->used == 0); - free(ht->table); - - /* Remap the new hashtable in the old */ - *ht = n; - return DICT_OK; -} - -/* Add an element to the target hash table */ -static int dictAdd(dict *ht, void *key, void *val) { - int index; - dictEntry *entry; - - /* Get the index of the new element, or -1 if - * the element already exists. */ - if ((index = _dictKeyIndex(ht, key)) == -1) - return DICT_ERR; - - /* Allocates the memory and stores key */ - entry = malloc(sizeof(*entry)); - entry->next = ht->table[index]; - ht->table[index] = entry; - - /* Set the hash entry fields. */ - dictSetHashKey(ht, entry, key); - dictSetHashVal(ht, entry, val); - ht->used++; - return DICT_OK; -} - -/* Add an element, discarding the old if the key already exists. - * Return 1 if the key was added from scratch, 0 if there was already an - * element with such key and dictReplace() just performed a value update - * operation. */ -static int dictReplace(dict *ht, void *key, void *val) { - dictEntry *entry, auxentry; - - /* Try to add the element. If the key - * does not exists dictAdd will succeed. */ - if (dictAdd(ht, key, val) == DICT_OK) - return 1; - /* It already exists, get the entry */ - entry = dictFind(ht, key); - /* Free the old value and set the new one */ - /* Set the new value and free the old one. Note that it is important - * to do that in this order, as the value may just be exactly the same - * as the previous one. In this context, think to reference counting, - * you want to increment (set), and then decrement (free), and not the - * reverse. */ - auxentry = *entry; - dictSetHashVal(ht, entry, val); - dictFreeEntryVal(ht, &auxentry); - return 0; -} - -/* Search and remove an element */ -static int dictDelete(dict *ht, const void *key) { - unsigned int h; - dictEntry *de, *prevde; - - if (ht->size == 0) - return DICT_ERR; - h = dictHashKey(ht, key) & ht->sizemask; - de = ht->table[h]; - - prevde = NULL; - while(de) { - if (dictCompareHashKeys(ht,key,de->key)) { - /* Unlink the element from the list */ - if (prevde) - prevde->next = de->next; - else - ht->table[h] = de->next; - - dictFreeEntryKey(ht,de); - dictFreeEntryVal(ht,de); - free(de); - ht->used--; - return DICT_OK; - } - prevde = de; - de = de->next; - } - return DICT_ERR; /* not found */ -} - -/* Destroy an entire hash table */ -static int _dictClear(dict *ht) { - unsigned long i; - - /* Free all the elements */ - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if ((he = ht->table[i]) == NULL) continue; - while(he) { - nextHe = he->next; - dictFreeEntryKey(ht, he); - dictFreeEntryVal(ht, he); - free(he); - ht->used--; - he = nextHe; - } - } - /* Free the table and the allocated cache structure */ - free(ht->table); - /* Re-initialize the table */ - _dictReset(ht); - return DICT_OK; /* never fails */ -} - -/* Clear & Release the hash table */ -static void dictRelease(dict *ht) { - _dictClear(ht); - free(ht); -} - -static dictEntry *dictFind(dict *ht, const void *key) { - dictEntry *he; - unsigned int h; - - if (ht->size == 0) return NULL; - h = dictHashKey(ht, key) & ht->sizemask; - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return he; - he = he->next; - } - return NULL; -} - -static dictIterator *dictGetIterator(dict *ht) { - dictIterator *iter = malloc(sizeof(*iter)); - - iter->ht = ht; - iter->index = -1; - iter->entry = NULL; - iter->nextEntry = NULL; - return iter; -} - -static dictEntry *dictNext(dictIterator *iter) { - while (1) { - if (iter->entry == NULL) { - iter->index++; - if (iter->index >= - (signed)iter->ht->size) break; - iter->entry = iter->ht->table[iter->index]; - } else { - iter->entry = iter->nextEntry; - } - if (iter->entry) { - /* We need to save the 'next' here, the iterator user - * may delete the entry we are returning. */ - iter->nextEntry = iter->entry->next; - return iter->entry; - } - } - return NULL; -} - -static void dictReleaseIterator(dictIterator *iter) { - free(iter); -} - -/* ------------------------- private functions ------------------------------ */ - -/* Expand the hash table if needed */ -static int _dictExpandIfNeeded(dict *ht) { - /* If the hash table is empty expand it to the initial size, - * if the table is "full" dobule its size. */ - if (ht->size == 0) - return dictExpand(ht, DICT_HT_INITIAL_SIZE); - if (ht->used == ht->size) - return dictExpand(ht, ht->size*2); - return DICT_OK; -} - -/* Our hash table capability is a power of two */ -static unsigned long _dictNextPower(unsigned long size) { - unsigned long i = DICT_HT_INITIAL_SIZE; - - if (size >= LONG_MAX) return LONG_MAX; - while(1) { - if (i >= size) - return i; - i *= 2; - } -} - -/* Returns the index of a free slot that can be populated with - * an hash entry for the given 'key'. - * If the key already exists, -1 is returned. */ -static int _dictKeyIndex(dict *ht, const void *key) { - unsigned int h; - dictEntry *he; - - /* Expand the hashtable if needed */ - if (_dictExpandIfNeeded(ht) == DICT_ERR) - return -1; - /* Compute the key hash value */ - h = dictHashKey(ht, key) & ht->sizemask; - /* Search if this slot does not already contain the given key */ - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return -1; - he = he->next; - } - return h; -} - diff --git a/thirdparty/hiredis/dict.h b/thirdparty/hiredis/dict.h deleted file mode 100644 index 95fcd280e2c..00000000000 --- a/thirdparty/hiredis/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 Redis 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. - */ - -#ifndef __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/thirdparty/hiredis/examples/example-ae.c b/thirdparty/hiredis/examples/example-ae.c deleted file mode 100644 index 8efa7306aec..00000000000 --- a/thirdparty/hiredis/examples/example-ae.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -/* Put event loop in the global scope, so it can be explicitly stopped */ -static aeEventLoop *loop; - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Disconnected...\n"); - aeStop(loop); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - loop = aeCreateEventLoop(64); - redisAeAttach(loop, c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - aeMain(loop); - return 0; -} - diff --git a/thirdparty/hiredis/examples/example-glib.c b/thirdparty/hiredis/examples/example-glib.c deleted file mode 100644 index d6e10f8e820..00000000000 --- a/thirdparty/hiredis/examples/example-glib.c +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#include -#include -#include - -static GMainLoop *mainloop; - -static void -connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_printerr("Failed to connect: %s\n", ac->errstr); - g_main_loop_quit(mainloop); - } else { - g_printerr("Connected...\n"); - } -} - -static void -disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_error("Failed to disconnect: %s", ac->errstr); - } else { - g_printerr("Disconnected...\n"); - g_main_loop_quit(mainloop); - } -} - -static void -command_cb(redisAsyncContext *ac, - gpointer r, - gpointer user_data G_GNUC_UNUSED) -{ - redisReply *reply = r; - - if (reply) { - g_print("REPLY: %s\n", reply->str); - } - - redisAsyncDisconnect(ac); -} - -gint -main (gint argc G_GNUC_UNUSED, - gchar *argv[] G_GNUC_UNUSED) -{ - redisAsyncContext *ac; - GMainContext *context = NULL; - GSource *source; - - ac = redisAsyncConnect("127.0.0.1", 6379); - if (ac->err) { - g_printerr("%s\n", ac->errstr); - exit(EXIT_FAILURE); - } - - source = redis_source_new(ac); - mainloop = g_main_loop_new(context, FALSE); - g_source_attach(source, context); - - redisAsyncSetConnectCallback(ac, connect_cb); - redisAsyncSetDisconnectCallback(ac, disconnect_cb); - redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); - redisAsyncCommand(ac, command_cb, NULL, "GET key"); - - g_main_loop_run(mainloop); - - return EXIT_SUCCESS; -} diff --git a/thirdparty/hiredis/examples/example-ivykis.c b/thirdparty/hiredis/examples/example-ivykis.c deleted file mode 100644 index 67affcef365..00000000000 --- a/thirdparty/hiredis/examples/example-ivykis.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - iv_init(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisIvykisAttach(c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - iv_main(); - - iv_deinit(); - - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libev.c b/thirdparty/hiredis/examples/example-libev.c deleted file mode 100644 index cc8b166ec62..00000000000 --- a/thirdparty/hiredis/examples/example-libev.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibevAttach(EV_DEFAULT_ c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - ev_loop(EV_DEFAULT_ 0); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libevent.c b/thirdparty/hiredis/examples/example-libevent.c deleted file mode 100644 index d333c22b79e..00000000000 --- a/thirdparty/hiredis/examples/example-libevent.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - struct event_base *base = event_base_new(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibeventAttach(c,base); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - event_base_dispatch(base); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libuv.c b/thirdparty/hiredis/examples/example-libuv.c deleted file mode 100644 index a5462d41051..00000000000 --- a/thirdparty/hiredis/examples/example-libuv.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - uv_loop_t* loop = uv_default_loop(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibuvAttach(c,loop); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - uv_run(loop, UV_RUN_DEFAULT); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-macosx.c b/thirdparty/hiredis/examples/example-macosx.c deleted file mode 100644 index bc84ed5ba76..00000000000 --- a/thirdparty/hiredis/examples/example-macosx.c +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - CFRunLoopStop(CFRunLoopGetCurrent()); - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - CFRunLoopRef loop = CFRunLoopGetCurrent(); - if( !loop ) { - printf("Error: Cannot get current run loop\n"); - return 1; - } - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisMacOSAttach(c, loop); - - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - CFRunLoopRun(); - - return 0; -} - diff --git a/thirdparty/hiredis/examples/example-qt.cpp b/thirdparty/hiredis/examples/example-qt.cpp deleted file mode 100644 index f524c3f3dde..00000000000 --- a/thirdparty/hiredis/examples/example-qt.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -using namespace std; - -#include -#include - -#include "example-qt.h" - -void getCallback(redisAsyncContext *, void * r, void * privdata) { - - redisReply * reply = static_cast(r); - ExampleQt * ex = static_cast(privdata); - if (reply == nullptr || ex == nullptr) return; - - cout << "key: " << reply->str << endl; - - ex->finish(); -} - -void ExampleQt::run() { - - m_ctx = redisAsyncConnect("localhost", 6379); - - if (m_ctx->err) { - cerr << "Error: " << m_ctx->errstr << endl; - redisAsyncFree(m_ctx); - emit finished(); - } - - m_adapter.setContext(m_ctx); - - redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); - redisAsyncCommand(m_ctx, getCallback, this, "GET key"); -} - -int main (int argc, char **argv) { - - QCoreApplication app(argc, argv); - - ExampleQt example(argv[argc-1]); - - QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); - QTimer::singleShot(0, &example, SLOT(run())); - - return app.exec(); -} diff --git a/thirdparty/hiredis/examples/example-qt.h b/thirdparty/hiredis/examples/example-qt.h deleted file mode 100644 index 374f47666bb..00000000000 --- a/thirdparty/hiredis/examples/example-qt.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __HIREDIS_EXAMPLE_QT_H -#define __HIREDIS_EXAMPLE_QT_H - -#include - -class ExampleQt : public QObject { - - Q_OBJECT - - public: - ExampleQt(const char * value, QObject * parent = 0) - : QObject(parent), m_value(value) {} - - signals: - void finished(); - - public slots: - void run(); - - private: - void finish() { emit finished(); } - - private: - const char * m_value; - redisAsyncContext * m_ctx; - RedisQtAdapter m_adapter; - - friend - void getCallback(redisAsyncContext *, void *, void *); -}; - -#endif /* !__HIREDIS_EXAMPLE_QT_H */ diff --git a/thirdparty/hiredis/examples/example.c b/thirdparty/hiredis/examples/example.c deleted file mode 100644 index 4d494c55ab6..00000000000 --- a/thirdparty/hiredis/examples/example.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include - -#include - -int main(int argc, char **argv) { - unsigned int j; - redisContext *c; - redisReply *reply; - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - int port = (argc > 2) ? atoi(argv[2]) : 6379; - - struct timeval timeout = { 1, 500000 }; // 1.5 seconds - c = redisConnectWithTimeout(hostname, port, timeout); - if (c == NULL || c->err) { - if (c) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - } else { - printf("Connection error: can't allocate redis context\n"); - } - exit(1); - } - - /* PING server */ - reply = redisCommand(c,"PING"); - printf("PING: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key */ - reply = redisCommand(c,"SET %s %s", "foo", "hello world"); - printf("SET: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key using binary safe API */ - reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); - printf("SET (binary API): %s\n", reply->str); - freeReplyObject(reply); - - /* Try a GET and two INCR */ - reply = redisCommand(c,"GET foo"); - printf("GET foo: %s\n", reply->str); - freeReplyObject(reply); - - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - /* again ... */ - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - - /* Create a list of numbers, from 0 to 9 */ - reply = redisCommand(c,"DEL mylist"); - freeReplyObject(reply); - for (j = 0; j < 10; j++) { - char buf[64]; - - snprintf(buf,64,"%u",j); - reply = redisCommand(c,"LPUSH mylist element-%s", buf); - freeReplyObject(reply); - } - - /* Let's check what we have inside the list */ - reply = redisCommand(c,"LRANGE mylist 0 -1"); - if (reply->type == REDIS_REPLY_ARRAY) { - for (j = 0; j < reply->elements; j++) { - printf("%u) %s\n", j, reply->element[j]->str); - } - } - freeReplyObject(reply); - - /* Disconnects and frees the context */ - redisFree(c); - - return 0; -} diff --git a/thirdparty/hiredis/hiredis.c b/thirdparty/hiredis/hiredis.c index 0a6c25b9720..41298addcd0 100644 --- a/thirdparty/hiredis/hiredis.c +++ b/thirdparty/hiredis/hiredis.c @@ -34,7 +34,6 @@ #include "fmacros.h" #include #include -#include #include #include #include @@ -43,13 +42,25 @@ #include "net.h" #include "sds.h" -#include "socket_hook.h" +#define SW_HOOK_POLL_FAKE +#include "swoole_socket_hook.h" + +extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout); +extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout); + +static redisContextFuncs redisContextDefaultFuncs = { + .free_privctx = NULL, + .read_ = redisNetRead, + .write_ = redisNetWrite +}; static redisReply *createReplyObject(int type); static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, int elements); +static void *createArrayObject(const redisReadTask *task, size_t elements); static void *createIntegerObject(const redisReadTask *task, long long value); +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len); static void *createNilObject(const redisReadTask *task); +static void *createBoolObject(const redisReadTask *task, int bval); /* Default set of functions to build the reply. Keep in mind that such a * function returning NULL is interpreted as OOM. */ @@ -57,13 +68,15 @@ static redisReplyObjectFunctions defaultFunctions = { createStringObject, createArrayObject, createIntegerObject, + createDoubleObject, createNilObject, + createBoolObject, freeReplyObject }; /* Create a reply object */ static redisReply *createReplyObject(int type) { - redisReply *r = calloc(1,sizeof(*r)); + redisReply *r = hi_calloc(1,sizeof(*r)); if (r == NULL) return NULL; @@ -82,21 +95,29 @@ void freeReplyObject(void *reply) { switch(r->type) { case REDIS_REPLY_INTEGER: + case REDIS_REPLY_NIL: + case REDIS_REPLY_BOOL: break; /* Nothing to free */ case REDIS_REPLY_ARRAY: + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: if (r->element != NULL) { for (j = 0; j < r->elements; j++) freeReplyObject(r->element[j]); - free(r->element); + hi_free(r->element); } break; case REDIS_REPLY_ERROR: case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: - free(r->str); + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_VERB: + case REDIS_REPLY_BIGNUM: + hi_free(r->str); break; } - free(r); + hi_free(r); } static void *createStringObject(const redisReadTask *task, char *str, size_t len) { @@ -107,39 +128,56 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len if (r == NULL) return NULL; - buf = malloc(len+1); - if (buf == NULL) { - freeReplyObject(r); - return NULL; - } - assert(task->type == REDIS_REPLY_ERROR || task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING); + task->type == REDIS_REPLY_STRING || + task->type == REDIS_REPLY_VERB || + task->type == REDIS_REPLY_BIGNUM); /* Copy string value */ - memcpy(buf,str,len); - buf[len] = '\0'; + if (task->type == REDIS_REPLY_VERB) { + buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */ + if (buf == NULL) goto oom; + + memcpy(r->vtype,str,3); + r->vtype[3] = '\0'; + memcpy(buf,str+4,len-4); + buf[len-4] = '\0'; + r->len = len - 4; + } else { + buf = hi_malloc(len+1); + if (buf == NULL) goto oom; + + memcpy(buf,str,len); + buf[len] = '\0'; + r->len = len; + } r->str = buf; - r->len = len; if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; + +oom: + freeReplyObject(r); + return NULL; } -static void *createArrayObject(const redisReadTask *task, int elements) { +static void *createArrayObject(const redisReadTask *task, size_t elements) { redisReply *r, *parent; - r = createReplyObject(REDIS_REPLY_ARRAY); + r = createReplyObject(task->type); if (r == NULL) return NULL; if (elements > 0) { - r->element = calloc(elements,sizeof(redisReply*)); + r->element = hi_calloc(elements,sizeof(redisReply*)); if (r->element == NULL) { freeReplyObject(r); return NULL; @@ -150,7 +188,10 @@ static void *createArrayObject(const redisReadTask *task, int elements) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -167,7 +208,44 @@ static void *createIntegerObject(const redisReadTask *task, long long value) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_DOUBLE); + if (r == NULL) + return NULL; + + r->dval = value; + r->str = hi_malloc(len+1); + if (r->str == NULL) { + freeReplyObject(r); + return NULL; + } + + /* The double reply also has the original protocol string representing a + * double as a null terminated string. This way the caller does not need + * to format back for string conversion, especially since Redis does efforts + * to make the string more human readable avoiding the calssical double + * decimal string conversion artifacts. */ + memcpy(r->str, str, len); + r->str[len] = '\0'; + r->len = len; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -182,7 +260,30 @@ static void *createNilObject(const redisReadTask *task) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createBoolObject(const redisReadTask *task, int bval) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_BOOL); + if (r == NULL) + return NULL; + + r->integer = bval != 0; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -200,6 +301,7 @@ static uint32_t countDigits(uint64_t v) { v /= 10000U; result += 4; } + return result; } /* Helper that calculates the bulk length given a certain string length. */ @@ -232,7 +334,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { if (*c != '%' || c[1] == '\0') { if (*c == ' ') { if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto memory_err; curargv = newargv; curargv[argc++] = curarg; @@ -381,7 +483,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { /* Add the last argument if needed */ if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto memory_err; curargv = newargv; curargv[argc++] = curarg; @@ -397,7 +499,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { totlen += 1+countDigits(argc)+2; /* Build the command at protocol level */ - cmd = malloc(totlen+1); + cmd = hi_malloc(totlen+1); if (cmd == NULL) goto memory_err; pos = sprintf(cmd,"*%d\r\n",argc); @@ -412,7 +514,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { assert(pos == totlen); cmd[pos] = '\0'; - free(curargv); + hi_free(curargv); *target = cmd; return totlen; @@ -428,11 +530,11 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { if (curargv) { while(argc--) sdsfree(curargv[argc]); - free(curargv); + hi_free(curargv); } sdsfree(curarg); - free(cmd); + hi_free(cmd); return error_type; } @@ -473,7 +575,7 @@ int redisFormatCommand(char **target, const char *format, ...) { int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, const size_t *argvlen) { - sds cmd; + sds cmd, aux; unsigned long long totlen; int j; size_t len; @@ -495,9 +597,13 @@ int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, return -1; /* We already know how much storage we need */ - cmd = sdsMakeRoomFor(cmd, totlen); - if (cmd == NULL) + aux = sdsMakeRoomFor(cmd, totlen); + if (aux == NULL) { + sdsfree(cmd); return -1; + } + + cmd = aux; /* Construct command */ cmd = sdscatfmt(cmd, "*%i\r\n", argc); @@ -541,7 +647,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz } /* Build the command at protocol level */ - cmd = malloc(totlen+1); + cmd = hi_malloc(totlen+1); if (cmd == NULL) return -1; @@ -562,7 +668,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz } void redisFreeCommand(char *cmd) { - free(cmd); + hi_free(cmd); } void __redisSetError(redisContext *c, int type, const char *str) { @@ -585,15 +691,23 @@ redisReader *redisReaderCreate(void) { return redisReaderCreateWithFunctions(&defaultFunctions); } +static void redisPushAutoFree(void *privdata, void *reply) { + (void)privdata; + freeReplyObject(reply); +} + static redisContext *redisContextInit(void) { redisContext *c; - c = calloc(1,sizeof(redisContext)); + c = hi_calloc(1, sizeof(*c)); if (c == NULL) return NULL; + c->funcs = &redisContextDefaultFuncs; + c->obuf = sdsempty(); c->reader = redisReaderCreate(); + c->fd = REDIS_INVALID_FD; if (c->obuf == NULL || c->reader == NULL) { redisFree(c); @@ -606,22 +720,30 @@ static redisContext *redisContextInit(void) { void redisFree(redisContext *c) { if (c == NULL) return; - if (c->fd > 0) - close(c->fd); + redisNetClose(c); sdsfree(c->obuf); redisReaderFree(c->reader); - free(c->tcp.host); - free(c->tcp.source_addr); - free(c->unix_sock.path); - free(c->timeout); - free(c->saddr); - free(c); + hi_free(c->tcp.host); + hi_free(c->tcp.source_addr); + hi_free(c->unix_sock.path); + hi_free(c->connect_timeout); + hi_free(c->command_timeout); + hi_free(c->saddr); + + if (c->privdata && c->free_privdata) + c->free_privdata(c->privdata); + + if (c->funcs->free_privctx) + c->funcs->free_privctx(c->privctx); + + memset(c, 0xff, sizeof(*c)); + hi_free(c); } -int redisFreeKeepFd(redisContext *c) { - int fd = c->fd; - c->fd = -1; +redisFD redisFreeKeepFd(redisContext *c) { + redisFD fd = c->fd; + c->fd = REDIS_INVALID_FD; redisFree(c); return fd; } @@ -630,136 +752,163 @@ int redisReconnect(redisContext *c) { c->err = 0; memset(c->errstr, '\0', strlen(c->errstr)); - if (c->fd > 0) { - close(c->fd); + if (c->privctx && c->funcs->free_privctx) { + c->funcs->free_privctx(c->privctx); + c->privctx = NULL; } + redisNetClose(c); + sdsfree(c->obuf); redisReaderFree(c->reader); c->obuf = sdsempty(); c->reader = redisReaderCreate(); + if (c->obuf == NULL || c->reader == NULL) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; + } + + int ret = REDIS_ERR; if (c->connection_type == REDIS_CONN_TCP) { - return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, - c->timeout, c->tcp.source_addr); + ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, + c->connect_timeout, c->tcp.source_addr); } else if (c->connection_type == REDIS_CONN_UNIX) { - return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); + ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout); } else { /* Something bad happened here and shouldn't have. There isn't enough information in the context to reconnect. */ __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); + ret = REDIS_ERR; } - return REDIS_ERR; -} + if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *c->command_timeout); + } -/* Connect to a Redis instance. On error the field error in the returned - * context will be set to the return value of the error function. - * When no set of reply functions is given, the default set will be used. */ -redisContext *redisConnect(const char *ip, int port) { - redisContext *c; + return ret; +} - c = redisContextInit(); - if (c == NULL) +redisContext *redisConnectWithOptions(const redisOptions *options) { + redisContext *c = redisContextInit(); + if (c == NULL) { return NULL; + } + if (!(options->options & REDIS_OPT_NONBLOCK)) { + c->flags |= REDIS_BLOCK; + } + if (options->options & REDIS_OPT_REUSEADDR) { + c->flags |= REDIS_REUSEADDR; + } + if (options->options & REDIS_OPT_NOAUTOFREE) { + c->flags |= REDIS_NO_AUTO_FREE; + } - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} + /* Set any user supplied RESP3 PUSH handler or use freeReplyObject + * as a default unless specifically flagged that we don't want one. */ + if (options->push_cb != NULL) + redisSetPushCallback(c, options->push_cb); + else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE)) + redisSetPushCallback(c, redisPushAutoFree); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { - redisContext *c; + c->privdata = options->privdata; + c->free_privdata = options->free_privdata; - c = redisContextInit(); - if (c == NULL) + if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK || + redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return c; + } + + if (options->type == REDIS_CONN_TCP) { + redisContextConnectBindTcp(c, options->endpoint.tcp.ip, + options->endpoint.tcp.port, options->connect_timeout, + options->endpoint.tcp.source_addr); + } else if (options->type == REDIS_CONN_UNIX) { + redisContextConnectUnix(c, options->endpoint.unix_socket, + options->connect_timeout); + } else if (options->type == REDIS_CONN_USERFD) { + c->fd = options->endpoint.fd; + c->flags |= REDIS_CONNECTED; + } else { + // Unknown type - FIXME - FREE return NULL; + } + + if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *options->command_timeout); + } - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,&tv); return c; } -redisContext *redisConnectNonBlock(const char *ip, int port) { - redisContext *c; +/* Connect to a Redis instance. On error the field error in the returned + * context will be set to the return value of the error function. + * When no set of reply functions is given, the default set will be used. */ +redisContext *redisConnect(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + return redisConnectWithOptions(&options); +} - c = redisContextInit(); - if (c == NULL) - return NULL; +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); +} - c->flags &= ~REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; +redisContext *redisConnectNonBlock(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } redisContext *redisConnectBindNonBlock(const char *ip, int port, const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - c->flags |= REDIS_REUSEADDR; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR; + return redisConnectWithOptions(&options); } redisContext *redisConnectUnix(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + return redisConnectWithOptions(&options); } redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,&tv); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); } redisContext *redisConnectUnixNonBlock(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } -redisContext *redisConnectFd(int fd) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->fd = fd; - c->flags |= REDIS_BLOCK | REDIS_CONNECTED; - return c; +redisContext *redisConnectFd(redisFD fd) { + redisOptions options = {0}; + options.type = REDIS_CONN_USERFD; + options.endpoint.fd = fd; + return redisConnectWithOptions(&options); } /* Set read/write timeout on a blocking socket. */ @@ -776,6 +925,13 @@ int redisEnableKeepAlive(redisContext *c) { return REDIS_OK; } +/* Set a user provided RESP3 PUSH handler and return any old one set. */ +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) { + redisPushFn *old = c->push_cb; + c->push_cb = fn; + return old; +} + /* Use this function to handle a read event on the descriptor. It will try * and read some bytes from the socket and feed them to the reply parser. * @@ -789,22 +945,13 @@ int redisBufferRead(redisContext *c) { if (c->err) return REDIS_ERR; - nread = read(c->fd,buf,sizeof(buf)); - if (nread == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nread == 0) { - __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); + nread = c->funcs->read_(c, buf, sizeof(buf)); + if (nread < 0) { + return REDIS_ERR; + } + if (nread > 0 && redisReaderFeed(c->reader, buf, nread) != REDIS_OK) { + __redisSetError(c, c->reader->err, c->reader->errstr); return REDIS_ERR; - } else { - if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } } return REDIS_OK; } @@ -819,41 +966,64 @@ int redisBufferRead(redisContext *c) { * c->errstr to hold the appropriate error string. */ int redisBufferWrite(redisContext *c, int *done) { - int nwritten; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; if (sdslen(c->obuf) > 0) { - nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); - if (nwritten == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } + ssize_t nwritten = c->funcs->write_(c); + if (nwritten < 0) { + return REDIS_ERR; } else if (nwritten > 0) { - if (nwritten == (signed)sdslen(c->obuf)) { + if (nwritten == (ssize_t)sdslen(c->obuf)) { sdsfree(c->obuf); c->obuf = sdsempty(); + if (c->obuf == NULL) + goto oom; } else { - sdsrange(c->obuf,nwritten,-1); + if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom; } } } if (done != NULL) *done = (sdslen(c->obuf) == 0); return REDIS_OK; + +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} + +/* Internal helper that returns 1 if the reply was a RESP3 PUSH + * message and we handled it with a user-provided callback. */ +static int redisHandledPushReply(redisContext *c, void *reply) { + if (reply && c->push_cb && redisIsPushReply(reply)) { + c->push_cb(c->privdata, reply); + return 1; + } + + return 0; } -/* Internal helper function to try and get a reply from the reader, - * or set an error in the context otherwise. */ +/* Get a reply from our reader or set an error in the context. */ int redisGetReplyFromReader(redisContext *c, void **reply) { - if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { + if (redisReaderGetReply(c->reader, reply) == REDIS_ERR) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } + + return REDIS_OK; +} + +/* Internal helper to get the next reply from our reader while handling + * any PUSH messages we encounter along the way. This is separate from + * redisGetReplyFromReader so as to not change its behavior. */ +static int redisNextInBandReplyFromReader(redisContext *c, void **reply) { + do { + if (redisGetReplyFromReader(c, reply) == REDIS_ERR) + return REDIS_ERR; + } while (redisHandledPushReply(c, *reply)); + return REDIS_OK; } @@ -862,11 +1032,11 @@ int redisGetReply(redisContext *c, void **reply) { void *aux = NULL; /* Try to read pending replies */ - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ - if (aux == NULL && c->flags & REDIS_BLOCK) { + if (aux == NULL && (c->flags & REDIS_BLOCK)) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) @@ -877,13 +1047,19 @@ int redisGetReply(redisContext *c, void **reply) { do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + + if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } - /* Set reply object */ - if (reply != NULL) *reply = aux; + /* Set reply or free it if we were passed NULL */ + if (reply != NULL) { + *reply = aux; + } else { + freeReplyObject(aux); + } + return REDIS_OK; } @@ -930,11 +1106,11 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { } if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - free(cmd); + hi_free(cmd); return REDIS_ERR; } - free(cmd); + hi_free(cmd); return REDIS_OK; } diff --git a/thirdparty/hiredis/hiredis.h b/thirdparty/hiredis/hiredis.h index 1b0d5e65929..dd29f0b1fc3 100644 --- a/thirdparty/hiredis/hiredis.h +++ b/thirdparty/hiredis/hiredis.h @@ -35,14 +35,20 @@ #define __HIREDIS_H #include "read.h" #include /* for va_list */ +#ifndef _MSC_VER #include /* for struct timeval */ +#else +struct timeval; /* forward declaration */ +typedef long long ssize_t; +#endif #include /* uintXX_t, etc */ #include "sds.h" /* for sds */ +#include "alloc.h" /* for allocation wrappers */ -#define HIREDIS_MAJOR 0 -#define HIREDIS_MINOR 14 -#define HIREDIS_PATCH 0 -#define HIREDIS_SONAME 0.14 +#define HIREDIS_MAJOR 1 +#define HIREDIS_MINOR 0 +#define HIREDIS_PATCH 1 +#define HIREDIS_SONAME 1.0.1-dev /* Connection type can be blocking or non-blocking and is set in the * least significant bit of the flags field in redisContext. */ @@ -74,12 +80,27 @@ /* Flag that is set when we should set SO_REUSEADDR before calling bind() */ #define REDIS_REUSEADDR 0x80 +/** + * Flag that indicates the user does not want the context to + * be automatically freed upon error + */ +#define REDIS_NO_AUTO_FREE 0x200 + #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ /* number of times we retry to connect in the case of EADDRNOTAVAIL and * SO_REUSEADDR is being used. */ #define REDIS_CONNECT_RETRIES 10 +/* Forward declarations for structs defined elsewhere */ +struct redisAsyncContext; +struct redisContext; + +/* RESP3 push helpers and callback prototypes */ +#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) +typedef void (redisPushFn)(void *, void *); +typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); + #ifdef __cplusplus extern "C" { #endif @@ -88,8 +109,13 @@ extern "C" { typedef struct redisReply { int type; /* REDIS_REPLY_* */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ + double dval; /* The double when type is REDIS_REPLY_DOUBLE */ size_t len; /* Length of string */ - char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ + char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING + REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval), + and REDIS_REPLY_BIGNUM. */ + char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null + terminated 3 character content type, such as "txt". */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ } redisReply; @@ -109,20 +135,113 @@ void redisFreeSdsCommand(sds cmd); enum redisConnectionType { REDIS_CONN_TCP, - REDIS_CONN_UNIX + REDIS_CONN_UNIX, + REDIS_CONN_USERFD }; +struct redisSsl; + +#define REDIS_OPT_NONBLOCK 0x01 +#define REDIS_OPT_REUSEADDR 0x02 + +/** + * Don't automatically free the async object on a connection failure, + * or other implicit conditions. Only free on an explicit call to disconnect() or free() + */ +#define REDIS_OPT_NOAUTOFREE 0x04 + +/* Don't automatically intercept and free RESP3 PUSH replies. */ +#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 + +/* In Unix systems a file descriptor is a regular signed int, with -1 + * representing an invalid descriptor. In Windows it is a SOCKET + * (32- or 64-bit unsigned integer depending on the architecture), where + * all bits set (~0) is INVALID_SOCKET. */ +#ifndef _WIN32 +typedef int redisFD; +#define REDIS_INVALID_FD -1 +#else +#ifdef _WIN64 +typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ +#else +typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ +#endif +#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ +#endif + +typedef struct { + /* + * the type of connection to use. This also indicates which + * `endpoint` member field to use + */ + int type; + /* bit field of REDIS_OPT_xxx */ + int options; + /* timeout value for connect operation. If NULL, no timeout is used */ + const struct timeval *connect_timeout; + /* timeout value for commands. If NULL, no timeout is used. This can be + * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ + const struct timeval *command_timeout; + union { + /** use this field for tcp/ip connections */ + struct { + const char *source_addr; + const char *ip; + int port; + } tcp; + /** use this field for unix domain sockets */ + const char *unix_socket; + /** + * use this field to have hiredis operate an already-open + * file descriptor */ + redisFD fd; + } endpoint; + + /* Optional user defined data/destructor */ + void *privdata; + void (*free_privdata)(void *); + + /* A user defined PUSH message callback */ + redisPushFn *push_cb; + redisAsyncPushFn *async_push_cb; +} redisOptions; + +/** + * Helper macros to initialize options to their specified fields. + */ +#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ + (opts)->type = REDIS_CONN_TCP; \ + (opts)->endpoint.tcp.ip = ip_; \ + (opts)->endpoint.tcp.port = port_; + +#define REDIS_OPTIONS_SET_UNIX(opts, path) \ + (opts)->type = REDIS_CONN_UNIX; \ + (opts)->endpoint.unix_socket = path; + +#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ + (opts)->privdata = data; \ + (opts)->free_privdata = dtor; \ + +typedef struct redisContextFuncs { + void (*free_privctx)(void *); + ssize_t (*read_)(struct redisContext *, char *, size_t); + ssize_t (*write_)(struct redisContext *); +} redisContextFuncs; + /* Context for a connection to Redis */ typedef struct redisContext { + const redisContextFuncs *funcs; /* Function table */ + int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ - int fd; + redisFD fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */ enum redisConnectionType connection_type; - struct timeval *timeout; + struct timeval *connect_timeout; + struct timeval *command_timeout; struct { char *host; @@ -135,10 +254,23 @@ typedef struct redisContext { } unix_sock; /* For non-blocking connect */ - struct sockadr *saddr; + struct sockaddr *saddr; size_t addrlen; + + /* Optional data and corresponding destructor users can use to provide + * context to a given redisContext. Not used by hiredis. */ + void *privdata; + void (*free_privdata)(void *); + + /* Internal context pointer presently used by hiredis to manage + * SSL connections. */ + void *privctx; + + /* An optional RESP3 PUSH handler */ + redisPushFn *push_cb; } redisContext; +redisContext *redisConnectWithOptions(const redisOptions *options); redisContext *redisConnect(const char *ip, int port); redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); redisContext *redisConnectNonBlock(const char *ip, int port); @@ -149,7 +281,7 @@ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(int fd); +redisContext *redisConnectFd(redisFD fd); /** * Reconnect the given context using the saved information. @@ -162,10 +294,11 @@ redisContext *redisConnectFd(int fd); */ int redisReconnect(redisContext *c); +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); int redisSetTimeout(redisContext *c, const struct timeval tv); int redisEnableKeepAlive(redisContext *c); void redisFree(redisContext *c); -int redisFreeKeepFd(redisContext *c); +redisFD redisFreeKeepFd(redisContext *c); int redisBufferRead(redisContext *c); int redisBufferWrite(redisContext *c, int *done); diff --git a/thirdparty/hiredis/net.c b/thirdparty/hiredis/net.c index 4570c8e9b85..510df7d44a7 100644 --- a/thirdparty/hiredis/net.c +++ b/thirdparty/hiredis/net.c @@ -54,18 +54,57 @@ #include "net.h" #include "sds.h" -#include "socket_hook.h" +#include "swoole_socket_hook.h" /* Defined in hiredis.c */ void __redisSetError(redisContext *c, int type, const char *str); -static void redisContextCloseFd(redisContext *c) { - if (c && c->fd >= 0) { +void redisNetClose(redisContext *c) { + if (c && c->fd != REDIS_INVALID_FD) { close(c->fd); - c->fd = -1; + c->fd = REDIS_INVALID_FD; } } +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { + ssize_t nread = recv(c->fd, buf, bufcap, 0); + if (nread == -1) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + return 0; + } +#if 0 + else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) { + /* especially in windows */ + __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); + return -1; + } +#endif + else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } else if (nread == 0) { + __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); + return -1; + } else { + return nread; + } +} + +ssize_t redisNetWrite(redisContext *c) { + ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0); + if (nwritten < 0) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } + return nwritten; +} + static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { int errorno = errno; /* snprintf() may change errno */ char buf[128] = { 0 }; @@ -81,15 +120,15 @@ static int redisSetReuseAddr(redisContext *c) { int on = 1; if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } return REDIS_OK; } static int redisCreateSocket(redisContext *c, int type) { - int s; - if ((s = socket(type, SOCK_STREAM, 0)) == -1) { + redisFD s; + if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } @@ -103,6 +142,8 @@ static int redisCreateSocket(redisContext *c, int type) { } static int redisSetBlocking(redisContext *c, int blocking) { +#if 0 +#ifndef _WIN32 int flags; /* Set the socket nonblocking. @@ -110,7 +151,7 @@ static int redisSetBlocking(redisContext *c, int blocking) { * interrupted by a signal. */ if ((flags = fcntl(c->fd, F_GETFL)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -121,15 +162,24 @@ static int redisSetBlocking(redisContext *c, int blocking) { if (fcntl(c->fd, F_SETFL, flags) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); - redisContextCloseFd(c); + redisNetClose(c); + return REDIS_ERR; + } +#else + u_long mode = blocking ? 0 : 1; + if (ioctl(c->fd, FIONBIO, &mode) == -1) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)"); + redisNetClose(c); return REDIS_ERR; } +#endif /* _WIN32 */ +#endif return REDIS_OK; } int redisKeepAlive(redisContext *c, int interval) { int val = 1; - int fd = c->fd; + redisFD fd = c->fd; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); @@ -168,11 +218,11 @@ int redisKeepAlive(redisContext *c, int interval) { return REDIS_OK; } -static int redisSetTcpNoDelay(redisContext *c) { +int redisSetTcpNoDelay(redisContext *c) { int yes = 1; if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } return REDIS_OK; @@ -182,7 +232,7 @@ static int redisSetTcpNoDelay(redisContext *c) { static int redisContextTimeoutMsec(redisContext *c, long *result) { - const struct timeval *timeout = c->timeout; + const struct timeval *timeout = c->connect_timeout; long msec = -1; /* Only use timeout when not NULL. */ @@ -214,12 +264,12 @@ static int redisContextWaitReady(redisContext *c, long msec) { if ((res = poll(wfd, 1, msec)) == -1) { __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } else if (res == 0) { errno = ETIMEDOUT; __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -232,7 +282,7 @@ static int redisContextWaitReady(redisContext *c, long msec) { } __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -279,21 +329,70 @@ int redisCheckSocketError(redisContext *c) { } int redisContextSetTimeout(redisContext *c, const struct timeval tv) { - if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { +#if 0 + const void *to_ptr = &tv; + size_t to_sz = sizeof(tv); + + if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); return REDIS_ERR; } - if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { + if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); return REDIS_ERR; } +#else + double timeout = tv.tv_sec ; + timeout += (double) tv.tv_usec / 1000 / 1000; + if (swoole_coroutine_socket_set_timeout(c->fd, SO_RCVTIMEO, timeout) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } + if (swoole_coroutine_socket_set_timeout(c->fd, SO_SNDTIMEO, timeout) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } +#endif + return REDIS_OK; +} + +int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->connect_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->connect_timeout == NULL) { + c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout)); + if (c->connect_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout)); + return REDIS_OK; +} + +int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->command_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->command_timeout == NULL) { + c->command_timeout = hi_malloc(sizeof(*c->command_timeout)); + if (c->command_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout)); return REDIS_OK; } static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout, const char *source_addr) { - int s, rv, n; + redisFD s; + int rv, n; char _port[6]; /* strlen("65535"); */ struct addrinfo hints, *servinfo, *bservinfo, *p, *b; int blocking = (c->flags & REDIS_BLOCK); @@ -313,21 +412,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, * This is a bit ugly, but atleast it works and doesn't leak memory. **/ if (c->tcp.host != addr) { - free(c->tcp.host); + hi_free(c->tcp.host); - c->tcp.host = strdup(addr); + c->tcp.host = hi_strdup(addr); + if (c->tcp.host == NULL) + goto oom; } if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = malloc(sizeof(struct timeval)); - - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; } else { - free(c->timeout); - c->timeout = NULL; + hi_free(c->connect_timeout); + c->connect_timeout = NULL; } if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { @@ -336,11 +433,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } if (source_addr == NULL) { - free(c->tcp.source_addr); + hi_free(c->tcp.source_addr); c->tcp.source_addr = NULL; } else if (c->tcp.source_addr != source_addr) { - free(c->tcp.source_addr); - c->tcp.source_addr = strdup(source_addr); + hi_free(c->tcp.source_addr); + c->tcp.source_addr = hi_strdup(source_addr); } snprintf(_port, 6, "%d", port); @@ -362,7 +459,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } for (p = servinfo; p != NULL; p = p->ai_next) { addrretry: - if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) + if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD) continue; c->fd = s; @@ -403,23 +500,23 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } /* For repeat connection */ - if (c->saddr) { - free(c->saddr); - } - c->saddr = malloc(p->ai_addrlen); + hi_free(c->saddr); + c->saddr = hi_malloc(p->ai_addrlen); + if (c->saddr == NULL) + goto oom; + memcpy(c->saddr, p->ai_addr, p->ai_addrlen); c->addrlen = p->ai_addrlen; - if (blocking && c->timeout) { - struct pollfd _rfd[1]; - _rfd[0].fd = c->fd; - _rfd[0].events = POLLOUT; - poll(_rfd, 1, (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); + if (c->connect_timeout) { + double timeout = c->connect_timeout->tv_sec ; + timeout += (double) c->connect_timeout->tv_usec / 1000 / 1000; + swoole_coroutine_socket_set_connect_timeout(c->fd, timeout); } if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { if (errno == EHOSTUNREACH) { - redisContextCloseFd(c); + redisNetClose(c); continue; } else if (errno == EINPROGRESS) { if (blocking) { @@ -433,19 +530,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, if (++reuses >= REDIS_CONNECT_RETRIES) { goto error; } else { - redisContextCloseFd(c); + redisNetClose(c); goto addrretry; } } else { wait_for_ready: if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) goto error; + if (redisSetTcpNoDelay(c) != REDIS_OK) + goto error; } } if (blocking && redisSetBlocking(c,1) != REDIS_OK) goto error; - if (redisSetTcpNoDelay(c) != REDIS_OK) - goto error; c->flags |= REDIS_CONNECTED; rv = REDIS_OK; @@ -458,6 +555,8 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, goto error; } +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); error: rv = REDIS_ERR; end: @@ -480,8 +579,9 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, } int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { +#ifndef _WIN32 int blocking = (c->flags & REDIS_BLOCK); - struct sockaddr_un sa; + struct sockaddr_un *sa; long timeout_msec = -1; if (redisCreateSocket(c,AF_UNIX) < 0) @@ -490,35 +590,36 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time return REDIS_ERR; c->connection_type = REDIS_CONN_UNIX; - if (c->unix_sock.path != path) - c->unix_sock.path = strdup(path); + if (c->unix_sock.path != path) { + hi_free(c->unix_sock.path); - if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = malloc(sizeof(struct timeval)); + c->unix_sock.path = hi_strdup(path); + if (c->unix_sock.path == NULL) + goto oom; + } - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } + if (timeout) { + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; } else { - free(c->timeout); - c->timeout = NULL; + hi_free(c->connect_timeout); + c->connect_timeout = NULL; } if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) return REDIS_ERR; - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); + /* Don't leak sockaddr if we're reconnecting */ + if (c->saddr) hi_free(c->saddr); - if (blocking && c->timeout) { - struct pollfd _rfd[1]; - _rfd[0].fd = c->fd; - _rfd[0].events = POLLOUT; - poll(_rfd, 1, (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); - } + sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un))); + if (sa == NULL) + goto oom; - if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { + c->addrlen = sizeof(struct sockaddr_un); + sa->sun_family = AF_UNIX; + strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); + if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { if (errno == EINPROGRESS && !blocking) { /* This is ok. */ } else { @@ -533,4 +634,13 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time c->flags |= REDIS_CONNECTED; return REDIS_OK; +#else + /* We currently do not support Unix sockets for Windows. */ + /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ + errno = EPROTONOSUPPORT; + return REDIS_ERR; +#endif /* _WIN32 */ +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; } diff --git a/thirdparty/hiredis/net.h b/thirdparty/hiredis/net.h index a11594e68df..9f43283a5f0 100644 --- a/thirdparty/hiredis/net.h +++ b/thirdparty/hiredis/net.h @@ -37,6 +37,10 @@ #include "hiredis.h" +void redisNetClose(redisContext *c); +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); +ssize_t redisNetWrite(redisContext *c); + int redisCheckSocketError(redisContext *c); int redisContextSetTimeout(redisContext *c, const struct timeval tv); int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); @@ -47,4 +51,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time int redisKeepAlive(redisContext *c, int interval); int redisCheckConnectDone(redisContext *c, int *completed); +int redisSetTcpNoDelay(redisContext *c); + #endif diff --git a/thirdparty/hiredis/read.c b/thirdparty/hiredis/read.c index cc2126778a3..468877c1f11 100644 --- a/thirdparty/hiredis/read.c +++ b/thirdparty/hiredis/read.c @@ -29,21 +29,26 @@ * POSSIBILITY OF SUCH DAMAGE. */ - #include "fmacros.h" #include #include #ifndef _MSC_VER #include +#include #endif #include #include #include #include +#include +#include "alloc.h" #include "read.h" #include "sds.h" +/* Initial size of our nested reply stack and how much we grow it when needd */ +#define REDIS_READER_STACK_SIZE 9 + static void __redisReaderSetError(redisReader *r, int type, const char *str) { size_t len; @@ -117,29 +122,28 @@ static char *readBytes(redisReader *r, unsigned int bytes) { /* Find pointer to \r\n. */ static char *seekNewline(char *s, size_t len) { - int pos = 0; - int _len = len-1; - - /* Position should be < len-1 because the character at "pos" should be - * followed by a \n. Note that strchr cannot be used because it doesn't - * allow to search a limited length and the buffer that is being searched - * might not have a trailing NULL character. */ - while (pos < _len) { - while(pos < _len && s[pos] != '\r') pos++; - if (pos==_len) { - /* Not found. */ - return NULL; - } else { - if (s[pos+1] == '\n') { - /* Found. */ - return s+pos; - } else { - /* Continue searching. */ - pos++; - } + char *ret; + + /* We cannot match with fewer than 2 bytes */ + if (len < 2) + return NULL; + + /* Search up to len - 1 characters */ + len--; + + /* Look for the \r */ + while ((ret = memchr(s, '\r', len)) != NULL) { + if (ret[1] == '\n') { + /* Found. */ + break; } + /* Continue searching. */ + ret++; + len -= ret - s; + s = ret; } - return NULL; + + return ret; } /* Convert a string into a long long. Returns REDIS_OK if the string could be @@ -241,9 +245,12 @@ static void moveToNextTask(redisReader *r) { return; } - cur = &(r->rstack[r->ridx]); - prv = &(r->rstack[r->ridx-1]); - assert(prv->type == REDIS_REPLY_ARRAY); + cur = r->task[r->ridx]; + prv = r->task[r->ridx-1]; + assert(prv->type == REDIS_REPLY_ARRAY || + prv->type == REDIS_REPLY_MAP || + prv->type == REDIS_REPLY_SET || + prv->type == REDIS_REPLY_PUSH); if (cur->idx == prv->elements-1) { r->ridx--; } else { @@ -258,26 +265,113 @@ static void moveToNextTask(redisReader *r) { } static int processLineItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; void *obj; char *p; int len; if ((p = readLine(r,&len)) != NULL) { if (cur->type == REDIS_REPLY_INTEGER) { + long long v; + + if (string2ll(p, len, &v) == REDIS_ERR) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad integer value"); + return REDIS_ERR; + } + if (r->fn && r->fn->createInteger) { - long long v; - if (string2ll(p, len, &v) == REDIS_ERR) { + obj = r->fn->createInteger(cur,v); + } else { + obj = (void*)REDIS_REPLY_INTEGER; + } + } else if (cur->type == REDIS_REPLY_DOUBLE) { + char buf[326], *eptr; + double d; + + if ((size_t)len >= sizeof(buf)) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Double value is too large"); + return REDIS_ERR; + } + + memcpy(buf,p,len); + buf[len] = '\0'; + + if (len == 3 && strcasecmp(buf,"inf") == 0) { + d = INFINITY; /* Positive infinite. */ + } else if (len == 4 && strcasecmp(buf,"-inf") == 0) { + d = -INFINITY; /* Negative infinite. */ + } else { + d = strtod((char*)buf,&eptr); + /* RESP3 only allows "inf", "-inf", and finite values, while + * strtod() allows other variations on infinity, NaN, + * etc. We explicity handle our two allowed infinite cases + * above, so strtod() should only result in finite values. */ + if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad integer value"); + "Bad double value"); return REDIS_ERR; } - obj = r->fn->createInteger(cur,v); + } + + if (r->fn && r->fn->createDouble) { + obj = r->fn->createDouble(cur,d,buf,len); } else { - obj = (void*)REDIS_REPLY_INTEGER; + obj = (void*)REDIS_REPLY_DOUBLE; + } + } else if (cur->type == REDIS_REPLY_NIL) { + if (len != 0) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad nil value"); + return REDIS_ERR; + } + + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + } else if (cur->type == REDIS_REPLY_BOOL) { + int bval; + + if (len != 1 || !strchr("tTfF", p[0])) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bool value"); + return REDIS_ERR; + } + + bval = p[0] == 't' || p[0] == 'T'; + if (r->fn && r->fn->createBool) + obj = r->fn->createBool(cur,bval); + else + obj = (void*)REDIS_REPLY_BOOL; + } else if (cur->type == REDIS_REPLY_BIGNUM) { + /* Ensure all characters are decimal digits (with possible leading + * minus sign). */ + int i; + for (i = 0; i < len; i++) { + /* XXX Consider: Allow leading '+'? Error on leading '0's? */ + if (i == 0 && p[0] == '-') continue; + if (p[i] < '0' || p[i] > '9') { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bignum value"); + return REDIS_ERR; + } } + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,p,len); + else + obj = (void*)REDIS_REPLY_BIGNUM; } else { /* Type will be error or status. */ + int i; + for (i = 0; i < len; i++) { + if (p[i] == '\r' || p[i] == '\n') { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad simple string value"); + return REDIS_ERR; + } + } if (r->fn && r->fn->createString) obj = r->fn->createString(cur,p,len); else @@ -299,7 +393,7 @@ static int processLineItem(redisReader *r) { } static int processBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; void *obj = NULL; char *p, *s; long long len; @@ -335,10 +429,18 @@ static int processBulkItem(redisReader *r) { /* Only continue when the buffer contains the entire bulk item. */ bytelen += len+2; /* include \r\n */ if (r->pos+bytelen <= r->len) { + if ((cur->type == REDIS_REPLY_VERB && len < 4) || + (cur->type == REDIS_REPLY_VERB && s[5] != ':')) + { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Verbatim string 4 bytes of content type are " + "missing or incorrectly encoded."); + return REDIS_ERR; + } if (r->fn && r->fn->createString) obj = r->fn->createString(cur,s+2,len); else - obj = (void*)REDIS_REPLY_STRING; + obj = (void*)(long)cur->type; success = 1; } } @@ -362,18 +464,42 @@ static int processBulkItem(redisReader *r) { return REDIS_ERR; } -static int processMultiBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); +static int redisReaderGrow(redisReader *r) { + redisReadTask **aux; + int newlen; + + /* Grow our stack size */ + newlen = r->tasks + REDIS_READER_STACK_SIZE; + aux = hi_realloc(r->task, sizeof(*r->task) * newlen); + if (aux == NULL) + goto oom; + + r->task = aux; + + /* Allocate new tasks */ + for (; r->tasks < newlen; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; + } + + return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; +} + +/* Process the array, map and set types. */ +static int processAggregateItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; void *obj; char *p; long long elements; int root = 0, len; - /* Set error for nested multi bulks with depth > 7 */ - if (r->ridx == 8) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "No support for nested multi bulk replies with depth > 7"); - return REDIS_ERR; + if (r->ridx == r->tasks - 1) { + if (redisReaderGrow(r) == REDIS_ERR) + return REDIS_ERR; } if ((p = readLine(r,&len)) != NULL) { @@ -385,7 +511,9 @@ static int processMultiBulkItem(redisReader *r) { root = (r->ridx == 0); - if (elements < -1 || elements > INT_MAX) { + if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) || + (r->maxelements > 0 && elements > r->maxelements)) + { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Multi-bulk length out of range"); return REDIS_ERR; @@ -404,10 +532,12 @@ static int processMultiBulkItem(redisReader *r) { moveToNextTask(r); } else { + if (cur->type == REDIS_REPLY_MAP) elements *= 2; + if (r->fn && r->fn->createArray) obj = r->fn->createArray(cur,elements); else - obj = (void*)REDIS_REPLY_ARRAY; + obj = (void*)(long)cur->type; if (obj == NULL) { __redisReaderSetErrorOOM(r); @@ -419,12 +549,12 @@ static int processMultiBulkItem(redisReader *r) { cur->elements = elements; cur->obj = obj; r->ridx++; - r->rstack[r->ridx].type = -1; - r->rstack[r->ridx].elements = -1; - r->rstack[r->ridx].idx = 0; - r->rstack[r->ridx].obj = NULL; - r->rstack[r->ridx].parent = cur; - r->rstack[r->ridx].privdata = r->privdata; + r->task[r->ridx]->type = -1; + r->task[r->ridx]->elements = -1; + r->task[r->ridx]->idx = 0; + r->task[r->ridx]->obj = NULL; + r->task[r->ridx]->parent = cur; + r->task[r->ridx]->privdata = r->privdata; } else { moveToNextTask(r); } @@ -439,7 +569,7 @@ static int processMultiBulkItem(redisReader *r) { } static int processItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; char *p; /* check if we need to read type */ @@ -455,12 +585,36 @@ static int processItem(redisReader *r) { case ':': cur->type = REDIS_REPLY_INTEGER; break; + case ',': + cur->type = REDIS_REPLY_DOUBLE; + break; + case '_': + cur->type = REDIS_REPLY_NIL; + break; case '$': cur->type = REDIS_REPLY_STRING; break; case '*': cur->type = REDIS_REPLY_ARRAY; break; + case '%': + cur->type = REDIS_REPLY_MAP; + break; + case '~': + cur->type = REDIS_REPLY_SET; + break; + case '#': + cur->type = REDIS_REPLY_BOOL; + break; + case '=': + cur->type = REDIS_REPLY_VERB; + break; + case '>': + cur->type = REDIS_REPLY_PUSH; + break; + case '(': + cur->type = REDIS_REPLY_BIGNUM; + break; default: __redisReaderSetErrorProtocolByte(r,*p); return REDIS_ERR; @@ -476,11 +630,19 @@ static int processItem(redisReader *r) { case REDIS_REPLY_ERROR: case REDIS_REPLY_STATUS: case REDIS_REPLY_INTEGER: + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_NIL: + case REDIS_REPLY_BOOL: + case REDIS_REPLY_BIGNUM: return processLineItem(r); case REDIS_REPLY_STRING: + case REDIS_REPLY_VERB: return processBulkItem(r); case REDIS_REPLY_ARRAY: - return processMultiBulkItem(r); + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: + return processAggregateItem(r); default: assert(NULL); return REDIS_ERR; /* Avoid warning. */ @@ -490,29 +652,53 @@ static int processItem(redisReader *r) { redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { redisReader *r; - r = calloc(1,sizeof(redisReader)); + r = hi_calloc(1,sizeof(redisReader)); if (r == NULL) return NULL; - r->fn = fn; r->buf = sdsempty(); - r->maxbuf = REDIS_READER_MAX_BUF; - if (r->buf == NULL) { - free(r); - return NULL; + if (r->buf == NULL) + goto oom; + + r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); + if (r->task == NULL) + goto oom; + + for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; } + r->fn = fn; + r->maxbuf = REDIS_READER_MAX_BUF; + r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS; r->ridx = -1; + return r; +oom: + redisReaderFree(r); + return NULL; } void redisReaderFree(redisReader *r) { if (r == NULL) return; + if (r->reply != NULL && r->fn && r->fn->freeObject) r->fn->freeObject(r->reply); + + if (r->task) { + /* We know r->task[i] is allocated if i < r->tasks */ + for (int i = 0; i < r->tasks; i++) { + hi_free(r->task[i]); + } + + hi_free(r->task); + } + sdsfree(r->buf); - free(r); + hi_free(r); } int redisReaderFeed(redisReader *r, const char *buf, size_t len) { @@ -528,23 +714,22 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) { if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { sdsfree(r->buf); r->buf = sdsempty(); - r->pos = 0; + if (r->buf == 0) goto oom; - /* r->buf should not be NULL since we just free'd a larger one. */ - assert(r->buf != NULL); + r->pos = 0; } newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } + if (newbuf == NULL) goto oom; r->buf = newbuf; r->len = sdslen(r->buf); } return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; } int redisReaderGetReply(redisReader *r, void **reply) { @@ -562,12 +747,12 @@ int redisReaderGetReply(redisReader *r, void **reply) { /* Set first item to process when the stack is empty. */ if (r->ridx == -1) { - r->rstack[0].type = -1; - r->rstack[0].elements = -1; - r->rstack[0].idx = -1; - r->rstack[0].obj = NULL; - r->rstack[0].parent = NULL; - r->rstack[0].privdata = r->privdata; + r->task[0]->type = -1; + r->task[0]->elements = -1; + r->task[0]->idx = -1; + r->task[0]->obj = NULL; + r->task[0]->parent = NULL; + r->task[0]->privdata = r->privdata; r->ridx = 0; } @@ -583,15 +768,18 @@ int redisReaderGetReply(redisReader *r, void **reply) { /* Discard part of the buffer when we've consumed at least 1k, to avoid * doing unnecessary calls to memmove() in sds.c. */ if (r->pos >= 1024) { - sdsrange(r->buf,r->pos,-1); + if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR; r->pos = 0; r->len = sdslen(r->buf); } /* Emit a reply when there is one. */ if (r->ridx == -1) { - if (reply != NULL) + if (reply != NULL) { *reply = r->reply; + } else if (r->reply != NULL && r->fn && r->fn->freeObject) { + r->fn->freeObject(r->reply); + } r->reply = NULL; } return REDIS_OK; diff --git a/thirdparty/hiredis/read.h b/thirdparty/hiredis/read.h index 2988aa453bd..2d74d77a5b4 100644 --- a/thirdparty/hiredis/read.h +++ b/thirdparty/hiredis/read.h @@ -45,6 +45,7 @@ #define REDIS_ERR_EOF 3 /* End of file */ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */ #define REDIS_ERR_OOM 5 /* Out of memory */ +#define REDIS_ERR_TIMEOUT 6 /* Timed out */ #define REDIS_ERR_OTHER 2 /* Everything else... */ #define REDIS_REPLY_STRING 1 @@ -53,8 +54,20 @@ #define REDIS_REPLY_NIL 4 #define REDIS_REPLY_STATUS 5 #define REDIS_REPLY_ERROR 6 +#define REDIS_REPLY_DOUBLE 7 +#define REDIS_REPLY_BOOL 8 +#define REDIS_REPLY_MAP 9 +#define REDIS_REPLY_SET 10 +#define REDIS_REPLY_ATTR 11 +#define REDIS_REPLY_PUSH 12 +#define REDIS_REPLY_BIGNUM 13 +#define REDIS_REPLY_VERB 14 -#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ +/* Default max unused reader buffer. */ +#define REDIS_READER_MAX_BUF (1024*16) + +/* Default multi-bulk element limit */ +#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) #ifdef __cplusplus extern "C" { @@ -62,7 +75,7 @@ extern "C" { typedef struct redisReadTask { int type; - int elements; /* number of elements in multibulk container */ + long long elements; /* number of elements in multibulk container */ int idx; /* index in parent (array) object */ void *obj; /* holds user-generated value for a read task */ struct redisReadTask *parent; /* parent task */ @@ -71,9 +84,11 @@ typedef struct redisReadTask { typedef struct redisReplyObjectFunctions { void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, int); + void *(*createArray)(const redisReadTask*, size_t); void *(*createInteger)(const redisReadTask*, long long); + void *(*createDouble)(const redisReadTask*, double, char*, size_t); void *(*createNil)(const redisReadTask*); + void *(*createBool)(const redisReadTask*, int); void (*freeObject)(void*); } redisReplyObjectFunctions; @@ -85,8 +100,11 @@ typedef struct redisReader { size_t pos; /* Buffer cursor */ size_t len; /* Buffer length */ size_t maxbuf; /* Max length of unused buffer */ + long long maxelements; /* Max multi-bulk elements */ + + redisReadTask **task; + int tasks; - redisReadTask rstack[9]; int ridx; /* Index of current read task */ void *reply; /* Temporary reply pointer */ diff --git a/thirdparty/hiredis/sds.c b/thirdparty/hiredis/sds.c index 44777b10c65..35baa057eb9 100644 --- a/thirdparty/hiredis/sds.c +++ b/thirdparty/hiredis/sds.c @@ -30,11 +30,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include "fmacros.h" #include #include #include #include #include +#include #include "sds.h" #include "sdsalloc.h" @@ -70,7 +72,7 @@ static inline char sdsReqType(size_t string_size) { * and 'initlen'. * If NULL is used for 'init' the string is initialized with zero bytes. * - * The string is always null-termined (all the sds strings are, always) so + * The string is always null-terminated (all the sds strings are, always) so * even if you create an sds string with: * * mystring = sdsnewlen("abc",3); @@ -219,10 +221,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { hdrlen = sdsHdrSize(type); if (oldtype==type) { newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) { - s_free(sh); - return NULL; - } + if (newsh == NULL) return NULL; s = (char*)newsh+hdrlen; } else { /* Since the header size changes, need to move the string forward, @@ -416,7 +415,7 @@ sds sdscpylen(sds s, const char *t, size_t len) { return s; } -/* Like sdscpylen() but 't' must be a null-termined string so that the length +/* Like sdscpylen() but 't' must be a null-terminated string so that the length * of the string is obtained with strlen(). */ sds sdscpy(sds s, const char *t) { return sdscpylen(s, t, strlen(t)); @@ -715,15 +714,20 @@ sds sdstrim(sds s, const char *cset) { * * The string is modified in-place. * + * Return value: + * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value. + * 0 on success. + * * Example: * * s = sdsnew("Hello World"); * sdsrange(s,1,-1); => "ello World" */ -void sdsrange(sds s, int start, int end) { +int sdsrange(sds s, ssize_t start, ssize_t end) { size_t newlen, len = sdslen(s); + if (len > SSIZE_MAX) return -1; - if (len == 0) return; + if (len == 0) return 0; if (start < 0) { start = len+start; if (start < 0) start = 0; @@ -734,9 +738,9 @@ void sdsrange(sds s, int start, int end) { } newlen = (start > end) ? 0 : (end-start)+1; if (newlen != 0) { - if (start >= (signed)len) { + if (start >= (ssize_t)len) { newlen = 0; - } else if (end >= (signed)len) { + } else if (end >= (ssize_t)len) { end = len-1; newlen = (start > end) ? 0 : (end-start)+1; } @@ -746,6 +750,7 @@ void sdsrange(sds s, int start, int end) { if (start && newlen) memmove(s, s+start, newlen); s[newlen] = 0; sdssetlen(s,newlen); + return 0; } /* Apply tolower() to every character of the sds string 's'. */ @@ -889,13 +894,6 @@ sds sdscatrepr(sds s, const char *p, size_t len) { return sdscatlen(s,"\"",1); } -/* Helper function for sdssplitargs() that returns non zero if 'c' - * is a valid hex digit. */ -int is_hex_digit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - /* Helper function for sdssplitargs() that converts a hex digit into an * integer from 0 to 15 */ int hex_digit_to_int(char c) { @@ -958,8 +956,8 @@ sds *sdssplitargs(const char *line, int *argc) { while(!done) { if (inq) { if (*p == '\\' && *(p+1) == 'x' && - is_hex_digit(*(p+2)) && - is_hex_digit(*(p+3))) + isxdigit(*(p+2)) && + isxdigit(*(p+3))) { unsigned char byte; @@ -1035,7 +1033,7 @@ sds *sdssplitargs(const char *line, int *argc) { s_free(vector); return NULL; } - + vector = new_vector; vector[*argc] = current; (*argc)++; diff --git a/thirdparty/hiredis/sds.h b/thirdparty/hiredis/sds.h index 13be75a9fca..eda8833b598 100644 --- a/thirdparty/hiredis/sds.h +++ b/thirdparty/hiredis/sds.h @@ -34,6 +34,11 @@ #define __SDS_H #define SDS_MAX_PREALLOC (1024*1024) +#ifdef _MSC_VER +#define __attribute__(x) +typedef long long ssize_t; +#define SSIZE_MAX (LLONG_MAX >> 1) +#endif #include #include @@ -132,20 +137,20 @@ static inline void sdssetlen(sds s, size_t newlen) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); } break; case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; + SDS_HDR(8,s)->len = (uint8_t)newlen; break; case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; + SDS_HDR(16,s)->len = (uint16_t)newlen; break; case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; + SDS_HDR(32,s)->len = (uint32_t)newlen; break; case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; + SDS_HDR(64,s)->len = (uint64_t)newlen; break; } } @@ -156,21 +161,21 @@ static inline void sdsinclen(sds s, size_t inc) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; + unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; + SDS_HDR(8,s)->len += (uint8_t)inc; break; case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; + SDS_HDR(16,s)->len += (uint16_t)inc; break; case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; + SDS_HDR(32,s)->len += (uint32_t)inc; break; case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; + SDS_HDR(64,s)->len += (uint64_t)inc; break; } } @@ -200,16 +205,16 @@ static inline void sdssetalloc(sds s, size_t newlen) { /* Nothing to do, this type has no total allocation info. */ break; case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; + SDS_HDR(8,s)->alloc = (uint8_t)newlen; break; case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; + SDS_HDR(16,s)->alloc = (uint16_t)newlen; break; case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; + SDS_HDR(32,s)->alloc = (uint32_t)newlen; break; case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; + SDS_HDR(64,s)->alloc = (uint64_t)newlen; break; } } @@ -236,7 +241,7 @@ sds sdscatprintf(sds s, const char *fmt, ...); sds sdscatfmt(sds s, char const *fmt, ...); sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, int start, int end); +int sdsrange(sds s, ssize_t start, ssize_t end); void sdsupdatelen(sds s); void sdsclear(sds s); int sdscmp(const sds s1, const sds s2); diff --git a/thirdparty/hiredis/sdsalloc.h b/thirdparty/hiredis/sdsalloc.h index f43023c4843..5538dd94c97 100644 --- a/thirdparty/hiredis/sdsalloc.h +++ b/thirdparty/hiredis/sdsalloc.h @@ -37,6 +37,8 @@ * the include of your alternate allocator if needed (not needed in order * to use the default libc allocator). */ -#define s_malloc malloc -#define s_realloc realloc -#define s_free free +#include "alloc.h" + +#define s_malloc hi_malloc +#define s_realloc hi_realloc +#define s_free hi_free diff --git a/thirdparty/hiredis/test.c b/thirdparty/hiredis/test.c deleted file mode 100644 index a5eb9c70e82..00000000000 --- a/thirdparty/hiredis/test.c +++ /dev/null @@ -1,938 +0,0 @@ -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" - -enum connection_type { - CONN_TCP, - CONN_UNIX, - CONN_FD -}; - -struct config { - enum connection_type type; - - struct { - const char *host; - int port; - struct timeval timeout; - } tcp; - - struct { - const char *path; - } unix_sock; -}; - -/* The following lines make up our testing "framework" :) */ -static int tests = 0, fails = 0; -#define test(_s) { printf("#%02d ", ++tests); printf(_s); } -#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;} - -static long long usec(void) { - struct timeval tv; - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; -} - -/* The assert() calls below have side effects, so we need assert() - * even if we are compiling without asserts (-DNDEBUG). */ -#ifdef NDEBUG -#undef assert -#define assert(e) (void)(e) -#endif - -static redisContext *select_database(redisContext *c) { - redisReply *reply; - - /* Switch to DB 9 for testing, now that we know we can chat. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Make sure the DB is emtpy */ - reply = redisCommand(c,"DBSIZE"); - assert(reply != NULL); - if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { - /* Awesome, DB 9 is empty and we can continue. */ - freeReplyObject(reply); - } else { - printf("Database #9 is not empty, test can not continue\n"); - exit(1); - } - - return c; -} - -static int disconnect(redisContext *c, int keep_fd) { - redisReply *reply; - - /* Make sure we're on DB 9. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - reply = redisCommand(c,"FLUSHDB"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Free the context as well, but keep the fd if requested. */ - if (keep_fd) - return redisFreeKeepFd(c); - redisFree(c); - return -1; -} - -static redisContext *do_connect(struct config config) { - redisContext *c = NULL; - - if (config.type == CONN_TCP) { - c = redisConnect(config.tcp.host, config.tcp.port); - } else if (config.type == CONN_UNIX) { - c = redisConnectUnix(config.unix_sock.path); - } else if (config.type == CONN_FD) { - /* Create a dummy connection just to get an fd to inherit */ - redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); - if (dummy_ctx) { - int fd = disconnect(dummy_ctx, 1); - printf("Connecting to inherited fd %d\n", fd); - c = redisConnectFd(fd); - } - } else { - assert(NULL); - } - - if (c == NULL) { - printf("Connection error: can't allocate redis context\n"); - exit(1); - } else if (c->err) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - exit(1); - } - - return select_database(c); -} - -static void test_format_commands(void) { - char *cmd; - int len; - - test("Format command without interpolation: "); - len = redisFormatCommand(&cmd,"SET foo bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s string interpolation: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s and an empty string: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo",""); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with an empty string in between proper interpolations: "); - len = redisFormatCommand(&cmd,"SET %s %s","","foo"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && - len == 4+4+(3+2)+4+(0+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b string interpolation: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b and an empty string: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with literal %%: "); - len = redisFormatCommand(&cmd,"SET %% %%"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && - len == 4+4+(3+2)+4+(1+2)+4+(1+2)); - free(cmd); - - /* Vararg width depends on the type. These tests make sure that the - * width is correctly determined using the format and subsequent varargs - * can correctly be interpolated. */ -#define INTEGER_WIDTH_TEST(fmt, type) do { \ - type value = 123; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - -#define FLOAT_WIDTH_TEST(type) do { \ - type value = 123.0; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - - INTEGER_WIDTH_TEST("d", int); - INTEGER_WIDTH_TEST("hhd", char); - INTEGER_WIDTH_TEST("hd", short); - INTEGER_WIDTH_TEST("ld", long); - INTEGER_WIDTH_TEST("lld", long long); - INTEGER_WIDTH_TEST("u", unsigned int); - INTEGER_WIDTH_TEST("hhu", unsigned char); - INTEGER_WIDTH_TEST("hu", unsigned short); - INTEGER_WIDTH_TEST("lu", unsigned long); - INTEGER_WIDTH_TEST("llu", unsigned long long); - FLOAT_WIDTH_TEST(float); - FLOAT_WIDTH_TEST(double); - - test("Format command with invalid printf format: "); - len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3); - test_cond(len == -1); - - const char *argv[3]; - argv[0] = "SET"; - argv[1] = "foo\0xxx"; - argv[2] = "bar"; - size_t lens[3] = { 3, 7, 3 }; - int argc = 3; - - test("Format command by passing argc/argv without lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,NULL); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command by passing argc/argv with lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,lens); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - free(cmd); - - sds sds_cmd; - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv without lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - sdsfree(sds_cmd); - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv with lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - sdsfree(sds_cmd); -} - -static void test_append_formatted_commands(struct config config) { - redisContext *c; - redisReply *reply; - char *cmd; - int len; - - c = do_connect(config); - - test("Append format command: "); - - len = redisFormatCommand(&cmd, "SET foo bar"); - - test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK); - - assert(redisGetReply(c, (void*)&reply) == REDIS_OK); - - free(cmd); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_reply_reader(void) { - redisReader *reader; - void *reply; - int ret; - int i; - - test("Error handling in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - /* when the reply already contains multiple items, they must be free'd - * on an error. valgrind will bark when this doesn't happen. */ - test("Memory cleanup in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*2\r\n",4); - redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - test("Set error on nested multi bulks with depth > 7: "); - reader = redisReaderCreate(); - - for (i = 0; i < 9; i++) { - redisReaderFeed(reader,(char*)"*1\r\n",4); - } - - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strncasecmp(reader->errstr,"No support for",14) == 0); - redisReaderFree(reader); - - test("Correctly parses LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775807\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MAX); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when > LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775808\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Correctly parses LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775808\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MIN); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when < LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775809\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when bulk < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$-2\r\nasdf\r\n",11); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array > INT_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - -#if LLONG_MAX > SIZE_MAX - test("Set error when bulk > SIZE_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); -#endif - - test("Works with NULL functions for reply: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r\n",5); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Works when a single newline (\\r\\n) covers two calls to feed: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r",4); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_OK && reply == NULL); - redisReaderFeed(reader,(char*)"\n",1); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Don't reset state after protocol error: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"x",1); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_ERR); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && reply == NULL); - redisReaderFree(reader); - - /* Regression test for issue #45 on GitHub. */ - test("Don't do empty allocation for empty multi bulk: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*0\r\n",4); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && - ((redisReply*)reply)->elements == 0); - freeReplyObject(reply); - redisReaderFree(reader); -} - -static void test_free_null(void) { - void *redisCtx = NULL; - void *reply = NULL; - - test("Don't fail when redisFree is passed a NULL value: "); - redisFree(redisCtx); - test_cond(redisCtx == NULL); - - test("Don't fail when freeReplyObject is passed a NULL value: "); - freeReplyObject(reply); - test_cond(reply == NULL); -} - -static void test_blocking_connection_errors(void) { - redisContext *c; - struct addrinfo hints = {.ai_family = AF_INET}; - struct addrinfo *ai_tmp = NULL; - const char *bad_domain = "idontexist.com"; - - int rv = getaddrinfo(bad_domain, "6379", &hints, &ai_tmp); - if (rv != 0) { - // Address does *not* exist - test("Returns error when host cannot be resolved: "); - // First see if this domain name *actually* resolves to NXDOMAIN - c = redisConnect("dontexist.com", 6379); - test_cond( - c->err == REDIS_ERR_OTHER && - (strcmp(c->errstr, "Name or service not known") == 0 || - strcmp(c->errstr, "Can't resolve: sadkfjaskfjsa.com") == 0 || - strcmp(c->errstr, - "nodename nor servname provided, or not known") == 0 || - strcmp(c->errstr, "No address associated with hostname") == 0 || - strcmp(c->errstr, "Temporary failure in name resolution") == 0 || - strcmp(c->errstr, - "hostname nor servname provided, or not known") == 0 || - strcmp(c->errstr, "no address associated with name") == 0)); - redisFree(c); - } else { - printf("Skipping NXDOMAIN test. Found evil ISP!\n"); - freeaddrinfo(ai_tmp); - } - - test("Returns error when the port is not open: "); - c = redisConnect((char*)"localhost", 1); - test_cond(c->err == REDIS_ERR_IO && - strcmp(c->errstr,"Connection refused") == 0); - redisFree(c); - - test("Returns error when the unix_sock socket path doesn't accept connections: "); - c = redisConnectUnix((char*)"/tmp/idontexist.sock"); - test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ - redisFree(c); -} - -static void test_blocking_connection(struct config config) { - redisContext *c; - redisReply *reply; - - c = do_connect(config); - - test("Is able to deliver commands: "); - reply = redisCommand(c,"PING"); - test_cond(reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"pong") == 0) - freeReplyObject(reply); - - test("Is a able to send commands verbatim: "); - reply = redisCommand(c,"SET foo bar"); - test_cond (reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"ok") == 0) - freeReplyObject(reply); - - test("%%s String interpolation works: "); - reply = redisCommand(c,"SET %s %s","foo","hello world"); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - strcmp(reply->str,"hello world") == 0); - freeReplyObject(reply); - - test("%%b String interpolation works: "); - reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - memcmp(reply->str,"hello\x00world",11) == 0) - - test("Binary reply length is correct: "); - test_cond(reply->len == 11) - freeReplyObject(reply); - - test("Can parse nil replies: "); - reply = redisCommand(c,"GET nokey"); - test_cond(reply->type == REDIS_REPLY_NIL) - freeReplyObject(reply); - - /* test 7 */ - test("Can parse integer replies: "); - reply = redisCommand(c,"INCR mycounter"); - test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) - freeReplyObject(reply); - - test("Can parse multi bulk replies: "); - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - freeReplyObject(redisCommand(c,"LPUSH mylist bar")); - reply = redisCommand(c,"LRANGE mylist 0 -1"); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - !memcmp(reply->element[0]->str,"bar",3) && - !memcmp(reply->element[1]->str,"foo",3)) - freeReplyObject(reply); - - /* m/e with multi bulk reply *before* other reply. - * specifically test ordering of reply items to parse. */ - test("Can handle nested multi bulk replies: "); - freeReplyObject(redisCommand(c,"MULTI")); - freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); - freeReplyObject(redisCommand(c,"PING")); - reply = (redisCommand(c,"EXEC")); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - reply->element[0]->type == REDIS_REPLY_ARRAY && - reply->element[0]->elements == 2 && - !memcmp(reply->element[0]->element[0]->str,"bar",3) && - !memcmp(reply->element[0]->element[1]->str,"foo",3) && - reply->element[1]->type == REDIS_REPLY_STATUS && - strcasecmp(reply->element[1]->str,"pong") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_connection_timeouts(struct config config) { - redisContext *c; - redisReply *reply; - ssize_t s; - const char *cmd = "DEBUG SLEEP 3\r\n"; - struct timeval tv; - - c = do_connect(config); - test("Successfully completes a command when the timeout is not exceeded: "); - reply = redisCommand(c,"SET foo fast"); - freeReplyObject(reply); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); - freeReplyObject(reply); - disconnect(c, 0); - - c = do_connect(config); - test("Does not return a reply when the command times out: "); - s = write(c->fd, cmd, strlen(cmd)); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0); - freeReplyObject(reply); - - test("Reconnect properly reconnects after a timeout: "); - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - test("Reconnect properly uses owned parameters: "); - config.tcp.host = "foo"; - config.unix_sock.path = "foo"; - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_io_errors(struct config config) { - redisContext *c; - redisReply *reply; - void *_reply; - int major, minor; - - /* Connect to target given by config. */ - c = do_connect(config); - { - /* Find out Redis version to determine the path for the next test */ - const char *field = "redis_version:"; - char *p, *eptr; - - reply = redisCommand(c,"INFO"); - p = strstr(reply->str,field); - major = strtol(p+strlen(field),&eptr,10); - p = eptr+1; /* char next to the first "." */ - minor = strtol(p,&eptr,10); - freeReplyObject(reply); - } - - test("Returns I/O error when the connection is lost: "); - reply = redisCommand(c,"QUIT"); - if (major > 2 || (major == 2 && minor > 0)) { - /* > 2.0 returns OK on QUIT and read() should be issued once more - * to know the descriptor is at EOF. */ - test_cond(strcasecmp(reply->str,"OK") == 0 && - redisGetReply(c,&_reply) == REDIS_ERR); - freeReplyObject(reply); - } else { - test_cond(reply == NULL); - } - - /* On 2.0, QUIT will cause the connection to be closed immediately and - * the read(2) for the reply on QUIT will set the error to EOF. - * On >2.0, QUIT will return with OK and another read(2) needed to be - * issued to find out the socket was closed by the server. In both - * conditions, the error will be set to EOF. */ - assert(c->err == REDIS_ERR_EOF && - strcmp(c->errstr,"Server closed the connection") == 0); - redisFree(c); - - c = do_connect(config); - test("Returns I/O error on socket timeout: "); - struct timeval tv = { 0, 1000 }; - assert(redisSetTimeout(c,tv) == REDIS_OK); - test_cond(redisGetReply(c,&_reply) == REDIS_ERR && - c->err == REDIS_ERR_IO && errno == EAGAIN); - redisFree(c); -} - -static void test_invalid_timeout_errors(struct config config) { - redisContext *c; - - test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = 0; - config.tcp.timeout.tv_usec = 10000001; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); - - test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1; - config.tcp.timeout.tv_usec = 0; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); -} - -static void test_throughput(struct config config) { - redisContext *c = do_connect(config); - redisReply **replies; - int i, num; - long long t1, t2; - - test("Throughput:\n"); - for (i = 0; i < 500; i++) - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - - num = 1000; - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"PING"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"LRANGE mylist 0 499"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); - - num = 10000; - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"PING"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"LRANGE mylist 0 499"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"INCRBY incrkey %d", 1000000); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - disconnect(c, 0); -} - -// static long __test_callback_flags = 0; -// static void __test_callback(redisContext *c, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// } -// -// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// if (reply) freeReplyObject(reply); -// } -// -// static redisContext *__connect_nonblock() { -// /* Reset callback flags */ -// __test_callback_flags = 0; -// return redisConnectNonBlock("127.0.0.1", port, NULL); -// } -// -// static void test_nonblocking_connection() { -// redisContext *c; -// int wdone = 0; -// -// test("Calls command callback when command is issued: "); -// c = __connect_nonblock(); -// redisSetCommandCallback(c,__test_callback,(void*)1); -// redisCommand(c,"PING"); -// test_cond(__test_callback_flags == 1); -// redisFree(c); -// -// test("Calls disconnect callback on redisDisconnect: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 2); -// redisFree(c); -// -// test("Calls disconnect callback and free callback on redisFree: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisSetFreeCallback(c,__test_callback,(void*)4); -// redisFree(c); -// test_cond(__test_callback_flags == ((2 << 8) | 4)); -// -// test("redisBufferWrite against empty write buffer: "); -// c = __connect_nonblock(); -// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); -// redisFree(c); -// -// test("redisBufferWrite against not yet connected fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("redisBufferWrite against closed fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// redisDisconnect(c); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("Process callbacks in the right sequence: "); -// c = __connect_nonblock(); -// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); -// -// /* Write output buffer */ -// wdone = 0; -// while(!wdone) { -// usleep(500); -// redisBufferWrite(c,&wdone); -// } -// -// /* Read until at least one callback is executed (the 3 replies will -// * arrive in a single packet, causing all callbacks to be executed in -// * a single pass). */ -// while(__test_callback_flags == 0) { -// assert(redisBufferRead(c) == REDIS_OK); -// redisProcessCallbacks(c); -// } -// test_cond(__test_callback_flags == 0x010203); -// redisFree(c); -// -// test("redisDisconnect executes pending callbacks with NULL reply: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)1); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 0x0201); -// redisFree(c); -// } - -int main(int argc, char **argv) { - struct config cfg = { - .tcp = { - .host = "127.0.0.1", - .port = 6379 - }, - .unix_sock = { - .path = "/tmp/redis.sock" - } - }; - int throughput = 1; - int test_inherit_fd = 1; - - /* Ignore broken pipe signal (for I/O error tests). */ - signal(SIGPIPE, SIG_IGN); - - /* Parse command line options. */ - argv++; argc--; - while (argc) { - if (argc >= 2 && !strcmp(argv[0],"-h")) { - argv++; argc--; - cfg.tcp.host = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"-p")) { - argv++; argc--; - cfg.tcp.port = atoi(argv[0]); - } else if (argc >= 2 && !strcmp(argv[0],"-s")) { - argv++; argc--; - cfg.unix_sock.path = argv[0]; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { - throughput = 0; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { - test_inherit_fd = 0; - } else { - fprintf(stderr, "Invalid argument: %s\n", argv[0]); - exit(1); - } - argv++; argc--; - } - - test_format_commands(); - test_reply_reader(); - test_blocking_connection_errors(); - test_free_null(); - - printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port); - cfg.type = CONN_TCP; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - test_invalid_timeout_errors(cfg); - test_append_formatted_commands(cfg); - if (throughput) test_throughput(cfg); - - printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_UNIX; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - if (throughput) test_throughput(cfg); - - if (test_inherit_fd) { - printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_FD; - test_blocking_connection(cfg); - } - - - if (fails) { - printf("*** %d TESTS FAILED ***\n", fails); - return 1; - } - - printf("ALL TESTS PASSED\n"); - return 0; -} diff --git a/thirdparty/hiredis/win32.h b/thirdparty/hiredis/win32.h deleted file mode 100644 index 1a27c18f2ae..00000000000 --- a/thirdparty/hiredis/win32.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _WIN32_HELPER_INCLUDE -#define _WIN32_HELPER_INCLUDE -#ifdef _MSC_VER - -#ifndef inline -#define inline __inline -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif - -#endif -#endif \ No newline at end of file diff --git a/thirdparty/jemalloc b/thirdparty/jemalloc deleted file mode 160000 index 04380e79f1e..00000000000 --- a/thirdparty/jemalloc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04380e79f1e2428bd0ad000bbc6e3d2dfc6b66a5 diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index 21ce6145aa7..96539faf816 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -9,281 +9,380 @@ #include #include -static void multipart_log(const char * format, ...) -{ #ifdef DEBUG_MULTIPART - va_list args; - va_start(args, format); - - fprintf(stderr, "[HTTP_MULTIPART_PARSER] %s:%d: ", __FILE__, __LINE__); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); +#include +#define multipart_log(format, ...) \ + do { \ + fprintf(stderr, "[MULTIPART_PARSER] line %d: " format "\n", __LINE__, __VA_ARGS__); \ + } while (0) +#define multipart_log_c(format) \ + do { \ + if (isprint(c)) { \ + multipart_log("parsing '%c' " format, c); \ + } else { \ + multipart_log("parsing '\\x%0.2x' " format, c); \ + } \ + } while (0) +#else +#define multipart_log(format, ...) +#define multipart_log_c(format, ...) #endif -} - -#define NOTIFY_CB(FOR) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p) != 0) { \ - return i; \ - } \ - } \ -} while (0) - -#define EMIT_DATA_CB(FOR, ptr, len) \ -do { \ - if (p->settings->on_##FOR) { \ - if (p->settings->on_##FOR(p, ptr, len) != 0) { \ - return i; \ - } \ - } \ -} while (0) +#define NOTIFY_CB(FOR, r) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define EMIT_DATA_CB(FOR, r, ptr, len) \ + do { \ + if (p->settings->on_##FOR) { \ + if ((ret = p->settings->on_##FOR(p, ptr, len)) == MPPE_PAUSED) { \ + return r; \ + } else if (ret != MPPE_OK) { \ + return MPPE_ERROR; \ + } \ + } \ + } while (0) + +#define ERROR_OUT(reason) \ + do { \ + p->error_unexpected = c; \ + p->error_i = i; \ + p->error_reason = reason; \ + return MPPE_ERROR; \ + } while (0) + +#define ERROR_EXPECT(reason, ch) \ + do { \ + p->error_expected = ch; \ + if (ch == LF) { \ + multipart_log("expecting LF at %zu, but it's \\x%.2x", i, c); \ + } else if (ch == CR) { \ + multipart_log("expecting CR at %zu, but it's \\x%.2x", i, c); \ + } else { \ + multipart_log("expecting '%c' at %zu, but it's \\x%.2x", ch, i, c); \ + } \ + ERROR_OUT(reason); \ + } while (0) #define LF 10 #define CR 13 enum state { - s_uninitialized = 1, - s_start, - s_start_boundary, - s_header_field_start, - s_header_field, - s_headers_almost_done, - s_header_value_start, - s_header_value, - s_header_value_almost_done, - s_part_data_start, - s_part_data, - s_part_data_almost_boundary, - s_part_data_boundary, - s_part_data_almost_end, - s_part_data_end, - s_part_data_final_hyphen, - s_end + s_uninitialized = 0, + s_start, + s_start_boundary, + s_header_field_start, + s_header_field, + s_headers_almost_done, + s_header_value_start, + s_header_value, + s_header_value_almost_done, + s_part_data_start, + s_part_data, + s_part_data_almost_boundary, + s_part_data_boundary, + s_part_data_almost_end, + s_part_data_end, + s_part_data_final_hyphen, + s_end }; -multipart_parser* multipart_parser_init(const char *boundary, size_t boundary_length, - const multipart_parser_settings* settings) -{ - multipart_parser* p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); - memcpy(p->multipart_boundary, "--", 2); - memcpy(p->multipart_boundary + 2, boundary, boundary_length); - p->multipart_boundary[2 + boundary_length] = 0; - -#if 0 - printf("boundary: %s\r\n\r\n", p->multipart_boundary); -#endif +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings) { + multipart_parser *p = calloc(sizeof(multipart_parser) + boundary_length + boundary_length + 9 + 4, sizeof(char)); + memcpy(p->boundary, "--", 2); + memcpy(p->boundary + 2, boundary, boundary_length); + p->boundary[2 + boundary_length] = 0; p->boundary_length = boundary_length + 2; - p->lookbehind = (p->multipart_boundary + p->boundary_length + 1); p->index = 0; p->state = s_start; + p->error_i = 0; + p->error_unexpected = 0; + p->error_expected = 0; + p->error_reason = MPPE_OK; + p->state = s_start; p->settings = settings; return p; } -void multipart_parser_free(multipart_parser* p) { - free(p); +void multipart_parser_free(multipart_parser *p) { + free(p); } -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len) { - size_t i = 0; - size_t mark = 0; - char c, cl; - int is_last = 0; - - while(i < len) { - c = buf[i]; - is_last = (i == (len - 1)); - switch (p->state) { - case s_start: - multipart_log("s_start"); - p->index = 0; - p->state = s_start_boundary; - - /* fallthrough */ - case s_start_boundary: - multipart_log("s_start_boundary"); - if (p->index == p->boundary_length) { - if (c != CR) { - return i; - } - p->index++; - break; - } else if (p->index == (p->boundary_length + 1)) { - if (c != LF) { - return i; - } - p->index = 0; - NOTIFY_CB(part_data_begin); - p->state = s_header_field_start; - break; - } - if (c != p->multipart_boundary[p->index]) { - return i; - } - p->index++; +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len) { + int ret; + switch (p->error_reason) { + case MPPE_OK: + return 0; + case MPPE_PAUSED: + return snprintf(buf, len, "parser paused"); + case MPPE_UNKNOWN: + return snprintf(buf, len, "parser unknown"); + default: + return snprintf(buf, len, "parser abort"); + case MPPE_BOUNDARY_END_NO_CRLF: + ret = snprintf(buf, len, "no CRLF at first boundary end: "); break; - - case s_header_field_start: - multipart_log("s_header_field_start"); - mark = i; - p->state = s_header_field; - - /* fallthrough */ - case s_header_field: - multipart_log("s_header_field"); - if (c == CR) { - p->state = s_headers_almost_done; - break; - } - - if (c == '-') { - break; - } - - if (c == ':') { - EMIT_DATA_CB(header_field, buf + mark, i - mark); - p->state = s_header_value_start; - break; - } - - cl = tolower(c); - if (cl < 'a' || cl > 'z') { - multipart_log("invalid character in header name"); - return i; - } - if (is_last) - EMIT_DATA_CB(header_field, buf + mark, (i - mark) + 1); + case MPPE_BAD_START_BOUNDARY: + ret = snprintf(buf, len, "first boundary mismatching: "); break; - - case s_headers_almost_done: - multipart_log("s_headers_almost_done"); - if (c != LF) { - return i; - } - - p->state = s_part_data_start; + case MPPE_INVALID_HEADER_FIELD_CHAR: + ret = snprintf(buf, len, "invalid char in header field: "); break; - - case s_header_value_start: - multipart_log("s_header_value_start"); - if (c == ' ') { - break; - } - - mark = i; - p->state = s_header_value; - - /* fallthrough */ - case s_header_value: - multipart_log("s_header_value"); - if (c == CR) { - EMIT_DATA_CB(header_value, buf + mark, i - mark); - p->state = s_header_value_almost_done; - } - if (is_last) - EMIT_DATA_CB(header_value, buf + mark, (i - mark) + 1); + case MPPE_INVALID_HEADER_VALUE_CHAR: + ret = snprintf(buf, len, "invalid char in header value: "); break; - - case s_header_value_almost_done: - multipart_log("s_header_value_almost_done"); - if (c != LF) { - return i; - } - p->state = s_header_field_start; + case MPPE_BAD_PART_END: + ret = snprintf(buf, len, "no next part or final hyphen: expecting CR or '-' "); break; - - case s_part_data_start: - multipart_log("s_part_data_start"); - NOTIFY_CB(headers_complete); - mark = i; - p->state = s_part_data; - - /* fallthrough */ - case s_part_data: - multipart_log("s_part_data"); - if (c == CR) { - EMIT_DATA_CB(part_data, buf + mark, i - mark); - mark = i; - p->state = s_part_data_almost_boundary; - p->lookbehind[0] = CR; - break; - } - if (is_last) - EMIT_DATA_CB(part_data, buf + mark, (i - mark) + 1); + case MPPE_END_BOUNDARY_NO_DASH: + ret = snprintf(buf, len, "bad final hyphen: "); break; - - case s_part_data_almost_boundary: - multipart_log("s_part_data_almost_boundary"); - if (c == LF) { - p->state = s_part_data_boundary; - p->lookbehind[1] = LF; - p->index = 0; - break; - } - EMIT_DATA_CB(part_data, p->lookbehind, 1); - p->state = s_part_data; - mark = i --; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + switch (p->error_expected) { + case '\0': break; - - case s_part_data_boundary: - multipart_log("s_part_data_boundary"); - if (p->multipart_boundary[p->index] != c) { - EMIT_DATA_CB(part_data, p->lookbehind, 2 + p->index); - p->state = s_part_data; - mark = i --; - break; - } - p->lookbehind[2 + p->index] = c; - if ((++ p->index) == p->boundary_length) { - NOTIFY_CB(part_data_end); - p->state = s_part_data_almost_end; - } + case CR: + ret += snprintf(buf + ret, len - ret, "expecting CR "); + break; + case LF: + ret += snprintf(buf + ret, len - ret, "expecting LF "); break; + default: + ret += snprintf(buf + ret, len - ret, "expecting '%c' ", p->error_expected); + break; + } + if (ret < 0) { + return 0; + } + if ((size_t) ret >= len) { + return ret; + } + if (isprint(p->error_unexpected)) { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '%c'", p->error_i, p->error_unexpected); + } else { + ret += snprintf(buf + ret, len - ret, "at %zu, but it is '\\x%.2x'", p->error_i, p->error_unexpected); + } + return ret; +} - case s_part_data_almost_end: - multipart_log("s_part_data_almost_end"); - if (c == '-') { - p->state = s_part_data_final_hyphen; +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len) { + size_t i = 0; + size_t mark = 0; + size_t mark_end = 0; + char c, cl; + int is_last = 0; + int ret; + + while (i < len) { + c = buf[i]; + is_last = (i == (len - 1)); + switch (p->state) { + case s_start: + multipart_log_c("s_start"); + p->index = 0; + p->state = s_start_boundary; + /* fallthrough */ + /* no break */ + case s_start_boundary: + multipart_log_c("s_start_boundary"); + if (p->index == p->boundary_length) { + // https://github.com/swoole/swoole-src/issues/5168 + if (c == '-') { + p->state = s_part_data_final_hyphen; + } else if (c != CR) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, CR); + } + p->index++; + break; + } else if (p->index == (size_t)(p->boundary_length + 1)) { + if (c != LF) { + ERROR_EXPECT(MPPE_BOUNDARY_END_NO_CRLF, LF); + } + p->index = 0; + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + if (c != p->boundary[p->index]) { + ERROR_EXPECT(MPPE_BAD_START_BOUNDARY, p->boundary[p->index]); + } + p->index++; break; - } - if (c == CR) { - p->state = s_part_data_end; + case s_header_field_start: + multipart_log_c("s_header_field_start"); + mark = i; + p->state = s_header_field; + /* fallthrough */ + /* no break */ + case s_header_field: + multipart_log_c("s_header_field"); + if (c == CR) { + p->state = s_headers_almost_done; + break; + } + if (c == '-') { + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); + } + break; + } + if (c == ':') { + p->state = s_header_value_start; + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark); + break; + } + cl = c | 0x20; + if (cl < 'a' || cl > 'z') { + multipart_log_c("invalid character in header field"); + p->error_unexpected = c; + ERROR_OUT(MPPE_INVALID_HEADER_FIELD_CHAR); + } + if (is_last) { + EMIT_DATA_CB(header_field, i + 1, buf + mark, i - mark + 1); + } break; - } - return i; - - case s_part_data_final_hyphen: - multipart_log("s_part_data_final_hyphen"); - if (c == '-') { - NOTIFY_CB(body_end); - p->state = s_end; + case s_headers_almost_done: + multipart_log_c("s_headers_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } + p->state = s_part_data_start; break; - } - return i; - - case s_part_data_end: - multipart_log("s_part_data_end"); - if (c == LF) { + case s_header_value_start: + multipart_log_c("s_header_value_start"); + if (c == ' ') { + break; + } + mark = i; + p->state = s_header_value; + /* fallthrough */ + /* no break */ + case s_header_value: + multipart_log_c("s_header_value"); + if (c == CR) { + p->state = s_header_value_almost_done; + EMIT_DATA_CB(header_value, i + 1, buf + mark, i - mark); + } else if (is_last) { + ERROR_EXPECT(MPPE_HEADER_VALUE_INCOMPLETE, CR); + } + break; + case s_header_value_almost_done: + multipart_log_c("s_header_value_almost_done"); + if (c != LF) { + ERROR_EXPECT(MPPE_INVALID_HEADER_VALUE_CHAR, LF); + } p->state = s_header_field_start; - NOTIFY_CB(part_data_begin); break; + case s_part_data_start: + multipart_log_c("s_part_data_start"); + mark = i; + p->state = s_part_data; + NOTIFY_CB(headers_complete, i); + /* fallthrough */ + /* no break */ + case s_part_data: + data_rollback: + multipart_log_c("s_part_data"); + mark_end = i + 1; + if (c == CR) { + if (mark_end - mark - 1 > 0) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark - 1); + } + mark = i; + p->state = s_part_data_almost_boundary; + break; + } + if (is_last) { + EMIT_DATA_CB(part_data, i + 1, buf + mark, mark_end - mark); + } + break; + case s_part_data_almost_boundary: + multipart_log_c("s_part_data_almost_boundary"); + if (c != LF) { + EMIT_DATA_CB(part_data, i + 1, "\r", 1); + p->state = s_part_data; + mark = i; + goto data_rollback; + } else { + p->state = s_part_data_boundary; + p->index = 0; + break; + } + case s_part_data_boundary: + multipart_log_c("s_part_data_boundary"); + if (p->boundary[p->index] != c) { + EMIT_DATA_CB(part_data, i + 1, "\r\n", 2); + if (p->index > 0) { + EMIT_DATA_CB(part_data, i + 1, p->boundary, p->index); + } + mark = i; + p->state = s_part_data; + goto data_rollback; + } else { + p->index++; + if (p->index == p->boundary_length) { + p->state = s_part_data_almost_end; + } + break; + } + case s_part_data_almost_end: + multipart_log_c("s_part_data_almost_end"); + if (c == '-') { + p->state = s_part_data_final_hyphen; + NOTIFY_CB(part_data_end, i + 1); + break; + } + if (c == CR) { + p->state = s_part_data_end; + NOTIFY_CB(part_data_end, i + 1); + break; + } + // should be end or another part + multipart_log("expecting '-' or CR at %zu but it's \\x%0.2x", i, c); + ERROR_OUT(MPPE_BAD_PART_END); + case s_part_data_final_hyphen: + multipart_log_c("s_part_data_final_hyphen"); + if (c == '-') { + p->state = s_end; + NOTIFY_CB(body_end, i); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_part_data_end: + multipart_log_c("s_part_data_end"); + if (c == LF) { + p->state = s_header_field_start; + NOTIFY_CB(part_data_begin, i + 1); + break; + } + // should be - + ERROR_EXPECT(MPPE_END_BOUNDARY_NO_DASH, '-'); + case s_end: + multipart_log_c("s_end"); + break; + default: + multipart_log_c("Multipart parser unrecoverable error"); + ERROR_OUT(MPPE_UNKNOWN); } - return i; - - case s_end: - multipart_log("s_end: %02X", (int) c); - break; - - default: - multipart_log("Multipart parser unrecoverable error"); - return 0; + ++i; } - ++ i; - } - - return len; + return i; } diff --git a/thirdparty/multipart_parser.h b/thirdparty/multipart_parser.h index abf913d255d..3d470b816e6 100644 --- a/thirdparty/multipart_parser.h +++ b/thirdparty/multipart_parser.h @@ -1,15 +1,17 @@ /* Based on node-formidable by Felix Geisendörfer * Igor Afonov - afonov@gmail.com - 2012 * MIT License - http://www.opensource.org/licenses/mit-license.php + * @link https://github.com/libcat/libcat/blob/develop/deps/multipart_parser */ + #ifndef _multipart_parser_h #define _multipart_parser_h #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif +#include #include #include @@ -17,41 +19,94 @@ typedef struct multipart_parser multipart_parser; typedef struct multipart_parser_settings multipart_parser_settings; typedef struct multipart_parser_state multipart_parser_state; -typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length); -typedef int (*multipart_notify_cb) (multipart_parser*); - -struct multipart_parser { - void * data; - void * fp; - - size_t index; - size_t boundary_length; +typedef int (*multipart_data_cb)(multipart_parser *, const char *at, size_t length); +typedef int (*multipart_notify_cb)(multipart_parser *); + +enum multipart_error { + MPPE_OK = 0, + MPPE_PAUSED, + MPPE_UNKNOWN, + MPPE_BOUNDARY_END_NO_CRLF, + MPPE_BAD_START_BOUNDARY, + MPPE_INVALID_HEADER_FIELD_CHAR, + MPPE_INVALID_HEADER_VALUE_CHAR, + MPPE_BAD_PART_END, + MPPE_END_BOUNDARY_NO_DASH, + MPPE_HEADER_VALUE_INCOMPLETE, +}; - unsigned char state; +#define MPPE_ERROR -1 - const multipart_parser_settings* settings; +// from RFC2046 +#define BOUNDARY_MAX_LEN 70 - char* lookbehind; - char multipart_boundary[1]; +struct multipart_parser { + /* private holder for callbacks */ + const multipart_parser_settings *settings; + /* private internal index for matching boundary */ + size_t index; + /* public error unexpected char index */ + size_t error_i; + /* private boundary length + 2 ("--") */ + unsigned char boundary_length; + FILE *fp; + void *data; + /* private FSM state */ + unsigned char state; + /* public error reason */ + unsigned char error_reason; + /* private boundary storage: "--" + boundary */ + char boundary[(2 + BOUNDARY_MAX_LEN) * 2 + 9]; + /* public error expected char */ + char error_expected; + /* public error unexpected char */ + char error_unexpected; }; struct multipart_parser_settings { - multipart_data_cb on_header_field; - multipart_data_cb on_header_value; - multipart_data_cb on_part_data; - - multipart_notify_cb on_part_data_begin; - multipart_notify_cb on_headers_complete; - multipart_notify_cb on_part_data_end; - multipart_notify_cb on_body_end; + /* + * data callback called on header field coming + * for example data is "Content-Type" with length 12 + */ + multipart_data_cb on_header_field; + /* + * data callback called on header value coming + * for example data is "plain/text" with length 10 + */ + multipart_data_cb on_header_value; + /* + * data callback called on body data coming, + * will be called repeatedly until data end + */ + multipart_data_cb on_part_data; + /* + * before "--" boundary + */ + multipart_notify_cb on_part_data_begin; + /* + * after all headers line "\r\n", before body + */ + multipart_notify_cb on_headers_complete; + /* + * after body, before next "--" boundary + */ + multipart_notify_cb on_part_data_end; + /* + * after last "--" boundary "--" + */ + multipart_notify_cb on_body_end; }; -multipart_parser* multipart_parser_init - (const char *boundary, size_t boundary_length, const multipart_parser_settings* settings); +multipart_parser *multipart_parser_init(const char *boundary, + size_t boundary_length, + const multipart_parser_settings *settings); +void multipart_parser_free(multipart_parser *p); -void multipart_parser_free(multipart_parser* p); - -size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len); +/** + * The multipart header must be complete, otherwise it will be parsed incorrectly + */ +ssize_t multipart_parser_execute(multipart_parser *p, const char *buf, size_t len); +int multipart_parser_error_msg(multipart_parser *p, char *buf, size_t len); #ifdef __cplusplus } /* extern "C" */ diff --git a/thirdparty/nghttp2/COPYING b/thirdparty/nghttp2/COPYING new file mode 100644 index 00000000000..80201792ec7 --- /dev/null +++ b/thirdparty/nghttp2/COPYING @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/thirdparty/nghttp2/LICENSE b/thirdparty/nghttp2/LICENSE new file mode 100644 index 00000000000..fe4d503d011 --- /dev/null +++ b/thirdparty/nghttp2/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. \ No newline at end of file diff --git a/thirdparty/nghttp2/nghttp2.h b/thirdparty/nghttp2/nghttp2.h index 870e33d8629..cf099d2e0c8 100644 --- a/thirdparty/nghttp2/nghttp2.h +++ b/thirdparty/nghttp2/nghttp2.h @@ -28,7 +28,12 @@ /* Define WIN32 when build target is Win32 API (borrowed from libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 +# define WIN32 +#endif + +/* Compatibility for non-Clang compilers */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 #endif #ifdef __cplusplus @@ -40,9 +45,9 @@ extern "C" { /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ -#include +# include #else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -#include +# include #endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ #include #include @@ -66,20 +71,21 @@ static inline uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len const char *nghttp2_strerror(int error_code); #ifdef NGHTTP2_STATICLIB -#define NGHTTP2_EXTERN -#elif defined(WIN32) -#ifdef BUILDING_NGHTTP2 -#define NGHTTP2_EXTERN __declspec(dllexport) -#else /* !BUILDING_NGHTTP2 */ -#define NGHTTP2_EXTERN __declspec(dllimport) -#endif /* !BUILDING_NGHTTP2 */ -#else /* !defined(WIN32) */ -#ifdef BUILDING_NGHTTP2 -#define NGHTTP2_EXTERN __attribute__((visibility("default"))) -#else /* !BUILDING_NGHTTP2 */ -#define NGHTTP2_EXTERN -#endif /* !BUILDING_NGHTTP2 */ -#endif /* !defined(WIN32) */ +# define NGHTTP2_EXTERN +#elif defined(WIN32) || (__has_declspec_attribute(dllexport) && \ + __has_declspec_attribute(dllimport)) +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGHTTP2 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN +# endif /* !BUILDING_NGHTTP2 */ +#endif /* !defined(WIN32) */ /** * @macro @@ -229,6 +235,13 @@ typedef struct { */ #define NGHTTP2_CLIENT_MAGIC_LEN 24 +/** + * @macro + * + * The default max number of settings per SETTINGS frame + */ +#define NGHTTP2_DEFAULT_MAX_SETTINGS 32 + /** * @enum * @@ -395,12 +408,22 @@ typedef enum { */ NGHTTP2_ERR_CANCEL = -535, /** - * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is - * under unexpected condition and processing was terminated (e.g., - * out of memory). If application receives this error code, it must - * stop using that :type:`nghttp2_session` object and only allowed - * operation for that object is deallocate it using - * `nghttp2_session_del()`. + * When a local endpoint expects to receive SETTINGS frame, it + * receives an other type of frame. + */ + NGHTTP2_ERR_SETTINGS_EXPECTED = -536, + /** + * When a local endpoint receives too many settings entries + * in a single SETTINGS frame. + */ + NGHTTP2_ERR_TOO_MANY_SETTINGS = -537, + /** + * The errors < :enum:`nghttp2_error.NGHTTP2_ERR_FATAL` mean that + * the library is under unexpected condition and processing was + * terminated (e.g., out of memory). If application receives this + * error code, it must stop using that :type:`nghttp2_session` + * object and only allowed operation for that object is deallocate + * it using `nghttp2_session_del()`. */ NGHTTP2_ERR_FATAL = -900, /** @@ -520,9 +543,9 @@ typedef struct { * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with - * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application - * is constructing this struct, |name| is not required to be - * NULL-terminated. + * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`). + * When application is constructing this struct, |name| is not + * required to be NULL-terminated. */ uint8_t *name; /** @@ -533,9 +556,9 @@ typedef struct { * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with - * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When - * application is constructing this struct, |value| is not required - * to be NULL-terminated. + * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`). + * When application is constructing this struct, |value| is not + * required to be NULL-terminated. */ uint8_t *value; /** @@ -604,7 +627,12 @@ typedef enum { * The ALTSVC frame, which is defined in `RFC 7383 * `_. */ - NGHTTP2_ALTSVC = 0x0a + NGHTTP2_ALTSVC = 0x0a, + /** + * The ORIGIN frame, which is defined by `RFC 8336 + * `_. + */ + NGHTTP2_ORIGIN = 0x0c } nghttp2_frame_type; /** @@ -668,7 +696,12 @@ typedef enum { /** * SETTINGS_MAX_HEADER_LIST_SIZE */ - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, + /** + * SETTINGS_ENABLE_CONNECT_PROTOCOL + * (`RFC 8441 `_) + */ + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ @@ -682,8 +715,8 @@ typedef enum { * * Default maximum number of incoming concurrent streams. Use * `nghttp2_submit_settings()` with - * :enum:`NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` to change the - * maximum number of incoming concurrent streams. + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` + * to change the maximum number of incoming concurrent streams. * * .. note:: * @@ -829,7 +862,6 @@ typedef enum { NGHTTP2_DATA_FLAG_NO_COPY = 0x04 } nghttp2_data_flag; - /** * @functypedef * @@ -886,7 +918,6 @@ typedef struct { nghttp2_realloc realloc; } nghttp2_mem; - /** * @struct * @@ -1260,7 +1291,7 @@ typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int @@ -1314,7 +1345,7 @@ NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int @@ -1328,24 +1359,24 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, * the |buf| of length |buflen|. * * If |buf| is not large enough to store the deflated header block, - * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The - * caller should use `nghttp2_hd_deflate_bound()` to know the upper - * bound of buffer size required to deflate given header name/value - * pairs. + * this function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always - * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, @@ -1361,23 +1392,24 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, * must be set in len field of :type:`nghttp2_vec`. If and only if * one chunk is filled up completely, next chunk will be used. If * |vec| is not large enough to store the deflated header block, this - * function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. * * Once this function fails, subsequent call of this function always - * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. * * After this function returns, it is safe to delete the |nva|. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Deflation process has failed. - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, @@ -1457,7 +1489,7 @@ typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); @@ -1506,9 +1538,9 @@ NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_STATE` + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called while header block is being inflated. * Probably, application missed to call * `nghttp2_hd_inflate_end_headers()`. @@ -1546,7 +1578,8 @@ typedef enum { * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of - * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. @@ -1569,11 +1602,11 @@ typedef enum { * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. - * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: @@ -1628,7 +1661,8 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of - * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. @@ -1644,8 +1678,9 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, * for the next header block input. * * In other words, if |in_final| is nonzero, and this function returns - * |inlen|, you can assert that :enum:`NGHTTP2_HD_INFLATE_FINAL` is - * set in |*inflate_flags|. + * |inlen|, you can assert that + * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in + * |*inflate_flags|. * * The caller can feed complete compressed header block. It also can * feed it in several chunks. The caller must set |in_final| to @@ -1655,11 +1690,11 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. - * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: @@ -1834,7 +1869,7 @@ typedef void (*nghttp2_debug_vprintf_callback)(const char *format, */ NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( nghttp2_debug_vprintf_callback debug_vprintf_callback); - + #ifdef __cplusplus } #endif diff --git a/thirdparty/nghttp2/nghttp2_buf.c b/thirdparty/nghttp2/nghttp2_buf.c index 9d4bbead38f..e618400b012 100644 --- a/thirdparty/nghttp2/nghttp2_buf.c +++ b/thirdparty/nghttp2/nghttp2_buf.c @@ -79,8 +79,10 @@ void nghttp2_buf_reset(nghttp2_buf *buf) { } void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { - buf->begin = buf->pos = buf->last = buf->mark = begin; - buf->end = begin + len; + buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin; + if (len) { + buf->end += len; + } } static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, diff --git a/thirdparty/nghttp2/nghttp2_buf.h b/thirdparty/nghttp2/nghttp2_buf.h index 9a21004ad38..eaddd868527 100644 --- a/thirdparty/nghttp2/nghttp2_buf.h +++ b/thirdparty/nghttp2/nghttp2_buf.h @@ -393,7 +393,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs); void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); /* - * Returns nonzero if bufs->cur->next is not emtpy. + * Returns nonzero if bufs->cur->next is not empty. */ int nghttp2_bufs_next_present(nghttp2_bufs *bufs); diff --git a/thirdparty/nghttp2/nghttp2_hd.c b/thirdparty/nghttp2/nghttp2_hd.c index 8d98abe0838..aa54243a665 100644 --- a/thirdparty/nghttp2/nghttp2_hd.c +++ b/thirdparty/nghttp2/nghttp2_hd.c @@ -41,7 +41,7 @@ /* 3rd parameter is nghttp2_token value for header field name. We use first enum value if same header names are repeated (e.g., :status). */ -static nghttp2_hd_static_entry static_table[] = { +static const nghttp2_hd_static_entry static_table[] = { MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), @@ -267,6 +267,15 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { break; } break; + case 9: + switch (name[8]) { + case 'l': + if (memeq(":protoco", name, 8)) { + return NGHTTP2_TOKEN__PROTOCOL; + } + break; + } + break; case 10: switch (name[9]) { case 'e': @@ -658,9 +667,9 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { context->mem = mem; context->bad = 0; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max / - NGHTTP2_HD_ENTRY_OVERHEAD, - mem); + rv = hd_ringbuf_init( + &context->hd_table, + context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); if (rv != 0) { return rv; } @@ -860,6 +869,11 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, for (; in != last; ++in, shift += 7) { uint32_t add = *in & 0x7f; + if (shift >= 32) { + DEBUGF("inflate: shift exponent overflow\n"); + return -1; + } + if ((UINT32_MAX >> shift) < add) { DEBUGF("inflate: integer overflow on shift\n"); return -1; @@ -999,7 +1013,7 @@ static uint8_t pack_first_byte(int indexing_mode) { case NGHTTP2_HD_NEVER_INDEXING: return 0x10u; default: - abort(); + assert(0); } /* This is required to compile with android NDK r10d + --enable-werror */ @@ -1150,7 +1164,7 @@ static search_result search_static_table(const nghttp2_nv *nv, int32_t token, int name_only) { search_result res = {token, 0}; int i; - nghttp2_hd_static_entry *ent; + const nghttp2_hd_static_entry *ent; if (name_only) { return res; @@ -1175,7 +1189,7 @@ static search_result search_hd_table(nghttp2_hd_context *context, int indexing_mode, nghttp2_hd_map *map, uint32_t hash) { search_result res = {-1, 0}; - nghttp2_hd_entry *ent; + const nghttp2_hd_entry *ent; int exact_match; int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; @@ -1280,8 +1294,9 @@ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) ->nv; } else { - nghttp2_hd_static_entry *ent = &static_table[idx]; - nghttp2_hd_nv nv = {&ent->name, &ent->value, ent->token, + const nghttp2_hd_static_entry *ent = &static_table[idx]; + nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, + (nghttp2_rcbuf *)&ent->value, ent->token, NGHTTP2_NV_FLAG_NONE}; return nv; } @@ -1371,7 +1386,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { nghttp2_hd_nv hd_nv; - if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { + if (idx != -1) { hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; nghttp2_rcbuf_incref(hd_nv.name); } else { @@ -1532,6 +1547,7 @@ size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) { size_t n = 0; size_t i; + (void)deflater; /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - 1 using 4 bit prefix requires 6 bytes. We may emit this at most @@ -2272,10 +2288,6 @@ ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); } -static size_t hd_get_num_table_entries(nghttp2_hd_context *context) { - return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; -} - static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, size_t idx) { if (idx == 0) { @@ -2292,7 +2304,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, } size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { - return hd_get_num_table_entries(&deflater->ctx); + return get_max_index(&deflater->ctx); } const nghttp2_nv * @@ -2311,7 +2323,7 @@ nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { } size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { - return hd_get_num_table_entries(&inflater->ctx); + return get_max_index(&inflater->ctx); } const nghttp2_nv * diff --git a/thirdparty/nghttp2/nghttp2_hd.h b/thirdparty/nghttp2/nghttp2_hd.h index ac104462426..6cd75e1310a 100644 --- a/thirdparty/nghttp2/nghttp2_hd.h +++ b/thirdparty/nghttp2/nghttp2_hd.h @@ -106,6 +106,7 @@ typedef enum { NGHTTP2_TOKEN_KEEP_ALIVE, NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_UPGRADE, + NGHTTP2_TOKEN__PROTOCOL, } nghttp2_token; struct nghttp2_hd_entry; @@ -206,7 +207,9 @@ typedef struct { #define HD_MAP_SIZE 128 -typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map; +typedef struct { + nghttp2_hd_entry *table[HD_MAP_SIZE]; +} nghttp2_hd_map; struct nghttp2_hd_deflater { nghttp2_hd_context ctx; @@ -308,7 +311,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); * * This function expands |bufs| as necessary to store the result. If * buffers is full and the process still requires more space, this - * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. + * function fails and returns NGHTTP2_ERR_HEADER_COMP. * * After this function returns, it is safe to delete the |nva|. * @@ -422,4 +425,10 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, nghttp2_buf *buf, const uint8_t *src, size_t srclen, int fin); +/* + * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| + * indicates that huffman decoding context is in failure state. + */ +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); + #endif /* NGHTTP2_HD_H */ diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman.c b/thirdparty/nghttp2/nghttp2_hd_huffman.c index 8881aacb2e6..c0ea3bc3e6d 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman.c @@ -30,114 +30,6 @@ #include "nghttp2_hd.h" -/* - * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits| - * bits are not filled yet. The |rembits| must be in range [1, 8], - * inclusive. At the end of the process, the |*dest_ptr| is updated - * and points where next output should be placed. The number of - * unfilled bits in the pointed location is returned. - */ -static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, - size_t rembits, const nghttp2_huff_sym *sym) { - int rv; - size_t nbits = sym->nbits; - uint32_t code = sym->code; - - /* We assume that sym->nbits <= 32 */ - if (rembits > nbits) { - nghttp2_bufs_fast_orb_hold(bufs, (uint8_t)(code << (rembits - nbits))); - return (ssize_t)(rembits - nbits); - } - - if (rembits == nbits) { - nghttp2_bufs_fast_orb(bufs, (uint8_t)code); - --*avail_ptr; - return 8; - } - - nghttp2_bufs_fast_orb(bufs, (uint8_t)(code >> (nbits - rembits))); - --*avail_ptr; - - nbits -= rembits; - if (nbits & 0x7) { - /* align code to MSB byte boundary */ - code <<= 8 - (nbits & 0x7); - } - - if (*avail_ptr < (nbits + 7) / 8) { - /* slow path */ - if (nbits > 24) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 24)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits > 16) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 16)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits > 8) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 8)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits == 8) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)code); - if (rv != 0) { - return rv; - } - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return 8; - } - - rv = nghttp2_bufs_addb_hold(bufs, (uint8_t)code); - if (rv != 0) { - return rv; - } - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); - } - - /* fast path, since most code is less than 8 */ - if (nbits < 8) { - nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); - } - - /* handle longer code path */ - if (nbits > 24) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 24)); - nbits -= 8; - } - - if (nbits > 16) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 16)); - nbits -= 8; - } - - if (nbits > 8) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 8)); - nbits -= 8; - } - - if (nbits == 8) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return 8; - } - - nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); -} - size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { size_t i; size_t nbits = 0; @@ -151,81 +43,101 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, size_t srclen) { - int rv; - ssize_t rembits = 8; - size_t i; + const nghttp2_huff_sym *sym; + const uint8_t *end = src + srclen; + uint64_t code = 0; + uint32_t x; + size_t nbits = 0; size_t avail; + int rv; avail = nghttp2_bufs_cur_avail(bufs); - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; - if (rembits == 8) { - if (avail) { - nghttp2_bufs_fast_addb_hold(bufs, 0); - } else { - rv = nghttp2_bufs_addb_hold(bufs, 0); - if (rv != 0) { - return rv; - } - avail = nghttp2_bufs_cur_avail(bufs); + for (; src != end;) { + sym = &huff_sym_table[*src++]; + code |= (uint64_t)sym->code << (32 - nbits); + nbits += sym->nbits; + if (nbits < 32) { + continue; + } + if (avail >= 4) { + x = htonl((uint32_t)(code >> 32)); + memcpy(bufs->cur->buf.last, &x, 4); + bufs->cur->buf.last += 4; + avail -= 4; + code <<= 32; + nbits -= 32; + continue; + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; } + code <<= 8; + nbits -= 8; } - rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym); - if (rembits < 0) { - return (int)rembits; + + avail = nghttp2_bufs_cur_avail(bufs); + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; } + code <<= 8; + nbits -= 8; } - /* 256 is special terminal symbol, pad with its prefix */ - if (rembits < 8) { - /* if rembits < 8, we should have at least 1 buffer space - available */ - const nghttp2_huff_sym *sym = &huff_sym_table[256]; - assert(avail); - /* Caution we no longer adjust avail here */ - nghttp2_bufs_fast_orb( - bufs, (uint8_t)(sym->code >> (sym->nbits - (size_t)rembits))); + + if (nbits) { + rv = nghttp2_bufs_addb( + bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); + if (rv != 0) { + return rv; + } } return 0; } void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { - ctx->state = 0; - ctx->accept = 1; + ctx->fstate = NGHTTP2_HUFF_ACCEPTED; } ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, nghttp2_buf *buf, const uint8_t *src, size_t srclen, int final) { - size_t i; + const uint8_t *end = src + srclen; + nghttp2_huff_decode node = {ctx->fstate, 0}; + const nghttp2_huff_decode *t = &node; + uint8_t c; /* We use the decoding algorithm described in http://graphics.ics.uci.edu/pub/Prefix.pdf */ - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_decode *t; - - t = &huff_decode_table[ctx->state][src[i] >> 4]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { + for (; src != end;) { + c = *src++; + t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; + if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } - t = &huff_decode_table[t->state][src[i] & 0xf]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { + t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf]; + if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } - - ctx->state = t->state; - ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; } - if (final && !ctx->accept) { + + ctx->fstate = t->fstate; + + if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) { return NGHTTP2_ERR_HEADER_COMP; } - return (ssize_t)i; + + return (ssize_t)srclen; +} + +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { + return ctx->fstate == 0x100; } diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman.h b/thirdparty/nghttp2/nghttp2_hd_huffman.h index eadb7566a73..4bd850d9975 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman.h +++ b/thirdparty/nghttp2/nghttp2_hd_huffman.h @@ -26,25 +26,25 @@ #define NGHTTP2_HD_HUFFMAN_H #include +#include typedef enum { /* FSA accepts this state as the end of huffman encoding sequence. */ - NGHTTP2_HUFF_ACCEPTED = 1, + NGHTTP2_HUFF_ACCEPTED = 1 << 14, /* This state emits symbol */ - NGHTTP2_HUFF_SYM = (1 << 1), - /* If state machine reaches this state, decoding fails. */ - NGHTTP2_HUFF_FAIL = (1 << 2) + NGHTTP2_HUFF_SYM = 1 << 15, } nghttp2_huff_decode_flag; typedef struct { - /* huffman decoding state, which is actually the node ID of internal - huffman tree. We have 257 leaf nodes, but they are identical to - root node other than emitting a symbol, so we have 256 internal - nodes [1..255], inclusive. */ - uint8_t state; - /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ - uint8_t flags; + /* fstate is the current huffman decoding state, which is actually + the node ID of internal huffman tree with + nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they + are identical to root node other than emitting a symbol, so we + have 256 internal nodes [1..255], inclusive. The node ID 256 is + a special node and it is a terminal state that means decoding + failed. */ + uint16_t fstate; /* symbol if NGHTTP2_HUFF_SYM flag set */ uint8_t sym; } nghttp2_huff_decode; @@ -52,12 +52,8 @@ typedef struct { typedef nghttp2_huff_decode huff_decode_table_type[16]; typedef struct { - /* Current huffman decoding state. We stripped leaf nodes, so the - value range is [0..255], inclusive. */ - uint8_t state; - /* nonzero if we can say that the decoding process succeeds at this - state */ - uint8_t accept; + /* fstate is the current huffman decoding state. */ + uint16_t fstate; } nghttp2_hd_huff_decode_context; typedef struct { diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c index 5ef4a956b93..2e2e13f7bee 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c @@ -27,4935 +27,4954 @@ /* Generated by mkhufftbl.py */ const nghttp2_huff_sym huff_sym_table[] = { - {13, 0x1ff8u}, {23, 0x7fffd8u}, {28, 0xfffffe2u}, {28, 0xfffffe3u}, - {28, 0xfffffe4u}, {28, 0xfffffe5u}, {28, 0xfffffe6u}, {28, 0xfffffe7u}, - {28, 0xfffffe8u}, {24, 0xffffeau}, {30, 0x3ffffffcu}, {28, 0xfffffe9u}, - {28, 0xfffffeau}, {30, 0x3ffffffdu}, {28, 0xfffffebu}, {28, 0xfffffecu}, - {28, 0xfffffedu}, {28, 0xfffffeeu}, {28, 0xfffffefu}, {28, 0xffffff0u}, - {28, 0xffffff1u}, {28, 0xffffff2u}, {30, 0x3ffffffeu}, {28, 0xffffff3u}, - {28, 0xffffff4u}, {28, 0xffffff5u}, {28, 0xffffff6u}, {28, 0xffffff7u}, - {28, 0xffffff8u}, {28, 0xffffff9u}, {28, 0xffffffau}, {28, 0xffffffbu}, - {6, 0x14u}, {10, 0x3f8u}, {10, 0x3f9u}, {12, 0xffau}, - {13, 0x1ff9u}, {6, 0x15u}, {8, 0xf8u}, {11, 0x7fau}, - {10, 0x3fau}, {10, 0x3fbu}, {8, 0xf9u}, {11, 0x7fbu}, - {8, 0xfau}, {6, 0x16u}, {6, 0x17u}, {6, 0x18u}, - {5, 0x0u}, {5, 0x1u}, {5, 0x2u}, {6, 0x19u}, - {6, 0x1au}, {6, 0x1bu}, {6, 0x1cu}, {6, 0x1du}, - {6, 0x1eu}, {6, 0x1fu}, {7, 0x5cu}, {8, 0xfbu}, - {15, 0x7ffcu}, {6, 0x20u}, {12, 0xffbu}, {10, 0x3fcu}, - {13, 0x1ffau}, {6, 0x21u}, {7, 0x5du}, {7, 0x5eu}, - {7, 0x5fu}, {7, 0x60u}, {7, 0x61u}, {7, 0x62u}, - {7, 0x63u}, {7, 0x64u}, {7, 0x65u}, {7, 0x66u}, - {7, 0x67u}, {7, 0x68u}, {7, 0x69u}, {7, 0x6au}, - {7, 0x6bu}, {7, 0x6cu}, {7, 0x6du}, {7, 0x6eu}, - {7, 0x6fu}, {7, 0x70u}, {7, 0x71u}, {7, 0x72u}, - {8, 0xfcu}, {7, 0x73u}, {8, 0xfdu}, {13, 0x1ffbu}, - {19, 0x7fff0u}, {13, 0x1ffcu}, {14, 0x3ffcu}, {6, 0x22u}, - {15, 0x7ffdu}, {5, 0x3u}, {6, 0x23u}, {5, 0x4u}, - {6, 0x24u}, {5, 0x5u}, {6, 0x25u}, {6, 0x26u}, - {6, 0x27u}, {5, 0x6u}, {7, 0x74u}, {7, 0x75u}, - {6, 0x28u}, {6, 0x29u}, {6, 0x2au}, {5, 0x7u}, - {6, 0x2bu}, {7, 0x76u}, {6, 0x2cu}, {5, 0x8u}, - {5, 0x9u}, {6, 0x2du}, {7, 0x77u}, {7, 0x78u}, - {7, 0x79u}, {7, 0x7au}, {7, 0x7bu}, {15, 0x7ffeu}, - {11, 0x7fcu}, {14, 0x3ffdu}, {13, 0x1ffdu}, {28, 0xffffffcu}, - {20, 0xfffe6u}, {22, 0x3fffd2u}, {20, 0xfffe7u}, {20, 0xfffe8u}, - {22, 0x3fffd3u}, {22, 0x3fffd4u}, {22, 0x3fffd5u}, {23, 0x7fffd9u}, - {22, 0x3fffd6u}, {23, 0x7fffdau}, {23, 0x7fffdbu}, {23, 0x7fffdcu}, - {23, 0x7fffddu}, {23, 0x7fffdeu}, {24, 0xffffebu}, {23, 0x7fffdfu}, - {24, 0xffffecu}, {24, 0xffffedu}, {22, 0x3fffd7u}, {23, 0x7fffe0u}, - {24, 0xffffeeu}, {23, 0x7fffe1u}, {23, 0x7fffe2u}, {23, 0x7fffe3u}, - {23, 0x7fffe4u}, {21, 0x1fffdcu}, {22, 0x3fffd8u}, {23, 0x7fffe5u}, - {22, 0x3fffd9u}, {23, 0x7fffe6u}, {23, 0x7fffe7u}, {24, 0xffffefu}, - {22, 0x3fffdau}, {21, 0x1fffddu}, {20, 0xfffe9u}, {22, 0x3fffdbu}, - {22, 0x3fffdcu}, {23, 0x7fffe8u}, {23, 0x7fffe9u}, {21, 0x1fffdeu}, - {23, 0x7fffeau}, {22, 0x3fffddu}, {22, 0x3fffdeu}, {24, 0xfffff0u}, - {21, 0x1fffdfu}, {22, 0x3fffdfu}, {23, 0x7fffebu}, {23, 0x7fffecu}, - {21, 0x1fffe0u}, {21, 0x1fffe1u}, {22, 0x3fffe0u}, {21, 0x1fffe2u}, - {23, 0x7fffedu}, {22, 0x3fffe1u}, {23, 0x7fffeeu}, {23, 0x7fffefu}, - {20, 0xfffeau}, {22, 0x3fffe2u}, {22, 0x3fffe3u}, {22, 0x3fffe4u}, - {23, 0x7ffff0u}, {22, 0x3fffe5u}, {22, 0x3fffe6u}, {23, 0x7ffff1u}, - {26, 0x3ffffe0u}, {26, 0x3ffffe1u}, {20, 0xfffebu}, {19, 0x7fff1u}, - {22, 0x3fffe7u}, {23, 0x7ffff2u}, {22, 0x3fffe8u}, {25, 0x1ffffecu}, - {26, 0x3ffffe2u}, {26, 0x3ffffe3u}, {26, 0x3ffffe4u}, {27, 0x7ffffdeu}, - {27, 0x7ffffdfu}, {26, 0x3ffffe5u}, {24, 0xfffff1u}, {25, 0x1ffffedu}, - {19, 0x7fff2u}, {21, 0x1fffe3u}, {26, 0x3ffffe6u}, {27, 0x7ffffe0u}, - {27, 0x7ffffe1u}, {26, 0x3ffffe7u}, {27, 0x7ffffe2u}, {24, 0xfffff2u}, - {21, 0x1fffe4u}, {21, 0x1fffe5u}, {26, 0x3ffffe8u}, {26, 0x3ffffe9u}, - {28, 0xffffffdu}, {27, 0x7ffffe3u}, {27, 0x7ffffe4u}, {27, 0x7ffffe5u}, - {20, 0xfffecu}, {24, 0xfffff3u}, {20, 0xfffedu}, {21, 0x1fffe6u}, - {22, 0x3fffe9u}, {21, 0x1fffe7u}, {21, 0x1fffe8u}, {23, 0x7ffff3u}, - {22, 0x3fffeau}, {22, 0x3fffebu}, {25, 0x1ffffeeu}, {25, 0x1ffffefu}, - {24, 0xfffff4u}, {24, 0xfffff5u}, {26, 0x3ffffeau}, {23, 0x7ffff4u}, - {26, 0x3ffffebu}, {27, 0x7ffffe6u}, {26, 0x3ffffecu}, {26, 0x3ffffedu}, - {27, 0x7ffffe7u}, {27, 0x7ffffe8u}, {27, 0x7ffffe9u}, {27, 0x7ffffeau}, - {27, 0x7ffffebu}, {28, 0xffffffeu}, {27, 0x7ffffecu}, {27, 0x7ffffedu}, - {27, 0x7ffffeeu}, {27, 0x7ffffefu}, {27, 0x7fffff0u}, {26, 0x3ffffeeu}, - {30, 0x3fffffffu}}; + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; const nghttp2_huff_decode huff_decode_table[][16] = { /* 0 */ { - {4, 0x00, 0}, - {5, 0x00, 0}, - {7, 0x00, 0}, - {8, 0x00, 0}, - {11, 0x00, 0}, - {12, 0x00, 0}, - {16, 0x00, 0}, - {19, 0x00, 0}, - {25, 0x00, 0}, - {28, 0x00, 0}, - {32, 0x00, 0}, - {35, 0x00, 0}, - {42, 0x00, 0}, - {49, 0x00, 0}, - {57, 0x00, 0}, - {64, 0x01, 0}, + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, }, /* 1 */ { - {0, 0x03, 48}, - {0, 0x03, 49}, - {0, 0x03, 50}, - {0, 0x03, 97}, - {0, 0x03, 99}, - {0, 0x03, 101}, - {0, 0x03, 105}, - {0, 0x03, 111}, - {0, 0x03, 115}, - {0, 0x03, 116}, - {13, 0x00, 0}, - {14, 0x00, 0}, - {17, 0x00, 0}, - {18, 0x00, 0}, - {20, 0x00, 0}, - {21, 0x00, 0}, + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, }, /* 2 */ { - {1, 0x02, 48}, - {22, 0x03, 48}, - {1, 0x02, 49}, - {22, 0x03, 49}, - {1, 0x02, 50}, - {22, 0x03, 50}, - {1, 0x02, 97}, - {22, 0x03, 97}, - {1, 0x02, 99}, - {22, 0x03, 99}, - {1, 0x02, 101}, - {22, 0x03, 101}, - {1, 0x02, 105}, - {22, 0x03, 105}, - {1, 0x02, 111}, - {22, 0x03, 111}, + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, }, /* 3 */ { - {2, 0x02, 48}, - {9, 0x02, 48}, - {23, 0x02, 48}, - {40, 0x03, 48}, - {2, 0x02, 49}, - {9, 0x02, 49}, - {23, 0x02, 49}, - {40, 0x03, 49}, - {2, 0x02, 50}, - {9, 0x02, 50}, - {23, 0x02, 50}, - {40, 0x03, 50}, - {2, 0x02, 97}, - {9, 0x02, 97}, - {23, 0x02, 97}, - {40, 0x03, 97}, + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, }, /* 4 */ { - {3, 0x02, 48}, - {6, 0x02, 48}, - {10, 0x02, 48}, - {15, 0x02, 48}, - {24, 0x02, 48}, - {31, 0x02, 48}, - {41, 0x02, 48}, - {56, 0x03, 48}, - {3, 0x02, 49}, - {6, 0x02, 49}, - {10, 0x02, 49}, - {15, 0x02, 49}, - {24, 0x02, 49}, - {31, 0x02, 49}, - {41, 0x02, 49}, - {56, 0x03, 49}, + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, }, /* 5 */ { - {3, 0x02, 50}, - {6, 0x02, 50}, - {10, 0x02, 50}, - {15, 0x02, 50}, - {24, 0x02, 50}, - {31, 0x02, 50}, - {41, 0x02, 50}, - {56, 0x03, 50}, - {3, 0x02, 97}, - {6, 0x02, 97}, - {10, 0x02, 97}, - {15, 0x02, 97}, - {24, 0x02, 97}, - {31, 0x02, 97}, - {41, 0x02, 97}, - {56, 0x03, 97}, + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, }, /* 6 */ { - {2, 0x02, 99}, - {9, 0x02, 99}, - {23, 0x02, 99}, - {40, 0x03, 99}, - {2, 0x02, 101}, - {9, 0x02, 101}, - {23, 0x02, 101}, - {40, 0x03, 101}, - {2, 0x02, 105}, - {9, 0x02, 105}, - {23, 0x02, 105}, - {40, 0x03, 105}, - {2, 0x02, 111}, - {9, 0x02, 111}, - {23, 0x02, 111}, - {40, 0x03, 111}, + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, }, /* 7 */ { - {3, 0x02, 99}, - {6, 0x02, 99}, - {10, 0x02, 99}, - {15, 0x02, 99}, - {24, 0x02, 99}, - {31, 0x02, 99}, - {41, 0x02, 99}, - {56, 0x03, 99}, - {3, 0x02, 101}, - {6, 0x02, 101}, - {10, 0x02, 101}, - {15, 0x02, 101}, - {24, 0x02, 101}, - {31, 0x02, 101}, - {41, 0x02, 101}, - {56, 0x03, 101}, + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, }, /* 8 */ { - {3, 0x02, 105}, - {6, 0x02, 105}, - {10, 0x02, 105}, - {15, 0x02, 105}, - {24, 0x02, 105}, - {31, 0x02, 105}, - {41, 0x02, 105}, - {56, 0x03, 105}, - {3, 0x02, 111}, - {6, 0x02, 111}, - {10, 0x02, 111}, - {15, 0x02, 111}, - {24, 0x02, 111}, - {31, 0x02, 111}, - {41, 0x02, 111}, - {56, 0x03, 111}, + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, }, /* 9 */ { - {1, 0x02, 115}, - {22, 0x03, 115}, - {1, 0x02, 116}, - {22, 0x03, 116}, - {0, 0x03, 32}, - {0, 0x03, 37}, - {0, 0x03, 45}, - {0, 0x03, 46}, - {0, 0x03, 47}, - {0, 0x03, 51}, - {0, 0x03, 52}, - {0, 0x03, 53}, - {0, 0x03, 54}, - {0, 0x03, 55}, - {0, 0x03, 56}, - {0, 0x03, 57}, + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, }, /* 10 */ { - {2, 0x02, 115}, - {9, 0x02, 115}, - {23, 0x02, 115}, - {40, 0x03, 115}, - {2, 0x02, 116}, - {9, 0x02, 116}, - {23, 0x02, 116}, - {40, 0x03, 116}, - {1, 0x02, 32}, - {22, 0x03, 32}, - {1, 0x02, 37}, - {22, 0x03, 37}, - {1, 0x02, 45}, - {22, 0x03, 45}, - {1, 0x02, 46}, - {22, 0x03, 46}, + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, }, /* 11 */ { - {3, 0x02, 115}, - {6, 0x02, 115}, - {10, 0x02, 115}, - {15, 0x02, 115}, - {24, 0x02, 115}, - {31, 0x02, 115}, - {41, 0x02, 115}, - {56, 0x03, 115}, - {3, 0x02, 116}, - {6, 0x02, 116}, - {10, 0x02, 116}, - {15, 0x02, 116}, - {24, 0x02, 116}, - {31, 0x02, 116}, - {41, 0x02, 116}, - {56, 0x03, 116}, + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, }, /* 12 */ { - {2, 0x02, 32}, - {9, 0x02, 32}, - {23, 0x02, 32}, - {40, 0x03, 32}, - {2, 0x02, 37}, - {9, 0x02, 37}, - {23, 0x02, 37}, - {40, 0x03, 37}, - {2, 0x02, 45}, - {9, 0x02, 45}, - {23, 0x02, 45}, - {40, 0x03, 45}, - {2, 0x02, 46}, - {9, 0x02, 46}, - {23, 0x02, 46}, - {40, 0x03, 46}, + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, }, /* 13 */ { - {3, 0x02, 32}, - {6, 0x02, 32}, - {10, 0x02, 32}, - {15, 0x02, 32}, - {24, 0x02, 32}, - {31, 0x02, 32}, - {41, 0x02, 32}, - {56, 0x03, 32}, - {3, 0x02, 37}, - {6, 0x02, 37}, - {10, 0x02, 37}, - {15, 0x02, 37}, - {24, 0x02, 37}, - {31, 0x02, 37}, - {41, 0x02, 37}, - {56, 0x03, 37}, + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, }, /* 14 */ { - {3, 0x02, 45}, - {6, 0x02, 45}, - {10, 0x02, 45}, - {15, 0x02, 45}, - {24, 0x02, 45}, - {31, 0x02, 45}, - {41, 0x02, 45}, - {56, 0x03, 45}, - {3, 0x02, 46}, - {6, 0x02, 46}, - {10, 0x02, 46}, - {15, 0x02, 46}, - {24, 0x02, 46}, - {31, 0x02, 46}, - {41, 0x02, 46}, - {56, 0x03, 46}, + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, }, /* 15 */ { - {1, 0x02, 47}, - {22, 0x03, 47}, - {1, 0x02, 51}, - {22, 0x03, 51}, - {1, 0x02, 52}, - {22, 0x03, 52}, - {1, 0x02, 53}, - {22, 0x03, 53}, - {1, 0x02, 54}, - {22, 0x03, 54}, - {1, 0x02, 55}, - {22, 0x03, 55}, - {1, 0x02, 56}, - {22, 0x03, 56}, - {1, 0x02, 57}, - {22, 0x03, 57}, + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, }, /* 16 */ { - {2, 0x02, 47}, - {9, 0x02, 47}, - {23, 0x02, 47}, - {40, 0x03, 47}, - {2, 0x02, 51}, - {9, 0x02, 51}, - {23, 0x02, 51}, - {40, 0x03, 51}, - {2, 0x02, 52}, - {9, 0x02, 52}, - {23, 0x02, 52}, - {40, 0x03, 52}, - {2, 0x02, 53}, - {9, 0x02, 53}, - {23, 0x02, 53}, - {40, 0x03, 53}, + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, }, /* 17 */ { - {3, 0x02, 47}, - {6, 0x02, 47}, - {10, 0x02, 47}, - {15, 0x02, 47}, - {24, 0x02, 47}, - {31, 0x02, 47}, - {41, 0x02, 47}, - {56, 0x03, 47}, - {3, 0x02, 51}, - {6, 0x02, 51}, - {10, 0x02, 51}, - {15, 0x02, 51}, - {24, 0x02, 51}, - {31, 0x02, 51}, - {41, 0x02, 51}, - {56, 0x03, 51}, + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, }, /* 18 */ { - {3, 0x02, 52}, - {6, 0x02, 52}, - {10, 0x02, 52}, - {15, 0x02, 52}, - {24, 0x02, 52}, - {31, 0x02, 52}, - {41, 0x02, 52}, - {56, 0x03, 52}, - {3, 0x02, 53}, - {6, 0x02, 53}, - {10, 0x02, 53}, - {15, 0x02, 53}, - {24, 0x02, 53}, - {31, 0x02, 53}, - {41, 0x02, 53}, - {56, 0x03, 53}, + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, }, /* 19 */ { - {2, 0x02, 54}, - {9, 0x02, 54}, - {23, 0x02, 54}, - {40, 0x03, 54}, - {2, 0x02, 55}, - {9, 0x02, 55}, - {23, 0x02, 55}, - {40, 0x03, 55}, - {2, 0x02, 56}, - {9, 0x02, 56}, - {23, 0x02, 56}, - {40, 0x03, 56}, - {2, 0x02, 57}, - {9, 0x02, 57}, - {23, 0x02, 57}, - {40, 0x03, 57}, + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, }, /* 20 */ { - {3, 0x02, 54}, - {6, 0x02, 54}, - {10, 0x02, 54}, - {15, 0x02, 54}, - {24, 0x02, 54}, - {31, 0x02, 54}, - {41, 0x02, 54}, - {56, 0x03, 54}, - {3, 0x02, 55}, - {6, 0x02, 55}, - {10, 0x02, 55}, - {15, 0x02, 55}, - {24, 0x02, 55}, - {31, 0x02, 55}, - {41, 0x02, 55}, - {56, 0x03, 55}, + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, }, /* 21 */ { - {3, 0x02, 56}, - {6, 0x02, 56}, - {10, 0x02, 56}, - {15, 0x02, 56}, - {24, 0x02, 56}, - {31, 0x02, 56}, - {41, 0x02, 56}, - {56, 0x03, 56}, - {3, 0x02, 57}, - {6, 0x02, 57}, - {10, 0x02, 57}, - {15, 0x02, 57}, - {24, 0x02, 57}, - {31, 0x02, 57}, - {41, 0x02, 57}, - {56, 0x03, 57}, + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, }, /* 22 */ { - {26, 0x00, 0}, - {27, 0x00, 0}, - {29, 0x00, 0}, - {30, 0x00, 0}, - {33, 0x00, 0}, - {34, 0x00, 0}, - {36, 0x00, 0}, - {37, 0x00, 0}, - {43, 0x00, 0}, - {46, 0x00, 0}, - {50, 0x00, 0}, - {53, 0x00, 0}, - {58, 0x00, 0}, - {61, 0x00, 0}, - {65, 0x00, 0}, - {68, 0x01, 0}, + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, }, /* 23 */ { - {0, 0x03, 61}, - {0, 0x03, 65}, - {0, 0x03, 95}, - {0, 0x03, 98}, - {0, 0x03, 100}, - {0, 0x03, 102}, - {0, 0x03, 103}, - {0, 0x03, 104}, - {0, 0x03, 108}, - {0, 0x03, 109}, - {0, 0x03, 110}, - {0, 0x03, 112}, - {0, 0x03, 114}, - {0, 0x03, 117}, - {38, 0x00, 0}, - {39, 0x00, 0}, + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, }, /* 24 */ { - {1, 0x02, 61}, - {22, 0x03, 61}, - {1, 0x02, 65}, - {22, 0x03, 65}, - {1, 0x02, 95}, - {22, 0x03, 95}, - {1, 0x02, 98}, - {22, 0x03, 98}, - {1, 0x02, 100}, - {22, 0x03, 100}, - {1, 0x02, 102}, - {22, 0x03, 102}, - {1, 0x02, 103}, - {22, 0x03, 103}, - {1, 0x02, 104}, - {22, 0x03, 104}, + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, }, /* 25 */ { - {2, 0x02, 61}, - {9, 0x02, 61}, - {23, 0x02, 61}, - {40, 0x03, 61}, - {2, 0x02, 65}, - {9, 0x02, 65}, - {23, 0x02, 65}, - {40, 0x03, 65}, - {2, 0x02, 95}, - {9, 0x02, 95}, - {23, 0x02, 95}, - {40, 0x03, 95}, - {2, 0x02, 98}, - {9, 0x02, 98}, - {23, 0x02, 98}, - {40, 0x03, 98}, + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, }, /* 26 */ { - {3, 0x02, 61}, - {6, 0x02, 61}, - {10, 0x02, 61}, - {15, 0x02, 61}, - {24, 0x02, 61}, - {31, 0x02, 61}, - {41, 0x02, 61}, - {56, 0x03, 61}, - {3, 0x02, 65}, - {6, 0x02, 65}, - {10, 0x02, 65}, - {15, 0x02, 65}, - {24, 0x02, 65}, - {31, 0x02, 65}, - {41, 0x02, 65}, - {56, 0x03, 65}, + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, }, /* 27 */ { - {3, 0x02, 95}, - {6, 0x02, 95}, - {10, 0x02, 95}, - {15, 0x02, 95}, - {24, 0x02, 95}, - {31, 0x02, 95}, - {41, 0x02, 95}, - {56, 0x03, 95}, - {3, 0x02, 98}, - {6, 0x02, 98}, - {10, 0x02, 98}, - {15, 0x02, 98}, - {24, 0x02, 98}, - {31, 0x02, 98}, - {41, 0x02, 98}, - {56, 0x03, 98}, + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, }, /* 28 */ { - {2, 0x02, 100}, - {9, 0x02, 100}, - {23, 0x02, 100}, - {40, 0x03, 100}, - {2, 0x02, 102}, - {9, 0x02, 102}, - {23, 0x02, 102}, - {40, 0x03, 102}, - {2, 0x02, 103}, - {9, 0x02, 103}, - {23, 0x02, 103}, - {40, 0x03, 103}, - {2, 0x02, 104}, - {9, 0x02, 104}, - {23, 0x02, 104}, - {40, 0x03, 104}, + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, }, /* 29 */ { - {3, 0x02, 100}, - {6, 0x02, 100}, - {10, 0x02, 100}, - {15, 0x02, 100}, - {24, 0x02, 100}, - {31, 0x02, 100}, - {41, 0x02, 100}, - {56, 0x03, 100}, - {3, 0x02, 102}, - {6, 0x02, 102}, - {10, 0x02, 102}, - {15, 0x02, 102}, - {24, 0x02, 102}, - {31, 0x02, 102}, - {41, 0x02, 102}, - {56, 0x03, 102}, + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, }, /* 30 */ { - {3, 0x02, 103}, - {6, 0x02, 103}, - {10, 0x02, 103}, - {15, 0x02, 103}, - {24, 0x02, 103}, - {31, 0x02, 103}, - {41, 0x02, 103}, - {56, 0x03, 103}, - {3, 0x02, 104}, - {6, 0x02, 104}, - {10, 0x02, 104}, - {15, 0x02, 104}, - {24, 0x02, 104}, - {31, 0x02, 104}, - {41, 0x02, 104}, - {56, 0x03, 104}, + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, }, /* 31 */ { - {1, 0x02, 108}, - {22, 0x03, 108}, - {1, 0x02, 109}, - {22, 0x03, 109}, - {1, 0x02, 110}, - {22, 0x03, 110}, - {1, 0x02, 112}, - {22, 0x03, 112}, - {1, 0x02, 114}, - {22, 0x03, 114}, - {1, 0x02, 117}, - {22, 0x03, 117}, - {0, 0x03, 58}, - {0, 0x03, 66}, - {0, 0x03, 67}, - {0, 0x03, 68}, + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, }, /* 32 */ { - {2, 0x02, 108}, - {9, 0x02, 108}, - {23, 0x02, 108}, - {40, 0x03, 108}, - {2, 0x02, 109}, - {9, 0x02, 109}, - {23, 0x02, 109}, - {40, 0x03, 109}, - {2, 0x02, 110}, - {9, 0x02, 110}, - {23, 0x02, 110}, - {40, 0x03, 110}, - {2, 0x02, 112}, - {9, 0x02, 112}, - {23, 0x02, 112}, - {40, 0x03, 112}, + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, }, /* 33 */ { - {3, 0x02, 108}, - {6, 0x02, 108}, - {10, 0x02, 108}, - {15, 0x02, 108}, - {24, 0x02, 108}, - {31, 0x02, 108}, - {41, 0x02, 108}, - {56, 0x03, 108}, - {3, 0x02, 109}, - {6, 0x02, 109}, - {10, 0x02, 109}, - {15, 0x02, 109}, - {24, 0x02, 109}, - {31, 0x02, 109}, - {41, 0x02, 109}, - {56, 0x03, 109}, + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, }, /* 34 */ { - {3, 0x02, 110}, - {6, 0x02, 110}, - {10, 0x02, 110}, - {15, 0x02, 110}, - {24, 0x02, 110}, - {31, 0x02, 110}, - {41, 0x02, 110}, - {56, 0x03, 110}, - {3, 0x02, 112}, - {6, 0x02, 112}, - {10, 0x02, 112}, - {15, 0x02, 112}, - {24, 0x02, 112}, - {31, 0x02, 112}, - {41, 0x02, 112}, - {56, 0x03, 112}, + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, }, /* 35 */ { - {2, 0x02, 114}, - {9, 0x02, 114}, - {23, 0x02, 114}, - {40, 0x03, 114}, - {2, 0x02, 117}, - {9, 0x02, 117}, - {23, 0x02, 117}, - {40, 0x03, 117}, - {1, 0x02, 58}, - {22, 0x03, 58}, - {1, 0x02, 66}, - {22, 0x03, 66}, - {1, 0x02, 67}, - {22, 0x03, 67}, - {1, 0x02, 68}, - {22, 0x03, 68}, + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, }, /* 36 */ { - {3, 0x02, 114}, - {6, 0x02, 114}, - {10, 0x02, 114}, - {15, 0x02, 114}, - {24, 0x02, 114}, - {31, 0x02, 114}, - {41, 0x02, 114}, - {56, 0x03, 114}, - {3, 0x02, 117}, - {6, 0x02, 117}, - {10, 0x02, 117}, - {15, 0x02, 117}, - {24, 0x02, 117}, - {31, 0x02, 117}, - {41, 0x02, 117}, - {56, 0x03, 117}, + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, }, /* 37 */ { - {2, 0x02, 58}, - {9, 0x02, 58}, - {23, 0x02, 58}, - {40, 0x03, 58}, - {2, 0x02, 66}, - {9, 0x02, 66}, - {23, 0x02, 66}, - {40, 0x03, 66}, - {2, 0x02, 67}, - {9, 0x02, 67}, - {23, 0x02, 67}, - {40, 0x03, 67}, - {2, 0x02, 68}, - {9, 0x02, 68}, - {23, 0x02, 68}, - {40, 0x03, 68}, + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, }, /* 38 */ { - {3, 0x02, 58}, - {6, 0x02, 58}, - {10, 0x02, 58}, - {15, 0x02, 58}, - {24, 0x02, 58}, - {31, 0x02, 58}, - {41, 0x02, 58}, - {56, 0x03, 58}, - {3, 0x02, 66}, - {6, 0x02, 66}, - {10, 0x02, 66}, - {15, 0x02, 66}, - {24, 0x02, 66}, - {31, 0x02, 66}, - {41, 0x02, 66}, - {56, 0x03, 66}, + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, }, /* 39 */ { - {3, 0x02, 67}, - {6, 0x02, 67}, - {10, 0x02, 67}, - {15, 0x02, 67}, - {24, 0x02, 67}, - {31, 0x02, 67}, - {41, 0x02, 67}, - {56, 0x03, 67}, - {3, 0x02, 68}, - {6, 0x02, 68}, - {10, 0x02, 68}, - {15, 0x02, 68}, - {24, 0x02, 68}, - {31, 0x02, 68}, - {41, 0x02, 68}, - {56, 0x03, 68}, + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, }, /* 40 */ { - {44, 0x00, 0}, - {45, 0x00, 0}, - {47, 0x00, 0}, - {48, 0x00, 0}, - {51, 0x00, 0}, - {52, 0x00, 0}, - {54, 0x00, 0}, - {55, 0x00, 0}, - {59, 0x00, 0}, - {60, 0x00, 0}, - {62, 0x00, 0}, - {63, 0x00, 0}, - {66, 0x00, 0}, - {67, 0x00, 0}, - {69, 0x00, 0}, - {72, 0x01, 0}, + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, }, /* 41 */ { - {0, 0x03, 69}, - {0, 0x03, 70}, - {0, 0x03, 71}, - {0, 0x03, 72}, - {0, 0x03, 73}, - {0, 0x03, 74}, - {0, 0x03, 75}, - {0, 0x03, 76}, - {0, 0x03, 77}, - {0, 0x03, 78}, - {0, 0x03, 79}, - {0, 0x03, 80}, - {0, 0x03, 81}, - {0, 0x03, 82}, - {0, 0x03, 83}, - {0, 0x03, 84}, + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, }, /* 42 */ { - {1, 0x02, 69}, - {22, 0x03, 69}, - {1, 0x02, 70}, - {22, 0x03, 70}, - {1, 0x02, 71}, - {22, 0x03, 71}, - {1, 0x02, 72}, - {22, 0x03, 72}, - {1, 0x02, 73}, - {22, 0x03, 73}, - {1, 0x02, 74}, - {22, 0x03, 74}, - {1, 0x02, 75}, - {22, 0x03, 75}, - {1, 0x02, 76}, - {22, 0x03, 76}, + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, }, /* 43 */ { - {2, 0x02, 69}, - {9, 0x02, 69}, - {23, 0x02, 69}, - {40, 0x03, 69}, - {2, 0x02, 70}, - {9, 0x02, 70}, - {23, 0x02, 70}, - {40, 0x03, 70}, - {2, 0x02, 71}, - {9, 0x02, 71}, - {23, 0x02, 71}, - {40, 0x03, 71}, - {2, 0x02, 72}, - {9, 0x02, 72}, - {23, 0x02, 72}, - {40, 0x03, 72}, + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, }, /* 44 */ { - {3, 0x02, 69}, - {6, 0x02, 69}, - {10, 0x02, 69}, - {15, 0x02, 69}, - {24, 0x02, 69}, - {31, 0x02, 69}, - {41, 0x02, 69}, - {56, 0x03, 69}, - {3, 0x02, 70}, - {6, 0x02, 70}, - {10, 0x02, 70}, - {15, 0x02, 70}, - {24, 0x02, 70}, - {31, 0x02, 70}, - {41, 0x02, 70}, - {56, 0x03, 70}, + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, }, /* 45 */ { - {3, 0x02, 71}, - {6, 0x02, 71}, - {10, 0x02, 71}, - {15, 0x02, 71}, - {24, 0x02, 71}, - {31, 0x02, 71}, - {41, 0x02, 71}, - {56, 0x03, 71}, - {3, 0x02, 72}, - {6, 0x02, 72}, - {10, 0x02, 72}, - {15, 0x02, 72}, - {24, 0x02, 72}, - {31, 0x02, 72}, - {41, 0x02, 72}, - {56, 0x03, 72}, + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, }, /* 46 */ { - {2, 0x02, 73}, - {9, 0x02, 73}, - {23, 0x02, 73}, - {40, 0x03, 73}, - {2, 0x02, 74}, - {9, 0x02, 74}, - {23, 0x02, 74}, - {40, 0x03, 74}, - {2, 0x02, 75}, - {9, 0x02, 75}, - {23, 0x02, 75}, - {40, 0x03, 75}, - {2, 0x02, 76}, - {9, 0x02, 76}, - {23, 0x02, 76}, - {40, 0x03, 76}, + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, }, /* 47 */ { - {3, 0x02, 73}, - {6, 0x02, 73}, - {10, 0x02, 73}, - {15, 0x02, 73}, - {24, 0x02, 73}, - {31, 0x02, 73}, - {41, 0x02, 73}, - {56, 0x03, 73}, - {3, 0x02, 74}, - {6, 0x02, 74}, - {10, 0x02, 74}, - {15, 0x02, 74}, - {24, 0x02, 74}, - {31, 0x02, 74}, - {41, 0x02, 74}, - {56, 0x03, 74}, + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, }, /* 48 */ { - {3, 0x02, 75}, - {6, 0x02, 75}, - {10, 0x02, 75}, - {15, 0x02, 75}, - {24, 0x02, 75}, - {31, 0x02, 75}, - {41, 0x02, 75}, - {56, 0x03, 75}, - {3, 0x02, 76}, - {6, 0x02, 76}, - {10, 0x02, 76}, - {15, 0x02, 76}, - {24, 0x02, 76}, - {31, 0x02, 76}, - {41, 0x02, 76}, - {56, 0x03, 76}, + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, }, /* 49 */ { - {1, 0x02, 77}, - {22, 0x03, 77}, - {1, 0x02, 78}, - {22, 0x03, 78}, - {1, 0x02, 79}, - {22, 0x03, 79}, - {1, 0x02, 80}, - {22, 0x03, 80}, - {1, 0x02, 81}, - {22, 0x03, 81}, - {1, 0x02, 82}, - {22, 0x03, 82}, - {1, 0x02, 83}, - {22, 0x03, 83}, - {1, 0x02, 84}, - {22, 0x03, 84}, + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, }, /* 50 */ { - {2, 0x02, 77}, - {9, 0x02, 77}, - {23, 0x02, 77}, - {40, 0x03, 77}, - {2, 0x02, 78}, - {9, 0x02, 78}, - {23, 0x02, 78}, - {40, 0x03, 78}, - {2, 0x02, 79}, - {9, 0x02, 79}, - {23, 0x02, 79}, - {40, 0x03, 79}, - {2, 0x02, 80}, - {9, 0x02, 80}, - {23, 0x02, 80}, - {40, 0x03, 80}, + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, }, /* 51 */ { - {3, 0x02, 77}, - {6, 0x02, 77}, - {10, 0x02, 77}, - {15, 0x02, 77}, - {24, 0x02, 77}, - {31, 0x02, 77}, - {41, 0x02, 77}, - {56, 0x03, 77}, - {3, 0x02, 78}, - {6, 0x02, 78}, - {10, 0x02, 78}, - {15, 0x02, 78}, - {24, 0x02, 78}, - {31, 0x02, 78}, - {41, 0x02, 78}, - {56, 0x03, 78}, + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, }, /* 52 */ { - {3, 0x02, 79}, - {6, 0x02, 79}, - {10, 0x02, 79}, - {15, 0x02, 79}, - {24, 0x02, 79}, - {31, 0x02, 79}, - {41, 0x02, 79}, - {56, 0x03, 79}, - {3, 0x02, 80}, - {6, 0x02, 80}, - {10, 0x02, 80}, - {15, 0x02, 80}, - {24, 0x02, 80}, - {31, 0x02, 80}, - {41, 0x02, 80}, - {56, 0x03, 80}, + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, }, /* 53 */ { - {2, 0x02, 81}, - {9, 0x02, 81}, - {23, 0x02, 81}, - {40, 0x03, 81}, - {2, 0x02, 82}, - {9, 0x02, 82}, - {23, 0x02, 82}, - {40, 0x03, 82}, - {2, 0x02, 83}, - {9, 0x02, 83}, - {23, 0x02, 83}, - {40, 0x03, 83}, - {2, 0x02, 84}, - {9, 0x02, 84}, - {23, 0x02, 84}, - {40, 0x03, 84}, + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, }, /* 54 */ { - {3, 0x02, 81}, - {6, 0x02, 81}, - {10, 0x02, 81}, - {15, 0x02, 81}, - {24, 0x02, 81}, - {31, 0x02, 81}, - {41, 0x02, 81}, - {56, 0x03, 81}, - {3, 0x02, 82}, - {6, 0x02, 82}, - {10, 0x02, 82}, - {15, 0x02, 82}, - {24, 0x02, 82}, - {31, 0x02, 82}, - {41, 0x02, 82}, - {56, 0x03, 82}, + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, }, /* 55 */ { - {3, 0x02, 83}, - {6, 0x02, 83}, - {10, 0x02, 83}, - {15, 0x02, 83}, - {24, 0x02, 83}, - {31, 0x02, 83}, - {41, 0x02, 83}, - {56, 0x03, 83}, - {3, 0x02, 84}, - {6, 0x02, 84}, - {10, 0x02, 84}, - {15, 0x02, 84}, - {24, 0x02, 84}, - {31, 0x02, 84}, - {41, 0x02, 84}, - {56, 0x03, 84}, + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, }, /* 56 */ { - {0, 0x03, 85}, - {0, 0x03, 86}, - {0, 0x03, 87}, - {0, 0x03, 89}, - {0, 0x03, 106}, - {0, 0x03, 107}, - {0, 0x03, 113}, - {0, 0x03, 118}, - {0, 0x03, 119}, - {0, 0x03, 120}, - {0, 0x03, 121}, - {0, 0x03, 122}, - {70, 0x00, 0}, - {71, 0x00, 0}, - {73, 0x00, 0}, - {74, 0x01, 0}, + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, }, /* 57 */ { - {1, 0x02, 85}, - {22, 0x03, 85}, - {1, 0x02, 86}, - {22, 0x03, 86}, - {1, 0x02, 87}, - {22, 0x03, 87}, - {1, 0x02, 89}, - {22, 0x03, 89}, - {1, 0x02, 106}, - {22, 0x03, 106}, - {1, 0x02, 107}, - {22, 0x03, 107}, - {1, 0x02, 113}, - {22, 0x03, 113}, - {1, 0x02, 118}, - {22, 0x03, 118}, + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, }, /* 58 */ { - {2, 0x02, 85}, - {9, 0x02, 85}, - {23, 0x02, 85}, - {40, 0x03, 85}, - {2, 0x02, 86}, - {9, 0x02, 86}, - {23, 0x02, 86}, - {40, 0x03, 86}, - {2, 0x02, 87}, - {9, 0x02, 87}, - {23, 0x02, 87}, - {40, 0x03, 87}, - {2, 0x02, 89}, - {9, 0x02, 89}, - {23, 0x02, 89}, - {40, 0x03, 89}, + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, }, /* 59 */ { - {3, 0x02, 85}, - {6, 0x02, 85}, - {10, 0x02, 85}, - {15, 0x02, 85}, - {24, 0x02, 85}, - {31, 0x02, 85}, - {41, 0x02, 85}, - {56, 0x03, 85}, - {3, 0x02, 86}, - {6, 0x02, 86}, - {10, 0x02, 86}, - {15, 0x02, 86}, - {24, 0x02, 86}, - {31, 0x02, 86}, - {41, 0x02, 86}, - {56, 0x03, 86}, + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, }, /* 60 */ { - {3, 0x02, 87}, - {6, 0x02, 87}, - {10, 0x02, 87}, - {15, 0x02, 87}, - {24, 0x02, 87}, - {31, 0x02, 87}, - {41, 0x02, 87}, - {56, 0x03, 87}, - {3, 0x02, 89}, - {6, 0x02, 89}, - {10, 0x02, 89}, - {15, 0x02, 89}, - {24, 0x02, 89}, - {31, 0x02, 89}, - {41, 0x02, 89}, - {56, 0x03, 89}, + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, }, /* 61 */ { - {2, 0x02, 106}, - {9, 0x02, 106}, - {23, 0x02, 106}, - {40, 0x03, 106}, - {2, 0x02, 107}, - {9, 0x02, 107}, - {23, 0x02, 107}, - {40, 0x03, 107}, - {2, 0x02, 113}, - {9, 0x02, 113}, - {23, 0x02, 113}, - {40, 0x03, 113}, - {2, 0x02, 118}, - {9, 0x02, 118}, - {23, 0x02, 118}, - {40, 0x03, 118}, + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, }, /* 62 */ { - {3, 0x02, 106}, - {6, 0x02, 106}, - {10, 0x02, 106}, - {15, 0x02, 106}, - {24, 0x02, 106}, - {31, 0x02, 106}, - {41, 0x02, 106}, - {56, 0x03, 106}, - {3, 0x02, 107}, - {6, 0x02, 107}, - {10, 0x02, 107}, - {15, 0x02, 107}, - {24, 0x02, 107}, - {31, 0x02, 107}, - {41, 0x02, 107}, - {56, 0x03, 107}, + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, }, /* 63 */ { - {3, 0x02, 113}, - {6, 0x02, 113}, - {10, 0x02, 113}, - {15, 0x02, 113}, - {24, 0x02, 113}, - {31, 0x02, 113}, - {41, 0x02, 113}, - {56, 0x03, 113}, - {3, 0x02, 118}, - {6, 0x02, 118}, - {10, 0x02, 118}, - {15, 0x02, 118}, - {24, 0x02, 118}, - {31, 0x02, 118}, - {41, 0x02, 118}, - {56, 0x03, 118}, + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, }, /* 64 */ { - {1, 0x02, 119}, - {22, 0x03, 119}, - {1, 0x02, 120}, - {22, 0x03, 120}, - {1, 0x02, 121}, - {22, 0x03, 121}, - {1, 0x02, 122}, - {22, 0x03, 122}, - {0, 0x03, 38}, - {0, 0x03, 42}, - {0, 0x03, 44}, - {0, 0x03, 59}, - {0, 0x03, 88}, - {0, 0x03, 90}, - {75, 0x00, 0}, - {78, 0x00, 0}, + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, }, /* 65 */ { - {2, 0x02, 119}, - {9, 0x02, 119}, - {23, 0x02, 119}, - {40, 0x03, 119}, - {2, 0x02, 120}, - {9, 0x02, 120}, - {23, 0x02, 120}, - {40, 0x03, 120}, - {2, 0x02, 121}, - {9, 0x02, 121}, - {23, 0x02, 121}, - {40, 0x03, 121}, - {2, 0x02, 122}, - {9, 0x02, 122}, - {23, 0x02, 122}, - {40, 0x03, 122}, + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, }, /* 66 */ { - {3, 0x02, 119}, - {6, 0x02, 119}, - {10, 0x02, 119}, - {15, 0x02, 119}, - {24, 0x02, 119}, - {31, 0x02, 119}, - {41, 0x02, 119}, - {56, 0x03, 119}, - {3, 0x02, 120}, - {6, 0x02, 120}, - {10, 0x02, 120}, - {15, 0x02, 120}, - {24, 0x02, 120}, - {31, 0x02, 120}, - {41, 0x02, 120}, - {56, 0x03, 120}, + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, }, /* 67 */ { - {3, 0x02, 121}, - {6, 0x02, 121}, - {10, 0x02, 121}, - {15, 0x02, 121}, - {24, 0x02, 121}, - {31, 0x02, 121}, - {41, 0x02, 121}, - {56, 0x03, 121}, - {3, 0x02, 122}, - {6, 0x02, 122}, - {10, 0x02, 122}, - {15, 0x02, 122}, - {24, 0x02, 122}, - {31, 0x02, 122}, - {41, 0x02, 122}, - {56, 0x03, 122}, + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, }, /* 68 */ { - {1, 0x02, 38}, - {22, 0x03, 38}, - {1, 0x02, 42}, - {22, 0x03, 42}, - {1, 0x02, 44}, - {22, 0x03, 44}, - {1, 0x02, 59}, - {22, 0x03, 59}, - {1, 0x02, 88}, - {22, 0x03, 88}, - {1, 0x02, 90}, - {22, 0x03, 90}, - {76, 0x00, 0}, - {77, 0x00, 0}, - {79, 0x00, 0}, - {81, 0x00, 0}, + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, }, /* 69 */ { - {2, 0x02, 38}, - {9, 0x02, 38}, - {23, 0x02, 38}, - {40, 0x03, 38}, - {2, 0x02, 42}, - {9, 0x02, 42}, - {23, 0x02, 42}, - {40, 0x03, 42}, - {2, 0x02, 44}, - {9, 0x02, 44}, - {23, 0x02, 44}, - {40, 0x03, 44}, - {2, 0x02, 59}, - {9, 0x02, 59}, - {23, 0x02, 59}, - {40, 0x03, 59}, + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, }, /* 70 */ { - {3, 0x02, 38}, - {6, 0x02, 38}, - {10, 0x02, 38}, - {15, 0x02, 38}, - {24, 0x02, 38}, - {31, 0x02, 38}, - {41, 0x02, 38}, - {56, 0x03, 38}, - {3, 0x02, 42}, - {6, 0x02, 42}, - {10, 0x02, 42}, - {15, 0x02, 42}, - {24, 0x02, 42}, - {31, 0x02, 42}, - {41, 0x02, 42}, - {56, 0x03, 42}, + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, }, /* 71 */ { - {3, 0x02, 44}, - {6, 0x02, 44}, - {10, 0x02, 44}, - {15, 0x02, 44}, - {24, 0x02, 44}, - {31, 0x02, 44}, - {41, 0x02, 44}, - {56, 0x03, 44}, - {3, 0x02, 59}, - {6, 0x02, 59}, - {10, 0x02, 59}, - {15, 0x02, 59}, - {24, 0x02, 59}, - {31, 0x02, 59}, - {41, 0x02, 59}, - {56, 0x03, 59}, + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, }, /* 72 */ { - {2, 0x02, 88}, - {9, 0x02, 88}, - {23, 0x02, 88}, - {40, 0x03, 88}, - {2, 0x02, 90}, - {9, 0x02, 90}, - {23, 0x02, 90}, - {40, 0x03, 90}, - {0, 0x03, 33}, - {0, 0x03, 34}, - {0, 0x03, 40}, - {0, 0x03, 41}, - {0, 0x03, 63}, - {80, 0x00, 0}, - {82, 0x00, 0}, - {84, 0x00, 0}, + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, }, /* 73 */ { - {3, 0x02, 88}, - {6, 0x02, 88}, - {10, 0x02, 88}, - {15, 0x02, 88}, - {24, 0x02, 88}, - {31, 0x02, 88}, - {41, 0x02, 88}, - {56, 0x03, 88}, - {3, 0x02, 90}, - {6, 0x02, 90}, - {10, 0x02, 90}, - {15, 0x02, 90}, - {24, 0x02, 90}, - {31, 0x02, 90}, - {41, 0x02, 90}, - {56, 0x03, 90}, + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, }, /* 74 */ { - {1, 0x02, 33}, - {22, 0x03, 33}, - {1, 0x02, 34}, - {22, 0x03, 34}, - {1, 0x02, 40}, - {22, 0x03, 40}, - {1, 0x02, 41}, - {22, 0x03, 41}, - {1, 0x02, 63}, - {22, 0x03, 63}, - {0, 0x03, 39}, - {0, 0x03, 43}, - {0, 0x03, 124}, - {83, 0x00, 0}, - {85, 0x00, 0}, - {88, 0x00, 0}, + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, }, /* 75 */ { - {2, 0x02, 33}, - {9, 0x02, 33}, - {23, 0x02, 33}, - {40, 0x03, 33}, - {2, 0x02, 34}, - {9, 0x02, 34}, - {23, 0x02, 34}, - {40, 0x03, 34}, - {2, 0x02, 40}, - {9, 0x02, 40}, - {23, 0x02, 40}, - {40, 0x03, 40}, - {2, 0x02, 41}, - {9, 0x02, 41}, - {23, 0x02, 41}, - {40, 0x03, 41}, + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, }, /* 76 */ { - {3, 0x02, 33}, - {6, 0x02, 33}, - {10, 0x02, 33}, - {15, 0x02, 33}, - {24, 0x02, 33}, - {31, 0x02, 33}, - {41, 0x02, 33}, - {56, 0x03, 33}, - {3, 0x02, 34}, - {6, 0x02, 34}, - {10, 0x02, 34}, - {15, 0x02, 34}, - {24, 0x02, 34}, - {31, 0x02, 34}, - {41, 0x02, 34}, - {56, 0x03, 34}, + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, }, /* 77 */ { - {3, 0x02, 40}, - {6, 0x02, 40}, - {10, 0x02, 40}, - {15, 0x02, 40}, - {24, 0x02, 40}, - {31, 0x02, 40}, - {41, 0x02, 40}, - {56, 0x03, 40}, - {3, 0x02, 41}, - {6, 0x02, 41}, - {10, 0x02, 41}, - {15, 0x02, 41}, - {24, 0x02, 41}, - {31, 0x02, 41}, - {41, 0x02, 41}, - {56, 0x03, 41}, + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, }, /* 78 */ { - {2, 0x02, 63}, - {9, 0x02, 63}, - {23, 0x02, 63}, - {40, 0x03, 63}, - {1, 0x02, 39}, - {22, 0x03, 39}, - {1, 0x02, 43}, - {22, 0x03, 43}, - {1, 0x02, 124}, - {22, 0x03, 124}, - {0, 0x03, 35}, - {0, 0x03, 62}, - {86, 0x00, 0}, - {87, 0x00, 0}, - {89, 0x00, 0}, - {90, 0x00, 0}, + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, }, /* 79 */ { - {3, 0x02, 63}, - {6, 0x02, 63}, - {10, 0x02, 63}, - {15, 0x02, 63}, - {24, 0x02, 63}, - {31, 0x02, 63}, - {41, 0x02, 63}, - {56, 0x03, 63}, - {2, 0x02, 39}, - {9, 0x02, 39}, - {23, 0x02, 39}, - {40, 0x03, 39}, - {2, 0x02, 43}, - {9, 0x02, 43}, - {23, 0x02, 43}, - {40, 0x03, 43}, + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, }, /* 80 */ { - {3, 0x02, 39}, - {6, 0x02, 39}, - {10, 0x02, 39}, - {15, 0x02, 39}, - {24, 0x02, 39}, - {31, 0x02, 39}, - {41, 0x02, 39}, - {56, 0x03, 39}, - {3, 0x02, 43}, - {6, 0x02, 43}, - {10, 0x02, 43}, - {15, 0x02, 43}, - {24, 0x02, 43}, - {31, 0x02, 43}, - {41, 0x02, 43}, - {56, 0x03, 43}, + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, }, /* 81 */ { - {2, 0x02, 124}, - {9, 0x02, 124}, - {23, 0x02, 124}, - {40, 0x03, 124}, - {1, 0x02, 35}, - {22, 0x03, 35}, - {1, 0x02, 62}, - {22, 0x03, 62}, - {0, 0x03, 0}, - {0, 0x03, 36}, - {0, 0x03, 64}, - {0, 0x03, 91}, - {0, 0x03, 93}, - {0, 0x03, 126}, - {91, 0x00, 0}, - {92, 0x00, 0}, + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, }, /* 82 */ { - {3, 0x02, 124}, - {6, 0x02, 124}, - {10, 0x02, 124}, - {15, 0x02, 124}, - {24, 0x02, 124}, - {31, 0x02, 124}, - {41, 0x02, 124}, - {56, 0x03, 124}, - {2, 0x02, 35}, - {9, 0x02, 35}, - {23, 0x02, 35}, - {40, 0x03, 35}, - {2, 0x02, 62}, - {9, 0x02, 62}, - {23, 0x02, 62}, - {40, 0x03, 62}, + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, }, /* 83 */ { - {3, 0x02, 35}, - {6, 0x02, 35}, - {10, 0x02, 35}, - {15, 0x02, 35}, - {24, 0x02, 35}, - {31, 0x02, 35}, - {41, 0x02, 35}, - {56, 0x03, 35}, - {3, 0x02, 62}, - {6, 0x02, 62}, - {10, 0x02, 62}, - {15, 0x02, 62}, - {24, 0x02, 62}, - {31, 0x02, 62}, - {41, 0x02, 62}, - {56, 0x03, 62}, + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, }, /* 84 */ { - {1, 0x02, 0}, - {22, 0x03, 0}, - {1, 0x02, 36}, - {22, 0x03, 36}, - {1, 0x02, 64}, - {22, 0x03, 64}, - {1, 0x02, 91}, - {22, 0x03, 91}, - {1, 0x02, 93}, - {22, 0x03, 93}, - {1, 0x02, 126}, - {22, 0x03, 126}, - {0, 0x03, 94}, - {0, 0x03, 125}, - {93, 0x00, 0}, - {94, 0x00, 0}, + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, }, /* 85 */ { - {2, 0x02, 0}, - {9, 0x02, 0}, - {23, 0x02, 0}, - {40, 0x03, 0}, - {2, 0x02, 36}, - {9, 0x02, 36}, - {23, 0x02, 36}, - {40, 0x03, 36}, - {2, 0x02, 64}, - {9, 0x02, 64}, - {23, 0x02, 64}, - {40, 0x03, 64}, - {2, 0x02, 91}, - {9, 0x02, 91}, - {23, 0x02, 91}, - {40, 0x03, 91}, + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, }, /* 86 */ { - {3, 0x02, 0}, - {6, 0x02, 0}, - {10, 0x02, 0}, - {15, 0x02, 0}, - {24, 0x02, 0}, - {31, 0x02, 0}, - {41, 0x02, 0}, - {56, 0x03, 0}, - {3, 0x02, 36}, - {6, 0x02, 36}, - {10, 0x02, 36}, - {15, 0x02, 36}, - {24, 0x02, 36}, - {31, 0x02, 36}, - {41, 0x02, 36}, - {56, 0x03, 36}, + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, }, /* 87 */ { - {3, 0x02, 64}, - {6, 0x02, 64}, - {10, 0x02, 64}, - {15, 0x02, 64}, - {24, 0x02, 64}, - {31, 0x02, 64}, - {41, 0x02, 64}, - {56, 0x03, 64}, - {3, 0x02, 91}, - {6, 0x02, 91}, - {10, 0x02, 91}, - {15, 0x02, 91}, - {24, 0x02, 91}, - {31, 0x02, 91}, - {41, 0x02, 91}, - {56, 0x03, 91}, + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, }, /* 88 */ { - {2, 0x02, 93}, - {9, 0x02, 93}, - {23, 0x02, 93}, - {40, 0x03, 93}, - {2, 0x02, 126}, - {9, 0x02, 126}, - {23, 0x02, 126}, - {40, 0x03, 126}, - {1, 0x02, 94}, - {22, 0x03, 94}, - {1, 0x02, 125}, - {22, 0x03, 125}, - {0, 0x03, 60}, - {0, 0x03, 96}, - {0, 0x03, 123}, - {95, 0x00, 0}, + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, }, /* 89 */ { - {3, 0x02, 93}, - {6, 0x02, 93}, - {10, 0x02, 93}, - {15, 0x02, 93}, - {24, 0x02, 93}, - {31, 0x02, 93}, - {41, 0x02, 93}, - {56, 0x03, 93}, - {3, 0x02, 126}, - {6, 0x02, 126}, - {10, 0x02, 126}, - {15, 0x02, 126}, - {24, 0x02, 126}, - {31, 0x02, 126}, - {41, 0x02, 126}, - {56, 0x03, 126}, + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, }, /* 90 */ { - {2, 0x02, 94}, - {9, 0x02, 94}, - {23, 0x02, 94}, - {40, 0x03, 94}, - {2, 0x02, 125}, - {9, 0x02, 125}, - {23, 0x02, 125}, - {40, 0x03, 125}, - {1, 0x02, 60}, - {22, 0x03, 60}, - {1, 0x02, 96}, - {22, 0x03, 96}, - {1, 0x02, 123}, - {22, 0x03, 123}, - {96, 0x00, 0}, - {110, 0x00, 0}, + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, }, /* 91 */ { - {3, 0x02, 94}, - {6, 0x02, 94}, - {10, 0x02, 94}, - {15, 0x02, 94}, - {24, 0x02, 94}, - {31, 0x02, 94}, - {41, 0x02, 94}, - {56, 0x03, 94}, - {3, 0x02, 125}, - {6, 0x02, 125}, - {10, 0x02, 125}, - {15, 0x02, 125}, - {24, 0x02, 125}, - {31, 0x02, 125}, - {41, 0x02, 125}, - {56, 0x03, 125}, + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, }, /* 92 */ { - {2, 0x02, 60}, - {9, 0x02, 60}, - {23, 0x02, 60}, - {40, 0x03, 60}, - {2, 0x02, 96}, - {9, 0x02, 96}, - {23, 0x02, 96}, - {40, 0x03, 96}, - {2, 0x02, 123}, - {9, 0x02, 123}, - {23, 0x02, 123}, - {40, 0x03, 123}, - {97, 0x00, 0}, - {101, 0x00, 0}, - {111, 0x00, 0}, - {133, 0x00, 0}, + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, }, /* 93 */ { - {3, 0x02, 60}, - {6, 0x02, 60}, - {10, 0x02, 60}, - {15, 0x02, 60}, - {24, 0x02, 60}, - {31, 0x02, 60}, - {41, 0x02, 60}, - {56, 0x03, 60}, - {3, 0x02, 96}, - {6, 0x02, 96}, - {10, 0x02, 96}, - {15, 0x02, 96}, - {24, 0x02, 96}, - {31, 0x02, 96}, - {41, 0x02, 96}, - {56, 0x03, 96}, + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, }, /* 94 */ { - {3, 0x02, 123}, - {6, 0x02, 123}, - {10, 0x02, 123}, - {15, 0x02, 123}, - {24, 0x02, 123}, - {31, 0x02, 123}, - {41, 0x02, 123}, - {56, 0x03, 123}, - {98, 0x00, 0}, - {99, 0x00, 0}, - {102, 0x00, 0}, - {105, 0x00, 0}, - {112, 0x00, 0}, - {119, 0x00, 0}, - {134, 0x00, 0}, - {153, 0x00, 0}, + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, }, /* 95 */ { - {0, 0x03, 92}, - {0, 0x03, 195}, - {0, 0x03, 208}, - {100, 0x00, 0}, - {103, 0x00, 0}, - {104, 0x00, 0}, - {106, 0x00, 0}, - {107, 0x00, 0}, - {113, 0x00, 0}, - {116, 0x00, 0}, - {120, 0x00, 0}, - {126, 0x00, 0}, - {135, 0x00, 0}, - {142, 0x00, 0}, - {154, 0x00, 0}, - {169, 0x00, 0}, + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, }, /* 96 */ { - {1, 0x02, 92}, - {22, 0x03, 92}, - {1, 0x02, 195}, - {22, 0x03, 195}, - {1, 0x02, 208}, - {22, 0x03, 208}, - {0, 0x03, 128}, - {0, 0x03, 130}, - {0, 0x03, 131}, - {0, 0x03, 162}, - {0, 0x03, 184}, - {0, 0x03, 194}, - {0, 0x03, 224}, - {0, 0x03, 226}, - {108, 0x00, 0}, - {109, 0x00, 0}, + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, }, /* 97 */ { - {2, 0x02, 92}, - {9, 0x02, 92}, - {23, 0x02, 92}, - {40, 0x03, 92}, - {2, 0x02, 195}, - {9, 0x02, 195}, - {23, 0x02, 195}, - {40, 0x03, 195}, - {2, 0x02, 208}, - {9, 0x02, 208}, - {23, 0x02, 208}, - {40, 0x03, 208}, - {1, 0x02, 128}, - {22, 0x03, 128}, - {1, 0x02, 130}, - {22, 0x03, 130}, + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, }, /* 98 */ { - {3, 0x02, 92}, - {6, 0x02, 92}, - {10, 0x02, 92}, - {15, 0x02, 92}, - {24, 0x02, 92}, - {31, 0x02, 92}, - {41, 0x02, 92}, - {56, 0x03, 92}, - {3, 0x02, 195}, - {6, 0x02, 195}, - {10, 0x02, 195}, - {15, 0x02, 195}, - {24, 0x02, 195}, - {31, 0x02, 195}, - {41, 0x02, 195}, - {56, 0x03, 195}, + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, }, /* 99 */ { - {3, 0x02, 208}, - {6, 0x02, 208}, - {10, 0x02, 208}, - {15, 0x02, 208}, - {24, 0x02, 208}, - {31, 0x02, 208}, - {41, 0x02, 208}, - {56, 0x03, 208}, - {2, 0x02, 128}, - {9, 0x02, 128}, - {23, 0x02, 128}, - {40, 0x03, 128}, - {2, 0x02, 130}, - {9, 0x02, 130}, - {23, 0x02, 130}, - {40, 0x03, 130}, + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, }, /* 100 */ { - {3, 0x02, 128}, - {6, 0x02, 128}, - {10, 0x02, 128}, - {15, 0x02, 128}, - {24, 0x02, 128}, - {31, 0x02, 128}, - {41, 0x02, 128}, - {56, 0x03, 128}, - {3, 0x02, 130}, - {6, 0x02, 130}, - {10, 0x02, 130}, - {15, 0x02, 130}, - {24, 0x02, 130}, - {31, 0x02, 130}, - {41, 0x02, 130}, - {56, 0x03, 130}, + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, }, /* 101 */ { - {1, 0x02, 131}, - {22, 0x03, 131}, - {1, 0x02, 162}, - {22, 0x03, 162}, - {1, 0x02, 184}, - {22, 0x03, 184}, - {1, 0x02, 194}, - {22, 0x03, 194}, - {1, 0x02, 224}, - {22, 0x03, 224}, - {1, 0x02, 226}, - {22, 0x03, 226}, - {0, 0x03, 153}, - {0, 0x03, 161}, - {0, 0x03, 167}, - {0, 0x03, 172}, + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, }, /* 102 */ { - {2, 0x02, 131}, - {9, 0x02, 131}, - {23, 0x02, 131}, - {40, 0x03, 131}, - {2, 0x02, 162}, - {9, 0x02, 162}, - {23, 0x02, 162}, - {40, 0x03, 162}, - {2, 0x02, 184}, - {9, 0x02, 184}, - {23, 0x02, 184}, - {40, 0x03, 184}, - {2, 0x02, 194}, - {9, 0x02, 194}, - {23, 0x02, 194}, - {40, 0x03, 194}, + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, }, /* 103 */ { - {3, 0x02, 131}, - {6, 0x02, 131}, - {10, 0x02, 131}, - {15, 0x02, 131}, - {24, 0x02, 131}, - {31, 0x02, 131}, - {41, 0x02, 131}, - {56, 0x03, 131}, - {3, 0x02, 162}, - {6, 0x02, 162}, - {10, 0x02, 162}, - {15, 0x02, 162}, - {24, 0x02, 162}, - {31, 0x02, 162}, - {41, 0x02, 162}, - {56, 0x03, 162}, + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, }, /* 104 */ { - {3, 0x02, 184}, - {6, 0x02, 184}, - {10, 0x02, 184}, - {15, 0x02, 184}, - {24, 0x02, 184}, - {31, 0x02, 184}, - {41, 0x02, 184}, - {56, 0x03, 184}, - {3, 0x02, 194}, - {6, 0x02, 194}, - {10, 0x02, 194}, - {15, 0x02, 194}, - {24, 0x02, 194}, - {31, 0x02, 194}, - {41, 0x02, 194}, - {56, 0x03, 194}, + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, }, /* 105 */ { - {2, 0x02, 224}, - {9, 0x02, 224}, - {23, 0x02, 224}, - {40, 0x03, 224}, - {2, 0x02, 226}, - {9, 0x02, 226}, - {23, 0x02, 226}, - {40, 0x03, 226}, - {1, 0x02, 153}, - {22, 0x03, 153}, - {1, 0x02, 161}, - {22, 0x03, 161}, - {1, 0x02, 167}, - {22, 0x03, 167}, - {1, 0x02, 172}, - {22, 0x03, 172}, + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, }, /* 106 */ { - {3, 0x02, 224}, - {6, 0x02, 224}, - {10, 0x02, 224}, - {15, 0x02, 224}, - {24, 0x02, 224}, - {31, 0x02, 224}, - {41, 0x02, 224}, - {56, 0x03, 224}, - {3, 0x02, 226}, - {6, 0x02, 226}, - {10, 0x02, 226}, - {15, 0x02, 226}, - {24, 0x02, 226}, - {31, 0x02, 226}, - {41, 0x02, 226}, - {56, 0x03, 226}, + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, }, /* 107 */ { - {2, 0x02, 153}, - {9, 0x02, 153}, - {23, 0x02, 153}, - {40, 0x03, 153}, - {2, 0x02, 161}, - {9, 0x02, 161}, - {23, 0x02, 161}, - {40, 0x03, 161}, - {2, 0x02, 167}, - {9, 0x02, 167}, - {23, 0x02, 167}, - {40, 0x03, 167}, - {2, 0x02, 172}, - {9, 0x02, 172}, - {23, 0x02, 172}, - {40, 0x03, 172}, + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, }, /* 108 */ { - {3, 0x02, 153}, - {6, 0x02, 153}, - {10, 0x02, 153}, - {15, 0x02, 153}, - {24, 0x02, 153}, - {31, 0x02, 153}, - {41, 0x02, 153}, - {56, 0x03, 153}, - {3, 0x02, 161}, - {6, 0x02, 161}, - {10, 0x02, 161}, - {15, 0x02, 161}, - {24, 0x02, 161}, - {31, 0x02, 161}, - {41, 0x02, 161}, - {56, 0x03, 161}, + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, }, /* 109 */ { - {3, 0x02, 167}, - {6, 0x02, 167}, - {10, 0x02, 167}, - {15, 0x02, 167}, - {24, 0x02, 167}, - {31, 0x02, 167}, - {41, 0x02, 167}, - {56, 0x03, 167}, - {3, 0x02, 172}, - {6, 0x02, 172}, - {10, 0x02, 172}, - {15, 0x02, 172}, - {24, 0x02, 172}, - {31, 0x02, 172}, - {41, 0x02, 172}, - {56, 0x03, 172}, + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, }, /* 110 */ { - {114, 0x00, 0}, - {115, 0x00, 0}, - {117, 0x00, 0}, - {118, 0x00, 0}, - {121, 0x00, 0}, - {123, 0x00, 0}, - {127, 0x00, 0}, - {130, 0x00, 0}, - {136, 0x00, 0}, - {139, 0x00, 0}, - {143, 0x00, 0}, - {146, 0x00, 0}, - {155, 0x00, 0}, - {162, 0x00, 0}, - {170, 0x00, 0}, - {180, 0x00, 0}, + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, }, /* 111 */ { - {0, 0x03, 176}, - {0, 0x03, 177}, - {0, 0x03, 179}, - {0, 0x03, 209}, - {0, 0x03, 216}, - {0, 0x03, 217}, - {0, 0x03, 227}, - {0, 0x03, 229}, - {0, 0x03, 230}, - {122, 0x00, 0}, - {124, 0x00, 0}, - {125, 0x00, 0}, - {128, 0x00, 0}, - {129, 0x00, 0}, - {131, 0x00, 0}, - {132, 0x00, 0}, + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, }, /* 112 */ { - {1, 0x02, 176}, - {22, 0x03, 176}, - {1, 0x02, 177}, - {22, 0x03, 177}, - {1, 0x02, 179}, - {22, 0x03, 179}, - {1, 0x02, 209}, - {22, 0x03, 209}, - {1, 0x02, 216}, - {22, 0x03, 216}, - {1, 0x02, 217}, - {22, 0x03, 217}, - {1, 0x02, 227}, - {22, 0x03, 227}, - {1, 0x02, 229}, - {22, 0x03, 229}, + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, }, /* 113 */ { - {2, 0x02, 176}, - {9, 0x02, 176}, - {23, 0x02, 176}, - {40, 0x03, 176}, - {2, 0x02, 177}, - {9, 0x02, 177}, - {23, 0x02, 177}, - {40, 0x03, 177}, - {2, 0x02, 179}, - {9, 0x02, 179}, - {23, 0x02, 179}, - {40, 0x03, 179}, - {2, 0x02, 209}, - {9, 0x02, 209}, - {23, 0x02, 209}, - {40, 0x03, 209}, + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, }, /* 114 */ { - {3, 0x02, 176}, - {6, 0x02, 176}, - {10, 0x02, 176}, - {15, 0x02, 176}, - {24, 0x02, 176}, - {31, 0x02, 176}, - {41, 0x02, 176}, - {56, 0x03, 176}, - {3, 0x02, 177}, - {6, 0x02, 177}, - {10, 0x02, 177}, - {15, 0x02, 177}, - {24, 0x02, 177}, - {31, 0x02, 177}, - {41, 0x02, 177}, - {56, 0x03, 177}, + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, }, /* 115 */ { - {3, 0x02, 179}, - {6, 0x02, 179}, - {10, 0x02, 179}, - {15, 0x02, 179}, - {24, 0x02, 179}, - {31, 0x02, 179}, - {41, 0x02, 179}, - {56, 0x03, 179}, - {3, 0x02, 209}, - {6, 0x02, 209}, - {10, 0x02, 209}, - {15, 0x02, 209}, - {24, 0x02, 209}, - {31, 0x02, 209}, - {41, 0x02, 209}, - {56, 0x03, 209}, + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, }, /* 116 */ { - {2, 0x02, 216}, - {9, 0x02, 216}, - {23, 0x02, 216}, - {40, 0x03, 216}, - {2, 0x02, 217}, - {9, 0x02, 217}, - {23, 0x02, 217}, - {40, 0x03, 217}, - {2, 0x02, 227}, - {9, 0x02, 227}, - {23, 0x02, 227}, - {40, 0x03, 227}, - {2, 0x02, 229}, - {9, 0x02, 229}, - {23, 0x02, 229}, - {40, 0x03, 229}, + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, }, /* 117 */ { - {3, 0x02, 216}, - {6, 0x02, 216}, - {10, 0x02, 216}, - {15, 0x02, 216}, - {24, 0x02, 216}, - {31, 0x02, 216}, - {41, 0x02, 216}, - {56, 0x03, 216}, - {3, 0x02, 217}, - {6, 0x02, 217}, - {10, 0x02, 217}, - {15, 0x02, 217}, - {24, 0x02, 217}, - {31, 0x02, 217}, - {41, 0x02, 217}, - {56, 0x03, 217}, + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, }, /* 118 */ { - {3, 0x02, 227}, - {6, 0x02, 227}, - {10, 0x02, 227}, - {15, 0x02, 227}, - {24, 0x02, 227}, - {31, 0x02, 227}, - {41, 0x02, 227}, - {56, 0x03, 227}, - {3, 0x02, 229}, - {6, 0x02, 229}, - {10, 0x02, 229}, - {15, 0x02, 229}, - {24, 0x02, 229}, - {31, 0x02, 229}, - {41, 0x02, 229}, - {56, 0x03, 229}, + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, }, /* 119 */ { - {1, 0x02, 230}, - {22, 0x03, 230}, - {0, 0x03, 129}, - {0, 0x03, 132}, - {0, 0x03, 133}, - {0, 0x03, 134}, - {0, 0x03, 136}, - {0, 0x03, 146}, - {0, 0x03, 154}, - {0, 0x03, 156}, - {0, 0x03, 160}, - {0, 0x03, 163}, - {0, 0x03, 164}, - {0, 0x03, 169}, - {0, 0x03, 170}, - {0, 0x03, 173}, + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, }, /* 120 */ { - {2, 0x02, 230}, - {9, 0x02, 230}, - {23, 0x02, 230}, - {40, 0x03, 230}, - {1, 0x02, 129}, - {22, 0x03, 129}, - {1, 0x02, 132}, - {22, 0x03, 132}, - {1, 0x02, 133}, - {22, 0x03, 133}, - {1, 0x02, 134}, - {22, 0x03, 134}, - {1, 0x02, 136}, - {22, 0x03, 136}, - {1, 0x02, 146}, - {22, 0x03, 146}, + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, }, /* 121 */ { - {3, 0x02, 230}, - {6, 0x02, 230}, - {10, 0x02, 230}, - {15, 0x02, 230}, - {24, 0x02, 230}, - {31, 0x02, 230}, - {41, 0x02, 230}, - {56, 0x03, 230}, - {2, 0x02, 129}, - {9, 0x02, 129}, - {23, 0x02, 129}, - {40, 0x03, 129}, - {2, 0x02, 132}, - {9, 0x02, 132}, - {23, 0x02, 132}, - {40, 0x03, 132}, + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, }, /* 122 */ { - {3, 0x02, 129}, - {6, 0x02, 129}, - {10, 0x02, 129}, - {15, 0x02, 129}, - {24, 0x02, 129}, - {31, 0x02, 129}, - {41, 0x02, 129}, - {56, 0x03, 129}, - {3, 0x02, 132}, - {6, 0x02, 132}, - {10, 0x02, 132}, - {15, 0x02, 132}, - {24, 0x02, 132}, - {31, 0x02, 132}, - {41, 0x02, 132}, - {56, 0x03, 132}, + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, }, /* 123 */ { - {2, 0x02, 133}, - {9, 0x02, 133}, - {23, 0x02, 133}, - {40, 0x03, 133}, - {2, 0x02, 134}, - {9, 0x02, 134}, - {23, 0x02, 134}, - {40, 0x03, 134}, - {2, 0x02, 136}, - {9, 0x02, 136}, - {23, 0x02, 136}, - {40, 0x03, 136}, - {2, 0x02, 146}, - {9, 0x02, 146}, - {23, 0x02, 146}, - {40, 0x03, 146}, + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, }, /* 124 */ { - {3, 0x02, 133}, - {6, 0x02, 133}, - {10, 0x02, 133}, - {15, 0x02, 133}, - {24, 0x02, 133}, - {31, 0x02, 133}, - {41, 0x02, 133}, - {56, 0x03, 133}, - {3, 0x02, 134}, - {6, 0x02, 134}, - {10, 0x02, 134}, - {15, 0x02, 134}, - {24, 0x02, 134}, - {31, 0x02, 134}, - {41, 0x02, 134}, - {56, 0x03, 134}, + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, }, /* 125 */ { - {3, 0x02, 136}, - {6, 0x02, 136}, - {10, 0x02, 136}, - {15, 0x02, 136}, - {24, 0x02, 136}, - {31, 0x02, 136}, - {41, 0x02, 136}, - {56, 0x03, 136}, - {3, 0x02, 146}, - {6, 0x02, 146}, - {10, 0x02, 146}, - {15, 0x02, 146}, - {24, 0x02, 146}, - {31, 0x02, 146}, - {41, 0x02, 146}, - {56, 0x03, 146}, + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, }, /* 126 */ { - {1, 0x02, 154}, - {22, 0x03, 154}, - {1, 0x02, 156}, - {22, 0x03, 156}, - {1, 0x02, 160}, - {22, 0x03, 160}, - {1, 0x02, 163}, - {22, 0x03, 163}, - {1, 0x02, 164}, - {22, 0x03, 164}, - {1, 0x02, 169}, - {22, 0x03, 169}, - {1, 0x02, 170}, - {22, 0x03, 170}, - {1, 0x02, 173}, - {22, 0x03, 173}, + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, }, /* 127 */ { - {2, 0x02, 154}, - {9, 0x02, 154}, - {23, 0x02, 154}, - {40, 0x03, 154}, - {2, 0x02, 156}, - {9, 0x02, 156}, - {23, 0x02, 156}, - {40, 0x03, 156}, - {2, 0x02, 160}, - {9, 0x02, 160}, - {23, 0x02, 160}, - {40, 0x03, 160}, - {2, 0x02, 163}, - {9, 0x02, 163}, - {23, 0x02, 163}, - {40, 0x03, 163}, + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, }, /* 128 */ { - {3, 0x02, 154}, - {6, 0x02, 154}, - {10, 0x02, 154}, - {15, 0x02, 154}, - {24, 0x02, 154}, - {31, 0x02, 154}, - {41, 0x02, 154}, - {56, 0x03, 154}, - {3, 0x02, 156}, - {6, 0x02, 156}, - {10, 0x02, 156}, - {15, 0x02, 156}, - {24, 0x02, 156}, - {31, 0x02, 156}, - {41, 0x02, 156}, - {56, 0x03, 156}, + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, }, /* 129 */ { - {3, 0x02, 160}, - {6, 0x02, 160}, - {10, 0x02, 160}, - {15, 0x02, 160}, - {24, 0x02, 160}, - {31, 0x02, 160}, - {41, 0x02, 160}, - {56, 0x03, 160}, - {3, 0x02, 163}, - {6, 0x02, 163}, - {10, 0x02, 163}, - {15, 0x02, 163}, - {24, 0x02, 163}, - {31, 0x02, 163}, - {41, 0x02, 163}, - {56, 0x03, 163}, + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, }, /* 130 */ { - {2, 0x02, 164}, - {9, 0x02, 164}, - {23, 0x02, 164}, - {40, 0x03, 164}, - {2, 0x02, 169}, - {9, 0x02, 169}, - {23, 0x02, 169}, - {40, 0x03, 169}, - {2, 0x02, 170}, - {9, 0x02, 170}, - {23, 0x02, 170}, - {40, 0x03, 170}, - {2, 0x02, 173}, - {9, 0x02, 173}, - {23, 0x02, 173}, - {40, 0x03, 173}, + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, }, /* 131 */ { - {3, 0x02, 164}, - {6, 0x02, 164}, - {10, 0x02, 164}, - {15, 0x02, 164}, - {24, 0x02, 164}, - {31, 0x02, 164}, - {41, 0x02, 164}, - {56, 0x03, 164}, - {3, 0x02, 169}, - {6, 0x02, 169}, - {10, 0x02, 169}, - {15, 0x02, 169}, - {24, 0x02, 169}, - {31, 0x02, 169}, - {41, 0x02, 169}, - {56, 0x03, 169}, + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, }, /* 132 */ { - {3, 0x02, 170}, - {6, 0x02, 170}, - {10, 0x02, 170}, - {15, 0x02, 170}, - {24, 0x02, 170}, - {31, 0x02, 170}, - {41, 0x02, 170}, - {56, 0x03, 170}, - {3, 0x02, 173}, - {6, 0x02, 173}, - {10, 0x02, 173}, - {15, 0x02, 173}, - {24, 0x02, 173}, - {31, 0x02, 173}, - {41, 0x02, 173}, - {56, 0x03, 173}, + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, }, /* 133 */ { - {137, 0x00, 0}, - {138, 0x00, 0}, - {140, 0x00, 0}, - {141, 0x00, 0}, - {144, 0x00, 0}, - {145, 0x00, 0}, - {147, 0x00, 0}, - {150, 0x00, 0}, - {156, 0x00, 0}, - {159, 0x00, 0}, - {163, 0x00, 0}, - {166, 0x00, 0}, - {171, 0x00, 0}, - {174, 0x00, 0}, - {181, 0x00, 0}, - {190, 0x00, 0}, + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, }, /* 134 */ { - {0, 0x03, 178}, - {0, 0x03, 181}, - {0, 0x03, 185}, - {0, 0x03, 186}, - {0, 0x03, 187}, - {0, 0x03, 189}, - {0, 0x03, 190}, - {0, 0x03, 196}, - {0, 0x03, 198}, - {0, 0x03, 228}, - {0, 0x03, 232}, - {0, 0x03, 233}, - {148, 0x00, 0}, - {149, 0x00, 0}, - {151, 0x00, 0}, - {152, 0x00, 0}, + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, }, /* 135 */ { - {1, 0x02, 178}, - {22, 0x03, 178}, - {1, 0x02, 181}, - {22, 0x03, 181}, - {1, 0x02, 185}, - {22, 0x03, 185}, - {1, 0x02, 186}, - {22, 0x03, 186}, - {1, 0x02, 187}, - {22, 0x03, 187}, - {1, 0x02, 189}, - {22, 0x03, 189}, - {1, 0x02, 190}, - {22, 0x03, 190}, - {1, 0x02, 196}, - {22, 0x03, 196}, + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, }, /* 136 */ { - {2, 0x02, 178}, - {9, 0x02, 178}, - {23, 0x02, 178}, - {40, 0x03, 178}, - {2, 0x02, 181}, - {9, 0x02, 181}, - {23, 0x02, 181}, - {40, 0x03, 181}, - {2, 0x02, 185}, - {9, 0x02, 185}, - {23, 0x02, 185}, - {40, 0x03, 185}, - {2, 0x02, 186}, - {9, 0x02, 186}, - {23, 0x02, 186}, - {40, 0x03, 186}, + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, }, /* 137 */ { - {3, 0x02, 178}, - {6, 0x02, 178}, - {10, 0x02, 178}, - {15, 0x02, 178}, - {24, 0x02, 178}, - {31, 0x02, 178}, - {41, 0x02, 178}, - {56, 0x03, 178}, - {3, 0x02, 181}, - {6, 0x02, 181}, - {10, 0x02, 181}, - {15, 0x02, 181}, - {24, 0x02, 181}, - {31, 0x02, 181}, - {41, 0x02, 181}, - {56, 0x03, 181}, + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, }, /* 138 */ { - {3, 0x02, 185}, - {6, 0x02, 185}, - {10, 0x02, 185}, - {15, 0x02, 185}, - {24, 0x02, 185}, - {31, 0x02, 185}, - {41, 0x02, 185}, - {56, 0x03, 185}, - {3, 0x02, 186}, - {6, 0x02, 186}, - {10, 0x02, 186}, - {15, 0x02, 186}, - {24, 0x02, 186}, - {31, 0x02, 186}, - {41, 0x02, 186}, - {56, 0x03, 186}, + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, }, /* 139 */ { - {2, 0x02, 187}, - {9, 0x02, 187}, - {23, 0x02, 187}, - {40, 0x03, 187}, - {2, 0x02, 189}, - {9, 0x02, 189}, - {23, 0x02, 189}, - {40, 0x03, 189}, - {2, 0x02, 190}, - {9, 0x02, 190}, - {23, 0x02, 190}, - {40, 0x03, 190}, - {2, 0x02, 196}, - {9, 0x02, 196}, - {23, 0x02, 196}, - {40, 0x03, 196}, + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, }, /* 140 */ { - {3, 0x02, 187}, - {6, 0x02, 187}, - {10, 0x02, 187}, - {15, 0x02, 187}, - {24, 0x02, 187}, - {31, 0x02, 187}, - {41, 0x02, 187}, - {56, 0x03, 187}, - {3, 0x02, 189}, - {6, 0x02, 189}, - {10, 0x02, 189}, - {15, 0x02, 189}, - {24, 0x02, 189}, - {31, 0x02, 189}, - {41, 0x02, 189}, - {56, 0x03, 189}, + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, }, /* 141 */ { - {3, 0x02, 190}, - {6, 0x02, 190}, - {10, 0x02, 190}, - {15, 0x02, 190}, - {24, 0x02, 190}, - {31, 0x02, 190}, - {41, 0x02, 190}, - {56, 0x03, 190}, - {3, 0x02, 196}, - {6, 0x02, 196}, - {10, 0x02, 196}, - {15, 0x02, 196}, - {24, 0x02, 196}, - {31, 0x02, 196}, - {41, 0x02, 196}, - {56, 0x03, 196}, + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, }, /* 142 */ { - {1, 0x02, 198}, - {22, 0x03, 198}, - {1, 0x02, 228}, - {22, 0x03, 228}, - {1, 0x02, 232}, - {22, 0x03, 232}, - {1, 0x02, 233}, - {22, 0x03, 233}, - {0, 0x03, 1}, - {0, 0x03, 135}, - {0, 0x03, 137}, - {0, 0x03, 138}, - {0, 0x03, 139}, - {0, 0x03, 140}, - {0, 0x03, 141}, - {0, 0x03, 143}, + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, }, /* 143 */ { - {2, 0x02, 198}, - {9, 0x02, 198}, - {23, 0x02, 198}, - {40, 0x03, 198}, - {2, 0x02, 228}, - {9, 0x02, 228}, - {23, 0x02, 228}, - {40, 0x03, 228}, - {2, 0x02, 232}, - {9, 0x02, 232}, - {23, 0x02, 232}, - {40, 0x03, 232}, - {2, 0x02, 233}, - {9, 0x02, 233}, - {23, 0x02, 233}, - {40, 0x03, 233}, + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, }, /* 144 */ { - {3, 0x02, 198}, - {6, 0x02, 198}, - {10, 0x02, 198}, - {15, 0x02, 198}, - {24, 0x02, 198}, - {31, 0x02, 198}, - {41, 0x02, 198}, - {56, 0x03, 198}, - {3, 0x02, 228}, - {6, 0x02, 228}, - {10, 0x02, 228}, - {15, 0x02, 228}, - {24, 0x02, 228}, - {31, 0x02, 228}, - {41, 0x02, 228}, - {56, 0x03, 228}, + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, }, /* 145 */ { - {3, 0x02, 232}, - {6, 0x02, 232}, - {10, 0x02, 232}, - {15, 0x02, 232}, - {24, 0x02, 232}, - {31, 0x02, 232}, - {41, 0x02, 232}, - {56, 0x03, 232}, - {3, 0x02, 233}, - {6, 0x02, 233}, - {10, 0x02, 233}, - {15, 0x02, 233}, - {24, 0x02, 233}, - {31, 0x02, 233}, - {41, 0x02, 233}, - {56, 0x03, 233}, + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, }, /* 146 */ { - {1, 0x02, 1}, - {22, 0x03, 1}, - {1, 0x02, 135}, - {22, 0x03, 135}, - {1, 0x02, 137}, - {22, 0x03, 137}, - {1, 0x02, 138}, - {22, 0x03, 138}, - {1, 0x02, 139}, - {22, 0x03, 139}, - {1, 0x02, 140}, - {22, 0x03, 140}, - {1, 0x02, 141}, - {22, 0x03, 141}, - {1, 0x02, 143}, - {22, 0x03, 143}, + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, }, /* 147 */ { - {2, 0x02, 1}, - {9, 0x02, 1}, - {23, 0x02, 1}, - {40, 0x03, 1}, - {2, 0x02, 135}, - {9, 0x02, 135}, - {23, 0x02, 135}, - {40, 0x03, 135}, - {2, 0x02, 137}, - {9, 0x02, 137}, - {23, 0x02, 137}, - {40, 0x03, 137}, - {2, 0x02, 138}, - {9, 0x02, 138}, - {23, 0x02, 138}, - {40, 0x03, 138}, + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, }, /* 148 */ { - {3, 0x02, 1}, - {6, 0x02, 1}, - {10, 0x02, 1}, - {15, 0x02, 1}, - {24, 0x02, 1}, - {31, 0x02, 1}, - {41, 0x02, 1}, - {56, 0x03, 1}, - {3, 0x02, 135}, - {6, 0x02, 135}, - {10, 0x02, 135}, - {15, 0x02, 135}, - {24, 0x02, 135}, - {31, 0x02, 135}, - {41, 0x02, 135}, - {56, 0x03, 135}, + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, }, /* 149 */ { - {3, 0x02, 137}, - {6, 0x02, 137}, - {10, 0x02, 137}, - {15, 0x02, 137}, - {24, 0x02, 137}, - {31, 0x02, 137}, - {41, 0x02, 137}, - {56, 0x03, 137}, - {3, 0x02, 138}, - {6, 0x02, 138}, - {10, 0x02, 138}, - {15, 0x02, 138}, - {24, 0x02, 138}, - {31, 0x02, 138}, - {41, 0x02, 138}, - {56, 0x03, 138}, + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, }, /* 150 */ { - {2, 0x02, 139}, - {9, 0x02, 139}, - {23, 0x02, 139}, - {40, 0x03, 139}, - {2, 0x02, 140}, - {9, 0x02, 140}, - {23, 0x02, 140}, - {40, 0x03, 140}, - {2, 0x02, 141}, - {9, 0x02, 141}, - {23, 0x02, 141}, - {40, 0x03, 141}, - {2, 0x02, 143}, - {9, 0x02, 143}, - {23, 0x02, 143}, - {40, 0x03, 143}, + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, }, /* 151 */ { - {3, 0x02, 139}, - {6, 0x02, 139}, - {10, 0x02, 139}, - {15, 0x02, 139}, - {24, 0x02, 139}, - {31, 0x02, 139}, - {41, 0x02, 139}, - {56, 0x03, 139}, - {3, 0x02, 140}, - {6, 0x02, 140}, - {10, 0x02, 140}, - {15, 0x02, 140}, - {24, 0x02, 140}, - {31, 0x02, 140}, - {41, 0x02, 140}, - {56, 0x03, 140}, + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, }, /* 152 */ { - {3, 0x02, 141}, - {6, 0x02, 141}, - {10, 0x02, 141}, - {15, 0x02, 141}, - {24, 0x02, 141}, - {31, 0x02, 141}, - {41, 0x02, 141}, - {56, 0x03, 141}, - {3, 0x02, 143}, - {6, 0x02, 143}, - {10, 0x02, 143}, - {15, 0x02, 143}, - {24, 0x02, 143}, - {31, 0x02, 143}, - {41, 0x02, 143}, - {56, 0x03, 143}, + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, }, /* 153 */ { - {157, 0x00, 0}, - {158, 0x00, 0}, - {160, 0x00, 0}, - {161, 0x00, 0}, - {164, 0x00, 0}, - {165, 0x00, 0}, - {167, 0x00, 0}, - {168, 0x00, 0}, - {172, 0x00, 0}, - {173, 0x00, 0}, - {175, 0x00, 0}, - {177, 0x00, 0}, - {182, 0x00, 0}, - {185, 0x00, 0}, - {191, 0x00, 0}, - {207, 0x00, 0}, + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, }, /* 154 */ { - {0, 0x03, 147}, - {0, 0x03, 149}, - {0, 0x03, 150}, - {0, 0x03, 151}, - {0, 0x03, 152}, - {0, 0x03, 155}, - {0, 0x03, 157}, - {0, 0x03, 158}, - {0, 0x03, 165}, - {0, 0x03, 166}, - {0, 0x03, 168}, - {0, 0x03, 174}, - {0, 0x03, 175}, - {0, 0x03, 180}, - {0, 0x03, 182}, - {0, 0x03, 183}, + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, }, /* 155 */ { - {1, 0x02, 147}, - {22, 0x03, 147}, - {1, 0x02, 149}, - {22, 0x03, 149}, - {1, 0x02, 150}, - {22, 0x03, 150}, - {1, 0x02, 151}, - {22, 0x03, 151}, - {1, 0x02, 152}, - {22, 0x03, 152}, - {1, 0x02, 155}, - {22, 0x03, 155}, - {1, 0x02, 157}, - {22, 0x03, 157}, - {1, 0x02, 158}, - {22, 0x03, 158}, + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, }, /* 156 */ { - {2, 0x02, 147}, - {9, 0x02, 147}, - {23, 0x02, 147}, - {40, 0x03, 147}, - {2, 0x02, 149}, - {9, 0x02, 149}, - {23, 0x02, 149}, - {40, 0x03, 149}, - {2, 0x02, 150}, - {9, 0x02, 150}, - {23, 0x02, 150}, - {40, 0x03, 150}, - {2, 0x02, 151}, - {9, 0x02, 151}, - {23, 0x02, 151}, - {40, 0x03, 151}, + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, }, /* 157 */ { - {3, 0x02, 147}, - {6, 0x02, 147}, - {10, 0x02, 147}, - {15, 0x02, 147}, - {24, 0x02, 147}, - {31, 0x02, 147}, - {41, 0x02, 147}, - {56, 0x03, 147}, - {3, 0x02, 149}, - {6, 0x02, 149}, - {10, 0x02, 149}, - {15, 0x02, 149}, - {24, 0x02, 149}, - {31, 0x02, 149}, - {41, 0x02, 149}, - {56, 0x03, 149}, + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, }, /* 158 */ { - {3, 0x02, 150}, - {6, 0x02, 150}, - {10, 0x02, 150}, - {15, 0x02, 150}, - {24, 0x02, 150}, - {31, 0x02, 150}, - {41, 0x02, 150}, - {56, 0x03, 150}, - {3, 0x02, 151}, - {6, 0x02, 151}, - {10, 0x02, 151}, - {15, 0x02, 151}, - {24, 0x02, 151}, - {31, 0x02, 151}, - {41, 0x02, 151}, - {56, 0x03, 151}, + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, }, /* 159 */ { - {2, 0x02, 152}, - {9, 0x02, 152}, - {23, 0x02, 152}, - {40, 0x03, 152}, - {2, 0x02, 155}, - {9, 0x02, 155}, - {23, 0x02, 155}, - {40, 0x03, 155}, - {2, 0x02, 157}, - {9, 0x02, 157}, - {23, 0x02, 157}, - {40, 0x03, 157}, - {2, 0x02, 158}, - {9, 0x02, 158}, - {23, 0x02, 158}, - {40, 0x03, 158}, + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, }, /* 160 */ { - {3, 0x02, 152}, - {6, 0x02, 152}, - {10, 0x02, 152}, - {15, 0x02, 152}, - {24, 0x02, 152}, - {31, 0x02, 152}, - {41, 0x02, 152}, - {56, 0x03, 152}, - {3, 0x02, 155}, - {6, 0x02, 155}, - {10, 0x02, 155}, - {15, 0x02, 155}, - {24, 0x02, 155}, - {31, 0x02, 155}, - {41, 0x02, 155}, - {56, 0x03, 155}, + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, }, /* 161 */ { - {3, 0x02, 157}, - {6, 0x02, 157}, - {10, 0x02, 157}, - {15, 0x02, 157}, - {24, 0x02, 157}, - {31, 0x02, 157}, - {41, 0x02, 157}, - {56, 0x03, 157}, - {3, 0x02, 158}, - {6, 0x02, 158}, - {10, 0x02, 158}, - {15, 0x02, 158}, - {24, 0x02, 158}, - {31, 0x02, 158}, - {41, 0x02, 158}, - {56, 0x03, 158}, + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, }, /* 162 */ { - {1, 0x02, 165}, - {22, 0x03, 165}, - {1, 0x02, 166}, - {22, 0x03, 166}, - {1, 0x02, 168}, - {22, 0x03, 168}, - {1, 0x02, 174}, - {22, 0x03, 174}, - {1, 0x02, 175}, - {22, 0x03, 175}, - {1, 0x02, 180}, - {22, 0x03, 180}, - {1, 0x02, 182}, - {22, 0x03, 182}, - {1, 0x02, 183}, - {22, 0x03, 183}, + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, }, /* 163 */ { - {2, 0x02, 165}, - {9, 0x02, 165}, - {23, 0x02, 165}, - {40, 0x03, 165}, - {2, 0x02, 166}, - {9, 0x02, 166}, - {23, 0x02, 166}, - {40, 0x03, 166}, - {2, 0x02, 168}, - {9, 0x02, 168}, - {23, 0x02, 168}, - {40, 0x03, 168}, - {2, 0x02, 174}, - {9, 0x02, 174}, - {23, 0x02, 174}, - {40, 0x03, 174}, + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, }, /* 164 */ { - {3, 0x02, 165}, - {6, 0x02, 165}, - {10, 0x02, 165}, - {15, 0x02, 165}, - {24, 0x02, 165}, - {31, 0x02, 165}, - {41, 0x02, 165}, - {56, 0x03, 165}, - {3, 0x02, 166}, - {6, 0x02, 166}, - {10, 0x02, 166}, - {15, 0x02, 166}, - {24, 0x02, 166}, - {31, 0x02, 166}, - {41, 0x02, 166}, - {56, 0x03, 166}, + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, }, /* 165 */ { - {3, 0x02, 168}, - {6, 0x02, 168}, - {10, 0x02, 168}, - {15, 0x02, 168}, - {24, 0x02, 168}, - {31, 0x02, 168}, - {41, 0x02, 168}, - {56, 0x03, 168}, - {3, 0x02, 174}, - {6, 0x02, 174}, - {10, 0x02, 174}, - {15, 0x02, 174}, - {24, 0x02, 174}, - {31, 0x02, 174}, - {41, 0x02, 174}, - {56, 0x03, 174}, + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, }, /* 166 */ { - {2, 0x02, 175}, - {9, 0x02, 175}, - {23, 0x02, 175}, - {40, 0x03, 175}, - {2, 0x02, 180}, - {9, 0x02, 180}, - {23, 0x02, 180}, - {40, 0x03, 180}, - {2, 0x02, 182}, - {9, 0x02, 182}, - {23, 0x02, 182}, - {40, 0x03, 182}, - {2, 0x02, 183}, - {9, 0x02, 183}, - {23, 0x02, 183}, - {40, 0x03, 183}, + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, }, /* 167 */ { - {3, 0x02, 175}, - {6, 0x02, 175}, - {10, 0x02, 175}, - {15, 0x02, 175}, - {24, 0x02, 175}, - {31, 0x02, 175}, - {41, 0x02, 175}, - {56, 0x03, 175}, - {3, 0x02, 180}, - {6, 0x02, 180}, - {10, 0x02, 180}, - {15, 0x02, 180}, - {24, 0x02, 180}, - {31, 0x02, 180}, - {41, 0x02, 180}, - {56, 0x03, 180}, + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, }, /* 168 */ { - {3, 0x02, 182}, - {6, 0x02, 182}, - {10, 0x02, 182}, - {15, 0x02, 182}, - {24, 0x02, 182}, - {31, 0x02, 182}, - {41, 0x02, 182}, - {56, 0x03, 182}, - {3, 0x02, 183}, - {6, 0x02, 183}, - {10, 0x02, 183}, - {15, 0x02, 183}, - {24, 0x02, 183}, - {31, 0x02, 183}, - {41, 0x02, 183}, - {56, 0x03, 183}, + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, }, /* 169 */ { - {0, 0x03, 188}, - {0, 0x03, 191}, - {0, 0x03, 197}, - {0, 0x03, 231}, - {0, 0x03, 239}, - {176, 0x00, 0}, - {178, 0x00, 0}, - {179, 0x00, 0}, - {183, 0x00, 0}, - {184, 0x00, 0}, - {186, 0x00, 0}, - {187, 0x00, 0}, - {192, 0x00, 0}, - {199, 0x00, 0}, - {208, 0x00, 0}, - {223, 0x00, 0}, + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, }, /* 170 */ { - {1, 0x02, 188}, - {22, 0x03, 188}, - {1, 0x02, 191}, - {22, 0x03, 191}, - {1, 0x02, 197}, - {22, 0x03, 197}, - {1, 0x02, 231}, - {22, 0x03, 231}, - {1, 0x02, 239}, - {22, 0x03, 239}, - {0, 0x03, 9}, - {0, 0x03, 142}, - {0, 0x03, 144}, - {0, 0x03, 145}, - {0, 0x03, 148}, - {0, 0x03, 159}, + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, }, /* 171 */ { - {2, 0x02, 188}, - {9, 0x02, 188}, - {23, 0x02, 188}, - {40, 0x03, 188}, - {2, 0x02, 191}, - {9, 0x02, 191}, - {23, 0x02, 191}, - {40, 0x03, 191}, - {2, 0x02, 197}, - {9, 0x02, 197}, - {23, 0x02, 197}, - {40, 0x03, 197}, - {2, 0x02, 231}, - {9, 0x02, 231}, - {23, 0x02, 231}, - {40, 0x03, 231}, + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, }, /* 172 */ { - {3, 0x02, 188}, - {6, 0x02, 188}, - {10, 0x02, 188}, - {15, 0x02, 188}, - {24, 0x02, 188}, - {31, 0x02, 188}, - {41, 0x02, 188}, - {56, 0x03, 188}, - {3, 0x02, 191}, - {6, 0x02, 191}, - {10, 0x02, 191}, - {15, 0x02, 191}, - {24, 0x02, 191}, - {31, 0x02, 191}, - {41, 0x02, 191}, - {56, 0x03, 191}, + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, }, /* 173 */ { - {3, 0x02, 197}, - {6, 0x02, 197}, - {10, 0x02, 197}, - {15, 0x02, 197}, - {24, 0x02, 197}, - {31, 0x02, 197}, - {41, 0x02, 197}, - {56, 0x03, 197}, - {3, 0x02, 231}, - {6, 0x02, 231}, - {10, 0x02, 231}, - {15, 0x02, 231}, - {24, 0x02, 231}, - {31, 0x02, 231}, - {41, 0x02, 231}, - {56, 0x03, 231}, + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, }, /* 174 */ { - {2, 0x02, 239}, - {9, 0x02, 239}, - {23, 0x02, 239}, - {40, 0x03, 239}, - {1, 0x02, 9}, - {22, 0x03, 9}, - {1, 0x02, 142}, - {22, 0x03, 142}, - {1, 0x02, 144}, - {22, 0x03, 144}, - {1, 0x02, 145}, - {22, 0x03, 145}, - {1, 0x02, 148}, - {22, 0x03, 148}, - {1, 0x02, 159}, - {22, 0x03, 159}, + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, }, /* 175 */ { - {3, 0x02, 239}, - {6, 0x02, 239}, - {10, 0x02, 239}, - {15, 0x02, 239}, - {24, 0x02, 239}, - {31, 0x02, 239}, - {41, 0x02, 239}, - {56, 0x03, 239}, - {2, 0x02, 9}, - {9, 0x02, 9}, - {23, 0x02, 9}, - {40, 0x03, 9}, - {2, 0x02, 142}, - {9, 0x02, 142}, - {23, 0x02, 142}, - {40, 0x03, 142}, + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, }, /* 176 */ { - {3, 0x02, 9}, - {6, 0x02, 9}, - {10, 0x02, 9}, - {15, 0x02, 9}, - {24, 0x02, 9}, - {31, 0x02, 9}, - {41, 0x02, 9}, - {56, 0x03, 9}, - {3, 0x02, 142}, - {6, 0x02, 142}, - {10, 0x02, 142}, - {15, 0x02, 142}, - {24, 0x02, 142}, - {31, 0x02, 142}, - {41, 0x02, 142}, - {56, 0x03, 142}, + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, }, /* 177 */ { - {2, 0x02, 144}, - {9, 0x02, 144}, - {23, 0x02, 144}, - {40, 0x03, 144}, - {2, 0x02, 145}, - {9, 0x02, 145}, - {23, 0x02, 145}, - {40, 0x03, 145}, - {2, 0x02, 148}, - {9, 0x02, 148}, - {23, 0x02, 148}, - {40, 0x03, 148}, - {2, 0x02, 159}, - {9, 0x02, 159}, - {23, 0x02, 159}, - {40, 0x03, 159}, + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, }, /* 178 */ { - {3, 0x02, 144}, - {6, 0x02, 144}, - {10, 0x02, 144}, - {15, 0x02, 144}, - {24, 0x02, 144}, - {31, 0x02, 144}, - {41, 0x02, 144}, - {56, 0x03, 144}, - {3, 0x02, 145}, - {6, 0x02, 145}, - {10, 0x02, 145}, - {15, 0x02, 145}, - {24, 0x02, 145}, - {31, 0x02, 145}, - {41, 0x02, 145}, - {56, 0x03, 145}, + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, }, /* 179 */ { - {3, 0x02, 148}, - {6, 0x02, 148}, - {10, 0x02, 148}, - {15, 0x02, 148}, - {24, 0x02, 148}, - {31, 0x02, 148}, - {41, 0x02, 148}, - {56, 0x03, 148}, - {3, 0x02, 159}, - {6, 0x02, 159}, - {10, 0x02, 159}, - {15, 0x02, 159}, - {24, 0x02, 159}, - {31, 0x02, 159}, - {41, 0x02, 159}, - {56, 0x03, 159}, + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, }, /* 180 */ { - {0, 0x03, 171}, - {0, 0x03, 206}, - {0, 0x03, 215}, - {0, 0x03, 225}, - {0, 0x03, 236}, - {0, 0x03, 237}, - {188, 0x00, 0}, - {189, 0x00, 0}, - {193, 0x00, 0}, - {196, 0x00, 0}, - {200, 0x00, 0}, - {203, 0x00, 0}, - {209, 0x00, 0}, - {216, 0x00, 0}, - {224, 0x00, 0}, - {238, 0x00, 0}, + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, }, /* 181 */ { - {1, 0x02, 171}, - {22, 0x03, 171}, - {1, 0x02, 206}, - {22, 0x03, 206}, - {1, 0x02, 215}, - {22, 0x03, 215}, - {1, 0x02, 225}, - {22, 0x03, 225}, - {1, 0x02, 236}, - {22, 0x03, 236}, - {1, 0x02, 237}, - {22, 0x03, 237}, - {0, 0x03, 199}, - {0, 0x03, 207}, - {0, 0x03, 234}, - {0, 0x03, 235}, + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, }, /* 182 */ { - {2, 0x02, 171}, - {9, 0x02, 171}, - {23, 0x02, 171}, - {40, 0x03, 171}, - {2, 0x02, 206}, - {9, 0x02, 206}, - {23, 0x02, 206}, - {40, 0x03, 206}, - {2, 0x02, 215}, - {9, 0x02, 215}, - {23, 0x02, 215}, - {40, 0x03, 215}, - {2, 0x02, 225}, - {9, 0x02, 225}, - {23, 0x02, 225}, - {40, 0x03, 225}, + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, }, /* 183 */ { - {3, 0x02, 171}, - {6, 0x02, 171}, - {10, 0x02, 171}, - {15, 0x02, 171}, - {24, 0x02, 171}, - {31, 0x02, 171}, - {41, 0x02, 171}, - {56, 0x03, 171}, - {3, 0x02, 206}, - {6, 0x02, 206}, - {10, 0x02, 206}, - {15, 0x02, 206}, - {24, 0x02, 206}, - {31, 0x02, 206}, - {41, 0x02, 206}, - {56, 0x03, 206}, + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, }, /* 184 */ { - {3, 0x02, 215}, - {6, 0x02, 215}, - {10, 0x02, 215}, - {15, 0x02, 215}, - {24, 0x02, 215}, - {31, 0x02, 215}, - {41, 0x02, 215}, - {56, 0x03, 215}, - {3, 0x02, 225}, - {6, 0x02, 225}, - {10, 0x02, 225}, - {15, 0x02, 225}, - {24, 0x02, 225}, - {31, 0x02, 225}, - {41, 0x02, 225}, - {56, 0x03, 225}, + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, }, /* 185 */ { - {2, 0x02, 236}, - {9, 0x02, 236}, - {23, 0x02, 236}, - {40, 0x03, 236}, - {2, 0x02, 237}, - {9, 0x02, 237}, - {23, 0x02, 237}, - {40, 0x03, 237}, - {1, 0x02, 199}, - {22, 0x03, 199}, - {1, 0x02, 207}, - {22, 0x03, 207}, - {1, 0x02, 234}, - {22, 0x03, 234}, - {1, 0x02, 235}, - {22, 0x03, 235}, + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, }, /* 186 */ { - {3, 0x02, 236}, - {6, 0x02, 236}, - {10, 0x02, 236}, - {15, 0x02, 236}, - {24, 0x02, 236}, - {31, 0x02, 236}, - {41, 0x02, 236}, - {56, 0x03, 236}, - {3, 0x02, 237}, - {6, 0x02, 237}, - {10, 0x02, 237}, - {15, 0x02, 237}, - {24, 0x02, 237}, - {31, 0x02, 237}, - {41, 0x02, 237}, - {56, 0x03, 237}, + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, }, /* 187 */ { - {2, 0x02, 199}, - {9, 0x02, 199}, - {23, 0x02, 199}, - {40, 0x03, 199}, - {2, 0x02, 207}, - {9, 0x02, 207}, - {23, 0x02, 207}, - {40, 0x03, 207}, - {2, 0x02, 234}, - {9, 0x02, 234}, - {23, 0x02, 234}, - {40, 0x03, 234}, - {2, 0x02, 235}, - {9, 0x02, 235}, - {23, 0x02, 235}, - {40, 0x03, 235}, + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, }, /* 188 */ { - {3, 0x02, 199}, - {6, 0x02, 199}, - {10, 0x02, 199}, - {15, 0x02, 199}, - {24, 0x02, 199}, - {31, 0x02, 199}, - {41, 0x02, 199}, - {56, 0x03, 199}, - {3, 0x02, 207}, - {6, 0x02, 207}, - {10, 0x02, 207}, - {15, 0x02, 207}, - {24, 0x02, 207}, - {31, 0x02, 207}, - {41, 0x02, 207}, - {56, 0x03, 207}, + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, }, /* 189 */ { - {3, 0x02, 234}, - {6, 0x02, 234}, - {10, 0x02, 234}, - {15, 0x02, 234}, - {24, 0x02, 234}, - {31, 0x02, 234}, - {41, 0x02, 234}, - {56, 0x03, 234}, - {3, 0x02, 235}, - {6, 0x02, 235}, - {10, 0x02, 235}, - {15, 0x02, 235}, - {24, 0x02, 235}, - {31, 0x02, 235}, - {41, 0x02, 235}, - {56, 0x03, 235}, + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, }, /* 190 */ { - {194, 0x00, 0}, - {195, 0x00, 0}, - {197, 0x00, 0}, - {198, 0x00, 0}, - {201, 0x00, 0}, - {202, 0x00, 0}, - {204, 0x00, 0}, - {205, 0x00, 0}, - {210, 0x00, 0}, - {213, 0x00, 0}, - {217, 0x00, 0}, - {220, 0x00, 0}, - {225, 0x00, 0}, - {231, 0x00, 0}, - {239, 0x00, 0}, - {246, 0x00, 0}, + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, }, /* 191 */ { - {0, 0x03, 192}, - {0, 0x03, 193}, - {0, 0x03, 200}, - {0, 0x03, 201}, - {0, 0x03, 202}, - {0, 0x03, 205}, - {0, 0x03, 210}, - {0, 0x03, 213}, - {0, 0x03, 218}, - {0, 0x03, 219}, - {0, 0x03, 238}, - {0, 0x03, 240}, - {0, 0x03, 242}, - {0, 0x03, 243}, - {0, 0x03, 255}, - {206, 0x00, 0}, + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, }, /* 192 */ { - {1, 0x02, 192}, - {22, 0x03, 192}, - {1, 0x02, 193}, - {22, 0x03, 193}, - {1, 0x02, 200}, - {22, 0x03, 200}, - {1, 0x02, 201}, - {22, 0x03, 201}, - {1, 0x02, 202}, - {22, 0x03, 202}, - {1, 0x02, 205}, - {22, 0x03, 205}, - {1, 0x02, 210}, - {22, 0x03, 210}, - {1, 0x02, 213}, - {22, 0x03, 213}, + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, }, /* 193 */ { - {2, 0x02, 192}, - {9, 0x02, 192}, - {23, 0x02, 192}, - {40, 0x03, 192}, - {2, 0x02, 193}, - {9, 0x02, 193}, - {23, 0x02, 193}, - {40, 0x03, 193}, - {2, 0x02, 200}, - {9, 0x02, 200}, - {23, 0x02, 200}, - {40, 0x03, 200}, - {2, 0x02, 201}, - {9, 0x02, 201}, - {23, 0x02, 201}, - {40, 0x03, 201}, + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, }, /* 194 */ { - {3, 0x02, 192}, - {6, 0x02, 192}, - {10, 0x02, 192}, - {15, 0x02, 192}, - {24, 0x02, 192}, - {31, 0x02, 192}, - {41, 0x02, 192}, - {56, 0x03, 192}, - {3, 0x02, 193}, - {6, 0x02, 193}, - {10, 0x02, 193}, - {15, 0x02, 193}, - {24, 0x02, 193}, - {31, 0x02, 193}, - {41, 0x02, 193}, - {56, 0x03, 193}, + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, }, /* 195 */ { - {3, 0x02, 200}, - {6, 0x02, 200}, - {10, 0x02, 200}, - {15, 0x02, 200}, - {24, 0x02, 200}, - {31, 0x02, 200}, - {41, 0x02, 200}, - {56, 0x03, 200}, - {3, 0x02, 201}, - {6, 0x02, 201}, - {10, 0x02, 201}, - {15, 0x02, 201}, - {24, 0x02, 201}, - {31, 0x02, 201}, - {41, 0x02, 201}, - {56, 0x03, 201}, + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, }, /* 196 */ { - {2, 0x02, 202}, - {9, 0x02, 202}, - {23, 0x02, 202}, - {40, 0x03, 202}, - {2, 0x02, 205}, - {9, 0x02, 205}, - {23, 0x02, 205}, - {40, 0x03, 205}, - {2, 0x02, 210}, - {9, 0x02, 210}, - {23, 0x02, 210}, - {40, 0x03, 210}, - {2, 0x02, 213}, - {9, 0x02, 213}, - {23, 0x02, 213}, - {40, 0x03, 213}, + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, }, /* 197 */ { - {3, 0x02, 202}, - {6, 0x02, 202}, - {10, 0x02, 202}, - {15, 0x02, 202}, - {24, 0x02, 202}, - {31, 0x02, 202}, - {41, 0x02, 202}, - {56, 0x03, 202}, - {3, 0x02, 205}, - {6, 0x02, 205}, - {10, 0x02, 205}, - {15, 0x02, 205}, - {24, 0x02, 205}, - {31, 0x02, 205}, - {41, 0x02, 205}, - {56, 0x03, 205}, + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, }, /* 198 */ { - {3, 0x02, 210}, - {6, 0x02, 210}, - {10, 0x02, 210}, - {15, 0x02, 210}, - {24, 0x02, 210}, - {31, 0x02, 210}, - {41, 0x02, 210}, - {56, 0x03, 210}, - {3, 0x02, 213}, - {6, 0x02, 213}, - {10, 0x02, 213}, - {15, 0x02, 213}, - {24, 0x02, 213}, - {31, 0x02, 213}, - {41, 0x02, 213}, - {56, 0x03, 213}, + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, }, /* 199 */ { - {1, 0x02, 218}, - {22, 0x03, 218}, - {1, 0x02, 219}, - {22, 0x03, 219}, - {1, 0x02, 238}, - {22, 0x03, 238}, - {1, 0x02, 240}, - {22, 0x03, 240}, - {1, 0x02, 242}, - {22, 0x03, 242}, - {1, 0x02, 243}, - {22, 0x03, 243}, - {1, 0x02, 255}, - {22, 0x03, 255}, - {0, 0x03, 203}, - {0, 0x03, 204}, + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, }, /* 200 */ { - {2, 0x02, 218}, - {9, 0x02, 218}, - {23, 0x02, 218}, - {40, 0x03, 218}, - {2, 0x02, 219}, - {9, 0x02, 219}, - {23, 0x02, 219}, - {40, 0x03, 219}, - {2, 0x02, 238}, - {9, 0x02, 238}, - {23, 0x02, 238}, - {40, 0x03, 238}, - {2, 0x02, 240}, - {9, 0x02, 240}, - {23, 0x02, 240}, - {40, 0x03, 240}, + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, }, /* 201 */ { - {3, 0x02, 218}, - {6, 0x02, 218}, - {10, 0x02, 218}, - {15, 0x02, 218}, - {24, 0x02, 218}, - {31, 0x02, 218}, - {41, 0x02, 218}, - {56, 0x03, 218}, - {3, 0x02, 219}, - {6, 0x02, 219}, - {10, 0x02, 219}, - {15, 0x02, 219}, - {24, 0x02, 219}, - {31, 0x02, 219}, - {41, 0x02, 219}, - {56, 0x03, 219}, + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, }, /* 202 */ { - {3, 0x02, 238}, - {6, 0x02, 238}, - {10, 0x02, 238}, - {15, 0x02, 238}, - {24, 0x02, 238}, - {31, 0x02, 238}, - {41, 0x02, 238}, - {56, 0x03, 238}, - {3, 0x02, 240}, - {6, 0x02, 240}, - {10, 0x02, 240}, - {15, 0x02, 240}, - {24, 0x02, 240}, - {31, 0x02, 240}, - {41, 0x02, 240}, - {56, 0x03, 240}, + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, }, /* 203 */ { - {2, 0x02, 242}, - {9, 0x02, 242}, - {23, 0x02, 242}, - {40, 0x03, 242}, - {2, 0x02, 243}, - {9, 0x02, 243}, - {23, 0x02, 243}, - {40, 0x03, 243}, - {2, 0x02, 255}, - {9, 0x02, 255}, - {23, 0x02, 255}, - {40, 0x03, 255}, - {1, 0x02, 203}, - {22, 0x03, 203}, - {1, 0x02, 204}, - {22, 0x03, 204}, + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, }, /* 204 */ { - {3, 0x02, 242}, - {6, 0x02, 242}, - {10, 0x02, 242}, - {15, 0x02, 242}, - {24, 0x02, 242}, - {31, 0x02, 242}, - {41, 0x02, 242}, - {56, 0x03, 242}, - {3, 0x02, 243}, - {6, 0x02, 243}, - {10, 0x02, 243}, - {15, 0x02, 243}, - {24, 0x02, 243}, - {31, 0x02, 243}, - {41, 0x02, 243}, - {56, 0x03, 243}, + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, }, /* 205 */ { - {3, 0x02, 255}, - {6, 0x02, 255}, - {10, 0x02, 255}, - {15, 0x02, 255}, - {24, 0x02, 255}, - {31, 0x02, 255}, - {41, 0x02, 255}, - {56, 0x03, 255}, - {2, 0x02, 203}, - {9, 0x02, 203}, - {23, 0x02, 203}, - {40, 0x03, 203}, - {2, 0x02, 204}, - {9, 0x02, 204}, - {23, 0x02, 204}, - {40, 0x03, 204}, + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, }, /* 206 */ { - {3, 0x02, 203}, - {6, 0x02, 203}, - {10, 0x02, 203}, - {15, 0x02, 203}, - {24, 0x02, 203}, - {31, 0x02, 203}, - {41, 0x02, 203}, - {56, 0x03, 203}, - {3, 0x02, 204}, - {6, 0x02, 204}, - {10, 0x02, 204}, - {15, 0x02, 204}, - {24, 0x02, 204}, - {31, 0x02, 204}, - {41, 0x02, 204}, - {56, 0x03, 204}, + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, }, /* 207 */ { - {211, 0x00, 0}, - {212, 0x00, 0}, - {214, 0x00, 0}, - {215, 0x00, 0}, - {218, 0x00, 0}, - {219, 0x00, 0}, - {221, 0x00, 0}, - {222, 0x00, 0}, - {226, 0x00, 0}, - {228, 0x00, 0}, - {232, 0x00, 0}, - {235, 0x00, 0}, - {240, 0x00, 0}, - {243, 0x00, 0}, - {247, 0x00, 0}, - {250, 0x00, 0}, + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, }, /* 208 */ { - {0, 0x03, 211}, - {0, 0x03, 212}, - {0, 0x03, 214}, - {0, 0x03, 221}, - {0, 0x03, 222}, - {0, 0x03, 223}, - {0, 0x03, 241}, - {0, 0x03, 244}, - {0, 0x03, 245}, - {0, 0x03, 246}, - {0, 0x03, 247}, - {0, 0x03, 248}, - {0, 0x03, 250}, - {0, 0x03, 251}, - {0, 0x03, 252}, - {0, 0x03, 253}, + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, }, /* 209 */ { - {1, 0x02, 211}, - {22, 0x03, 211}, - {1, 0x02, 212}, - {22, 0x03, 212}, - {1, 0x02, 214}, - {22, 0x03, 214}, - {1, 0x02, 221}, - {22, 0x03, 221}, - {1, 0x02, 222}, - {22, 0x03, 222}, - {1, 0x02, 223}, - {22, 0x03, 223}, - {1, 0x02, 241}, - {22, 0x03, 241}, - {1, 0x02, 244}, - {22, 0x03, 244}, + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, }, /* 210 */ { - {2, 0x02, 211}, - {9, 0x02, 211}, - {23, 0x02, 211}, - {40, 0x03, 211}, - {2, 0x02, 212}, - {9, 0x02, 212}, - {23, 0x02, 212}, - {40, 0x03, 212}, - {2, 0x02, 214}, - {9, 0x02, 214}, - {23, 0x02, 214}, - {40, 0x03, 214}, - {2, 0x02, 221}, - {9, 0x02, 221}, - {23, 0x02, 221}, - {40, 0x03, 221}, + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, }, /* 211 */ { - {3, 0x02, 211}, - {6, 0x02, 211}, - {10, 0x02, 211}, - {15, 0x02, 211}, - {24, 0x02, 211}, - {31, 0x02, 211}, - {41, 0x02, 211}, - {56, 0x03, 211}, - {3, 0x02, 212}, - {6, 0x02, 212}, - {10, 0x02, 212}, - {15, 0x02, 212}, - {24, 0x02, 212}, - {31, 0x02, 212}, - {41, 0x02, 212}, - {56, 0x03, 212}, + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, }, /* 212 */ { - {3, 0x02, 214}, - {6, 0x02, 214}, - {10, 0x02, 214}, - {15, 0x02, 214}, - {24, 0x02, 214}, - {31, 0x02, 214}, - {41, 0x02, 214}, - {56, 0x03, 214}, - {3, 0x02, 221}, - {6, 0x02, 221}, - {10, 0x02, 221}, - {15, 0x02, 221}, - {24, 0x02, 221}, - {31, 0x02, 221}, - {41, 0x02, 221}, - {56, 0x03, 221}, + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, }, /* 213 */ { - {2, 0x02, 222}, - {9, 0x02, 222}, - {23, 0x02, 222}, - {40, 0x03, 222}, - {2, 0x02, 223}, - {9, 0x02, 223}, - {23, 0x02, 223}, - {40, 0x03, 223}, - {2, 0x02, 241}, - {9, 0x02, 241}, - {23, 0x02, 241}, - {40, 0x03, 241}, - {2, 0x02, 244}, - {9, 0x02, 244}, - {23, 0x02, 244}, - {40, 0x03, 244}, + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, }, /* 214 */ { - {3, 0x02, 222}, - {6, 0x02, 222}, - {10, 0x02, 222}, - {15, 0x02, 222}, - {24, 0x02, 222}, - {31, 0x02, 222}, - {41, 0x02, 222}, - {56, 0x03, 222}, - {3, 0x02, 223}, - {6, 0x02, 223}, - {10, 0x02, 223}, - {15, 0x02, 223}, - {24, 0x02, 223}, - {31, 0x02, 223}, - {41, 0x02, 223}, - {56, 0x03, 223}, + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, }, /* 215 */ { - {3, 0x02, 241}, - {6, 0x02, 241}, - {10, 0x02, 241}, - {15, 0x02, 241}, - {24, 0x02, 241}, - {31, 0x02, 241}, - {41, 0x02, 241}, - {56, 0x03, 241}, - {3, 0x02, 244}, - {6, 0x02, 244}, - {10, 0x02, 244}, - {15, 0x02, 244}, - {24, 0x02, 244}, - {31, 0x02, 244}, - {41, 0x02, 244}, - {56, 0x03, 244}, + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, }, /* 216 */ { - {1, 0x02, 245}, - {22, 0x03, 245}, - {1, 0x02, 246}, - {22, 0x03, 246}, - {1, 0x02, 247}, - {22, 0x03, 247}, - {1, 0x02, 248}, - {22, 0x03, 248}, - {1, 0x02, 250}, - {22, 0x03, 250}, - {1, 0x02, 251}, - {22, 0x03, 251}, - {1, 0x02, 252}, - {22, 0x03, 252}, - {1, 0x02, 253}, - {22, 0x03, 253}, + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, }, /* 217 */ { - {2, 0x02, 245}, - {9, 0x02, 245}, - {23, 0x02, 245}, - {40, 0x03, 245}, - {2, 0x02, 246}, - {9, 0x02, 246}, - {23, 0x02, 246}, - {40, 0x03, 246}, - {2, 0x02, 247}, - {9, 0x02, 247}, - {23, 0x02, 247}, - {40, 0x03, 247}, - {2, 0x02, 248}, - {9, 0x02, 248}, - {23, 0x02, 248}, - {40, 0x03, 248}, + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, }, /* 218 */ { - {3, 0x02, 245}, - {6, 0x02, 245}, - {10, 0x02, 245}, - {15, 0x02, 245}, - {24, 0x02, 245}, - {31, 0x02, 245}, - {41, 0x02, 245}, - {56, 0x03, 245}, - {3, 0x02, 246}, - {6, 0x02, 246}, - {10, 0x02, 246}, - {15, 0x02, 246}, - {24, 0x02, 246}, - {31, 0x02, 246}, - {41, 0x02, 246}, - {56, 0x03, 246}, + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, }, /* 219 */ { - {3, 0x02, 247}, - {6, 0x02, 247}, - {10, 0x02, 247}, - {15, 0x02, 247}, - {24, 0x02, 247}, - {31, 0x02, 247}, - {41, 0x02, 247}, - {56, 0x03, 247}, - {3, 0x02, 248}, - {6, 0x02, 248}, - {10, 0x02, 248}, - {15, 0x02, 248}, - {24, 0x02, 248}, - {31, 0x02, 248}, - {41, 0x02, 248}, - {56, 0x03, 248}, + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, }, /* 220 */ { - {2, 0x02, 250}, - {9, 0x02, 250}, - {23, 0x02, 250}, - {40, 0x03, 250}, - {2, 0x02, 251}, - {9, 0x02, 251}, - {23, 0x02, 251}, - {40, 0x03, 251}, - {2, 0x02, 252}, - {9, 0x02, 252}, - {23, 0x02, 252}, - {40, 0x03, 252}, - {2, 0x02, 253}, - {9, 0x02, 253}, - {23, 0x02, 253}, - {40, 0x03, 253}, + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, }, /* 221 */ { - {3, 0x02, 250}, - {6, 0x02, 250}, - {10, 0x02, 250}, - {15, 0x02, 250}, - {24, 0x02, 250}, - {31, 0x02, 250}, - {41, 0x02, 250}, - {56, 0x03, 250}, - {3, 0x02, 251}, - {6, 0x02, 251}, - {10, 0x02, 251}, - {15, 0x02, 251}, - {24, 0x02, 251}, - {31, 0x02, 251}, - {41, 0x02, 251}, - {56, 0x03, 251}, + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, }, /* 222 */ { - {3, 0x02, 252}, - {6, 0x02, 252}, - {10, 0x02, 252}, - {15, 0x02, 252}, - {24, 0x02, 252}, - {31, 0x02, 252}, - {41, 0x02, 252}, - {56, 0x03, 252}, - {3, 0x02, 253}, - {6, 0x02, 253}, - {10, 0x02, 253}, - {15, 0x02, 253}, - {24, 0x02, 253}, - {31, 0x02, 253}, - {41, 0x02, 253}, - {56, 0x03, 253}, + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, }, /* 223 */ { - {0, 0x03, 254}, - {227, 0x00, 0}, - {229, 0x00, 0}, - {230, 0x00, 0}, - {233, 0x00, 0}, - {234, 0x00, 0}, - {236, 0x00, 0}, - {237, 0x00, 0}, - {241, 0x00, 0}, - {242, 0x00, 0}, - {244, 0x00, 0}, - {245, 0x00, 0}, - {248, 0x00, 0}, - {249, 0x00, 0}, - {251, 0x00, 0}, - {252, 0x00, 0}, + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, }, /* 224 */ { - {1, 0x02, 254}, - {22, 0x03, 254}, - {0, 0x03, 2}, - {0, 0x03, 3}, - {0, 0x03, 4}, - {0, 0x03, 5}, - {0, 0x03, 6}, - {0, 0x03, 7}, - {0, 0x03, 8}, - {0, 0x03, 11}, - {0, 0x03, 12}, - {0, 0x03, 14}, - {0, 0x03, 15}, - {0, 0x03, 16}, - {0, 0x03, 17}, - {0, 0x03, 18}, + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, }, /* 225 */ { - {2, 0x02, 254}, - {9, 0x02, 254}, - {23, 0x02, 254}, - {40, 0x03, 254}, - {1, 0x02, 2}, - {22, 0x03, 2}, - {1, 0x02, 3}, - {22, 0x03, 3}, - {1, 0x02, 4}, - {22, 0x03, 4}, - {1, 0x02, 5}, - {22, 0x03, 5}, - {1, 0x02, 6}, - {22, 0x03, 6}, - {1, 0x02, 7}, - {22, 0x03, 7}, + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, }, /* 226 */ { - {3, 0x02, 254}, - {6, 0x02, 254}, - {10, 0x02, 254}, - {15, 0x02, 254}, - {24, 0x02, 254}, - {31, 0x02, 254}, - {41, 0x02, 254}, - {56, 0x03, 254}, - {2, 0x02, 2}, - {9, 0x02, 2}, - {23, 0x02, 2}, - {40, 0x03, 2}, - {2, 0x02, 3}, - {9, 0x02, 3}, - {23, 0x02, 3}, - {40, 0x03, 3}, + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, }, /* 227 */ { - {3, 0x02, 2}, - {6, 0x02, 2}, - {10, 0x02, 2}, - {15, 0x02, 2}, - {24, 0x02, 2}, - {31, 0x02, 2}, - {41, 0x02, 2}, - {56, 0x03, 2}, - {3, 0x02, 3}, - {6, 0x02, 3}, - {10, 0x02, 3}, - {15, 0x02, 3}, - {24, 0x02, 3}, - {31, 0x02, 3}, - {41, 0x02, 3}, - {56, 0x03, 3}, + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, }, /* 228 */ { - {2, 0x02, 4}, - {9, 0x02, 4}, - {23, 0x02, 4}, - {40, 0x03, 4}, - {2, 0x02, 5}, - {9, 0x02, 5}, - {23, 0x02, 5}, - {40, 0x03, 5}, - {2, 0x02, 6}, - {9, 0x02, 6}, - {23, 0x02, 6}, - {40, 0x03, 6}, - {2, 0x02, 7}, - {9, 0x02, 7}, - {23, 0x02, 7}, - {40, 0x03, 7}, + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, }, /* 229 */ { - {3, 0x02, 4}, - {6, 0x02, 4}, - {10, 0x02, 4}, - {15, 0x02, 4}, - {24, 0x02, 4}, - {31, 0x02, 4}, - {41, 0x02, 4}, - {56, 0x03, 4}, - {3, 0x02, 5}, - {6, 0x02, 5}, - {10, 0x02, 5}, - {15, 0x02, 5}, - {24, 0x02, 5}, - {31, 0x02, 5}, - {41, 0x02, 5}, - {56, 0x03, 5}, + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, }, /* 230 */ { - {3, 0x02, 6}, - {6, 0x02, 6}, - {10, 0x02, 6}, - {15, 0x02, 6}, - {24, 0x02, 6}, - {31, 0x02, 6}, - {41, 0x02, 6}, - {56, 0x03, 6}, - {3, 0x02, 7}, - {6, 0x02, 7}, - {10, 0x02, 7}, - {15, 0x02, 7}, - {24, 0x02, 7}, - {31, 0x02, 7}, - {41, 0x02, 7}, - {56, 0x03, 7}, + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, }, /* 231 */ { - {1, 0x02, 8}, - {22, 0x03, 8}, - {1, 0x02, 11}, - {22, 0x03, 11}, - {1, 0x02, 12}, - {22, 0x03, 12}, - {1, 0x02, 14}, - {22, 0x03, 14}, - {1, 0x02, 15}, - {22, 0x03, 15}, - {1, 0x02, 16}, - {22, 0x03, 16}, - {1, 0x02, 17}, - {22, 0x03, 17}, - {1, 0x02, 18}, - {22, 0x03, 18}, + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, }, /* 232 */ { - {2, 0x02, 8}, - {9, 0x02, 8}, - {23, 0x02, 8}, - {40, 0x03, 8}, - {2, 0x02, 11}, - {9, 0x02, 11}, - {23, 0x02, 11}, - {40, 0x03, 11}, - {2, 0x02, 12}, - {9, 0x02, 12}, - {23, 0x02, 12}, - {40, 0x03, 12}, - {2, 0x02, 14}, - {9, 0x02, 14}, - {23, 0x02, 14}, - {40, 0x03, 14}, + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, }, /* 233 */ { - {3, 0x02, 8}, - {6, 0x02, 8}, - {10, 0x02, 8}, - {15, 0x02, 8}, - {24, 0x02, 8}, - {31, 0x02, 8}, - {41, 0x02, 8}, - {56, 0x03, 8}, - {3, 0x02, 11}, - {6, 0x02, 11}, - {10, 0x02, 11}, - {15, 0x02, 11}, - {24, 0x02, 11}, - {31, 0x02, 11}, - {41, 0x02, 11}, - {56, 0x03, 11}, + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, }, /* 234 */ { - {3, 0x02, 12}, - {6, 0x02, 12}, - {10, 0x02, 12}, - {15, 0x02, 12}, - {24, 0x02, 12}, - {31, 0x02, 12}, - {41, 0x02, 12}, - {56, 0x03, 12}, - {3, 0x02, 14}, - {6, 0x02, 14}, - {10, 0x02, 14}, - {15, 0x02, 14}, - {24, 0x02, 14}, - {31, 0x02, 14}, - {41, 0x02, 14}, - {56, 0x03, 14}, + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, }, /* 235 */ { - {2, 0x02, 15}, - {9, 0x02, 15}, - {23, 0x02, 15}, - {40, 0x03, 15}, - {2, 0x02, 16}, - {9, 0x02, 16}, - {23, 0x02, 16}, - {40, 0x03, 16}, - {2, 0x02, 17}, - {9, 0x02, 17}, - {23, 0x02, 17}, - {40, 0x03, 17}, - {2, 0x02, 18}, - {9, 0x02, 18}, - {23, 0x02, 18}, - {40, 0x03, 18}, + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, }, /* 236 */ { - {3, 0x02, 15}, - {6, 0x02, 15}, - {10, 0x02, 15}, - {15, 0x02, 15}, - {24, 0x02, 15}, - {31, 0x02, 15}, - {41, 0x02, 15}, - {56, 0x03, 15}, - {3, 0x02, 16}, - {6, 0x02, 16}, - {10, 0x02, 16}, - {15, 0x02, 16}, - {24, 0x02, 16}, - {31, 0x02, 16}, - {41, 0x02, 16}, - {56, 0x03, 16}, + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, }, /* 237 */ { - {3, 0x02, 17}, - {6, 0x02, 17}, - {10, 0x02, 17}, - {15, 0x02, 17}, - {24, 0x02, 17}, - {31, 0x02, 17}, - {41, 0x02, 17}, - {56, 0x03, 17}, - {3, 0x02, 18}, - {6, 0x02, 18}, - {10, 0x02, 18}, - {15, 0x02, 18}, - {24, 0x02, 18}, - {31, 0x02, 18}, - {41, 0x02, 18}, - {56, 0x03, 18}, + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, }, /* 238 */ { - {0, 0x03, 19}, - {0, 0x03, 20}, - {0, 0x03, 21}, - {0, 0x03, 23}, - {0, 0x03, 24}, - {0, 0x03, 25}, - {0, 0x03, 26}, - {0, 0x03, 27}, - {0, 0x03, 28}, - {0, 0x03, 29}, - {0, 0x03, 30}, - {0, 0x03, 31}, - {0, 0x03, 127}, - {0, 0x03, 220}, - {0, 0x03, 249}, - {253, 0x00, 0}, + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, }, /* 239 */ { - {1, 0x02, 19}, - {22, 0x03, 19}, - {1, 0x02, 20}, - {22, 0x03, 20}, - {1, 0x02, 21}, - {22, 0x03, 21}, - {1, 0x02, 23}, - {22, 0x03, 23}, - {1, 0x02, 24}, - {22, 0x03, 24}, - {1, 0x02, 25}, - {22, 0x03, 25}, - {1, 0x02, 26}, - {22, 0x03, 26}, - {1, 0x02, 27}, - {22, 0x03, 27}, + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, }, /* 240 */ { - {2, 0x02, 19}, - {9, 0x02, 19}, - {23, 0x02, 19}, - {40, 0x03, 19}, - {2, 0x02, 20}, - {9, 0x02, 20}, - {23, 0x02, 20}, - {40, 0x03, 20}, - {2, 0x02, 21}, - {9, 0x02, 21}, - {23, 0x02, 21}, - {40, 0x03, 21}, - {2, 0x02, 23}, - {9, 0x02, 23}, - {23, 0x02, 23}, - {40, 0x03, 23}, + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, }, /* 241 */ { - {3, 0x02, 19}, - {6, 0x02, 19}, - {10, 0x02, 19}, - {15, 0x02, 19}, - {24, 0x02, 19}, - {31, 0x02, 19}, - {41, 0x02, 19}, - {56, 0x03, 19}, - {3, 0x02, 20}, - {6, 0x02, 20}, - {10, 0x02, 20}, - {15, 0x02, 20}, - {24, 0x02, 20}, - {31, 0x02, 20}, - {41, 0x02, 20}, - {56, 0x03, 20}, + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, }, /* 242 */ { - {3, 0x02, 21}, - {6, 0x02, 21}, - {10, 0x02, 21}, - {15, 0x02, 21}, - {24, 0x02, 21}, - {31, 0x02, 21}, - {41, 0x02, 21}, - {56, 0x03, 21}, - {3, 0x02, 23}, - {6, 0x02, 23}, - {10, 0x02, 23}, - {15, 0x02, 23}, - {24, 0x02, 23}, - {31, 0x02, 23}, - {41, 0x02, 23}, - {56, 0x03, 23}, + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, }, /* 243 */ { - {2, 0x02, 24}, - {9, 0x02, 24}, - {23, 0x02, 24}, - {40, 0x03, 24}, - {2, 0x02, 25}, - {9, 0x02, 25}, - {23, 0x02, 25}, - {40, 0x03, 25}, - {2, 0x02, 26}, - {9, 0x02, 26}, - {23, 0x02, 26}, - {40, 0x03, 26}, - {2, 0x02, 27}, - {9, 0x02, 27}, - {23, 0x02, 27}, - {40, 0x03, 27}, + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, }, /* 244 */ { - {3, 0x02, 24}, - {6, 0x02, 24}, - {10, 0x02, 24}, - {15, 0x02, 24}, - {24, 0x02, 24}, - {31, 0x02, 24}, - {41, 0x02, 24}, - {56, 0x03, 24}, - {3, 0x02, 25}, - {6, 0x02, 25}, - {10, 0x02, 25}, - {15, 0x02, 25}, - {24, 0x02, 25}, - {31, 0x02, 25}, - {41, 0x02, 25}, - {56, 0x03, 25}, + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, }, /* 245 */ { - {3, 0x02, 26}, - {6, 0x02, 26}, - {10, 0x02, 26}, - {15, 0x02, 26}, - {24, 0x02, 26}, - {31, 0x02, 26}, - {41, 0x02, 26}, - {56, 0x03, 26}, - {3, 0x02, 27}, - {6, 0x02, 27}, - {10, 0x02, 27}, - {15, 0x02, 27}, - {24, 0x02, 27}, - {31, 0x02, 27}, - {41, 0x02, 27}, - {56, 0x03, 27}, + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, }, /* 246 */ { - {1, 0x02, 28}, - {22, 0x03, 28}, - {1, 0x02, 29}, - {22, 0x03, 29}, - {1, 0x02, 30}, - {22, 0x03, 30}, - {1, 0x02, 31}, - {22, 0x03, 31}, - {1, 0x02, 127}, - {22, 0x03, 127}, - {1, 0x02, 220}, - {22, 0x03, 220}, - {1, 0x02, 249}, - {22, 0x03, 249}, - {254, 0x00, 0}, - {255, 0x00, 0}, + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, }, /* 247 */ { - {2, 0x02, 28}, - {9, 0x02, 28}, - {23, 0x02, 28}, - {40, 0x03, 28}, - {2, 0x02, 29}, - {9, 0x02, 29}, - {23, 0x02, 29}, - {40, 0x03, 29}, - {2, 0x02, 30}, - {9, 0x02, 30}, - {23, 0x02, 30}, - {40, 0x03, 30}, - {2, 0x02, 31}, - {9, 0x02, 31}, - {23, 0x02, 31}, - {40, 0x03, 31}, + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, }, /* 248 */ { - {3, 0x02, 28}, - {6, 0x02, 28}, - {10, 0x02, 28}, - {15, 0x02, 28}, - {24, 0x02, 28}, - {31, 0x02, 28}, - {41, 0x02, 28}, - {56, 0x03, 28}, - {3, 0x02, 29}, - {6, 0x02, 29}, - {10, 0x02, 29}, - {15, 0x02, 29}, - {24, 0x02, 29}, - {31, 0x02, 29}, - {41, 0x02, 29}, - {56, 0x03, 29}, + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, }, /* 249 */ { - {3, 0x02, 30}, - {6, 0x02, 30}, - {10, 0x02, 30}, - {15, 0x02, 30}, - {24, 0x02, 30}, - {31, 0x02, 30}, - {41, 0x02, 30}, - {56, 0x03, 30}, - {3, 0x02, 31}, - {6, 0x02, 31}, - {10, 0x02, 31}, - {15, 0x02, 31}, - {24, 0x02, 31}, - {31, 0x02, 31}, - {41, 0x02, 31}, - {56, 0x03, 31}, + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, }, /* 250 */ { - {2, 0x02, 127}, - {9, 0x02, 127}, - {23, 0x02, 127}, - {40, 0x03, 127}, - {2, 0x02, 220}, - {9, 0x02, 220}, - {23, 0x02, 220}, - {40, 0x03, 220}, - {2, 0x02, 249}, - {9, 0x02, 249}, - {23, 0x02, 249}, - {40, 0x03, 249}, - {0, 0x03, 10}, - {0, 0x03, 13}, - {0, 0x03, 22}, - {0, 0x04, 0}, + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, }, /* 251 */ { - {3, 0x02, 127}, - {6, 0x02, 127}, - {10, 0x02, 127}, - {15, 0x02, 127}, - {24, 0x02, 127}, - {31, 0x02, 127}, - {41, 0x02, 127}, - {56, 0x03, 127}, - {3, 0x02, 220}, - {6, 0x02, 220}, - {10, 0x02, 220}, - {15, 0x02, 220}, - {24, 0x02, 220}, - {31, 0x02, 220}, - {41, 0x02, 220}, - {56, 0x03, 220}, + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, }, /* 252 */ { - {3, 0x02, 249}, - {6, 0x02, 249}, - {10, 0x02, 249}, - {15, 0x02, 249}, - {24, 0x02, 249}, - {31, 0x02, 249}, - {41, 0x02, 249}, - {56, 0x03, 249}, - {1, 0x02, 10}, - {22, 0x03, 10}, - {1, 0x02, 13}, - {22, 0x03, 13}, - {1, 0x02, 22}, - {22, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, }, /* 253 */ { - {2, 0x02, 10}, - {9, 0x02, 10}, - {23, 0x02, 10}, - {40, 0x03, 10}, - {2, 0x02, 13}, - {9, 0x02, 13}, - {23, 0x02, 13}, - {40, 0x03, 13}, - {2, 0x02, 22}, - {9, 0x02, 22}, - {23, 0x02, 22}, - {40, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, }, /* 254 */ { - {3, 0x02, 10}, - {6, 0x02, 10}, - {10, 0x02, 10}, - {15, 0x02, 10}, - {24, 0x02, 10}, - {31, 0x02, 10}, - {41, 0x02, 10}, - {56, 0x03, 10}, - {3, 0x02, 13}, - {6, 0x02, 13}, - {10, 0x02, 13}, - {15, 0x02, 13}, - {24, 0x02, 13}, - {31, 0x02, 13}, - {41, 0x02, 13}, - {56, 0x03, 13}, + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, }, /* 255 */ { - {3, 0x02, 22}, - {6, 0x02, 22}, - {10, 0x02, 22}, - {15, 0x02, 22}, - {24, 0x02, 22}, - {31, 0x02, 22}, - {41, 0x02, 22}, - {56, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, }, }; diff --git a/thirdparty/nghttp2/nghttp2_helper.c b/thirdparty/nghttp2/nghttp2_helper.c index 9dbfa7ddfda..765e5492ce5 100644 --- a/thirdparty/nghttp2/nghttp2_helper.c +++ b/thirdparty/nghttp2/nghttp2_helper.c @@ -103,6 +103,9 @@ const char *nghttp2_strerror(int error_code) { return "Internal error"; case NGHTTP2_ERR_CANCEL: return "Cancel"; + case NGHTTP2_ERR_SETTINGS_EXPECTED: + return "When a local endpoint expects to receive SETTINGS frame, it " + "receives an other type of frame"; case NGHTTP2_ERR_NOMEM: return "Out of memory"; case NGHTTP2_ERR_CALLBACK_FAILURE: @@ -112,6 +115,8 @@ const char *nghttp2_strerror(int error_code) { case NGHTTP2_ERR_FLOODED: return "Flooding was detected in this HTTP/2 session, and it must be " "closed"; + case NGHTTP2_ERR_TOO_MANY_SETTINGS: + return "SETTINGS frame contained more than the maximum allowed entries"; default: return "Unknown error code"; } diff --git a/thirdparty/nghttp2/nghttp2_mem.c b/thirdparty/nghttp2/nghttp2_mem.c index 10c31ccf734..8ca2ed332fd 100644 --- a/thirdparty/nghttp2/nghttp2_mem.c +++ b/thirdparty/nghttp2/nghttp2_mem.c @@ -28,14 +28,21 @@ static void *default_malloc(size_t size, void *mem_user_data) { return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { free(ptr); } +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; -static void *default_calloc(size_t nmemb, size_t size, - void *mem_user_data) { return calloc(nmemb, size); } static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + return realloc(ptr, size); } diff --git a/thirdparty/nlohmann/LICENSE.MIT b/thirdparty/nlohmann/LICENSE.MIT new file mode 100644 index 00000000000..f0622d6dc24 --- /dev/null +++ b/thirdparty/nlohmann/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2021 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/thirdparty/nlohmann/adl_serializer.hpp b/thirdparty/nlohmann/adl_serializer.hpp new file mode 100644 index 00000000000..eeaa1425740 --- /dev/null +++ b/thirdparty/nlohmann/adl_serializer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/from_json.hpp b/thirdparty/nlohmann/detail/conversions/from_json.hpp new file mode 100644 index 00000000000..c389dca7ad3 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/from_json.hpp @@ -0,0 +1,389 @@ +#pragma once + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value and + not std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.begin(), j.end(), std::begin(l)); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template ::value and + not is_constructible_object_type::value and + not is_constructible_string_type::value and + not is_basic_json::value, + int > = 0 > + +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +template ::value>> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template ::value>> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_chars.hpp b/thirdparty/nlohmann/detail/conversions/to_chars.hpp new file mode 100644 index 00000000000..d99703a54c1 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_chars.hpp @@ -0,0 +1,1106 @@ +#pragma once + +#include // array +#include // assert +#include // or, and, not +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional +#include + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + assert(x.e == y.e); + assert(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + assert(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + assert(std::isfinite(value)); + assert(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 and E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + assert(kAlpha <= cached.e + e + 64); + assert(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + and delta - rest >= ten_k + and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + { + assert(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + assert(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + assert(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + assert(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + assert(e > -1000); + assert(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n and n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n - k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (n + 2); + } + + if (0 < n and n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + assert(k > n); + + std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + buf[n] = '.'; + return buf + (k + 1); + } + + if (min_exp < n and n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2 + (-n) + k); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + buf[1] = '.'; + buf += 1 + k; + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + assert(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last - first >= kMaxExp + 2); + assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + assert(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_json.hpp b/thirdparty/nlohmann/detail/conversions/to_json.hpp new file mode 100644 index 00000000000..a1def699fd7 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_json.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include // copy +#include // or, and, not +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template ::value and + not is_compatible_object_type< + BasicJsonType, CompatibleArrayType>::value and + not is_compatible_string_type::value and + not is_basic_json::value, + int> = 0> +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template::value and not is_basic_json::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t::value, + int> = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template < typename BasicJsonType, typename T, + enable_if_t>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/exceptions.hpp b/thirdparty/nlohmann/detail/exceptions.hpp new file mode 100644 index 00000000000..ed836188ccc --- /dev/null +++ b/thirdparty/nlohmann/detail/exceptions.hpp @@ -0,0 +1,356 @@ +#pragma once + +#include // exception +#include // runtime_error +#include // to_string + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/https/github.com/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/binary_reader.hpp b/thirdparty/nlohmann/detail/input/binary_reader.hpp new file mode 100644 index 00000000000..1b6e0f9b7b3 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/binary_reader.hpp @@ -0,0 +1,1983 @@ +#pragma once + +#include // generate_n +#include // array +#include // assert +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move + +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using json_sax_t = SAX; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + assert(ia); + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result and strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } + + return result; + } + + /*! + @brief determine system byte order + + @return true if and only if system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static constexpr bool little_endianess(int num = 1) noexcept + { + return *reinterpret_cast(&num) == 1; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + + return true; + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const int element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number; + return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len; + string_t value; + return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + while (int element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) + { + return false; + } + + if (not is_array and not sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) and sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu)); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1)); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu)); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1)); + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const int byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + assert(0 <= exp and exp <= 32); + assert(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (not get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) and sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xCA: // float 32 + { + float number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'I': + { + std::int16_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const int prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'I': + { + std::int16_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'l': + { + std::int32_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'L': + { + std::int64_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'd': + { + float number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) and sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return current = ia->get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + int get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string"))) + { + success = false; + } + return static_cast(current); + }); + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/input_adapters.hpp b/thirdparty/nlohmann/detail/input/input_adapters.hpp new file mode 100644 index 00000000000..9512a771eef --- /dev/null +++ b/thirdparty/nlohmann/detail/input/input_adapters.hpp @@ -0,0 +1,442 @@ +#pragma once + +#include // array +#include // assert +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +/*! +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of +exactly one non-EOF character for future input. The int_type characters +returned consist of all valid char values as positive values (typically +unsigned char), plus an EOF value outside that range, specified by the value +of the function std::char_traits::eof(). This value is typically -1, but +could be any arbitrary value which is not a valid char value. +*/ +struct input_adapter_protocol +{ + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + virtual ~input_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; + +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter : public input_adapter_protocol +{ + public: + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = default; + ~file_input_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter : public input_adapter_protocol +{ + public: + ~input_stream_adapter() override + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + is.clear(is.rdstate() & std::ios::eofbit); + } + + explicit input_stream_adapter(std::istream& i) + : is(i), sb(*i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() override + { + auto res = sb.sbumpc(); + // set eof manually, as we don't use the istream interface. + if (res == EOF) + { + is.clear(is.rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream& is; + std::streambuf& sb; +}; + +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol +{ + public: + input_buffer_adapter(const char* b, const std::size_t l) noexcept + : cursor(b), limit(b == nullptr ? nullptr : (b + l)) + {} + + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + ~input_buffer_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + if (JSON_HEDLEY_LIKELY(cursor < limit)) + { + assert(cursor != nullptr and limit != nullptr); + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + private: + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* const limit; +}; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc or wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (current_wchar < str.size()) + { + const auto wc2 = static_cast(str[current_wchar++]); + const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + ++current_wchar; + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +template +class wide_string_input_adapter : public input_adapter_protocol +{ + public: + explicit wide_string_input_adapter(const WideStringType& w) noexcept + : str(w) + {} + + std::char_traits::int_type get_character() noexcept override + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index == 0); + } + + // use buffer + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// the wstring to process + const WideStringType& str; + + /// index of the current wchar in str + std::size_t current_wchar = 0; + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + +class input_adapter +{ + public: + // native support + JSON_HEDLEY_NON_NULL(2) + input_adapter(std::FILE* file) + : ia(std::make_shared(file)) {} + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + input_adapter(const std::wstring& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u16string& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u32string& ws) + : ia(std::make_shared>(ws)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) + { +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; + } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/json_sax.hpp b/thirdparty/nlohmann/detail/input/json_sax.hpp new file mode 100644 index 00000000000..606b7862ebd --- /dev/null +++ b/thirdparty/nlohmann/detail/input/json_sax.hpp @@ -0,0 +1,701 @@ +#pragma once + +#include // assert +#include +#include // string +#include // move +#include // vector + +#include +#include + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + /// type for (signed) integers + using number_integer_t = typename BasicJsonType::number_integer_t; + /// type for unsigned integers + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + /// type for floating-point numbers + using number_float_t = typename BasicJsonType::number_float_t; + /// type for strings + using string_t = typename BasicJsonType::string_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + assert(ref_stack.back()->is_object()); + assert(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep and ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() and not callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (not keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + assert(not keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (not keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback or callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (not keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (not ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + assert(ref_stack.back()->is_object()); + // check if we should store an element for the current key + assert(not key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (not store_element) + { + return {false, nullptr}; + } + + assert(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/lexer.hpp b/thirdparty/nlohmann/detail/input/lexer.hpp new file mode 100644 index 00000000000..0843d749d6a --- /dev/null +++ b/thirdparty/nlohmann/detail/input/lexer.hpp @@ -0,0 +1,1512 @@ +#pragma once + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } + + explicit lexer(detail::input_adapter_t&& adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = delete; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = delete; + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' and current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + assert(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char* literal_text, const std::size_t length, + token_type return_type) + { + assert(current == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + std::char_traits::int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia->get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + assert(not token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(int c) + { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if ('\x00' <= c and c <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(c); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB and get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 and not skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + detail::input_adapter_t ia = nullptr; + + /// the current character + std::char_traits::int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/parser.hpp b/thirdparty/nlohmann/detail/input/parser.hpp new file mode 100644 index 00000000000..8d4febcbfab --- /dev/null +++ b/thirdparty/nlohmann/detail/input/parser.hpp @@ -0,0 +1,498 @@ +#pragma once + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +/*! +@brief syntax analysis + +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + using parser_callback_t = + std::function; + + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result and strict and (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (not skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(not sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (not context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/position_t.hpp b/thirdparty/nlohmann/detail/input/position_t.hpp new file mode 100644 index 00000000000..14e9649fb29 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/position_t.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp new file mode 100644 index 00000000000..2c81f723fd2 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iter_impl.hpp b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp new file mode 100644 index 00000000000..3a362971909 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp @@ -0,0 +1,638 @@ +#pragma once + +#include // not +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + assert(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp new file mode 100644 index 00000000000..c61d96296ce --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + target = std::to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std diff --git a/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp new file mode 100644 index 00000000000..4cced80caf4 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include // random_access_iterator_tag + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp new file mode 100644 index 00000000000..f3b5b5db6b8 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp new file mode 100644 index 00000000000..28d6f1a65df --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_pointer.hpp b/thirdparty/nlohmann/detail/json_pointer.hpp new file mode 100644 index 00000000000..87af3423315 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_pointer.hpp @@ -0,0 +1,1011 @@ +#pragma once + +#include // all_of +#include // assert +#include // isdigit +#include // accumulate +#include // string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_index array index to append + @return JSON pointer with @a array_index appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_index) + { + return *this /= std::to_string(array_index); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_index array index + @return a new JSON pointer with @a array_index appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) + { + return json_pointer(ptr) /= array_index; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) + { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; + } + + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + JSON_TRY + { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // use unchecked array access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (not ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + JSON_TRY + { + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(not value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return not (lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_ref.hpp b/thirdparty/nlohmann/detail/json_ref.hpp new file mode 100644 index 00000000000..c8dec7330f2 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_ref.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)), is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), + is_rvalue(true) {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/macro_scope.hpp b/thirdparty/nlohmann/detail/macro_scope.hpp new file mode 100644 index 00000000000..27dddc6be76 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_scope.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include // pair +#include + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json diff --git a/thirdparty/nlohmann/detail/macro_unscope.hpp b/thirdparty/nlohmann/detail/macro_unscope.hpp new file mode 100644 index 00000000000..80b293e7d59 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_unscope.hpp @@ -0,0 +1,21 @@ +#pragma once + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL + +#include diff --git a/thirdparty/nlohmann/detail/meta/cpp_future.hpp b/thirdparty/nlohmann/detail/meta/cpp_future.hpp new file mode 100644 index 00000000000..948cd4fb0cb --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/cpp_future.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/meta/detected.hpp b/thirdparty/nlohmann/detail/meta/detected.hpp new file mode 100644 index 00000000000..5b52460acf6 --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/detected.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template