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