增加PHP扩展
This commit is contained in:
37
PHP/.gitignore
vendored
Normal file
37
PHP/.gitignore
vendored
Normal file
@@ -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/
|
||||||
36
PHP/.travis.yml
Normal file
36
PHP/.travis.yml
Normal file
@@ -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
|
||||||
2
PHP/CREDITS
Normal file
2
PHP/CREDITS
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
snowdrift
|
||||||
|
63851587@qq.com
|
||||||
0
PHP/EXPERIMENTAL
Normal file
0
PHP/EXPERIMENTAL
Normal file
68
PHP/LICENSE
Normal file
68
PHP/LICENSE
Normal file
@@ -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>.
|
||||||
50
PHP/README.md
Normal file
50
PHP/README.md
Normal file
@@ -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);
|
||||||
|
```
|
||||||
85
PHP/config.m4
Normal file
85
PHP/config.m4
Normal file
@@ -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
|
||||||
17
PHP/config.w32
Normal file
17
PHP/config.w32
Normal file
@@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
75
PHP/php_snowdrift.h
Normal file
75
PHP/php_snowdrift.h
Normal file
@@ -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 */
|
||||||
247
PHP/snowdrift.c
Normal file
247
PHP/snowdrift.c
Normal file
@@ -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
|
||||||
94
PHP/snowdrift.php
Normal file
94
PHP/snowdrift.php
Normal file
@@ -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;
|
||||||
34
PHP/src/makefile
Normal file
34
PHP/src/makefile
Normal file
@@ -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)
|
||||||
65
PHP/src/snowflake/shm.c
Normal file
65
PHP/src/snowflake/shm.c
Normal file
@@ -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
|
||||||
14
PHP/src/snowflake/shm.h
Normal file
14
PHP/src/snowflake/shm.h
Normal file
@@ -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
|
||||||
207
PHP/src/snowflake/snowflake.c
Normal file
207
PHP/src/snowflake/snowflake.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
28
PHP/src/snowflake/snowflake.h
Normal file
28
PHP/src/snowflake/snowflake.h
Normal file
@@ -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);
|
||||||
47
PHP/src/snowflake/spinlock.c
Normal file
47
PHP/src/snowflake/spinlock.c
Normal file
@@ -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);
|
||||||
|
}
|
||||||
11
PHP/src/snowflake/spinlock.h
Normal file
11
PHP/src/snowflake/spinlock.h
Normal file
@@ -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
|
||||||
104
PHP/src/test.c
Normal file
104
PHP/src/test.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
21
PHP/tests/001.phpt
Normal file
21
PHP/tests/001.phpt
Normal file
@@ -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
|
||||||
16
PHP/tests/002.phpt
Normal file
16
PHP/tests/002.phpt
Normal file
@@ -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)
|
||||||
16
PHP/tests/003.phpt
Normal file
16
PHP/tests/003.phpt
Normal file
@@ -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)
|
||||||
15
PHP/tests/004.phpt
Normal file
15
PHP/tests/004.phpt
Normal file
@@ -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)
|
||||||
2
PHP/travis/compile.sh
Normal file
2
PHP/travis/compile.sh
Normal file
@@ -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
|
||||||
24
PHP/travis/run-tests.sh
Normal file
24
PHP/travis/run-tests.sh
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user