├── EXPERIMENTAL ├── CREDITS ├── doc ├── bits.jpg └── bits.png ├── src ├── shm.h ├── spinlock.h ├── shm.c ├── spinlock.c ├── donkeyid.h └── donkeyid.c ├── config.w32 ├── donkeytest.php ├── .gitignore ├── donkeyid.php ├── tests ├── 001.phpt └── main.c ├── php_wrapper.h ├── CMakeLists.txt ├── config.m4 ├── README.md ├── php_donkeyid.h ├── Donkeyid.php └── donkeyid.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | donkeyid -------------------------------------------------------------------------------- /doc/bits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osgochina/donkeyid/HEAD/doc/bits.jpg -------------------------------------------------------------------------------- /doc/bits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osgochina/donkeyid/HEAD/doc/bits.png -------------------------------------------------------------------------------- /src/shm.h: -------------------------------------------------------------------------------- 1 | #ifndef __SHM_H 2 | #define __SHM_H 3 | 4 | struct shm { 5 | void *addr; 6 | size_t size; 7 | }; 8 | 9 | int shm_alloc(struct shm *shm); 10 | 11 | void shm_free(struct shm *shm); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/spinlock.h: -------------------------------------------------------------------------------- 1 | #ifndef __SPINLOCK_H 2 | #define __SPINLOCK_H 3 | 4 | typedef volatile unsigned int atomic_t; 5 | 6 | void spin_lock(atomic_t *lock, int which); 7 | 8 | void spin_unlock(atomic_t *lock, int which); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("donkeyid", "for donkeyid support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("donkeyid", "enable donkeyid support", "no"); 9 | 10 | if (PHP_DONKEYID != "no") { 11 | EXTENSION("donkeyid", "donkeyid.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /donkeytest.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('donkeyid')) { 5 | dl('donkeyid.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'donkeyid'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for donkeyid presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | donkeyid extension is available 22 | -------------------------------------------------------------------------------- /tests/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vic on 16-4-20. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "donkeyid.h" 9 | int main() { 10 | int type = 1; 11 | long epoch = 0; 12 | donkeyid_init(); 13 | dk_p_t pt = {dtype:0,node_id:1,epoch:0}; 14 | int i = 0; 15 | for (i = 0; i < 100; ++i) { 16 | __uint64_t donkeyid = donkeyid_next_id(pt); 17 | printf("%"PRIu64"\n", donkeyid); 18 | // printf("%"PRIu64"\n",GET_TIMESTAMP_BY_DONKEY_ID(donkeyid,type,epoch)); 19 | // printf("%d\n",GET_NODE_ID_BY_DONKEY_ID(donkeyid,type)); 20 | // printf("%d\n",GET_WORKER_ID_BY_DONKEY_ID(donkeyid,type)); 21 | // printf("%d\n",GET_SEQUENCE_BY_DONKEY_ID(donkeyid,type)); 22 | } 23 | donkeyid_shutdown(1); 24 | } 25 | -------------------------------------------------------------------------------- /php_wrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vic on 16-4-18. 3 | // 4 | 5 | #ifndef DONKEYID_PHP_WRAPPER_H 6 | #define DONKEYID_PHP_WRAPPER_H 7 | 8 | #include 9 | 10 | 11 | #if PHP_MAJOR_VERSION < 7 12 | 13 | #define DK_RETURN_STRINGL RETURN_STRINGL 14 | typedef int zend_size_t; 15 | 16 | #define dk_add_next_index_stringl(a,s,l,d) add_next_index_stringl(a,s,l,d) 17 | #define dk_add_assoc_stringl_ex(a,ks,kl,s,l,d) add_assoc_stringl_ex(a,ks,kl,s,l,d) 18 | 19 | #else 20 | 21 | #define DK_RETURN_STRINGL(s,l,dup) RETURN_STRINGL(s,l) 22 | #define dk_add_next_index_stringl(a,s,l,d) add_next_index_stringl(a,s,l) 23 | #define dk_add_assoc_stringl_ex(a,ks,kl,s,l,d) add_assoc_stringl_ex(a,ks,kl,s,l) 24 | 25 | typedef size_t zend_size_t; 26 | 27 | #endif 28 | 29 | 30 | 31 | #if PHP_VERSION_ID >= 80000 32 | #define TSRMLS_C 33 | #define TSRMLS_CC 34 | #define TSRMLS_D 35 | #define TSRMLS_DC 36 | #define TSRMLS_FETCH_FROM_CTX(ctx) 37 | #define TSRMLS_SET_CTX(ctx) 38 | #endif 39 | 40 | 41 | #endif //DONKEYID_PHP_WRAPPER_H 42 | -------------------------------------------------------------------------------- /src/shm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "shm.h" 6 | 7 | 8 | #ifdef MAP_ANON 9 | 10 | int 11 | shm_alloc(struct shm *shm) { 12 | shm->addr = (void *) mmap(NULL, shm->size, 13 | PROT_READ | PROT_WRITE, 14 | MAP_ANONYMOUS | MAP_SHARED, -1, 0); 15 | 16 | if (shm->addr == NULL) { 17 | return -1; 18 | } 19 | 20 | return 0; 21 | } 22 | 23 | 24 | void 25 | shm_free(struct shm *shm) { 26 | if (shm->addr) { 27 | munmap((void *) shm->addr, shm->size); 28 | } 29 | } 30 | 31 | #else 32 | 33 | int 34 | shm_alloc(struct shm *shm) 35 | { 36 | int fd; 37 | 38 | fd = open("/dev/zero", O_RDWR); 39 | if (fd == -1) { 40 | return -1; 41 | } 42 | 43 | shm->addr = (void *) mmap(NULL, shm->size, 44 | PROT_READ|PROT_WRITE, 45 | MAP_SHARED, fd, 0); 46 | 47 | close(fd); 48 | 49 | if (shm->addr == NULL) { 50 | return -1; 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | 57 | void 58 | shm_free(struct shm *shm) 59 | { 60 | if (shm->addr) { 61 | munmap((void *) shm->addr, shm->size); 62 | } 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(donkeyid) 3 | 4 | set(PHP_INCLUDE_H /usr/local/php56/include) 5 | #set(PHP_INCLUDE_H /usr/local/php54/include) 6 | #set(PHP_INCLUDE_H /usr/local/php7/include) 7 | INCLUDE_DIRECTORIES( 8 | /usr/local/include 9 | ${PHP_INCLUDE_H}/php 10 | ${PHP_INCLUDE_H}/php/main 11 | ${PHP_INCLUDE_H}/php/TSRM 12 | ${PHP_INCLUDE_H}/php/Zend 13 | 14 | ) 15 | AUX_SOURCE_DIRECTORY( /home/liuzhiming/code/php-src/Zend DIR_SRCS) 16 | #AUX_SOURCE_DIRECTORY( /home/vic/workspace/php/Zend DIR_SRCS) 17 | #AUX_SOURCE_DIRECTORY( /home/vic/workspace/php-5.6.14/Zend DIR_SRCS) 18 | 19 | 20 | add_custom_target(makefile COMMAND phpize --clean && phpize && ./configure && make && make install WORKING_DIRECTORY .) 21 | 22 | set(SOURCE_FILES donkeyid/donkeyid.c donkeyid/src/donkeyid.c donkeyid/src/shm.c donkeyid/src/spinlock.c) 23 | add_executable(donkeyid ${SOURCE_FILES}) 24 | 25 | 26 | 27 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 28 | 29 | file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS donkeyid/src/*.c) 30 | file(GLOB_RECURSE HEAD_FILES FOLLOW_SYMLINKS donkeyid/src/*.h) 31 | message("${HEAD_FILES}") 32 | INCLUDE_DIRECTORIES(BEFORE ./donkeyid/src) 33 | set(DONKEYTEST_FILES donkeyid/tests/main.c) 34 | add_executable(donkeytest ${DONKEYTEST_FILES};${SRC_LIST}) -------------------------------------------------------------------------------- /src/spinlock.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2007 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Liexusong <280259971@qq.com> | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* GCC support */ 20 | 21 | #include 22 | #include "spinlock.h" 23 | #include 24 | 25 | extern int ncpu; 26 | 27 | 28 | void spin_lock(atomic_t *lock, int which) { 29 | int i, n; 30 | 31 | for (; ;) { 32 | 33 | if (*lock == 0 && 34 | __sync_bool_compare_and_swap(lock, 0, which)) { 35 | return; 36 | } 37 | 38 | if (ncpu > 1) { 39 | 40 | for (n = 1; n < 129; n << 1) { 41 | 42 | for (i = 0; i < n; i++) { 43 | __asm("pause"); 44 | } 45 | 46 | if (*lock == 0 && 47 | __sync_bool_compare_and_swap(lock, 0, which)) { 48 | return; 49 | } 50 | } 51 | } 52 | 53 | sched_yield(); 54 | } 55 | } 56 | 57 | 58 | void spin_unlock(atomic_t *lock, int which) { 59 | __sync_bool_compare_and_swap(lock, which, 0); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension donkeyid 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | PHP_ARG_WITH(donkeyid, for donkeyid support, 11 | dnl Make sure that the comment is aligned: 12 | [ --with-donkeyid Include donkeyid support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | dnl PHP_ARG_ENABLE(donkeyid, whether to enable donkeyid support, 17 | dnl Make sure that the comment is aligned: 18 | dnl [ --enable-donkeyid Enable donkeyid support]) 19 | 20 | if test "$PHP_DONKEYID" != "no"; then 21 | dnl Write more examples of tests here... 22 | 23 | dnl # --with-donkeyid -> check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/donkeyid.h" # you most likely want to change this 26 | dnl if test -r $PHP_DONKEYID/$SEARCH_FOR; then # path given as parameter 27 | dnl DONKEYID_DIR=$PHP_DONKEYID 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for donkeyid files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl DONKEYID_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$DONKEYID_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the donkeyid distribution]) 41 | dnl fi 42 | 43 | dnl # --with-donkeyid -> add include path 44 | dnl PHP_ADD_INCLUDE($DONKEYID_DIR/include) 45 | 46 | dnl # --with-donkeyid -> check for lib and symbol presence 47 | dnl LIBNAME=donkeyid # you may want to change this 48 | dnl LIBSYMBOL=donkeyid # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $DONKEYID_DIR/$PHP_LIBDIR, DONKEYID_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_DONKEYIDLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong donkeyid lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$DONKEYID_DIR/$PHP_LIBDIR -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(DONKEYID_SHARED_LIBADD) 61 | 62 | donkeyid_source_file="donkeyid.c \ 63 | src/donkeyid.c \ 64 | src/shm.c \ 65 | src/spinlock.c" 66 | PHP_NEW_EXTENSION(donkeyid, $donkeyid_source_file, $ext_shared) 67 | fi 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DonkeyID---php扩展-64位自增ID生成器 2 | [0.7版本请访问](https://github.com/osgochina/donkeyid/tree/donkeyid-0.7) 3 | ## 原理 4 | 参考Twitter-Snowflake 算法,扩展了其中的细节。具体组成如下图: 5 | 6 | ![bits.jpg](https://github.com/osgochina/donkeyid/blob/master/doc/bits.png?raw=true) 7 | 8 | > 如图所示,64bits 咱们分成了4个部分。 9 | 10 | > 1. 毫秒级的时间戳,有42个bit.能够使用139年,从1970年开始计算,能使用到2109年,当然这些是可以扩展的,可以通知指定起始时间来延长这个日期长度。 11 | > 2. 自定义节点id,防止多进程运行产生重复id,占位12个bit,能够支持4096个节点。部署的时候可以配置好服务器id; 12 | > 4. ~~进程workerid,占位5bit,能够生成32个进程id。根据pid运算获得。(已经取消)~~ 13 | > 4. 进程内毫秒时间自增序号。占位10bit,一毫秒能产生1024个id。也就是说并发1秒能产生1024000个id。 14 | 15 | ### 唯一性保证 16 | > 100%唯一性保证,根据nodeid的不一样保证多服务器的唯一性,使用共享内存+自旋锁保证单节点多进程的唯一性 17 | > 同一毫秒内自增变量保证并发的唯一性。 18 | 19 | 20 | 21 | ## 使用 22 | ### 安装 23 | 24 | > 下载代码到本地,进入项目文件夹,执行 25 | 26 | ```Bash 27 | cd ./donkeyid 28 | /path/to/phpize 29 | ./configure --with-php-config=/path/to/php-config 30 | make 31 | make install 32 | ``` 33 | 34 | ```Bssh 35 | echo "extension=donkeyid.so" >> /path/to/php.ini 36 | ``` 37 | ### 模式介绍 38 | 39 | > DonkeyId 有三种id生成模式: 40 | 41 | > 1. 默认模式,以上的介绍都是基于默认模式,(mysql字段请使用bigint(20)). dk_get_next_id()。 42 | > 2. 第二种模式是10进制模式 生成最多20位数字,(mysql字段请使用varchar(20)).从右开始算第十位以后的数字是时间戳的秒, 43 | > 第7位到第9位 是节点id。三位数字,最多到999.从第2位到第6位是秒内的自增id, 44 | > 最后一位是留给业务方的自定义位数。2016053010150316300120001.dk_get_ts_id(). 45 | > 3. 第三种模式是字符串模式,生成一个25位的字符串,前17位是年月日时分秒毫秒,第18位到21位是节点id,第22-25位是毫秒内自增id,(mysql字段请使用varchar(25))。dk_get_dt_id(). 46 | 47 | ### 运行 48 | 49 | * 注意,该扩展不支持php的线程安全模式,请在非线程安全模式下使用。 50 | 51 | #### 配置 52 | 53 | > 在php.ini 中配置节点id 54 | 55 | ``` 56 | [DonkeyId] 57 | ;0-4095 58 | donkeyid.node_id=0 59 | ;0-当前时间戳 60 | donkeyid.epoch=0 61 | 62 | ``` 63 | 64 | #### api接口 65 | 66 | * dk_get_next_id() 67 | 68 | > 获取基于Snowflake算法的id 69 | 70 | * dk_get_next_ids($num,$time=0) 71 | 72 | > 获取基于Snowflake算法的id列表.$num:生成id的数量,$time:需要生成指定时间的id.$time 默认为0 生成当前时间指定数量的id 73 | 74 | * dk_parse_id($id) 75 | 76 | > 解析基于Snowflake算法的id元数据,返回值包括:time id生成时间,node_id 节点id,sequence 自增数 77 | 78 | * dk_get_ts_id() 79 | 80 | > 获取10进制的时间戳类型的id 81 | 82 | * dk_get_ts_ids($num,$time=0) 83 | 84 | > 获取10进制的时间戳类型的id列表.$num:生成id的数量,$time:需要生成指定时间的id.$time 默认为0 生成当前时间指定数量的id 85 | 86 | * dk_parse_ts_id($tsid) 87 | 88 | > 解析10进制的时间戳类型的id元数据,返回值包括:time id生成时间,node_id 节点id,sequence 自增数 89 | 90 | * dk_get_dt_id() 91 | 92 | > 获取字符串类型的id,显式包含日期时间属性 93 | 94 | #### 测试代码 95 | 96 | ```php 97 | 98 | $nextid = dk_get_next_id(); 99 | echo "nextid:".$nextid."\n"; 100 | print_r(dk_parse_id($nextid)); 101 | 102 | $tsid = dk_get_ts_id(); 103 | echo "tsid:".$tsid."\n"; 104 | print_r(dk_parse_ts_id($tsid)); 105 | 106 | $dtid = dk_get_dt_id(); 107 | echo "dtid:".$dtid."\n"; 108 | 109 | echo "nextids:\n"; 110 | print_r(dk_get_next_ids(100,1470298401)); 111 | echo "nextids2:\n"; 112 | print_r(dk_get_next_ids(100)); 113 | 114 | echo "tsids:\n"; 115 | print_r(dk_get_ts_ids(100,1470298401)); 116 | echo "tsids2:\n"; 117 | print_r(dk_get_ts_ids(100)); 118 | 119 | ``` 120 | #### 支持版本 121 | > 支持 php5.3+ ,支持php 7 - 7.4 122 | > 支持php 8 123 | -------------------------------------------------------------------------------- /src/donkeyid.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: liuzhiming 187231450@qq.com | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef DONKEYID_DONKEYID_H 20 | #define DONKEYID_DONKEYID_H 21 | 22 | #include 23 | #include 24 | 25 | #define TIMESTAMP_BITS 42 //时间所占bit位数 26 | #define NODE_ID_BITS 12 //节点所占bit位数 27 | #define SEQUENCE_BITS 10 //毫秒内自增 28 | 29 | #define TIMESTAMP_MASK (-1LL^(-1LL<>TIMESTAMP_LEFT_SHIFT)+(epoch*1000)):(uint64_t)((id+epoch*TYPE_1_TIMESTAMP)/TYPE_1_TIMESTAMP) 47 | #define GET_NODE_ID_BY_DONKEY_ID(id,type) (type==0)?(int)((id>>NODE_ID_LEFT_SHIFT)&NODE_ID_MASK):(int)((id-((id/TYPE_1_TIMESTAMP)*TYPE_1_TIMESTAMP))/TYPE_1_NODE_ID) 48 | #define GET_SEQUENCE_BY_DONKEY_ID(id,type) (type==0)?(int)(id&SEQUENCE_MASK):(int)((((id-((id/TYPE_1_TIMESTAMP)*TYPE_1_TIMESTAMP))-(GET_NODE_ID_BY_DONKEY_ID(id,type)*TYPE_1_NODE_ID)))/10) 49 | 50 | //最多有几种类型 51 | #define MAX_DONKEYID_TYPE 3 52 | 53 | typedef struct { 54 | uint64_t last_timestamp; //最后使用毫秒数 55 | int sequence; //单服务器毫秒内的自增值 56 | } donkeyid_context_t; 57 | 58 | #define NEXTTYPE 0 59 | #define TSTYPE 1 60 | #define DTTYPE 2 61 | 62 | //批量获取id时最大能够获取的数量 63 | #define MAX_BATCH_ID_LEN ((1<= 4 32 | # define PHP_DONKEYID_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_DONKEYID_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | 39 | #include "TSRM.h" 40 | 41 | #endif 42 | 43 | PHP_MINIT_FUNCTION (donkeyid); 44 | 45 | PHP_MSHUTDOWN_FUNCTION (donkeyid); 46 | 47 | PHP_RINIT_FUNCTION (donkeyid); 48 | 49 | PHP_RSHUTDOWN_FUNCTION (donkeyid); 50 | 51 | PHP_MINFO_FUNCTION (donkeyid); 52 | 53 | PHP_FUNCTION(dk_get_next_id);//获取基于Snowflake算法的id 54 | PHP_FUNCTION(dk_get_next_ids);//获取基于Snowflake算法的id列表 55 | PHP_FUNCTION(dk_parse_id);//解析基于Snowflake算法的id元数据 56 | 57 | PHP_FUNCTION(dk_get_ts_id);//获取10进制的时间戳类型的id 58 | PHP_FUNCTION(dk_get_ts_ids);//获取10进制的时间戳类型的id列表 59 | PHP_FUNCTION(dk_parse_ts_id);//解析id元数据 60 | 61 | PHP_FUNCTION(dk_get_dt_id);//获取字符串类型的id,显式包含日期时间属性 62 | /* 63 | Declare any global variables you may need between the BEGIN 64 | and END macros here: 65 | */ 66 | ZEND_BEGIN_MODULE_GLOBALS(donkeyid) 67 | long dk_node_id;//节点id 68 | long dk_epoch; //起始计算时间 69 | ZEND_END_MODULE_GLOBALS(donkeyid) 70 | 71 | 72 | /* In every utility function you add that needs to use variables 73 | in php_donkeyid_globals, call TSRMLS_FETCH(); after declaring other 74 | variables used by that function, or better yet, pass in TSRMLS_CC 75 | after the last function argument and declare your utility function 76 | with TSRMLS_DC after the last declared argument. Always refer to 77 | the globals in your function as DONKEYID_G(variable). You are 78 | encouraged to rename these macros something shorter, see 79 | examples in any other php module directory. 80 | */ 81 | 82 | #ifdef ZTS 83 | #define DONKEYID_G(v) TSRMG(donkeyid_globals_id, zend_donkeyid_globals *, v) 84 | #else 85 | #define DONKEYID_G(v) (donkeyid_globals.v) 86 | #endif 87 | 88 | #endif /* PHP_DONKEYID_H */ 89 | 90 | 91 | /* 92 | * Local variables: 93 | * tab-width: 4 94 | * c-basic-offset: 4 95 | * End: 96 | * vim600: noet sw=4 ts=4 fdm=marker 97 | * vim<600: noet sw=4 ts=4 98 | */ 99 | -------------------------------------------------------------------------------- /Donkeyid.php: -------------------------------------------------------------------------------- 1 | node_id = ($node_id == false || $node_id < 0)?0:$node_id; 38 | $this->epoch = ($epoch == false || $epoch < 0)?0:$epoch; 39 | $this->create_table(); 40 | } 41 | 42 | static function getInstance() 43 | { 44 | if (!self::$donkeyid) 45 | { 46 | self::$donkeyid = new Donkeyid(); 47 | } 48 | return self::$donkeyid; 49 | } 50 | /** 51 | * 创建共享内存 52 | */ 53 | private function create_table() 54 | { 55 | $this->table = new swoole_table(3); 56 | $this->table->column("last_timestamp",swoole_table::TYPE_INT, 8); 57 | $this->table->column("sequence",swoole_table::TYPE_INT, 4); 58 | $this->table->create(); 59 | 60 | $this->lock = new swoole_lock(SWOOLE_SPINLOCK); 61 | } 62 | 63 | /** 64 | * 获取当前毫秒 65 | * @return int 66 | */ 67 | private function get_curr_timestamp_ms() 68 | { 69 | return (int)(microtime(true)*1000); 70 | } 71 | 72 | /** 73 | * 暂停一毫秒 74 | * @return int 75 | */ 76 | private function wait_next_ms() 77 | { 78 | usleep(1000); 79 | return $this->get_curr_timestamp_ms(); 80 | } 81 | 82 | /** 83 | * 获取id 84 | * @return int 85 | */ 86 | public function dk_get_next_id() 87 | { 88 | $now = $this->get_curr_timestamp_ms(); 89 | $this->lock->lock(); 90 | $col = $this->table->get(self::snowflake); 91 | if ($col == false || $col["last_timestamp"] > $now){ 92 | $last_timestamp = $now; 93 | $sequence = rand(0,10) % 2; 94 | }else{ 95 | $last_timestamp = $col["last_timestamp"]; 96 | $sequence = $col["sequence"]; 97 | } 98 | if ($now == $last_timestamp){ 99 | $sequence = ($sequence+1)&((-1^(-1<wait_next_ms(); 102 | } 103 | } 104 | $this->table->set(self::snowflake,array("last_timestamp"=>$now,"sequence"=>$sequence)); 105 | $id = (($now-($this->epoch*1000)&(-1^(-1<node_id&(-1^(-1<lock->unlock(); 110 | return $id; 111 | } 112 | 113 | public function dk_parse_id($id) 114 | { 115 | $ret["time"] = ($id>>self::TIMESTAMP_LEFT_SHIFT)+($this->epoch*1000); 116 | $ret["node_id"] = ($id>>self::NODE_ID_LEFT_SHIFT)&(-1^(-1< 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "donkeyid.h" 27 | #include "shm.h" 28 | #include "spinlock.h" 29 | 30 | 31 | 32 | int ncpu; 33 | static struct shm shmctx; 34 | static pid_t pid = -1; 35 | typedef struct { 36 | atomic_t lock; 37 | donkeyid_context_t donkeyid_context; 38 | } mlocks; 39 | 40 | static mlocks *mlock; 41 | static mlocks dtypes[MAX_DONKEYID_TYPE] = {0}; 42 | 43 | /** 44 | * 初始化 45 | */ 46 | int donkeyid_init() { 47 | 48 | shmctx.size = sizeof(dtypes); 49 | if (shm_alloc(&shmctx) == -1) { 50 | return -1; 51 | } 52 | bzero(shmctx.addr, sizeof(dtypes)); 53 | mlock = (mlocks *) shmctx.addr; 54 | //获取cpu核心数量 55 | ncpu = (int) sysconf(_SC_NPROCESSORS_ONLN); 56 | if (ncpu <= 0) { 57 | ncpu = 1; 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | /** 64 | * 正常结束结束释放内存 65 | */ 66 | void donkeyid_shutdown() { 67 | if (shmctx.size){ 68 | int i; 69 | for (i = 0; i <= MAX_DONKEYID_TYPE ; i++) { 70 | if((mlock+i)->lock == pid){ 71 | spin_unlock(&((mlock+i)->lock),pid); 72 | } 73 | } 74 | shm_free(&shmctx); 75 | shmctx.size = 0; 76 | } 77 | } 78 | 79 | 80 | /** 81 | * 设置pid 82 | */ 83 | static void init_pid() { 84 | if (pid == -1) 85 | pid = (int) getpid(); 86 | } 87 | 88 | 89 | static void get_date(char *s,size_t n){ 90 | time_t t = time(0); 91 | strftime( s, n, "%Y%m%d%H%M%S",localtime(&t)); 92 | } 93 | 94 | /** 95 | * 获取当前毫秒数 96 | */ 97 | static uint64_t get_curr_timestamp_ms() { 98 | struct timeval tv; 99 | if (gettimeofday(&tv, NULL) == -1) { 100 | return 0ULL; 101 | } 102 | uint64_t ms_t = (uint64_t) tv.tv_sec * 1000ULL + (uint64_t) tv.tv_usec / 1000ULL; 103 | return ms_t; 104 | } 105 | /** 106 | * 获取当前秒数 107 | */ 108 | uint64_t get_curr_timestamp(){ 109 | struct timeval tv; 110 | if (gettimeofday(&tv, NULL) == -1) { 111 | return 0ULL; 112 | } 113 | return (uint64_t) tv.tv_sec; 114 | } 115 | 116 | /** 117 | * 等待1毫秒 118 | */ 119 | static uint64_t wait_next_ms() { 120 | struct timeval tv; 121 | tv.tv_sec = 0; 122 | tv.tv_usec = 1000; 123 | select(0, NULL, NULL, NULL, &tv); 124 | return get_curr_timestamp_ms(); 125 | } 126 | 127 | /** 128 | * 等待一秒 129 | */ 130 | static uint64_t wait_next_stamp(uint64_t now) { 131 | struct timeval tv; 132 | tv.tv_sec = 0; 133 | tv.tv_usec = 1000; 134 | uint64_t cur = 0; 135 | do{ 136 | cur = get_curr_timestamp(); 137 | select(0, NULL, NULL, NULL, &tv); 138 | }while (cur <= now); 139 | return cur; 140 | } 141 | 142 | 143 | /** 144 | * 获取唯一id 145 | */ 146 | uint64_t donkeyid_next_id(long node_id,time_t epoch) { 147 | //初始化pid 148 | init_pid(); 149 | uint64_t id; 150 | spin_lock(&((mlock+NEXTTYPE)->lock), pid); 151 | uint64_t now = get_curr_timestamp_ms(); 152 | if (now == 0ULL) { 153 | id = 0ULL; 154 | goto unlock_end; 155 | } 156 | if (now < (mlock+NEXTTYPE)->donkeyid_context.last_timestamp) { 157 | (mlock+NEXTTYPE)->donkeyid_context.last_timestamp = now; 158 | } 159 | if (now == (mlock+NEXTTYPE)->donkeyid_context.last_timestamp) { 160 | (mlock+NEXTTYPE)->donkeyid_context.sequence = ((mlock+NEXTTYPE)->donkeyid_context.sequence + 1) & SEQUENCE_MASK; 161 | if ((mlock+NEXTTYPE)->donkeyid_context.sequence == 0) { 162 | now = wait_next_ms(); 163 | } 164 | } else { 165 | //使得生成的id尾号均匀 166 | srand((unsigned int)now); 167 | (mlock+NEXTTYPE)->donkeyid_context.sequence = rand() % 2; 168 | } 169 | (mlock+NEXTTYPE)->donkeyid_context.last_timestamp = now; 170 | id = ((uint64_t) ((now - epoch*1000) & TIMESTAMP_MASK) << TIMESTAMP_LEFT_SHIFT) 171 | | ((uint64_t) (node_id & NODE_ID_MASK) << NODE_ID_LEFT_SHIFT) 172 | | ((uint64_t) (mlock+NEXTTYPE)->donkeyid_context.sequence); 173 | unlock_end: 174 | spin_unlock(&((mlock+NEXTTYPE)->lock), pid); 175 | return id; 176 | } 177 | 178 | //生成10进制基地的自增id 179 | uint64_t donkeyid_ts_id(long node_id,time_t epoch) 180 | { 181 | //初始化pid 182 | init_pid(); 183 | 184 | uint64_t id; 185 | spin_lock(&((mlock+TSTYPE)->lock), pid); 186 | uint64_t now = get_curr_timestamp(); 187 | if (now == 0ULL) { 188 | id = 0ULL; 189 | goto unlock_end; 190 | } 191 | 192 | if (now < (mlock+TSTYPE)->donkeyid_context.last_timestamp) { 193 | (mlock+TSTYPE)->donkeyid_context.last_timestamp = now; 194 | } 195 | if (now == (mlock+TSTYPE)->donkeyid_context.last_timestamp) { 196 | (mlock+TSTYPE)->donkeyid_context.sequence = ((mlock+TSTYPE)->donkeyid_context.sequence + 1); 197 | if ((mlock+TSTYPE)->donkeyid_context.sequence == TYPE_1_SEQUENCE_MASK) { 198 | now = wait_next_stamp(now); 199 | (mlock+TSTYPE)->donkeyid_context.sequence = 0; 200 | } 201 | } else { 202 | //使得生成的id尾号均匀 203 | srand((unsigned int)now); 204 | (mlock+TSTYPE)->donkeyid_context.sequence = rand() % 10; 205 | } 206 | (mlock+TSTYPE)->donkeyid_context.last_timestamp = now; 207 | id = ((uint64_t) (((now - (epoch != 0?epoch:0))) & TYPE_1_TIMESTAMP_MASK) * TYPE_1_TIMESTAMP) 208 | +((uint64_t)(node_id) * TYPE_1_NODE_ID) 209 | +((uint64_t)(mlock+TSTYPE)->donkeyid_context.sequence * 10); 210 | unlock_end: 211 | spin_unlock(&((mlock+TSTYPE)->lock), pid); 212 | return id; 213 | 214 | } 215 | //获取日期时间型字符串id 216 | int donkeyid_dt_id(long node_id,char *buffer) { 217 | 218 | //初始化pid 219 | init_pid(); 220 | int len = 0; 221 | spin_lock(&((mlock+DTTYPE)->lock), pid); 222 | uint64_t now = get_curr_timestamp_ms(); 223 | if (now == 0ULL) { 224 | len = 0; 225 | goto unlock_end; 226 | } 227 | if (now < (mlock+DTTYPE)->donkeyid_context.last_timestamp) { 228 | (mlock+DTTYPE)->donkeyid_context.last_timestamp = now; 229 | } 230 | if (now == (mlock+DTTYPE)->donkeyid_context.last_timestamp) { 231 | (mlock+DTTYPE)->donkeyid_context.sequence = (mlock+DTTYPE)->donkeyid_context.sequence + 1 & TYPE_2_SEQUENCE_MASK; 232 | if ((mlock+DTTYPE)->donkeyid_context.sequence == 0) { 233 | now = wait_next_ms(); 234 | } 235 | } else { 236 | //使得生成的id尾号均匀 237 | srand((unsigned int)now); 238 | (mlock+DTTYPE)->donkeyid_context.sequence = rand() % 2; 239 | } 240 | (mlock+DTTYPE)->donkeyid_context.last_timestamp = now; 241 | char s[15]; 242 | get_date(s,15); 243 | len = sprintf(buffer,"%s%.3d%.4d%.4d",s,(int)(now%1000),(int)(node_id & TYPE_2_NODE_ID_MASK),(mlock+DTTYPE)->donkeyid_context.sequence); 244 | unlock_end: 245 | spin_unlock(&((mlock+DTTYPE)->lock), pid); 246 | return len; 247 | } 248 | //获取snowflake类型的id列表 249 | int donkeyid_get_next_ids(uint64_t *list,time_t time,long sum,long node_id,time_t epoch) 250 | { 251 | //初始化pid 252 | init_pid(); 253 | 254 | //时间不能小于0,不能小于起始纪元 255 | if (time < 0 || (time*1000) < epoch){ 256 | return -1; 257 | } 258 | 259 | int msec,sequence; 260 | int n=0; 261 | 262 | //单次获取数量不能超过上限 263 | if (sum >= MAX_BATCH_ID_LEN){ 264 | return -1; 265 | } 266 | int max_sequence = MAX_BATCH_ID_LEN/1000; 267 | for (msec = 0; msec < 1000; msec++) { 268 | for (sequence = 0;sequence= sum){ break;} 270 | *(list+n) = ((uint64_t) (((time*1000+msec) - epoch*1000) & TIMESTAMP_MASK) << TIMESTAMP_LEFT_SHIFT) 271 | | ((uint64_t) (node_id & NODE_ID_MASK) << NODE_ID_LEFT_SHIFT) 272 | | ((uint64_t) sequence); 273 | n++; 274 | } 275 | } 276 | return 0; 277 | } 278 | //生成10进制基地的自增id列表 279 | int donkeyid_get_ts_ids(uint64_t *list,time_t time,long sum,long node_id,time_t epoch) 280 | { 281 | //初始化pid 282 | init_pid(); 283 | //时间不能小于0,不能小于起始纪元 284 | if (time < 0 || (time*1000) < epoch){ 285 | return -1; 286 | } 287 | int sequence; 288 | int n=0; 289 | 290 | //单次获取数量不能超过上限 291 | if (sum > TYPE_1_SEQUENCE_MASK){ 292 | return -1; 293 | } 294 | int max_sequence = TYPE_1_SEQUENCE_MASK; 295 | for (sequence = 0;sequence= sum){ break;} 297 | *(list+n) = ((uint64_t) ((((time) - (epoch != 0?epoch:0))) & TYPE_1_TIMESTAMP_MASK) * TYPE_1_TIMESTAMP) 298 | +((uint64_t)(node_id) * TYPE_1_NODE_ID) 299 | +((uint64_t)sequence * 10); 300 | n++; 301 | } 302 | return 0; 303 | 304 | } 305 | -------------------------------------------------------------------------------- /donkeyid.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: liuzhiming 187231450@qq.com | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "ext/standard/info.h" 27 | #include "php_donkeyid.h" 28 | #include "src/donkeyid.h" 29 | #include "php_wrapper.h" 30 | 31 | 32 | /* If you declare any globals in php_donkeyid.h uncomment this: 33 | * 34 | * */ 35 | ZEND_DECLARE_MODULE_GLOBALS(donkeyid) 36 | 37 | 38 | /* True global resources - no need for thread safety here */ 39 | static int le_donkeyid; 40 | /* {{{ PHP_INI 41 | */ 42 | /* Remove comments and fill if you need to have entries in php.ini 43 | PHP_INI_BEGIN() 44 | STD_PHP_INI_ENTRY("donkeyid.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_donkeyid_globals, donkeyid_globals) 45 | STD_PHP_INI_ENTRY("donkeyid.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_donkeyid_globals, donkeyid_globals) 46 | PHP_INI_END() 47 | */ 48 | /* }}} */ 49 | /* {{{ PHP_INI 50 | */ 51 | PHP_INI_BEGIN() 52 | STD_PHP_INI_ENTRY("donkeyid.node_id", "0", PHP_INI_ALL, OnUpdateLong,dk_node_id,zend_donkeyid_globals,donkeyid_globals) 53 | STD_PHP_INI_ENTRY("donkeyid.epoch", "0", PHP_INI_ALL, OnUpdateLong,dk_epoch,zend_donkeyid_globals,donkeyid_globals) 54 | PHP_INI_END() 55 | /* }}} */ 56 | //类方法参数定义 57 | ZEND_BEGIN_ARG_INFO_EX(arginfo_donkeyid_getIdByTime, 0, 0, 0) 58 | ZEND_ARG_INFO(0, num) 59 | ZEND_ARG_INFO(0, time) 60 | ZEND_END_ARG_INFO() 61 | ZEND_BEGIN_ARG_INFO_EX(arginfo_donkeyid_parseId, 0, 0, 0) 62 | ZEND_ARG_INFO(0, id) 63 | ZEND_END_ARG_INFO() 64 | 65 | ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) 66 | ZEND_END_ARG_INFO() 67 | 68 | /* Remove the following function when you have successfully modified config.m4 69 | so that your module can be compiled into PHP, it exists only for testing 70 | purposes. */ 71 | /* {{{ donkeyid_functions[] 72 | * 73 | * Every user visible function must have an entry in donkeyid_functions[]. 74 | */ 75 | const zend_function_entry donkeyid_methods[] = { 76 | PHP_FE(dk_get_next_id, arginfo_void) 77 | PHP_FE(dk_get_next_ids, arginfo_donkeyid_getIdByTime) 78 | PHP_FE(dk_parse_id, arginfo_donkeyid_parseId) 79 | PHP_FE(dk_get_ts_id, arginfo_void) 80 | PHP_FE(dk_get_ts_ids, arginfo_donkeyid_getIdByTime) 81 | PHP_FE(dk_parse_ts_id, arginfo_donkeyid_parseId) 82 | PHP_FE(dk_get_dt_id, arginfo_void) 83 | PHP_FE_END /* Must be the last line in donkeyid_functions[] */ 84 | }; 85 | /* }}} */ 86 | /* {{{ donkeyid_module_entry 87 | */ 88 | zend_module_entry donkeyid_module_entry = { 89 | STANDARD_MODULE_HEADER, 90 | "donkeyid", 91 | donkeyid_methods, 92 | PHP_MINIT(donkeyid), 93 | PHP_MSHUTDOWN(donkeyid), 94 | PHP_RINIT(donkeyid), /* Replace with NULL if there's nothing to do at request start */ 95 | PHP_RSHUTDOWN(donkeyid), /* Replace with NULL if there's nothing to do at request end */ 96 | PHP_MINFO(donkeyid), 97 | PHP_DONKEYID_VERSION, 98 | STANDARD_MODULE_PROPERTIES 99 | }; 100 | 101 | /* }}} */ 102 | 103 | #ifdef COMPILE_DL_DONKEYID 104 | ZEND_GET_MODULE(donkeyid) 105 | #endif 106 | 107 | 108 | /* }}} */ 109 | 110 | 111 | /* {{{ php_donkeyid_init_globals 112 | */ 113 | /* Uncomment this function if you have INI entries 114 | static void php_donkeyid_init_globals(zend_donkeyid_globals *donkeyid_globals) 115 | { 116 | donkeyid_globals->global_value = 0; 117 | donkeyid_globals->global_string = NULL; 118 | } 119 | */ 120 | /* }}} */ 121 | 122 | 123 | /* {{{ PHP_MINIT_FUNCTION 124 | */ 125 | PHP_MINIT_FUNCTION (donkeyid) { 126 | /* If you have INI entries, uncomment these lines 127 | * */ 128 | REGISTER_INI_ENTRIES(); 129 | if (donkeyid_init() == -1){ 130 | return FAILURE; 131 | } 132 | atexit(donkeyid_shutdown); 133 | return SUCCESS; 134 | } 135 | /* }}} */ 136 | 137 | /* {{{ PHP_MSHUTDOWN_FUNCTION 138 | */ 139 | PHP_MSHUTDOWN_FUNCTION (donkeyid) { 140 | /* uncomment this line if you have INI entries 141 | * */ 142 | UNREGISTER_INI_ENTRIES(); 143 | 144 | donkeyid_shutdown(); 145 | return SUCCESS; 146 | } 147 | /* }}} */ 148 | 149 | /* Remove if there's nothing to do at request start */ 150 | /* {{{ PHP_RINIT_FUNCTION 151 | */ 152 | PHP_RINIT_FUNCTION (donkeyid) { 153 | return SUCCESS; 154 | } 155 | /* }}} */ 156 | 157 | /* Remove if there's nothing to do at request end */ 158 | /* {{{ PHP_RSHUTDOWN_FUNCTION 159 | */ 160 | PHP_RSHUTDOWN_FUNCTION (donkeyid) { 161 | return SUCCESS; 162 | } 163 | /* }}} */ 164 | 165 | 166 | 167 | 168 | /* {{{ PHP_MINFO_FUNCTION 169 | */ 170 | PHP_MINFO_FUNCTION (donkeyid) { 171 | php_info_print_table_start(); 172 | php_info_print_table_header(2, "DonkeyId support", "enabled"); 173 | php_info_print_table_row(2, "Version", PHP_DONKEYID_VERSION); 174 | php_info_print_table_row(2, "Author", "zhiming.liu[email: 187231450@qq.com]"); 175 | php_info_print_table_end(); 176 | 177 | /* Remove comments if you have entries in php.ini 178 | DISPLAY_INI_ENTRIES(); 179 | */ 180 | } 181 | /* }}} */ 182 | 183 | /* The previous line is meant for vim and emacs, so it can correctly fold and 184 | unfold functions in source code. See the corresponding marks just before 185 | function definition, where the functions purpose is also documented. Please 186 | follow this convention for the convenience of others editing your code. 187 | */ 188 | 189 | 190 | 191 | 192 | /* The previous line is meant for vim and emacs, so it can correctly fold and 193 | unfold functions in source code. See the corresponding marks just before 194 | function definition, where the functions purpose is also documented. Please 195 | follow this convention for the convenience of others editing your code. 196 | */ 197 | PHP_FUNCTION(dk_get_next_id) 198 | { 199 | char buffer[64]; 200 | int len; 201 | long node_id = DONKEYID_G(dk_node_id); 202 | time_t epoch = DONKEYID_G(dk_epoch); 203 | uint64_t donkeyid = donkeyid_next_id(node_id,epoch); 204 | len = sprintf(buffer, "%"PRIu64, donkeyid); 205 | DK_RETURN_STRINGL(buffer, len, 1); 206 | } 207 | PHP_FUNCTION(dk_get_ts_id) 208 | { 209 | char buffer[64]; 210 | int len; 211 | long node_id = DONKEYID_G(dk_node_id); 212 | time_t epoch = DONKEYID_G(dk_epoch); 213 | uint64_t donkeyid = donkeyid_ts_id(node_id,epoch); 214 | len = sprintf(buffer, "%"PRIu64, donkeyid); 215 | DK_RETURN_STRINGL(buffer, len, 1); 216 | } 217 | 218 | PHP_FUNCTION(dk_get_dt_id) 219 | { 220 | char buffer[64]; 221 | int len; 222 | long node_id = DONKEYID_G(dk_node_id); 223 | len = donkeyid_dt_id(node_id,buffer); 224 | DK_RETURN_STRINGL(buffer, len, 1); 225 | } 226 | 227 | PHP_FUNCTION(dk_get_next_ids) 228 | { 229 | char buffer[64]; 230 | uint len; 231 | char *val = 0; 232 | zend_size_t val_len; 233 | long num; 234 | int n; 235 | //获取类方法的参数 236 | if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "l|s",&num, &val, &val_len) == FAILURE) { 237 | return; 238 | } 239 | if(num >= MAX_BATCH_ID_LEN){ 240 | num = MAX_BATCH_ID_LEN; 241 | } else if(num <= 0){ 242 | num = 1; 243 | } 244 | uint64_t *idlist = (uint64_t *)malloc(sizeof(uint64_t)*num); 245 | 246 | long node_id = DONKEYID_G(dk_node_id); 247 | time_t epoch = DONKEYID_G(dk_epoch); 248 | if (val == 0) { 249 | for (n = 0; n < num ; n++) { 250 | *(idlist+n) = donkeyid_next_id(node_id,epoch); 251 | } 252 | } else{ 253 | uint64_t time = strtoul(val, NULL, 10); 254 | if (time == 0){ 255 | RETURN_FALSE; 256 | } 257 | if (donkeyid_get_next_ids(idlist,time,num,node_id,epoch) != 0){ 258 | RETURN_FALSE; 259 | } 260 | } 261 | array_init(return_value); 262 | for (n = 0; n < num ; n++) { 263 | len = (uint)sprintf(buffer, "%"PRIu64, *(idlist+n)); 264 | dk_add_next_index_stringl(return_value,buffer,len,1); 265 | } 266 | free(idlist); 267 | } 268 | 269 | PHP_FUNCTION(dk_get_ts_ids) 270 | { 271 | char buffer[64]; 272 | uint len; 273 | char *val = NULL; 274 | zend_size_t val_len; 275 | long num; 276 | int n; 277 | //获取类方法的参数 278 | if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "l|s",&num, &val, &val_len) == FAILURE) { 279 | return; 280 | } 281 | if(num >= TYPE_1_SEQUENCE_MASK){ 282 | num = TYPE_1_SEQUENCE_MASK; 283 | } else if(num <= 0){ 284 | num = 1; 285 | } 286 | uint64_t *idlist = (uint64_t *)malloc(sizeof(uint64_t)*num); 287 | long node_id = DONKEYID_G(dk_node_id); 288 | time_t epoch = DONKEYID_G(dk_epoch); 289 | 290 | 291 | if (val == 0) { 292 | for (n = 0; n < num ; n++) { 293 | *(idlist+n) = donkeyid_ts_id(node_id,epoch); 294 | } 295 | } else{ 296 | uint64_t time = strtoul(val, NULL, 10); 297 | if (time == 0){ 298 | RETURN_FALSE; 299 | } 300 | if (donkeyid_get_ts_ids(idlist,time,num,node_id,epoch) != 0){ 301 | RETURN_FALSE; 302 | } 303 | } 304 | array_init(return_value); 305 | for (n = 0; n < num ; n++) { 306 | len = (uint)sprintf(buffer, "%"PRIu64, *(idlist+n)); 307 | dk_add_next_index_stringl(return_value,buffer,len,1); 308 | } 309 | free(idlist); 310 | } 311 | PHP_FUNCTION(dk_parse_ts_id) 312 | { 313 | char *val = NULL; 314 | zend_size_t val_len; 315 | char buffer[64]; 316 | int len; 317 | //获取类方法的参数 318 | if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "s", &val, &val_len) == FAILURE) { 319 | return; 320 | } 321 | if (val_len > 20){ 322 | RETURN_FALSE; 323 | } 324 | uint64_t id = strtoul(val, NULL, 10); 325 | if (id == 0) { 326 | RETURN_FALSE; 327 | } 328 | array_init(return_value); 329 | uint64_t time = GET_TIMESTAMP_BY_DONKEY_ID(id, TSTYPE, DONKEYID_G(dk_epoch)); 330 | len = sprintf(buffer, "%"PRIu64, time); 331 | int sequence = GET_SEQUENCE_BY_DONKEY_ID(id, TSTYPE); 332 | int nodeid = GET_NODE_ID_BY_DONKEY_ID(id, TSTYPE); 333 | 334 | dk_add_assoc_stringl_ex(return_value,"time",4,buffer,(uint)len,1); 335 | add_assoc_long_ex(return_value,"node_id",7,nodeid); 336 | add_assoc_long_ex(return_value,"sequence",8,sequence); 337 | } 338 | 339 | PHP_FUNCTION(dk_parse_id) 340 | { 341 | char *val = NULL; 342 | zend_size_t val_len; 343 | char buffer[64]; 344 | int len; 345 | //获取类方法的参数 346 | if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "s", &val, &val_len) == FAILURE) { 347 | return; 348 | } 349 | if (val_len > 20){ 350 | RETURN_FALSE; 351 | } 352 | uint64_t id = strtoul(val, NULL, 10); 353 | if (id == 0) { 354 | RETURN_FALSE; 355 | } 356 | array_init(return_value); 357 | uint64_t time = GET_TIMESTAMP_BY_DONKEY_ID(id, NEXTTYPE, DONKEYID_G(dk_epoch)); 358 | len = sprintf(buffer, "%"PRIu64, time); 359 | int sequence = GET_SEQUENCE_BY_DONKEY_ID(id, NEXTTYPE); 360 | int nodeid = GET_NODE_ID_BY_DONKEY_ID(id, NEXTTYPE); 361 | 362 | dk_add_assoc_stringl_ex(return_value,"time",4,buffer,(uint)len,1); 363 | add_assoc_long_ex(return_value,"node_id",7,nodeid); 364 | add_assoc_long_ex(return_value,"sequence",8,sequence); 365 | 366 | } 367 | 368 | 369 | /* 370 | * Local variables: 371 | * tab-width: 4 372 | * c-basic-offset: 4 373 | * End: 374 | * vim600: noet sw=4 ts=4 fdm=marker 375 | * vim<600: noet sw=4 ts=4 376 | */ 377 | --------------------------------------------------------------------------------