@@ -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 |