| @@ -0,0 +1,37 @@ | |||
| *.lo | |||
| *.la | |||
| .libs | |||
| .deps | |||
| acinclude.m4 | |||
| aclocal.m4 | |||
| autom4te.cache | |||
| build | |||
| config.guess | |||
| config.h | |||
| config.h.in | |||
| config.log | |||
| config.nice | |||
| config.status | |||
| config.sub | |||
| configure | |||
| configure.ac | |||
| include | |||
| install-sh | |||
| libtool | |||
| ltmain.sh | |||
| # Makefile | |||
| Makefile.fragments | |||
| Makefile.global | |||
| Makefile.objects | |||
| missing | |||
| mkinstalldirs | |||
| modules | |||
| run-tests.php | |||
| tests/*/*.diff | |||
| tests/*/*.out | |||
| tests/*/*.php | |||
| tests/*/*.exp | |||
| tests/*/*.log | |||
| tests/*/*.sh | |||
| .vscode/ | |||
| vendor/ | |||
| @@ -0,0 +1,36 @@ | |||
| language: php | |||
| compiler: | |||
| - gcc | |||
| - clang | |||
| os: | |||
| - linux | |||
| php: | |||
| - 7.0 | |||
| - 7.1 | |||
| - 7.2 | |||
| - 7.3 | |||
| - 7.4 | |||
| - 8.0 | |||
| # - nightly | |||
| notifications: | |||
| email: 63851587@qq.com | |||
| env: | |||
| - REPORT_EXIT_STATUS=1 NO_INTERACTION=1 | |||
| before_install: | |||
| - chmod +x ./travis/compile.sh | |||
| - chmod +x ./travis/run-tests.sh | |||
| #Compile | |||
| before_script: | |||
| - ./travis/compile.sh | |||
| # Run PHPs run-tests.php | |||
| script: | |||
| - ./travis/run-tests.sh | |||
| @@ -0,0 +1,2 @@ | |||
| snowdrift | |||
| 63851587@qq.com | |||
| @@ -0,0 +1,68 @@ | |||
| -------------------------------------------------------------------- | |||
| The PHP License, version 3.01 | |||
| Copyright (c) 1999 - 2011 The PHP Group. All rights reserved. | |||
| -------------------------------------------------------------------- | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, is 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. | |||
| 3. The name "PHP" must not be used to endorse or promote products | |||
| derived from this software without prior written permission. For | |||
| written permission, please contact group@php.net. | |||
| 4. Products derived from this software may not be called "PHP", nor | |||
| may "PHP" appear in their name, without prior written permission | |||
| from group@php.net. You may indicate that your software works in | |||
| conjunction with PHP by saying "Foo for PHP" instead of calling | |||
| it "PHP Foo" or "phpfoo" | |||
| 5. The PHP Group may publish revised and/or new versions of the | |||
| license from time to time. Each version will be given a | |||
| distinguishing version number. | |||
| Once covered code has been published under a particular version | |||
| of the license, you may always continue to use it under the terms | |||
| of that version. You may also choose to use such covered code | |||
| under the terms of any subsequent version of the license | |||
| published by the PHP Group. No one other than the PHP Group has | |||
| the right to modify the terms applicable to covered code created | |||
| under this License. | |||
| 6. Redistributions of any form whatsoever must retain the following | |||
| acknowledgment: | |||
| "This product includes PHP software, freely available from | |||
| <http://www.php.net/software/>". | |||
| THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND | |||
| ANY EXPRESSED 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 PHP | |||
| DEVELOPMENT TEAM OR ITS 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. | |||
| -------------------------------------------------------------------- | |||
| This software consists of voluntary contributions made by many | |||
| individuals on behalf of the PHP Group. | |||
| The PHP Group can be contacted via Email at group@php.net. | |||
| For more information on the PHP Group and the PHP project, | |||
| please see <http://www.php.net>. | |||
| PHP includes the Zend Engine, freely available at | |||
| <http://www.zend.com>. | |||
| @@ -0,0 +1,50 @@ | |||
| # ❄ idenerator-php-extension | |||
| ## 介绍 | |||
| 项目更多介绍参照:https://github.com/yitter/idgenerator | |||
| ## PHP环境 | |||
| * PHP7 or later | |||
| ## 安装方式 | |||
| ```shell | |||
| git clone https://gitee.com/yitter/idgenerator.git | |||
| cd idgenerator/PHP | |||
| phpize | |||
| ./configure --with-php-config=/path/php-config | |||
| make | |||
| make install | |||
| ``` | |||
| ## 如何使用(PHP) | |||
| **配置文件配置参数**: | |||
| ```shell | |||
| //snowdrift.ini | |||
| snowdrift.Method=1 //1 漂移算法 2 传统算法 | |||
| snowdrift.BaseTime=1582136402000 | |||
| snowdrift.WorkerId=1 //默认workerid | |||
| snowdrift.WorkerIdNum=1 //支持的WorkerId数量,默认1,不超过(-1L << snowdrift.WorkerIdBitLength) ^ -1L | |||
| snowdrift.WorkerIdBitLength=6 | |||
| snowdrift.SeqBitLength=6 //自增序号位数 | |||
| snowdrift.MaxSeqNumber=0 | |||
| snowdrift.MinSeqNumber=0 | |||
| snowdrift.TopOverCostCount=2000 //最大漂移次数 | |||
| ``` | |||
| **函数签名**: | |||
| ```php | |||
| \SnowDrift::NextId(int $wid=snowdrift.WorkerId):?int //获取单个id,$wid可选,默认值=snowdrift.WorkerId | |||
| \SnowDrift::NextNumId(int $num, int $wid=snowdrift.WorkerId):?array //获取$num个id,$wid可选,默认值=snowdrift.WorkerId | |||
| ``` | |||
| **调用示例** | |||
| ```php | |||
| $id=\SnowDrift::NextId(); | |||
| $id=\SnowDrift::NextId(3); | |||
| $ids=\SnowDrift::NextNumId(10000); | |||
| $ids=\SnowDrift::NextNumId(10000,3); | |||
| ``` | |||
| @@ -0,0 +1,85 @@ | |||
| dnl $Id$ | |||
| dnl config.m4 for extension snowdrift | |||
| dnl Comments in this file start with the string 'dnl'. | |||
| dnl Remove where necessary. This file will not work | |||
| dnl without editing. | |||
| dnl If your extension references something external, use with: | |||
| dnl PHP_ARG_WITH(snowdrift, for snowdrift support, | |||
| dnl Make sure that the comment is aligned: | |||
| dnl [ --with-snowdrift Include snowdrift support]) | |||
| dnl Otherwise use enable: | |||
| PHP_ARG_ENABLE(snowdrift, whether to enable snowdrift support, | |||
| dnl Make sure that the comment is aligned: | |||
| [ --enable-snowdrift Enable snowdrift support]) | |||
| if test "$PHP_SNOWDRIFT" != "no"; then | |||
| dnl Write more examples of tests here... | |||
| dnl # get library FOO build options from pkg-config output | |||
| dnl AC_PATH_PROG(PKG_CONFIG, pkg-config, no) | |||
| dnl AC_MSG_CHECKING(for libfoo) | |||
| dnl if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists foo; then | |||
| dnl if $PKG_CONFIG foo --atleast-version 1.2.3; then | |||
| dnl LIBFOO_CFLAGS=`$PKG_CONFIG foo --cflags` | |||
| dnl LIBFOO_LIBDIR=`$PKG_CONFIG foo --libs` | |||
| dnl LIBFOO_VERSON=`$PKG_CONFIG foo --modversion` | |||
| dnl AC_MSG_RESULT(from pkgconfig: version $LIBFOO_VERSON) | |||
| dnl else | |||
| dnl AC_MSG_ERROR(system libfoo is too old: version 1.2.3 required) | |||
| dnl fi | |||
| dnl else | |||
| dnl AC_MSG_ERROR(pkg-config not found) | |||
| dnl fi | |||
| dnl PHP_EVAL_LIBLINE($LIBFOO_LIBDIR, SNOWDRIFT_SHARED_LIBADD) | |||
| dnl PHP_EVAL_INCLINE($LIBFOO_CFLAGS) | |||
| dnl # --with-snowdrift -> check with-path | |||
| dnl SEARCH_PATH="/usr/local /usr" # you might want to change this | |||
| dnl SEARCH_FOR="/include/snowdrift.h" # you most likely want to change this | |||
| dnl if test -r $PHP_SNOWDRIFT/$SEARCH_FOR; then # path given as parameter | |||
| dnl SNOWDRIFT_DIR=$PHP_SNOWDRIFT | |||
| dnl else # search default path list | |||
| dnl AC_MSG_CHECKING([for snowdrift files in default path]) | |||
| dnl for i in $SEARCH_PATH ; do | |||
| dnl if test -r $i/$SEARCH_FOR; then | |||
| dnl SNOWDRIFT_DIR=$i | |||
| dnl AC_MSG_RESULT(found in $i) | |||
| dnl fi | |||
| dnl done | |||
| dnl fi | |||
| dnl | |||
| dnl if test -z "$SNOWDRIFT_DIR"; then | |||
| dnl AC_MSG_RESULT([not found]) | |||
| dnl AC_MSG_ERROR([Please reinstall the snowdrift distribution]) | |||
| dnl fi | |||
| dnl # --with-snowdrift -> add include path | |||
| dnl PHP_ADD_INCLUDE($SNOWDRIFT_DIR/include) | |||
| dnl # --with-snowdrift -> check for lib and symbol presence | |||
| dnl LIBNAME=snowdrift # you may want to change this | |||
| dnl LIBSYMBOL=snowdrift # you most likely want to change this | |||
| dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, | |||
| dnl [ | |||
| dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SNOWDRIFT_DIR/$PHP_LIBDIR, SNOWDRIFT_SHARED_LIBADD) | |||
| dnl AC_DEFINE(HAVE_SNOWDRIFTLIB,1,[ ]) | |||
| dnl ],[ | |||
| dnl AC_MSG_ERROR([wrong snowdrift lib version or lib not found]) | |||
| dnl ],[ | |||
| dnl -L$SNOWDRIFT_DIR/$PHP_LIBDIR -lm | |||
| dnl ]) | |||
| dnl | |||
| dnl PHP_SUBST(SNOWDRIFT_SHARED_LIBADD) | |||
| snowdrift_source_file="snowdrift.c\ | |||
| src/snowflake/snowflake.c\ | |||
| src/snowflake/shm.c\ | |||
| src/snowflake/spinlock.c | |||
| " | |||
| PHP_NEW_EXTENSION(snowdrift, $snowdrift_source_file, $ext_shared) | |||
| fi | |||
| @@ -0,0 +1,17 @@ | |||
| // $Id$ | |||
| // vim:ft=javascript | |||
| // If your extension references something external, use ARG_WITH | |||
| // ARG_WITH("snowdrift", "for snowdrift support", "no"); | |||
| // Otherwise, use ARG_ENABLE | |||
| ARG_ENABLE("snowdrift", "enable snowdrift support", "no"); | |||
| if (PHP_SNOWDRIFT != "no") { | |||
| THIS_DIR=`dirname $0` | |||
| snowdrift_source_file="snowdrift.c\ | |||
| $THIS_DIR/src/snowflake/snowflake.c | |||
| " | |||
| EXTENSION("snowdrift", $snowdrift_source_file, PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); | |||
| } | |||
| @@ -0,0 +1,75 @@ | |||
| /* | |||
| +----------------------------------------------------------------------+ | |||
| | snowdrift | | |||
| +----------------------------------------------------------------------+ | |||
| | Copyright (c) 1997-2018 The PHP Group | | |||
| +----------------------------------------------------------------------+ | |||
| | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt | | |||
| | If you did not receive a copy of the PHP license and are unable to | | |||
| | obtain it through the world-wide-web, please send a note to | | |||
| | license@php.net so we can mail you a copy immediately. | | |||
| +----------------------------------------------------------------------+ | |||
| | Author: 63851587@qq.com | | |||
| +----------------------------------------------------------------------+ | |||
| */ | |||
| /* $Id$ */ | |||
| #ifndef PHP_SNOWDRIFT_H | |||
| #define PHP_SNOWDRIFT_H | |||
| extern zend_module_entry snowdrift_module_entry; | |||
| #define phpext_snowdrift_ptr &snowdrift_module_entry | |||
| #define PHP_SNOWDRIFT_VERSION \ | |||
| "1.0.0" /* Replace with version number for your extension */ | |||
| #ifdef PHP_WIN32 | |||
| #define PHP_SNOWDRIFT_API __declspec(dllexport) | |||
| #elif defined(__GNUC__) && __GNUC__ >= 4 | |||
| #define PHP_SNOWDRIFT_API __attribute__((visibility("default"))) | |||
| #else | |||
| #define PHP_SNOWDRIFT_API | |||
| #endif | |||
| #ifdef ZTS | |||
| #include "TSRM.h" | |||
| #endif | |||
| // PHP8 | |||
| #if PHP_VERSION_ID >= 80000 | |||
| #define TSRMLS_D void | |||
| #define TSRMLS_DC | |||
| #define TSRMLS_C | |||
| #define TSRMLS_CC | |||
| #define TSRMLS_FETCH() | |||
| #define ZEND_ACC_DTOR 0 | |||
| #endif | |||
| ZEND_BEGIN_MODULE_GLOBALS(snowdrift) | |||
| uint8_t Method; | |||
| uint64_t BaseTime; | |||
| uint8_t WorkerId; | |||
| uint8_t WorkerIdNum; | |||
| uint8_t WorkerIdBitLength; | |||
| uint8_t SeqBitLength; | |||
| uint32_t MaxSeqNumber; | |||
| uint32_t MinSeqNumber; | |||
| uint16_t TopOverCostCount; | |||
| ZEND_END_MODULE_GLOBALS(snowdrift) | |||
| ZEND_DECLARE_MODULE_GLOBALS(snowdrift) | |||
| #define SD_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(snowdrift, v) | |||
| #if defined(ZTS) && defined(COMPILE_DL_SNOWDRIFT) | |||
| ZEND_TSRMLS_CACHE_EXTERN() | |||
| #endif | |||
| static int snowdrift_init(); | |||
| #endif /* PHP_SNOWDRIFT_H */ | |||
| @@ -0,0 +1,247 @@ | |||
| /* | |||
| +----------------------------------------------------------------------+ | |||
| | snowdrift | | |||
| +----------------------------------------------------------------------+ | |||
| | Copyright (c) 1997-2018 The PHP Group | | |||
| +----------------------------------------------------------------------+ | |||
| | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt | | |||
| | If you did not receive a copy of the PHP license and are unable to | | |||
| | obtain it through the world-wide-web, please send a note to | | |||
| | license@php.net so we can mail you a copy immediately. | | |||
| +----------------------------------------------------------------------+ | |||
| | Author: 63851587@qq.com | | |||
| +----------------------------------------------------------------------+ | |||
| */ | |||
| /* $Id$ */ | |||
| #ifdef HAVE_CONFIG_H | |||
| #include "config.h" | |||
| #endif | |||
| #include "php.h" | |||
| #include "php_ini.h" | |||
| #include "zend_exceptions.h" | |||
| #include "ext/standard/info.h" | |||
| #include "src/snowflake/shm.h" | |||
| #include "php_snowdrift.h" | |||
| #include "src/snowflake/snowflake.h" | |||
| /* True global resources - no need for thread safety here */ | |||
| static struct shm shmctx; | |||
| static snowflake *sf; | |||
| zend_class_entry snowdrift_ce; | |||
| uint8_t num = 0; | |||
| /* {{{ PHP_INI */ | |||
| PHP_INI_BEGIN() | |||
| STD_PHP_INI_ENTRY("snowdrift.Method", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, Method, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.BaseTime", "1582136402000", PHP_INI_SYSTEM, OnUpdateLongGEZero, BaseTime, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.WorkerId", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerId, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.WorkerIdNum", "1", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerIdNum, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.WorkerIdBitLength", "6", PHP_INI_SYSTEM, OnUpdateLongGEZero, WorkerIdBitLength, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.SeqBitLength", "6", PHP_INI_SYSTEM, OnUpdateLongGEZero, SeqBitLength, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.MaxSeqNumber", "0", PHP_INI_SYSTEM, OnUpdateLongGEZero, MaxSeqNumber, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.MinSeqNumber", "0", PHP_INI_SYSTEM, OnUpdateLongGEZero, MinSeqNumber, zend_snowdrift_globals, snowdrift_globals) | |||
| STD_PHP_INI_ENTRY("snowdrift.TopOverCostCount", "2000", PHP_INI_SYSTEM, OnUpdateLongGEZero, TopOverCostCount, zend_snowdrift_globals, snowdrift_globals) | |||
| PHP_INI_END() | |||
| /* }}} */ | |||
| static int snowdrift_init() | |||
| { | |||
| num = (-1L << SD_G(WorkerIdBitLength)) ^ -1L; | |||
| if (SD_G(WorkerIdNum) < num) | |||
| { | |||
| num = SD_G(WorkerIdNum); | |||
| } | |||
| shmctx.size = num * sizeof(snowflake); | |||
| if (shm_alloc(&shmctx) == -1) | |||
| { | |||
| zend_throw_exception_ex(NULL, 0, "shared memory malloc failed"); | |||
| RETURN_FALSE; | |||
| } | |||
| if (SD_G(MaxSeqNumber) <= SD_G(MinSeqNumber)) | |||
| { | |||
| zend_throw_exception_ex(NULL, 0, "MaxSeqNumber must GE then MinSeqNumber"); | |||
| RETURN_FALSE; | |||
| } | |||
| bzero(shmctx.addr, num * sizeof(snowflake)); | |||
| sf = (snowflake *)shmctx.addr; | |||
| for (int i = 0; i < num; i++) | |||
| { | |||
| snowflake *tmp = (sf + i); | |||
| tmp->Method = SD_G(Method); | |||
| tmp->BaseTime = SD_G(BaseTime); | |||
| tmp->WorkerId = i + 1; | |||
| tmp->WorkerIdBitLength = SD_G(WorkerIdBitLength); | |||
| tmp->SeqBitLength = SD_G(SeqBitLength); | |||
| tmp->MaxSeqNumber = SD_G(MaxSeqNumber); | |||
| tmp->MinSeqNumber = SD_G(MinSeqNumber); | |||
| tmp->TopOverCostCount = SD_G(TopOverCostCount); | |||
| Config(tmp); | |||
| } | |||
| return SUCCESS; | |||
| } | |||
| PHP_METHOD(snowdrift, NextId) | |||
| { | |||
| zend_long wid = SD_G(WorkerId); | |||
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &wid) == FAILURE) | |||
| { | |||
| RETURN_FALSE; | |||
| } | |||
| wid--; | |||
| if (wid < 0 || wid > num - 1) | |||
| { | |||
| zend_throw_exception_ex(NULL, 0, "wid error! wid between 0 and %d", num - 1); | |||
| RETURN_NULL(); | |||
| } | |||
| snowflake *flake = (sf + wid); | |||
| RETURN_LONG(NextId(flake)); | |||
| } | |||
| /** 这种方式性能比不上PHP直接循环调用NextId快 | |||
| */ | |||
| PHP_METHOD(snowdrift, NextNumId) | |||
| { | |||
| zend_long num = 1; | |||
| zend_long wid = SD_G(WorkerId); | |||
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &num, &wid) == FAILURE) | |||
| { | |||
| RETURN_FALSE; | |||
| } | |||
| wid--; | |||
| if (wid < 0 || wid > num - 1) | |||
| { | |||
| zend_throw_exception_ex(NULL, 0, "wid error! wid between 0 and %d", num - 1); | |||
| RETURN_NULL(); | |||
| } | |||
| snowflake *flake = (sf + wid); | |||
| array_init(return_value); | |||
| for (int i = 0; i < num; i++) | |||
| { | |||
| add_next_index_long(return_value, NextId(flake)); | |||
| } | |||
| } | |||
| /* {{{ PHP_MSHUTDOWN_FUNCTION | |||
| */ | |||
| PHP_MSHUTDOWN_FUNCTION(snowdrift) | |||
| { | |||
| UNREGISTER_INI_ENTRIES(); | |||
| shm_free(&shmctx); | |||
| return SUCCESS; | |||
| } | |||
| /* }}} */ | |||
| /* Remove if there's nothing to do at request start */ | |||
| /* {{{ PHP_RINIT_FUNCTION | |||
| */ | |||
| PHP_RINIT_FUNCTION(snowdrift) | |||
| { | |||
| #if defined(COMPILE_DL_SNOWFLAKE) && defined(ZTS) | |||
| ZEND_TSRMLS_CACHE_UPDATE(); | |||
| #endif | |||
| return SUCCESS; | |||
| } | |||
| /* }}} */ | |||
| /* Remove if there's nothing to do at request end */ | |||
| /* {{{ PHP_RSHUTDOWN_FUNCTION | |||
| */ | |||
| PHP_RSHUTDOWN_FUNCTION(snowdrift) | |||
| { | |||
| return SUCCESS; | |||
| } | |||
| /* }}} */ | |||
| /* {{{ PHP_MINFO_FUNCTION | |||
| */ | |||
| PHP_MINFO_FUNCTION(snowdrift) | |||
| { | |||
| php_info_print_table_start(); | |||
| php_info_print_table_header(2, "snowfrift support", "enabled"); | |||
| php_info_print_table_row(2, "Version", PHP_SNOWDRIFT_VERSION); | |||
| php_info_print_table_end(); | |||
| /* Remove comments if you have entries in php.ini */ | |||
| DISPLAY_INI_ENTRIES(); | |||
| } | |||
| /* }}} */ | |||
| /* {{{ arginfo | |||
| */ | |||
| ZEND_BEGIN_ARG_INFO(arginfo_snowdrift_void, 0) | |||
| ZEND_END_ARG_INFO() | |||
| ZEND_BEGIN_ARG_INFO_EX(arginfo_snowdrift_nextid, 0, 0, 1) | |||
| ZEND_ARG_INFO(0, wid) | |||
| ZEND_END_ARG_INFO() | |||
| ZEND_BEGIN_ARG_INFO_EX(arginfo_snowdrift_nextnumid, 0, 0, 1) | |||
| ZEND_ARG_INFO(0, num) | |||
| ZEND_ARG_INFO(0, wid) | |||
| ZEND_END_ARG_INFO() | |||
| /* }}} */ | |||
| /* {{{ snowdrift_functions[] | |||
| * | |||
| * Every user visible function must have an entry in snowdrift_functions[]. | |||
| */ | |||
| const zend_function_entry snowdrift_functions[] = { | |||
| PHP_FE_END /* Must be the last line in snowdrift_functions[] */ | |||
| }; | |||
| /* }}} */ | |||
| /* {{{ snowdrift_method[] | |||
| * | |||
| * Every user visible function must have an entry in snowdrift_functions[]. | |||
| */ | |||
| static const zend_function_entry snowdrift_methods[] = { | |||
| PHP_ME(snowdrift, NextId, arginfo_snowdrift_nextid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) | |||
| PHP_ME(snowdrift, NextNumId, arginfo_snowdrift_nextnumid, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; | |||
| /* }}} */ | |||
| /* {{{ PHP_MINIT_FUNCTION | |||
| */ | |||
| PHP_MINIT_FUNCTION(snowdrift) | |||
| { | |||
| INIT_CLASS_ENTRY(snowdrift_ce, "snowdrift", snowdrift_methods); | |||
| REGISTER_INI_ENTRIES(); | |||
| zend_register_internal_class(&snowdrift_ce); | |||
| if (snowdrift_init() == FAILURE) | |||
| { | |||
| return FAILURE; | |||
| } | |||
| return SUCCESS; | |||
| } | |||
| /* }}} */ | |||
| /* {{{ snowdrift_module_entry | |||
| */ | |||
| zend_module_entry snowdrift_module_entry = { | |||
| STANDARD_MODULE_HEADER, | |||
| "snowdrift", | |||
| snowdrift_functions, | |||
| PHP_MINIT(snowdrift), | |||
| PHP_MSHUTDOWN(snowdrift), | |||
| PHP_RINIT(snowdrift), /* Replace with NULL if there's nothing to do at | |||
| request start */ | |||
| PHP_RSHUTDOWN(snowdrift), /* Replace with NULL if there's nothing to do at | |||
| request end */ | |||
| PHP_MINFO(snowdrift), | |||
| PHP_SNOWDRIFT_VERSION, | |||
| STANDARD_MODULE_PROPERTIES}; | |||
| /* }}} */ | |||
| #ifdef COMPILE_DL_SNOWDRIFT | |||
| #ifdef ZTS | |||
| ZEND_TSRMLS_CACHE_DEFINE() | |||
| #endif | |||
| ZEND_GET_MODULE(snowdrift) | |||
| #endif | |||
| @@ -0,0 +1,94 @@ | |||
| <?php | |||
| declare(strict_types=1); | |||
| namespace SnowDrift; | |||
| use FFI; | |||
| use FFI\CData; | |||
| final class SnowFlake | |||
| { | |||
| private FFI $ffi; | |||
| private CData $flake; | |||
| private CData $pflake; | |||
| public function __construct(array $config = ['Method' => 1, 'BaseTime' => 0, 'WorkerId' => 1, 'WorkerIdBitLength' => 6, 'SeqBitLength' => 10, 'TopOverCostCount' => 2000]) | |||
| { | |||
| $this->ffi = FFI::cdef(file_get_contents($this->getHeaders()), $this->getLibrary()); | |||
| $this->flake = $this->ffi->new("struct snowflake"); | |||
| foreach ($config as $name => $val) { | |||
| $this->flake->$name = $val; | |||
| } | |||
| $this->pflake = FFI::addr($this->flake); | |||
| $this->ffi->Config($this->pflake); | |||
| } | |||
| public function getFFI(): FFI | |||
| { | |||
| return $this->ffi; | |||
| } | |||
| public function getFlake(): CData | |||
| { | |||
| return $this->flake; | |||
| } | |||
| public function getPflake(): CData | |||
| { | |||
| return $this->pflake; | |||
| } | |||
| public function nextId(): int | |||
| { | |||
| return $this->ffi->NextId($this->pflake); | |||
| } | |||
| public function getHeaders(): string | |||
| { | |||
| return __DIR__ . '/src/snowflake/snowflake.h'; | |||
| } | |||
| public function getLibrary(): ?string | |||
| { | |||
| return __DIR__ . '/src/snowflake/libsnow.so'; | |||
| } | |||
| } | |||
| $total = 50000; | |||
| $snowflake = new SnowFlake(['Method' => 1, 'BaseTime' => 1577808000000, 'WorkerId' => 1, 'WorkerIdBitLength' => 1, 'SeqBitLength' => 10, 'TopOverCostCount' => 2000]); | |||
| $ffi = $snowflake->getFFI(); | |||
| $pflake = $snowflake->getPflake(); | |||
| // $res = []; | |||
| $start = microtime(true); | |||
| for ($i = 0; $i < $total; $i++) { | |||
| // $res[] = \SnowDrift::NextId(); | |||
| \SnowDrift::NextId(2); | |||
| } | |||
| echo sprintf("扩展漂移算法,PHP循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL; | |||
| // $res = []; | |||
| $start = microtime(true); | |||
| foreach (\SnowDrift::NextNumId($total) as $val) { | |||
| // $res[] = $val; | |||
| } | |||
| echo sprintf("扩展漂移算法,C循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL; | |||
| // $res = []; | |||
| $start = microtime(true); | |||
| for ($i = 0; $i < $total; $i++) { | |||
| // $res[] = $ffi->NextId($pflake); | |||
| $ffi->NextId($pflake); | |||
| } | |||
| echo sprintf("FFI漂移算法,PHP循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL; | |||
| // $res = []; | |||
| $start = microtime(true); | |||
| $tmp = $ffi->NextNumId($pflake, $total); | |||
| for ($i = 0; $i < $total; $i++) { | |||
| // $res[] = $tmp[$i]; | |||
| } | |||
| echo sprintf("FFI漂移算法,C循环获取:%d,%s ms", $total, ((microtime(true) - $start)) * 1000) . PHP_EOL; | |||
| @@ -0,0 +1,34 @@ | |||
| #Makefile | |||
| #自定义变量 | |||
| CC = gcc | |||
| #编译选项,生成所有警告、不优化、采用c++11标准、输出调试信息、只编译并生成目标文件 | |||
| CFLAGS = -Wall -O2 -g -c | |||
| FILE = ./test.c | |||
| #wildcard为Makefile模式匹配关键字,获取目标目录符合匹配模式的所有文件名 | |||
| SRCS = $(FILE) $(wildcard ./snowflake/*.c) | |||
| #patsubst为Makefile模式替换关键字,查找字符串SRCS中按空格分开的单词,并将符合模式%.cpp的字符串全部替换成%.o | |||
| OBJS = $(patsubst ./%.c, ./%.o, $(SRCS)) | |||
| EXES = test | |||
| RM = rm -f | |||
| #默认任务 | |||
| default: | |||
| #默认任务要执行的命令,按上面的变量名替换为变量值后执行,前面必须有一个Tab符 | |||
| $(MAKE) $(EXES) | |||
| #模式匹配,冒号前者为目标项,冒号后面为依赖项 | |||
| $(EXES): $(OBJS) | |||
| #$^表示规则中所有的依赖项,$@表示规则中的目标 | |||
| $(CC) $^ -lm -lpthread -o $@ | |||
| # %模式自动匹配符 | |||
| %.o: %.c | |||
| # $<表示规则中的第一个依赖项 | |||
| $(CC) $(CFLAGS) $< -o $@ | |||
| #伪目标,声明clean为伪目标或标签,为了避免该清理任务与文件名相同而被错识别 | |||
| .PHONY: clean | |||
| clean: | |||
| #清理之前的目标文件,以便下次完整的重新编译 | |||
| $(RM) $(OBJS) $(EXES) | |||
| @@ -0,0 +1,65 @@ | |||
| #include <stdlib.h> | |||
| #include <fcntl.h> | |||
| #include <sys/types.h> | |||
| #include <sys/mman.h> | |||
| #include "shm.h" | |||
| #ifdef MAP_ANON | |||
| int shm_alloc(struct shm *shm) | |||
| { | |||
| shm->addr = (void *)mmap(NULL, shm->size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_ANONYMOUS | MAP_SHARED, -1, 0); | |||
| if (shm->addr == NULL) | |||
| { | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| void shm_free(struct shm *shm) | |||
| { | |||
| if (shm->addr) | |||
| { | |||
| munmap((void *)shm->addr, shm->size); | |||
| } | |||
| } | |||
| #else | |||
| int shm_alloc(struct shm *shm) | |||
| { | |||
| int fd; | |||
| fd = open("/dev/zero", O_RDWR); | |||
| if (fd == -1) | |||
| { | |||
| return -1; | |||
| } | |||
| shm->addr = (void *)mmap(NULL, shm->size, | |||
| PROT_READ | PROT_WRITE, | |||
| MAP_SHARED, fd, 0); | |||
| close(fd); | |||
| if (shm->addr == NULL) | |||
| { | |||
| return -1; | |||
| } | |||
| return 0; | |||
| } | |||
| void shm_free(struct shm *shm) | |||
| { | |||
| if (shm->addr) | |||
| { | |||
| munmap((void *)shm->addr, shm->size); | |||
| } | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,14 @@ | |||
| #ifndef __SHM_H | |||
| #define __SHM_H | |||
| struct shm | |||
| { | |||
| void *addr; | |||
| size_t size; | |||
| }; | |||
| int shm_alloc(struct shm *shm); | |||
| void shm_free(struct shm *shm); | |||
| #endif | |||
| @@ -0,0 +1,207 @@ | |||
| #include <unistd.h> | |||
| #include <sys/time.h> | |||
| #include <stdlib.h> | |||
| #include "snowflake.h" | |||
| #include "spinlock.h" | |||
| #if defined(WIN32) | |||
| #include "windows.h" | |||
| #endif | |||
| static void EndOverCostAction(uint64_t useTimeTick, snowflake *flake); | |||
| static uint64_t NextOverCostId(snowflake *flake); | |||
| static uint64_t NextNormalId(snowflake *flake); | |||
| static uint64_t GetCurrentTimeTick(snowflake *flake); | |||
| static uint64_t GetNextTimeTick(snowflake *flake); | |||
| static uint64_t CalcId(snowflake *flake); | |||
| static uint64_t CalcTurnBackId(snowflake *flake); | |||
| int ncpu; | |||
| uint16_t spin = 2048; | |||
| uint32_t pid = 0; | |||
| void Config(snowflake *flake) | |||
| { | |||
| if (pid == 0) | |||
| { | |||
| pid = (uint32_t)getpid(); | |||
| #if defined(WIN32) | |||
| SYSTEM_INFO sysInfo; | |||
| GetSystemInfo(&sysInfo); | |||
| ncpu = sysInfo.dwNumberOfProcessors; | |||
| #else | |||
| ncpu = sysconf(_SC_NPROCESSORS_ONLN); | |||
| #endif | |||
| if (ncpu <= 0) | |||
| { | |||
| ncpu = 1; | |||
| } | |||
| } | |||
| flake->WorkerIdBitLength = flake->WorkerIdBitLength == 0 ? 6 : flake->WorkerIdBitLength; | |||
| flake->SeqBitLength = flake->SeqBitLength == 0 ? 6 : flake->SeqBitLength; | |||
| flake->MaxSeqNumber = flake->MaxSeqNumber > 0 ? flake->MaxSeqNumber : (-1L << flake->SeqBitLength) ^ -1L; | |||
| flake->BaseTime = flake->BaseTime != 0 ? flake->BaseTime : 1577808000000; | |||
| flake->_TimestampShift = (uint8_t)(flake->WorkerIdBitLength + flake->SeqBitLength); | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| return; | |||
| } | |||
| void inline EndOverCostAction(uint64_t useTimeTick, snowflake *flake) | |||
| { | |||
| if (flake->_TermIndex > 10000) | |||
| { | |||
| flake->_TermIndex = 0; | |||
| } | |||
| } | |||
| uint64_t inline NextOverCostId(snowflake *flake) | |||
| { | |||
| uint64_t currentTimeTick = GetCurrentTimeTick(flake); | |||
| if (currentTimeTick > flake->_LastTimeTick) | |||
| { | |||
| EndOverCostAction(currentTimeTick, flake); | |||
| flake->_LastTimeTick = currentTimeTick; | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| flake->_IsOverCost = 0; | |||
| flake->_OverCostCountInOneTerm = 0; | |||
| flake->_GenCountInOneTerm = 0; | |||
| return CalcId(flake); | |||
| } | |||
| if (flake->_OverCostCountInOneTerm > flake->TopOverCostCount) | |||
| { | |||
| EndOverCostAction(currentTimeTick, flake); | |||
| flake->_LastTimeTick = GetNextTimeTick(flake); | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| flake->_IsOverCost = 0; | |||
| flake->_OverCostCountInOneTerm = 0; | |||
| flake->_GenCountInOneTerm = 0; | |||
| return CalcId(flake); | |||
| } | |||
| if (flake->_CurrentSeqNumber > flake->MaxSeqNumber) | |||
| { | |||
| flake->_LastTimeTick++; | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| flake->_IsOverCost = 1; | |||
| flake->_OverCostCountInOneTerm++; | |||
| flake->_GenCountInOneTerm++; | |||
| return CalcId(flake); | |||
| } | |||
| flake->_GenCountInOneTerm++; | |||
| return CalcId(flake); | |||
| } | |||
| uint64_t inline NextNormalId(snowflake *flake) | |||
| { | |||
| uint64_t currentTimeTick = GetCurrentTimeTick(flake); | |||
| if (currentTimeTick < flake->_LastTimeTick) | |||
| { | |||
| if (flake->_TurnBackTimeTick < 1) | |||
| { | |||
| flake->_TurnBackTimeTick = flake->_LastTimeTick - 1; | |||
| flake->_TurnBackIndex++; | |||
| if (flake->_TurnBackIndex > 4) | |||
| { | |||
| flake->_TurnBackIndex = 1; | |||
| } | |||
| } | |||
| return CalcTurnBackId(flake); | |||
| } | |||
| if (flake->_TurnBackTimeTick > 0) | |||
| { | |||
| flake->_TurnBackTimeTick = 0; | |||
| } | |||
| if (currentTimeTick > flake->_LastTimeTick) | |||
| { | |||
| flake->_LastTimeTick = currentTimeTick; | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| return CalcId(flake); | |||
| } | |||
| if (flake->_CurrentSeqNumber > flake->MaxSeqNumber) | |||
| { | |||
| flake->_TermIndex++; | |||
| flake->_LastTimeTick++; | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| flake->_IsOverCost = 1; | |||
| flake->_OverCostCountInOneTerm = 1; | |||
| flake->_GenCountInOneTerm = 1; | |||
| return CalcId(flake); | |||
| } | |||
| return CalcId(flake); | |||
| } | |||
| uint64_t inline GetCurrentTimeTick(snowflake *flake) | |||
| { | |||
| struct timeval t; | |||
| gettimeofday(&t, NULL); | |||
| return (uint64_t)((t.tv_sec * 1000 + t.tv_usec / 1000) - flake->BaseTime); | |||
| } | |||
| uint64_t inline GetNextTimeTick(snowflake *flake) | |||
| { | |||
| uint64_t tempTimeTicker = GetCurrentTimeTick(flake); | |||
| while (tempTimeTicker <= flake->_LastTimeTick) | |||
| { | |||
| tempTimeTicker = GetCurrentTimeTick(flake); | |||
| } | |||
| return tempTimeTicker; | |||
| } | |||
| uint64_t inline CalcId(snowflake *flake) | |||
| { | |||
| uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_CurrentSeqNumber); | |||
| flake->_CurrentSeqNumber++; | |||
| return result; | |||
| } | |||
| uint64_t inline CalcTurnBackId(snowflake *flake) | |||
| { | |||
| uint64_t result = (flake->_LastTimeTick << flake->_TimestampShift) + (flake->WorkerId << flake->SeqBitLength) + (flake->_TurnBackTimeTick); | |||
| flake->_TurnBackTimeTick--; | |||
| return result; | |||
| } | |||
| uint64_t inline NextSonwId(snowflake *flake) | |||
| { | |||
| uint64_t currentTimeTick = GetCurrentTimeTick(flake); | |||
| if (flake->_LastTimeTick == currentTimeTick) | |||
| { | |||
| flake->_CurrentSeqNumber++; | |||
| if (flake->_CurrentSeqNumber > flake->MaxSeqNumber) | |||
| { | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| currentTimeTick = GetNextTimeTick(flake); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| flake->_CurrentSeqNumber = flake->MinSeqNumber; | |||
| } | |||
| flake->_LastTimeTick = currentTimeTick; | |||
| return (uint64_t)((currentTimeTick << flake->_TimestampShift) | (flake->WorkerId << flake->SeqBitLength) | flake->_CurrentSeqNumber); | |||
| } | |||
| uint64_t inline GetId(snowflake *flake) | |||
| { | |||
| return flake->Method == 1 ? (flake->_IsOverCost != 0 ? NextOverCostId(flake) : NextNormalId(flake)) : NextSonwId(flake); | |||
| } | |||
| uint64_t NextId(snowflake *flake) | |||
| { | |||
| spin_lock(&flake->_Lock, pid); | |||
| uint64_t id = GetId(flake); | |||
| spin_unlock(&flake->_Lock, pid); | |||
| return id; | |||
| } | |||
| uint64_t *NextNumId(snowflake *flake, uint32_t num) | |||
| { | |||
| uint64_t *arr = (uint64_t *)malloc(sizeof(uint64_t) * num); | |||
| spin_lock(&flake->_Lock, pid); | |||
| for (uint32_t i = 0; i < num; i++) | |||
| { | |||
| arr[i] = GetId(flake); | |||
| } | |||
| spin_unlock(&flake->_Lock, pid); | |||
| return arr; | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| #include <stdint.h> | |||
| typedef struct snowflake | |||
| { | |||
| uint8_t Method; | |||
| uint64_t BaseTime; | |||
| uint16_t WorkerId; | |||
| uint8_t WorkerIdBitLength; | |||
| uint8_t SeqBitLength; | |||
| uint32_t MaxSeqNumber; | |||
| uint32_t MinSeqNumber; | |||
| uint32_t TopOverCostCount; | |||
| uint8_t _TimestampShift; | |||
| uint32_t _CurrentSeqNumber; | |||
| int64_t _LastTimeTick; | |||
| int64_t _TurnBackTimeTick; | |||
| uint8_t _TurnBackIndex; | |||
| uint8_t _IsOverCost; | |||
| uint32_t _OverCostCountInOneTerm; | |||
| uint32_t _GenCountInOneTerm; | |||
| uint32_t _TermIndex; | |||
| volatile unsigned int _Lock; | |||
| } snowflake; | |||
| void Config(snowflake *flake); | |||
| uint64_t NextId(snowflake *flake); | |||
| uint64_t *NextNumId(snowflake *flake, uint32_t num); | |||
| @@ -0,0 +1,47 @@ | |||
| #include <stdlib.h> | |||
| #include <sched.h> | |||
| #include "spinlock.h" | |||
| extern int ncpu; | |||
| extern int spin; | |||
| void spin_lock(atomic_t *lock, uint32_t pid) | |||
| { | |||
| int i, n; | |||
| for (;;) | |||
| { | |||
| if (*lock == 0 && | |||
| __sync_bool_compare_and_swap(lock, 0, pid)) | |||
| { | |||
| return; | |||
| } | |||
| if (ncpu > 1) | |||
| { | |||
| for (n = 1; n < spin; n <<= 1) | |||
| { | |||
| for (i = 0; i < n; i++) | |||
| { | |||
| __asm("pause"); | |||
| } | |||
| if (*lock == 0 && | |||
| __sync_bool_compare_and_swap(lock, 0, pid)) | |||
| { | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| sched_yield(); | |||
| } | |||
| } | |||
| void spin_unlock(atomic_t *lock, uint32_t pid) | |||
| { | |||
| __sync_bool_compare_and_swap(lock, pid, 0); | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| #ifndef __SPINLOCK_H | |||
| #define __SPINLOCK_H | |||
| #include <stdint.h> | |||
| typedef volatile unsigned int atomic_t; | |||
| extern void spin_lock(atomic_t *lock, uint32_t pid); | |||
| extern void spin_unlock(atomic_t *lock, uint32_t pid); | |||
| #endif | |||
| @@ -0,0 +1,104 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <sys/time.h> | |||
| #include <pthread.h> | |||
| #include <unistd.h> | |||
| #include "snowflake/snowflake.h" | |||
| #if defined(WIN32) | |||
| #include "windows.h" | |||
| #endif | |||
| #define THREAD 2 | |||
| #define TOTAL 50000 | |||
| static snowflake snowf = {1, 0, 1, 6, 6, 0, 0, 2000}; | |||
| static snowflake *flake = &snowf; | |||
| uint64_t arr[TOTAL]; | |||
| static uint64_t index = 0; | |||
| uint64_t compar(const void *a, const void *b) | |||
| { | |||
| return (*(uint64_t *)a - *(uint64_t *)b); | |||
| } | |||
| uint64_t containsDuplicate() | |||
| { | |||
| uint32_t i, j; | |||
| qsort(arr, TOTAL, sizeof(uint64_t), compar); | |||
| for (i = 0, j = 1; j < TOTAL; i++, j++) | |||
| { | |||
| if (arr[i] > 0 && arr[j] > 0 && arr[i] == arr[j]) | |||
| { | |||
| return j; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| void run() | |||
| { | |||
| for (int i = 0; i < TOTAL / THREAD; i++) | |||
| { | |||
| arr[__sync_fetch_and_add(&index, 1)] = NextId(flake); | |||
| } | |||
| } | |||
| int main() | |||
| { | |||
| flake->Method = 1; | |||
| Config(flake); | |||
| pthread_t tid[THREAD]; | |||
| struct timeval t_start, t_end; | |||
| while (1) | |||
| { | |||
| // clock_gettime(CLOCK_REALTIME, &t_start); | |||
| // for (int i = 0; i < THREAD; i++) | |||
| // { | |||
| // if (pthread_create(&tid[i], NULL, (void *)run, NULL) != 0) | |||
| // { | |||
| // printf("thread creation failed\n"); | |||
| // exit(1); | |||
| // } | |||
| // } | |||
| // for (int i = 0; i < THREAD; i++) | |||
| // { | |||
| // pthread_join(tid[i], NULL); //等待线程结束 | |||
| // } | |||
| // clock_gettime(CLOCK_REALTIME, &t_end); | |||
| // printf("%d 线程 %s,总共:%d,%lf ms\n", THREAD, flake->Method == 1 ? "漂移" : "传统", index, (double)(t_end.tv_nsec - t_start.tv_nsec) / 1000000.0); | |||
| // uint64_t re = containsDuplicate(); | |||
| // if (re > 0) | |||
| // { | |||
| // printf("有重复数据:%ld,%ld\n", arr[re - 1], arr[re]); | |||
| // } | |||
| // else | |||
| // { | |||
| // printf("没有重复数据\n"); | |||
| // } | |||
| // for (int i = 0; i < TOTAL; i++) | |||
| // { | |||
| // arr[i] = 0; | |||
| // } | |||
| // index = 0; | |||
| gettimeofday(&t_start, NULL); | |||
| for (int i = 0; i < TOTAL; i++) | |||
| { | |||
| NextId(flake); | |||
| } | |||
| gettimeofday(&t_end, NULL); | |||
| printf("单线程 %s,总共:%d,%lf ms\n", flake->Method == 1 ? "漂移" : "传统", TOTAL, (double)(t_end.tv_usec - t_start.tv_usec) / 1000.0); | |||
| sleep(1); | |||
| } | |||
| return 1; | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| --TEST-- | |||
| Check for snowdrift presence | |||
| --SKIPIF-- | |||
| <?php if (!extension_loaded("snowdrift")) print "skip"; ?> | |||
| --FILE-- | |||
| <?php | |||
| echo "snowdrift extension is available"; | |||
| /* | |||
| you can add regression tests for your extension here | |||
| the output of your test code has to be equal to the | |||
| text in the --EXPECT-- section below for the tests | |||
| to pass, differences between the output and the | |||
| expected text are interpreted as failure | |||
| see php7/README.TESTING for further information on | |||
| writing regression tests | |||
| */ | |||
| ?> | |||
| --EXPECT-- | |||
| snowdrift extension is available | |||
| @@ -0,0 +1,16 @@ | |||
| --TEST-- | |||
| Check for snowdrift unique | |||
| --SKIPIF-- | |||
| <?php if (!extension_loaded("snowdrift")) print "skip"; ?> | |||
| --FILE-- | |||
| <?php | |||
| $arr = []; | |||
| $max = 100000; | |||
| for ($i = 0; $i < $max; $i++) { | |||
| $id = SnowDrift::NextId(); | |||
| $arr[$id] = ''; | |||
| } | |||
| var_dump(count($arr)); | |||
| ?> | |||
| --EXPECT-- | |||
| int(100000) | |||
| @@ -0,0 +1,16 @@ | |||
| --TEST-- | |||
| Check for snowdrift serial | |||
| --SKIPIF-- | |||
| <?php if (!extension_loaded("snowdrift")) print "skip"; ?> | |||
| --FILE-- | |||
| <?php | |||
| $arr = []; | |||
| $max = 1024; | |||
| for ($i = 0; $i < $max; $i++) { | |||
| $arr[$i] = SnowDrift::NextId(); | |||
| } | |||
| var_dump(($arr[$max-1] - $arr[0]) == ($max-1)); | |||
| ?> | |||
| --EXPECT-- | |||
| bool(true) | |||
| @@ -0,0 +1,15 @@ | |||
| --TEST-- | |||
| Check for snowdrift batch get unique | |||
| --SKIPIF-- | |||
| <?php if (!extension_loaded("snowdrift")) print "skip"; ?> | |||
| --FILE-- | |||
| <?php | |||
| $arr = []; | |||
| $max = 100000; | |||
| foreach (SnowDrift::NextNumId($max) as $id) { | |||
| $arr[$id] = ''; | |||
| } | |||
| var_dump(count($arr)); | |||
| ?> | |||
| --EXPECT-- | |||
| int(100000) | |||
| @@ -0,0 +1,2 @@ | |||
| #!/bin/sh | |||
| phpize && ./configure && make clean && make && gcc -O2 -fPIC -shared -g src/snowflake/snowflake.c -o src/snowflake/libsnow.so | |||
| @@ -0,0 +1,24 @@ | |||
| #!/bin/bash | |||
| TEST_DIR="`pwd`/tests/" | |||
| make test | |||
| for file in `find $TEST_DIR -name "*.diff" 2>/dev/null` | |||
| do | |||
| grep "\-\-XFAIL--" ${file/%diff/phpt} >/dev/null 2>&1 | |||
| if [ $? -gt 0 ] | |||
| then | |||
| FAILS[${#FAILS[@]}]="$file" | |||
| fi | |||
| done | |||
| if [ ${#FAILS[@]} -gt 0 ] | |||
| then | |||
| for fail in "${FAILS[@]}" | |||
| do | |||
| sh -xc "cat $fail" | |||
| done | |||
| exit 1 | |||
| else | |||
| exit 0 | |||
| fi | |||