diff --git a/PHP/.gitignore b/PHP/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0ea089d0d990c41c5851ed9a4d48b2949dc3de90 --- /dev/null +++ b/PHP/.gitignore @@ -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/ diff --git a/PHP/.travis.yml b/PHP/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..41c4afa4b61def1fd796371a370dd7057ff0831b --- /dev/null +++ b/PHP/.travis.yml @@ -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 \ No newline at end of file diff --git a/PHP/CREDITS b/PHP/CREDITS new file mode 100644 index 0000000000000000000000000000000000000000..6812075f9dcdb5a9a5c7137e9273fa7353d565f3 --- /dev/null +++ b/PHP/CREDITS @@ -0,0 +1,2 @@ +snowdrift +63851587@qq.com \ No newline at end of file diff --git a/PHP/EXPERIMENTAL b/PHP/EXPERIMENTAL new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/PHP/LICENSE b/PHP/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..808c001cbd9ab193ac25e17b1685def86722feca --- /dev/null +++ b/PHP/LICENSE @@ -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 + ". + +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 . + +PHP includes the Zend Engine, freely available at +. diff --git a/PHP/README.md b/PHP/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3e21322a5feb850f762726329aa5263b94130758 --- /dev/null +++ b/PHP/README.md @@ -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); +``` diff --git a/PHP/config.m4 b/PHP/config.m4 new file mode 100644 index 0000000000000000000000000000000000000000..25d77edfd59b79412a12ccb38cfea2512ebdb2cd --- /dev/null +++ b/PHP/config.m4 @@ -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 diff --git a/PHP/config.w32 b/PHP/config.w32 new file mode 100644 index 0000000000000000000000000000000000000000..8e8a6f76fc98b7bdec21387a98d1d716b3f06087 --- /dev/null +++ b/PHP/config.w32 @@ -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"); +} + diff --git a/PHP/php_snowdrift.h b/PHP/php_snowdrift.h new file mode 100644 index 0000000000000000000000000000000000000000..22b5d062377cef0a2ccb4df50109e5cbd2b0d5cf --- /dev/null +++ b/PHP/php_snowdrift.h @@ -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 */ diff --git a/PHP/snowdrift.c b/PHP/snowdrift.c new file mode 100644 index 0000000000000000000000000000000000000000..c92f5c9799eb6fcaf9097438f4a8e499101223d4 --- /dev/null +++ b/PHP/snowdrift.c @@ -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 diff --git a/PHP/snowdrift.php b/PHP/snowdrift.php new file mode 100644 index 0000000000000000000000000000000000000000..31830c7c426e6ea051a59ffb1c5fcf2f1ea0e7e7 --- /dev/null +++ b/PHP/snowdrift.php @@ -0,0 +1,94 @@ + 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; diff --git a/PHP/src/makefile b/PHP/src/makefile new file mode 100644 index 0000000000000000000000000000000000000000..662d491f78c17d911bb8353b8af21e651a086c04 --- /dev/null +++ b/PHP/src/makefile @@ -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) \ No newline at end of file diff --git a/PHP/src/snowflake/shm.c b/PHP/src/snowflake/shm.c new file mode 100644 index 0000000000000000000000000000000000000000..942e5bbf9f4b3bb6353c521957d33ce4676bbb28 --- /dev/null +++ b/PHP/src/snowflake/shm.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#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 diff --git a/PHP/src/snowflake/shm.h b/PHP/src/snowflake/shm.h new file mode 100644 index 0000000000000000000000000000000000000000..8a1304fffdb37aa0c5af848ca136fa2f0837e126 --- /dev/null +++ b/PHP/src/snowflake/shm.h @@ -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 diff --git a/PHP/src/snowflake/snowflake.c b/PHP/src/snowflake/snowflake.c new file mode 100644 index 0000000000000000000000000000000000000000..5a298fbb002c8d28e835e052cc94426c793723ef --- /dev/null +++ b/PHP/src/snowflake/snowflake.c @@ -0,0 +1,207 @@ +#include +#include +#include +#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; +} diff --git a/PHP/src/snowflake/snowflake.h b/PHP/src/snowflake/snowflake.h new file mode 100644 index 0000000000000000000000000000000000000000..7e2ac13a680fbd4f2e24302aeead1b50a96e18bd --- /dev/null +++ b/PHP/src/snowflake/snowflake.h @@ -0,0 +1,28 @@ +#include + +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); \ No newline at end of file diff --git a/PHP/src/snowflake/spinlock.c b/PHP/src/snowflake/spinlock.c new file mode 100644 index 0000000000000000000000000000000000000000..7a2de49e26e6278b14847dc8b6b0cb61bade33fc --- /dev/null +++ b/PHP/src/snowflake/spinlock.c @@ -0,0 +1,47 @@ +#include +#include +#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); +} diff --git a/PHP/src/snowflake/spinlock.h b/PHP/src/snowflake/spinlock.h new file mode 100644 index 0000000000000000000000000000000000000000..c1535f14142f7119fb288705167d66f6733689b6 --- /dev/null +++ b/PHP/src/snowflake/spinlock.h @@ -0,0 +1,11 @@ +#ifndef __SPINLOCK_H +#define __SPINLOCK_H +#include + +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 diff --git a/PHP/src/test.c b/PHP/src/test.c new file mode 100644 index 0000000000000000000000000000000000000000..533bf947eddef656618db9cf9941feef00bdad8e --- /dev/null +++ b/PHP/src/test.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/PHP/tests/001.phpt b/PHP/tests/001.phpt new file mode 100644 index 0000000000000000000000000000000000000000..de59232e4078dbe987bd92a0f79d296e80ad8dc6 --- /dev/null +++ b/PHP/tests/001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Check for snowdrift presence +--SKIPIF-- + +--FILE-- + +--EXPECT-- +snowdrift extension is available diff --git a/PHP/tests/002.phpt b/PHP/tests/002.phpt new file mode 100644 index 0000000000000000000000000000000000000000..fcc59eb1c87823edb5b37e672c5a67e62a2fcbe3 --- /dev/null +++ b/PHP/tests/002.phpt @@ -0,0 +1,16 @@ +--TEST-- +Check for snowdrift unique +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(100000) \ No newline at end of file diff --git a/PHP/tests/003.phpt b/PHP/tests/003.phpt new file mode 100644 index 0000000000000000000000000000000000000000..b3d4647c24c4fe2e4cae3750b495c92c42a11e12 --- /dev/null +++ b/PHP/tests/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Check for snowdrift serial +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/PHP/tests/004.phpt b/PHP/tests/004.phpt new file mode 100644 index 0000000000000000000000000000000000000000..5a0564b569775aa85eeb42892ab2c7b7720610b0 --- /dev/null +++ b/PHP/tests/004.phpt @@ -0,0 +1,15 @@ +--TEST-- +Check for snowdrift batch get unique +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(100000) \ No newline at end of file diff --git a/PHP/travis/compile.sh b/PHP/travis/compile.sh new file mode 100644 index 0000000000000000000000000000000000000000..4de86a71e87d93ab4f4c196756d43b1a4f617094 --- /dev/null +++ b/PHP/travis/compile.sh @@ -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 \ No newline at end of file diff --git a/PHP/travis/run-tests.sh b/PHP/travis/run-tests.sh new file mode 100644 index 0000000000000000000000000000000000000000..1df9a84c113cc6860f1b75aa1caf0e8e048e80fb --- /dev/null +++ b/PHP/travis/run-tests.sh @@ -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 \ No newline at end of file