├── data ├── db.c ├── aof.c ├── rdb.c ├── rdb.h ├── config.c ├── config.h ├── multi.c └── replication.c ├── READ_ME.txt ├── event ├── ae.c ├── ae.h ├── ae_epoll.c ├── ae_select.c ├── ae_kqueue.c └── ae_evport.c ├── net ├── anet.c ├── anet.h └── networking.c ├── tool ├── help.h ├── rand.c ├── sha1.c ├── util.c ├── util.h ├── crc64.c ├── crc64.h ├── endianconv.c ├── endianconv.h ├── sha1.h ├── rand.h ├── release.c ├── lzf.h ├── lzf_d.c ├── lzfP.h ├── lzf_c.c ├── bitops.c └── debug.c ├── main ├── redis.c ├── redis.h └── redis-cli.c ├── struct ├── dict.c ├── dict.h ├── sds.c ├── sds.h ├── adlist.c ├── adlist.h ├── t_hash.c ├── t_list.c ├── ziplist.c ├── ziplist.h ├── zipmap.c ├── zipmap.h ├── sparkline.c ├── sparkline.h ├── t_string.c └── t_set.c ├── test ├── memtest.c ├── testhelp.h ├── redis-benchmark.c ├── redis-check-aof.c └── redis-check-dump.c ├── wrapper ├── bio.c ├── bio.h ├── rio.c ├── rio.h ├── notify.c ├── object.c ├── pubsub.c ├── latency.c ├── latency.h ├── slowlog.c ├── slowlog.h ├── zmalloc.c └── zmalloc.h ├── baseinfo ├── version.h └── asciilogo.h ├── .gitattributes ├── .gitignore └── README.md /data/db.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/db.c -------------------------------------------------------------------------------- /READ_ME.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/READ_ME.txt -------------------------------------------------------------------------------- /data/aof.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/aof.c -------------------------------------------------------------------------------- /data/rdb.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/rdb.c -------------------------------------------------------------------------------- /data/rdb.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/rdb.h -------------------------------------------------------------------------------- /event/ae.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/event/ae.c -------------------------------------------------------------------------------- /event/ae.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/event/ae.h -------------------------------------------------------------------------------- /net/anet.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/net/anet.c -------------------------------------------------------------------------------- /net/anet.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/net/anet.h -------------------------------------------------------------------------------- /tool/help.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/help.h -------------------------------------------------------------------------------- /tool/rand.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/rand.c -------------------------------------------------------------------------------- /tool/sha1.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/sha1.c -------------------------------------------------------------------------------- /tool/util.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/util.c -------------------------------------------------------------------------------- /tool/util.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/util.h -------------------------------------------------------------------------------- /data/config.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/config.c -------------------------------------------------------------------------------- /data/config.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/config.h -------------------------------------------------------------------------------- /data/multi.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/multi.c -------------------------------------------------------------------------------- /main/redis.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/main/redis.c -------------------------------------------------------------------------------- /main/redis.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/main/redis.h -------------------------------------------------------------------------------- /struct/dict.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/dict.c -------------------------------------------------------------------------------- /struct/dict.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/dict.h -------------------------------------------------------------------------------- /struct/sds.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/sds.c -------------------------------------------------------------------------------- /struct/sds.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/sds.h -------------------------------------------------------------------------------- /test/memtest.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/test/memtest.c -------------------------------------------------------------------------------- /tool/crc64.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/crc64.c -------------------------------------------------------------------------------- /tool/crc64.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/crc64.h -------------------------------------------------------------------------------- /wrapper/bio.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/bio.c -------------------------------------------------------------------------------- /wrapper/bio.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/bio.h -------------------------------------------------------------------------------- /wrapper/rio.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/rio.c -------------------------------------------------------------------------------- /wrapper/rio.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/rio.h -------------------------------------------------------------------------------- /event/ae_epoll.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/event/ae_epoll.c -------------------------------------------------------------------------------- /main/redis-cli.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/main/redis-cli.c -------------------------------------------------------------------------------- /net/networking.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/net/networking.c -------------------------------------------------------------------------------- /struct/adlist.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/adlist.c -------------------------------------------------------------------------------- /struct/adlist.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/adlist.h -------------------------------------------------------------------------------- /struct/t_hash.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/t_hash.c -------------------------------------------------------------------------------- /struct/t_list.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/t_list.c -------------------------------------------------------------------------------- /struct/ziplist.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/ziplist.c -------------------------------------------------------------------------------- /struct/ziplist.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/ziplist.h -------------------------------------------------------------------------------- /struct/zipmap.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/zipmap.c -------------------------------------------------------------------------------- /struct/zipmap.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/zipmap.h -------------------------------------------------------------------------------- /test/testhelp.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/test/testhelp.h -------------------------------------------------------------------------------- /wrapper/notify.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/notify.c -------------------------------------------------------------------------------- /wrapper/object.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/object.c -------------------------------------------------------------------------------- /wrapper/pubsub.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/pubsub.c -------------------------------------------------------------------------------- /baseinfo/version.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/baseinfo/version.h -------------------------------------------------------------------------------- /data/replication.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/data/replication.c -------------------------------------------------------------------------------- /struct/sparkline.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/sparkline.c -------------------------------------------------------------------------------- /struct/sparkline.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/sparkline.h -------------------------------------------------------------------------------- /struct/t_string.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/struct/t_string.c -------------------------------------------------------------------------------- /tool/endianconv.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/endianconv.c -------------------------------------------------------------------------------- /tool/endianconv.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/tool/endianconv.h -------------------------------------------------------------------------------- /wrapper/latency.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/latency.c -------------------------------------------------------------------------------- /wrapper/latency.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/latency.h -------------------------------------------------------------------------------- /wrapper/slowlog.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/slowlog.c -------------------------------------------------------------------------------- /wrapper/slowlog.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/slowlog.h -------------------------------------------------------------------------------- /wrapper/zmalloc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/zmalloc.c -------------------------------------------------------------------------------- /wrapper/zmalloc.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/wrapper/zmalloc.h -------------------------------------------------------------------------------- /baseinfo/asciilogo.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/baseinfo/asciilogo.h -------------------------------------------------------------------------------- /test/redis-benchmark.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/test/redis-benchmark.c -------------------------------------------------------------------------------- /test/redis-check-aof.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/test/redis-check-aof.c -------------------------------------------------------------------------------- /test/redis-check-dump.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linyiqun/Redis-Code/HEAD/test/redis-check-dump.c -------------------------------------------------------------------------------- /tool/sha1.h: -------------------------------------------------------------------------------- 1 | /* ================ sha1.h ================ */ 2 | /* 3 | SHA-1 in C 4 | By Steve Reid 5 | 100% Public Domain 6 | */ 7 | 8 | typedef struct { 9 | u_int32_t state[5]; 10 | u_int32_t count[2]; 11 | unsigned char buffer[64]; 12 | } SHA1_CTX; 13 | 14 | void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]); 15 | void SHA1Init(SHA1_CTX* context); 16 | void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len); 17 | void SHA1Final(unsigned char digest[20], SHA1_CTX* context); 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must end with two \r 29 | Icon 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /tool/rand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef REDIS_RANDOM_H 31 | #define REDIS_RANDOM_H 32 | 33 | int32_t redisLrand48(); 34 | void redisSrand48(int32_t seedval); 35 | 36 | #define REDIS_LRAND48_MAX INT32_MAX 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /tool/release.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /* Every time the Redis Git SHA1 or Dirty status changes only this small 31 | * file is recompiled, as we access this information in all the other 32 | * files using this functions. */ 33 | 34 | #include 35 | 36 | #include "release.h" 37 | #include "version.h" 38 | #include "crc64.h" 39 | 40 | char *redisGitSHA1(void) { 41 | return REDIS_GIT_SHA1; 42 | } 43 | 44 | char *redisGitDirty(void) { 45 | return REDIS_GIT_DIRTY; 46 | } 47 | 48 | uint64_t redisBuildId(void) { 49 | char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1; 50 | 51 | return crc64(0,(unsigned char*)buildid,strlen(buildid)); 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Redis-Code 2 | ========== 3 | 4 | # redis键值数据库源码分析 5 | ## test:(测试) 6 | * memtest.c 内存检测 7 | * redis_benchmark.c 用于redis性能测试的实现。 8 | * redis_check_aof.c 用于更新日志检查的实现。 9 | * redis_check_dump.c 用于本地数据库检查的实现。 10 | * testhelp.c 一个C风格的小型测试框架。 11 | 12 | # struct:(结构体) 13 | * adlist.c 用于对list的定义,它是个双向链表结构 14 | * dict.c 主要对于内存中的hash进行管理 15 | * sds.c 用于对字符串的定义 16 | * sparkline.c 一个拥有sample列表的序列 17 | * t_hash.c hash在Server/Client中的应答操作。主要通过redisObject进行类型转换。 18 | * t_list.c list在Server/Client中的应答操作。主要通过redisObject进行类型转换。 19 | * t_set.c set在Server/Client中的应答操作。主要通过redisObject进行类型转换。 20 | * t_string.c string在Server/Client中的应答操作。主要通过redisObject进行类型转换。 21 | * t_zset.c zset在Server/Client中的应答操作。主要通过redisObject进行类型转换。 22 | * ziplist.c ziplist是一个类似于list的存储对象。它的原理类似于zipmap。 23 | * zipmap.c zipmap是一个类似于hash的存储对象。 24 | 25 | # data:(数据操作) 26 | * aof.c 全称为append only file,作用就是记录每次的写操作,在遇到断电等问题时可以用它来恢复数据库状态。 27 | * config.c 用于将配置文件redis.conf文件中的配置读取出来的属性通过程序放到server对象中。 28 | * db.c对于Redis内存数据库的相关操作。 29 | * multi.c用于事务处理操作。 30 | * rdb.c 对于Redis本地数据库的相关操作,默认文件是dump.rdb(通过配置文件获得),包括的操作包括保存,移除,查询等等。 31 | * replication.c 用于主从数据库的复制操作的实现。 32 | 33 | # tool:(工具) 34 | * bitops.c 位操作相关类 35 | * debug.c 用于调试时使用 36 | * endianconv.c 高低位转换,不同系统,高低位顺序不同 37 | * help.h 辅助于命令的提示信息 38 | * lzf_c.c 压缩算法系列 39 | * lzf_d.c 压缩算法系列 40 | * rand.c 用于产生随机数 41 | * release.c 用于发步时使用 42 | * sha1.c sha加密算法的实现 43 | * util.c 通用工具方法 44 | * crc64.c 循环冗余校验 45 | 46 | # event:(事件) 47 | * ae.c 用于Redis的事件处理,包括句柄事件和超时事件。 48 | * ae_epoll.c 实现了epoll系统调用的接口 49 | * ae_evport.c 实现了evport系统调用的接口 50 | * ae_kqueue.c 实现了kqueuex系统调用的接口 51 | * ae_select.c 实现了select系统调用的接口 52 | 53 | # baseinfo:(基本信息) 54 | * asciilogo,c redis的logo显示 55 | * version.h定有Redis的版本号 56 | 57 | # compatible:(兼容) 58 | * fmacros.h 兼容Mac系统下的问题 59 | * solarisfixes.h 兼容solary下的问题 60 | 61 | # main:(主程序) 62 | * redis.c redis服务端程序 63 | * redis_cli.c redis客户端程序 64 | 65 | # net:(网络) 66 | * anet.c 作为Server/Client通信的基础封装 67 | * networking.c 网络协议传输方法定义相关的都放在这个文件里面了。 68 | 69 | # wrapper:(封装类) 70 | * bio.c background I/O的意思,开启后台线程用的 71 | * hyperloglog.c 一种用于大数据处理的,基数统计算法 72 | * intset.c 整数范围内的使用set,并包含相关set操作。 73 | * latency.c 延迟类 74 | * migrate.c 命令迁移类,包括命令的还原迁移等 75 | * notify.c 通知类 76 | * object.c 用于创建和释放redisObject对象 77 | * pqsort.c 排序算法类 78 | * pubsub.c 用于订阅模式的实现,有点类似于Client广播发送的方式。 79 | * rio.c redis定义的一个I/O类 80 | * slowlog.c 一种日志类型的,记录超时查询记录 81 | * sort.c 排序算法类,与pqsort.c使用的场景不同 82 | * syncio.c 用于同步Socket和文件I/O操作。 83 | * zmalloc.c 关于Redis的内存分配的封装实现 84 | 85 | # others:(存放了一些我暂时还不是很清楚的类,所以没有解释了) 86 | * scripting.c 87 | * sentinel.c 88 | * setproctitle.c 89 | * valgrind.sh 90 | * redisassert.h 91 | 92 | ## Redis中的11大优秀设计: 93 | * [hyperloglog基量统计算法的实现](http://blog.csdn.net/androidlushangderen/article/details/40683763) 94 | * [zmalloc内存分配的重新实现](http://blog.csdn.net/androidlushangderen/article/details/40659331) 95 | * [multi事务操作](http://blog.csdn.net/androidlushangderen/article/details/40392209) 96 | * [redis-benchmark性能测试](http://blog.csdn.net/androidlushangderen/article/details/40211907) 97 | * [zipmap压缩结构的设计](http://blog.csdn.net/androidlushangderen/article/details/39994599) 98 | * [sparkline微线图的重新设计](http://blog.csdn.net/androidlushangderen/article/details/39964591) 99 | * [对象引用计数实现内存管理](http://blog.csdn.net/androidlushangderen/article/details/40716469) 100 | * [fork子进程实现后台程序](http://blog.csdn.net/androidlushangderen/article/details/40266579) 101 | * [long long 类型转为String类型方法](http://blog.csdn.net/androidlushangderen/article/details/40649623) 102 | * [正则表达式的实现算法](http://blog.csdn.net/androidlushangderen/article/details/40649623) 103 | * [Redis的drand48()随机算法重实现](http://blog.csdn.net/androidlushangderen/article/details/40582189) 104 | -------------------------------------------------------------------------------- /event/ae_select.c: -------------------------------------------------------------------------------- 1 | /* Select()-based ae.c module. 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | 34 | typedef struct aeApiState { 35 | fd_set rfds, wfds; 36 | /* We need to have a copy of the fd sets as it's not safe to reuse 37 | * FD sets after select(). */ 38 | fd_set _rfds, _wfds; 39 | } aeApiState; 40 | 41 | static int aeApiCreate(aeEventLoop *eventLoop) { 42 | aeApiState *state = zmalloc(sizeof(aeApiState)); 43 | 44 | if (!state) return -1; 45 | FD_ZERO(&state->rfds); 46 | FD_ZERO(&state->wfds); 47 | eventLoop->apidata = state; 48 | return 0; 49 | } 50 | 51 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 52 | /* Just ensure we have enough room in the fd_set type. */ 53 | if (setsize >= FD_SETSIZE) return -1; 54 | return 0; 55 | } 56 | 57 | static void aeApiFree(aeEventLoop *eventLoop) { 58 | zfree(eventLoop->apidata); 59 | } 60 | 61 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 62 | aeApiState *state = eventLoop->apidata; 63 | 64 | if (mask & AE_READABLE) FD_SET(fd,&state->rfds); 65 | if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds); 66 | return 0; 67 | } 68 | 69 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 70 | aeApiState *state = eventLoop->apidata; 71 | 72 | if (mask & AE_READABLE) FD_CLR(fd,&state->rfds); 73 | if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds); 74 | } 75 | 76 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 77 | aeApiState *state = eventLoop->apidata; 78 | int retval, j, numevents = 0; 79 | 80 | memcpy(&state->_rfds,&state->rfds,sizeof(fd_set)); 81 | memcpy(&state->_wfds,&state->wfds,sizeof(fd_set)); 82 | 83 | retval = select(eventLoop->maxfd+1, 84 | &state->_rfds,&state->_wfds,NULL,tvp); 85 | if (retval > 0) { 86 | for (j = 0; j <= eventLoop->maxfd; j++) { 87 | int mask = 0; 88 | aeFileEvent *fe = &eventLoop->events[j]; 89 | 90 | if (fe->mask == AE_NONE) continue; 91 | if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds)) 92 | mask |= AE_READABLE; 93 | if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds)) 94 | mask |= AE_WRITABLE; 95 | eventLoop->fired[numevents].fd = j; 96 | eventLoop->fired[numevents].mask = mask; 97 | numevents++; 98 | } 99 | } 100 | return numevents; 101 | } 102 | 103 | static char *aeApiName(void) { 104 | return "select"; 105 | } 106 | -------------------------------------------------------------------------------- /tool/lzf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2008 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | */ 36 | 37 | #ifndef LZF_H 38 | #define LZF_H 39 | 40 | /*********************************************************************** 41 | ** 42 | ** lzf -- an extremely fast/free compression/decompression-method 43 | ** http://liblzf.plan9.de/ 44 | ** 45 | ** This algorithm is believed to be patent-free. 46 | ** 47 | ***********************************************************************/ 48 | 49 | #define LZF_VERSION 0x0105 /* 1.5, API version */ 50 | 51 | /* 52 | * Compress in_len bytes stored at the memory block starting at 53 | * in_data and write the result to out_data, up to a maximum length 54 | * of out_len bytes. 55 | * 56 | * If the output buffer is not large enough or any error occurs return 0, 57 | * otherwise return the number of bytes used, which might be considerably 58 | * more than in_len (but less than 104% of the original size), so it 59 | * makes sense to always use out_len == in_len - 1), to ensure _some_ 60 | * compression, and store the data uncompressed otherwise (with a flag, of 61 | * course. 62 | * 63 | * lzf_compress might use different algorithms on different systems and 64 | * even different runs, thus might result in different compressed strings 65 | * depending on the phase of the moon or similar factors. However, all 66 | * these strings are architecture-independent and will result in the 67 | * original data when decompressed using lzf_decompress. 68 | * 69 | * The buffers must not be overlapping. 70 | * 71 | * If the option LZF_STATE_ARG is enabled, an extra argument must be 72 | * supplied which is not reflected in this header file. Refer to lzfP.h 73 | * and lzf_c.c. 74 | * 75 | */ 76 | unsigned int 77 | lzf_compress (const void *const in_data, unsigned int in_len, 78 | void *out_data, unsigned int out_len); 79 | 80 | /* 81 | * Decompress data compressed with some version of the lzf_compress 82 | * function and stored at location in_data and length in_len. The result 83 | * will be stored at out_data up to a maximum of out_len characters. 84 | * 85 | * If the output buffer is not large enough to hold the decompressed 86 | * data, a 0 is returned and errno is set to E2BIG. Otherwise the number 87 | * of decompressed bytes (i.e. the original length of the data) is 88 | * returned. 89 | * 90 | * If an error in the compressed data is detected, a zero is returned and 91 | * errno is set to EINVAL. 92 | * 93 | * This function is very fast, about as fast as a copying loop. 94 | */ 95 | unsigned int 96 | lzf_decompress (const void *const in_data, unsigned int in_len, 97 | void *out_data, unsigned int out_len); 98 | 99 | #endif 100 | 101 | -------------------------------------------------------------------------------- /tool/lzf_d.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2007 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | */ 36 | 37 | #include "lzfP.h" 38 | 39 | #if AVOID_ERRNO 40 | # define SET_ERRNO(n) 41 | #else 42 | # include 43 | # define SET_ERRNO(n) errno = (n) 44 | #endif 45 | 46 | /* 47 | #if (__i386 || __amd64) && __GNUC__ >= 3 48 | # define lzf_movsb(dst, src, len) \ 49 | asm ("rep movsb" \ 50 | : "=D" (dst), "=S" (src), "=c" (len) \ 51 | : "0" (dst), "1" (src), "2" (len)); 52 | #endif 53 | */ 54 | 55 | unsigned int 56 | lzf_decompress (const void *const in_data, unsigned int in_len, 57 | void *out_data, unsigned int out_len) 58 | { 59 | u8 const *ip = (const u8 *)in_data; 60 | u8 *op = (u8 *)out_data; 61 | u8 const *const in_end = ip + in_len; 62 | u8 *const out_end = op + out_len; 63 | 64 | do 65 | { 66 | unsigned int ctrl = *ip++; 67 | 68 | if (ctrl < (1 << 5)) /* literal run */ 69 | { 70 | ctrl++; 71 | 72 | if (op + ctrl > out_end) 73 | { 74 | SET_ERRNO (E2BIG); 75 | return 0; 76 | } 77 | 78 | #if CHECK_INPUT 79 | if (ip + ctrl > in_end) 80 | { 81 | SET_ERRNO (EINVAL); 82 | return 0; 83 | } 84 | #endif 85 | 86 | #ifdef lzf_movsb 87 | lzf_movsb (op, ip, ctrl); 88 | #else 89 | do 90 | *op++ = *ip++; 91 | while (--ctrl); 92 | #endif 93 | } 94 | else /* back reference */ 95 | { 96 | unsigned int len = ctrl >> 5; 97 | 98 | u8 *ref = op - ((ctrl & 0x1f) << 8) - 1; 99 | 100 | #if CHECK_INPUT 101 | if (ip >= in_end) 102 | { 103 | SET_ERRNO (EINVAL); 104 | return 0; 105 | } 106 | #endif 107 | if (len == 7) 108 | { 109 | len += *ip++; 110 | #if CHECK_INPUT 111 | if (ip >= in_end) 112 | { 113 | SET_ERRNO (EINVAL); 114 | return 0; 115 | } 116 | #endif 117 | } 118 | 119 | ref -= *ip++; 120 | 121 | if (op + len + 2 > out_end) 122 | { 123 | SET_ERRNO (E2BIG); 124 | return 0; 125 | } 126 | 127 | if (ref < (u8 *)out_data) 128 | { 129 | SET_ERRNO (EINVAL); 130 | return 0; 131 | } 132 | 133 | #ifdef lzf_movsb 134 | len += 2; 135 | lzf_movsb (op, ref, len); 136 | #else 137 | *op++ = *ref++; 138 | *op++ = *ref++; 139 | 140 | do 141 | *op++ = *ref++; 142 | while (--len); 143 | #endif 144 | } 145 | } 146 | while (ip < in_end); 147 | 148 | return op - (u8 *)out_data; 149 | } 150 | 151 | -------------------------------------------------------------------------------- /event/ae_kqueue.c: -------------------------------------------------------------------------------- 1 | /* Kqueue(2)-based ae.c module 2 | * 3 | * Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | typedef struct aeApiState { 37 | int kqfd; 38 | struct kevent *events; 39 | } aeApiState; 40 | 41 | static int aeApiCreate(aeEventLoop *eventLoop) { 42 | aeApiState *state = zmalloc(sizeof(aeApiState)); 43 | 44 | if (!state) return -1; 45 | state->events = zmalloc(sizeof(struct kevent)*eventLoop->setsize); 46 | if (!state->events) { 47 | zfree(state); 48 | return -1; 49 | } 50 | state->kqfd = kqueue(); 51 | if (state->kqfd == -1) { 52 | zfree(state->events); 53 | zfree(state); 54 | return -1; 55 | } 56 | eventLoop->apidata = state; 57 | return 0; 58 | } 59 | 60 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 61 | aeApiState *state = eventLoop->apidata; 62 | 63 | state->events = zrealloc(state->events, sizeof(struct kevent)*setsize); 64 | return 0; 65 | } 66 | 67 | static void aeApiFree(aeEventLoop *eventLoop) { 68 | aeApiState *state = eventLoop->apidata; 69 | 70 | close(state->kqfd); 71 | zfree(state->events); 72 | zfree(state); 73 | } 74 | 75 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 76 | aeApiState *state = eventLoop->apidata; 77 | struct kevent ke; 78 | 79 | if (mask & AE_READABLE) { 80 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 81 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 82 | } 83 | if (mask & AE_WRITABLE) { 84 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 85 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 91 | aeApiState *state = eventLoop->apidata; 92 | struct kevent ke; 93 | 94 | if (mask & AE_READABLE) { 95 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 96 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 97 | } 98 | if (mask & AE_WRITABLE) { 99 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 100 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 101 | } 102 | } 103 | 104 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 105 | aeApiState *state = eventLoop->apidata; 106 | int retval, numevents = 0; 107 | 108 | if (tvp != NULL) { 109 | struct timespec timeout; 110 | timeout.tv_sec = tvp->tv_sec; 111 | timeout.tv_nsec = tvp->tv_usec * 1000; 112 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 113 | &timeout); 114 | } else { 115 | retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->setsize, 116 | NULL); 117 | } 118 | 119 | if (retval > 0) { 120 | int j; 121 | 122 | numevents = retval; 123 | for(j = 0; j < numevents; j++) { 124 | int mask = 0; 125 | struct kevent *e = state->events+j; 126 | 127 | if (e->filter == EVFILT_READ) mask |= AE_READABLE; 128 | if (e->filter == EVFILT_WRITE) mask |= AE_WRITABLE; 129 | eventLoop->fired[j].fd = e->ident; 130 | eventLoop->fired[j].mask = mask; 131 | } 132 | } 133 | return numevents; 134 | } 135 | 136 | static char *aeApiName(void) { 137 | return "kqueue"; 138 | } 139 | -------------------------------------------------------------------------------- /tool/lzfP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2007 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | */ 36 | 37 | #ifndef LZFP_h 38 | #define LZFP_h 39 | 40 | #define STANDALONE 1 /* at the moment, this is ok. */ 41 | 42 | #ifndef STANDALONE 43 | # include "lzf.h" 44 | #endif 45 | 46 | /* 47 | * Size of hashtable is (1 << HLOG) * sizeof (char *) 48 | * decompression is independent of the hash table size 49 | * the difference between 15 and 14 is very small 50 | * for small blocks (and 14 is usually a bit faster). 51 | * For a low-memory/faster configuration, use HLOG == 13; 52 | * For best compression, use 15 or 16 (or more, up to 23). 53 | */ 54 | #ifndef HLOG 55 | # define HLOG 16 56 | #endif 57 | 58 | /* 59 | * Sacrifice very little compression quality in favour of compression speed. 60 | * This gives almost the same compression as the default code, and is 61 | * (very roughly) 15% faster. This is the preferred mode of operation. 62 | */ 63 | #ifndef VERY_FAST 64 | # define VERY_FAST 1 65 | #endif 66 | 67 | /* 68 | * Sacrifice some more compression quality in favour of compression speed. 69 | * (roughly 1-2% worse compression for large blocks and 70 | * 9-10% for small, redundant, blocks and >>20% better speed in both cases) 71 | * In short: when in need for speed, enable this for binary data, 72 | * possibly disable this for text data. 73 | */ 74 | #ifndef ULTRA_FAST 75 | # define ULTRA_FAST 0 76 | #endif 77 | 78 | /* 79 | * Unconditionally aligning does not cost very much, so do it if unsure 80 | */ 81 | #ifndef STRICT_ALIGN 82 | # define STRICT_ALIGN !(defined(__i386) || defined (__amd64)) 83 | #endif 84 | 85 | /* 86 | * You may choose to pre-set the hash table (might be faster on some 87 | * modern cpus and large (>>64k) blocks, and also makes compression 88 | * deterministic/repeatable when the configuration otherwise is the same). 89 | */ 90 | #ifndef INIT_HTAB 91 | # define INIT_HTAB 0 92 | #endif 93 | 94 | /* 95 | * Avoid assigning values to errno variable? for some embedding purposes 96 | * (linux kernel for example), this is necessary. NOTE: this breaks 97 | * the documentation in lzf.h. 98 | */ 99 | #ifndef AVOID_ERRNO 100 | # define AVOID_ERRNO 0 101 | #endif 102 | 103 | /* 104 | * Whether to pass the LZF_STATE variable as argument, or allocate it 105 | * on the stack. For small-stack environments, define this to 1. 106 | * NOTE: this breaks the prototype in lzf.h. 107 | */ 108 | #ifndef LZF_STATE_ARG 109 | # define LZF_STATE_ARG 0 110 | #endif 111 | 112 | /* 113 | * Whether to add extra checks for input validity in lzf_decompress 114 | * and return EINVAL if the input stream has been corrupted. This 115 | * only shields against overflowing the input buffer and will not 116 | * detect most corrupted streams. 117 | * This check is not normally noticeable on modern hardware 118 | * (<1% slowdown), but might slow down older cpus considerably. 119 | */ 120 | #ifndef CHECK_INPUT 121 | # define CHECK_INPUT 1 122 | #endif 123 | 124 | /*****************************************************************************/ 125 | /* nothing should be changed below */ 126 | 127 | typedef unsigned char u8; 128 | 129 | typedef const u8 *LZF_STATE[1 << (HLOG)]; 130 | 131 | #if !STRICT_ALIGN 132 | /* for unaligned accesses we need a 16 bit datatype. */ 133 | # include 134 | # if USHRT_MAX == 65535 135 | typedef unsigned short u16; 136 | # elif UINT_MAX == 65535 137 | typedef unsigned int u16; 138 | # else 139 | # undef STRICT_ALIGN 140 | # define STRICT_ALIGN 1 141 | # endif 142 | #endif 143 | 144 | #if ULTRA_FAST 145 | # if defined(VERY_FAST) 146 | # undef VERY_FAST 147 | # endif 148 | #endif 149 | 150 | #if INIT_HTAB 151 | # ifdef __cplusplus 152 | # include 153 | # else 154 | # include 155 | # endif 156 | #endif 157 | 158 | #endif 159 | 160 | -------------------------------------------------------------------------------- /tool/lzf_c.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000-2008 Marc Alexander Lehmann 3 | * 4 | * Redistribution and use in source and binary forms, with or without modifica- 5 | * tion, are permitted provided that the following conditions are met: 6 | * 7 | * 1. Redistributions of source code must retain the above copyright notice, 8 | * this list of conditions and the following disclaimer. 9 | * 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 16 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 17 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 18 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 22 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 23 | * OF THE POSSIBILITY OF SUCH DAMAGE. 24 | * 25 | * Alternatively, the contents of this file may be used under the terms of 26 | * the GNU General Public License ("GPL") version 2 or any later version, 27 | * in which case the provisions of the GPL are applicable instead of 28 | * the above. If you wish to allow the use of your version of this file 29 | * only under the terms of the GPL and not to allow others to use your 30 | * version of this file under the BSD license, indicate your decision 31 | * by deleting the provisions above and replace them with the notice 32 | * and other provisions required by the GPL. If you do not delete the 33 | * provisions above, a recipient may use your version of this file under 34 | * either the BSD or the GPL. 35 | */ 36 | 37 | #include "lzfP.h" 38 | 39 | #define HSIZE (1 << (HLOG)) 40 | 41 | /* 42 | * don't play with this unless you benchmark! 43 | * decompression is not dependent on the hash function 44 | * the hashing function might seem strange, just believe me 45 | * it works ;) 46 | */ 47 | #ifndef FRST 48 | # define FRST(p) (((p[0]) << 8) | p[1]) 49 | # define NEXT(v,p) (((v) << 8) | p[2]) 50 | # if ULTRA_FAST 51 | # define IDX(h) ((( h >> (3*8 - HLOG)) - h ) & (HSIZE - 1)) 52 | # elif VERY_FAST 53 | # define IDX(h) ((( h >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) 54 | # else 55 | # define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) 56 | # endif 57 | #endif 58 | /* 59 | * IDX works because it is very similar to a multiplicative hash, e.g. 60 | * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1)) 61 | * the latter is also quite fast on newer CPUs, and compresses similarly. 62 | * 63 | * the next one is also quite good, albeit slow ;) 64 | * (int)(cos(h & 0xffffff) * 1e6) 65 | */ 66 | 67 | #if 0 68 | /* original lzv-like hash function, much worse and thus slower */ 69 | # define FRST(p) (p[0] << 5) ^ p[1] 70 | # define NEXT(v,p) ((v) << 5) ^ p[2] 71 | # define IDX(h) ((h) & (HSIZE - 1)) 72 | #endif 73 | 74 | #define MAX_LIT (1 << 5) 75 | #define MAX_OFF (1 << 13) 76 | #define MAX_REF ((1 << 8) + (1 << 3)) 77 | 78 | #if __GNUC__ >= 3 79 | # define expect(expr,value) __builtin_expect ((expr),(value)) 80 | # define inline inline 81 | #else 82 | # define expect(expr,value) (expr) 83 | # define inline static 84 | #endif 85 | 86 | #define expect_false(expr) expect ((expr) != 0, 0) 87 | #define expect_true(expr) expect ((expr) != 0, 1) 88 | 89 | /* 90 | * compressed format 91 | * 92 | * 000LLLLL ; literal 93 | * LLLooooo oooooooo ; backref L 94 | * 111ooooo LLLLLLLL oooooooo ; backref L+7 95 | * 96 | */ 97 | 98 | unsigned int 99 | lzf_compress (const void *const in_data, unsigned int in_len, 100 | void *out_data, unsigned int out_len 101 | #if LZF_STATE_ARG 102 | , LZF_STATE htab 103 | #endif 104 | ) 105 | { 106 | #if !LZF_STATE_ARG 107 | LZF_STATE htab; 108 | #endif 109 | const u8 **hslot; 110 | const u8 *ip = (const u8 *)in_data; 111 | u8 *op = (u8 *)out_data; 112 | const u8 *in_end = ip + in_len; 113 | u8 *out_end = op + out_len; 114 | const u8 *ref; 115 | 116 | /* off requires a type wide enough to hold a general pointer difference. 117 | * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only 118 | * works for differences within a single object). We also assume that no 119 | * no bit pattern traps. Since the only platform that is both non-POSIX 120 | * and fails to support both assumptions is windows 64 bit, we make a 121 | * special workaround for it. 122 | */ 123 | #if defined (WIN32) && defined (_M_X64) 124 | unsigned _int64 off; /* workaround for missing POSIX compliance */ 125 | #else 126 | unsigned long off; 127 | #endif 128 | unsigned int hval; 129 | int lit; 130 | 131 | if (!in_len || !out_len) 132 | return 0; 133 | 134 | #if INIT_HTAB 135 | memset (htab, 0, sizeof (htab)); 136 | # if 0 137 | for (hslot = htab; hslot < htab + HSIZE; hslot++) 138 | *hslot++ = ip; 139 | # endif 140 | #endif 141 | 142 | lit = 0; op++; /* start run */ 143 | 144 | hval = FRST (ip); 145 | while (ip < in_end - 2) 146 | { 147 | hval = NEXT (hval, ip); 148 | hslot = htab + IDX (hval); 149 | ref = *hslot; *hslot = ip; 150 | 151 | if (1 152 | #if INIT_HTAB 153 | && ref < ip /* the next test will actually take care of this, but this is faster */ 154 | #endif 155 | && (off = ip - ref - 1) < MAX_OFF 156 | && ip + 4 < in_end 157 | && ref > (u8 *)in_data 158 | #if STRICT_ALIGN 159 | && ref[0] == ip[0] 160 | && ref[1] == ip[1] 161 | && ref[2] == ip[2] 162 | #else 163 | && *(u16 *)ref == *(u16 *)ip 164 | && ref[2] == ip[2] 165 | #endif 166 | ) 167 | { 168 | /* match found at *ref++ */ 169 | unsigned int len = 2; 170 | unsigned int maxlen = in_end - ip - len; 171 | maxlen = maxlen > MAX_REF ? MAX_REF : maxlen; 172 | 173 | op [- lit - 1] = lit - 1; /* stop run */ 174 | op -= !lit; /* undo run if length is zero */ 175 | 176 | if (expect_false (op + 3 + 1 >= out_end)) 177 | return 0; 178 | 179 | for (;;) 180 | { 181 | if (expect_true (maxlen > 16)) 182 | { 183 | len++; if (ref [len] != ip [len]) break; 184 | len++; if (ref [len] != ip [len]) break; 185 | len++; if (ref [len] != ip [len]) break; 186 | len++; if (ref [len] != ip [len]) break; 187 | 188 | len++; if (ref [len] != ip [len]) break; 189 | len++; if (ref [len] != ip [len]) break; 190 | len++; if (ref [len] != ip [len]) break; 191 | len++; if (ref [len] != ip [len]) break; 192 | 193 | len++; if (ref [len] != ip [len]) break; 194 | len++; if (ref [len] != ip [len]) break; 195 | len++; if (ref [len] != ip [len]) break; 196 | len++; if (ref [len] != ip [len]) break; 197 | 198 | len++; if (ref [len] != ip [len]) break; 199 | len++; if (ref [len] != ip [len]) break; 200 | len++; if (ref [len] != ip [len]) break; 201 | len++; if (ref [len] != ip [len]) break; 202 | } 203 | 204 | do 205 | len++; 206 | while (len < maxlen && ref[len] == ip[len]); 207 | 208 | break; 209 | } 210 | 211 | len -= 2; /* len is now #octets - 1 */ 212 | ip++; 213 | 214 | if (len < 7) 215 | { 216 | *op++ = (off >> 8) + (len << 5); 217 | } 218 | else 219 | { 220 | *op++ = (off >> 8) + ( 7 << 5); 221 | *op++ = len - 7; 222 | } 223 | 224 | *op++ = off; 225 | lit = 0; op++; /* start run */ 226 | 227 | ip += len + 1; 228 | 229 | if (expect_false (ip >= in_end - 2)) 230 | break; 231 | 232 | #if ULTRA_FAST || VERY_FAST 233 | --ip; 234 | # if VERY_FAST && !ULTRA_FAST 235 | --ip; 236 | # endif 237 | hval = FRST (ip); 238 | 239 | hval = NEXT (hval, ip); 240 | htab[IDX (hval)] = ip; 241 | ip++; 242 | 243 | # if VERY_FAST && !ULTRA_FAST 244 | hval = NEXT (hval, ip); 245 | htab[IDX (hval)] = ip; 246 | ip++; 247 | # endif 248 | #else 249 | ip -= len + 1; 250 | 251 | do 252 | { 253 | hval = NEXT (hval, ip); 254 | htab[IDX (hval)] = ip; 255 | ip++; 256 | } 257 | while (len--); 258 | #endif 259 | } 260 | else 261 | { 262 | /* one more literal byte we must copy */ 263 | if (expect_false (op >= out_end)) 264 | return 0; 265 | 266 | lit++; *op++ = *ip++; 267 | 268 | if (expect_false (lit == MAX_LIT)) 269 | { 270 | op [- lit - 1] = lit - 1; /* stop run */ 271 | lit = 0; op++; /* start run */ 272 | } 273 | } 274 | } 275 | 276 | if (op + 3 > out_end) /* at most 3 bytes can be missing here */ 277 | return 0; 278 | 279 | while (ip < in_end) 280 | { 281 | lit++; *op++ = *ip++; 282 | 283 | if (expect_false (lit == MAX_LIT)) 284 | { 285 | op [- lit - 1] = lit - 1; /* stop run */ 286 | lit = 0; op++; /* start run */ 287 | } 288 | } 289 | 290 | op [- lit - 1] = lit - 1; /* end run */ 291 | op -= !lit; /* undo run if length is zero */ 292 | 293 | return op - (u8 *)out_data; 294 | } 295 | 296 | -------------------------------------------------------------------------------- /event/ae_evport.c: -------------------------------------------------------------------------------- 1 | /* ae.c module for illumos event ports. 2 | * 3 | * Copyright (c) 2012, Joyent, Inc. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | static int evport_debug = 0; 42 | 43 | /* 44 | * This file implements the ae API using event ports, present on Solaris-based 45 | * systems since Solaris 10. Using the event port interface, we associate file 46 | * descriptors with the port. Each association also includes the set of poll(2) 47 | * events that the consumer is interested in (e.g., POLLIN and POLLOUT). 48 | * 49 | * There's one tricky piece to this implementation: when we return events via 50 | * aeApiPoll, the corresponding file descriptors become dissociated from the 51 | * port. This is necessary because poll events are level-triggered, so if the 52 | * fd didn't become dissociated, it would immediately fire another event since 53 | * the underlying state hasn't changed yet. We must re-associate the file 54 | * descriptor, but only after we know that our caller has actually read from it. 55 | * The ae API does not tell us exactly when that happens, but we do know that 56 | * it must happen by the time aeApiPoll is called again. Our solution is to 57 | * keep track of the last fds returned by aeApiPoll and re-associate them next 58 | * time aeApiPoll is invoked. 59 | * 60 | * To summarize, in this module, each fd association is EITHER (a) represented 61 | * only via the in-kernel association OR (b) represented by pending_fds and 62 | * pending_masks. (b) is only true for the last fds we returned from aeApiPoll, 63 | * and only until we enter aeApiPoll again (at which point we restore the 64 | * in-kernel association). 65 | */ 66 | #define MAX_EVENT_BATCHSZ 512 67 | 68 | typedef struct aeApiState { 69 | int portfd; /* event port */ 70 | int npending; /* # of pending fds */ 71 | int pending_fds[MAX_EVENT_BATCHSZ]; /* pending fds */ 72 | int pending_masks[MAX_EVENT_BATCHSZ]; /* pending fds' masks */ 73 | } aeApiState; 74 | 75 | static int aeApiCreate(aeEventLoop *eventLoop) { 76 | int i; 77 | aeApiState *state = zmalloc(sizeof(aeApiState)); 78 | if (!state) return -1; 79 | 80 | state->portfd = port_create(); 81 | if (state->portfd == -1) { 82 | zfree(state); 83 | return -1; 84 | } 85 | 86 | state->npending = 0; 87 | 88 | for (i = 0; i < MAX_EVENT_BATCHSZ; i++) { 89 | state->pending_fds[i] = -1; 90 | state->pending_masks[i] = AE_NONE; 91 | } 92 | 93 | eventLoop->apidata = state; 94 | return 0; 95 | } 96 | 97 | static int aeApiResize(aeEventLoop *eventLoop, int setsize) { 98 | /* Nothing to resize here. */ 99 | return 0; 100 | } 101 | 102 | static void aeApiFree(aeEventLoop *eventLoop) { 103 | aeApiState *state = eventLoop->apidata; 104 | 105 | close(state->portfd); 106 | zfree(state); 107 | } 108 | 109 | static int aeApiLookupPending(aeApiState *state, int fd) { 110 | int i; 111 | 112 | for (i = 0; i < state->npending; i++) { 113 | if (state->pending_fds[i] == fd) 114 | return (i); 115 | } 116 | 117 | return (-1); 118 | } 119 | 120 | /* 121 | * Helper function to invoke port_associate for the given fd and mask. 122 | */ 123 | static int aeApiAssociate(const char *where, int portfd, int fd, int mask) { 124 | int events = 0; 125 | int rv, err; 126 | 127 | if (mask & AE_READABLE) 128 | events |= POLLIN; 129 | if (mask & AE_WRITABLE) 130 | events |= POLLOUT; 131 | 132 | if (evport_debug) 133 | fprintf(stderr, "%s: port_associate(%d, 0x%x) = ", where, fd, events); 134 | 135 | rv = port_associate(portfd, PORT_SOURCE_FD, fd, events, 136 | (void *)(uintptr_t)mask); 137 | err = errno; 138 | 139 | if (evport_debug) 140 | fprintf(stderr, "%d (%s)\n", rv, rv == 0 ? "no error" : strerror(err)); 141 | 142 | if (rv == -1) { 143 | fprintf(stderr, "%s: port_associate: %s\n", where, strerror(err)); 144 | 145 | if (err == EAGAIN) 146 | fprintf(stderr, "aeApiAssociate: event port limit exceeded."); 147 | } 148 | 149 | return rv; 150 | } 151 | 152 | static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { 153 | aeApiState *state = eventLoop->apidata; 154 | int fullmask, pfd; 155 | 156 | if (evport_debug) 157 | fprintf(stderr, "aeApiAddEvent: fd %d mask 0x%x\n", fd, mask); 158 | 159 | /* 160 | * Since port_associate's "events" argument replaces any existing events, we 161 | * must be sure to include whatever events are already associated when 162 | * we call port_associate() again. 163 | */ 164 | fullmask = mask | eventLoop->events[fd].mask; 165 | pfd = aeApiLookupPending(state, fd); 166 | 167 | if (pfd != -1) { 168 | /* 169 | * This fd was recently returned from aeApiPoll. It should be safe to 170 | * assume that the consumer has processed that poll event, but we play 171 | * it safer by simply updating pending_mask. The fd will be 172 | * re-associated as usual when aeApiPoll is called again. 173 | */ 174 | if (evport_debug) 175 | fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd); 176 | state->pending_masks[pfd] |= fullmask; 177 | return 0; 178 | } 179 | 180 | return (aeApiAssociate("aeApiAddEvent", state->portfd, fd, fullmask)); 181 | } 182 | 183 | static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { 184 | aeApiState *state = eventLoop->apidata; 185 | int fullmask, pfd; 186 | 187 | if (evport_debug) 188 | fprintf(stderr, "del fd %d mask 0x%x\n", fd, mask); 189 | 190 | pfd = aeApiLookupPending(state, fd); 191 | 192 | if (pfd != -1) { 193 | if (evport_debug) 194 | fprintf(stderr, "deleting event from pending fd %d\n", fd); 195 | 196 | /* 197 | * This fd was just returned from aeApiPoll, so it's not currently 198 | * associated with the port. All we need to do is update 199 | * pending_mask appropriately. 200 | */ 201 | state->pending_masks[pfd] &= ~mask; 202 | 203 | if (state->pending_masks[pfd] == AE_NONE) 204 | state->pending_fds[pfd] = -1; 205 | 206 | return; 207 | } 208 | 209 | /* 210 | * The fd is currently associated with the port. Like with the add case 211 | * above, we must look at the full mask for the file descriptor before 212 | * updating that association. We don't have a good way of knowing what the 213 | * events are without looking into the eventLoop state directly. We rely on 214 | * the fact that our caller has already updated the mask in the eventLoop. 215 | */ 216 | 217 | fullmask = eventLoop->events[fd].mask; 218 | if (fullmask == AE_NONE) { 219 | /* 220 | * We're removing *all* events, so use port_dissociate to remove the 221 | * association completely. Failure here indicates a bug. 222 | */ 223 | if (evport_debug) 224 | fprintf(stderr, "aeApiDelEvent: port_dissociate(%d)\n", fd); 225 | 226 | if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) { 227 | perror("aeApiDelEvent: port_dissociate"); 228 | abort(); /* will not return */ 229 | } 230 | } else if (aeApiAssociate("aeApiDelEvent", state->portfd, fd, 231 | fullmask) != 0) { 232 | /* 233 | * ENOMEM is a potentially transient condition, but the kernel won't 234 | * generally return it unless things are really bad. EAGAIN indicates 235 | * we've reached an resource limit, for which it doesn't make sense to 236 | * retry (counter-intuitively). All other errors indicate a bug. In any 237 | * of these cases, the best we can do is to abort. 238 | */ 239 | abort(); /* will not return */ 240 | } 241 | } 242 | 243 | static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { 244 | aeApiState *state = eventLoop->apidata; 245 | struct timespec timeout, *tsp; 246 | int mask, i; 247 | uint_t nevents; 248 | port_event_t event[MAX_EVENT_BATCHSZ]; 249 | 250 | /* 251 | * If we've returned fd events before, we must re-associate them with the 252 | * port now, before calling port_get(). See the block comment at the top of 253 | * this file for an explanation of why. 254 | */ 255 | for (i = 0; i < state->npending; i++) { 256 | if (state->pending_fds[i] == -1) 257 | /* This fd has since been deleted. */ 258 | continue; 259 | 260 | if (aeApiAssociate("aeApiPoll", state->portfd, 261 | state->pending_fds[i], state->pending_masks[i]) != 0) { 262 | /* See aeApiDelEvent for why this case is fatal. */ 263 | abort(); 264 | } 265 | 266 | state->pending_masks[i] = AE_NONE; 267 | state->pending_fds[i] = -1; 268 | } 269 | 270 | state->npending = 0; 271 | 272 | if (tvp != NULL) { 273 | timeout.tv_sec = tvp->tv_sec; 274 | timeout.tv_nsec = tvp->tv_usec * 1000; 275 | tsp = &timeout; 276 | } else { 277 | tsp = NULL; 278 | } 279 | 280 | /* 281 | * port_getn can return with errno == ETIME having returned some events (!). 282 | * So if we get ETIME, we check nevents, too. 283 | */ 284 | nevents = 1; 285 | if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents, 286 | tsp) == -1 && (errno != ETIME || nevents == 0)) { 287 | if (errno == ETIME || errno == EINTR) 288 | return 0; 289 | 290 | /* Any other error indicates a bug. */ 291 | perror("aeApiPoll: port_get"); 292 | abort(); 293 | } 294 | 295 | state->npending = nevents; 296 | 297 | for (i = 0; i < nevents; i++) { 298 | mask = 0; 299 | if (event[i].portev_events & POLLIN) 300 | mask |= AE_READABLE; 301 | if (event[i].portev_events & POLLOUT) 302 | mask |= AE_WRITABLE; 303 | 304 | eventLoop->fired[i].fd = event[i].portev_object; 305 | eventLoop->fired[i].mask = mask; 306 | 307 | if (evport_debug) 308 | fprintf(stderr, "aeApiPoll: fd %d mask 0x%x\n", 309 | (int)event[i].portev_object, mask); 310 | 311 | state->pending_fds[i] = event[i].portev_object; 312 | state->pending_masks[i] = (uintptr_t)event[i].portev_user; 313 | } 314 | 315 | return nevents; 316 | } 317 | 318 | static char *aeApiName(void) { 319 | return "evport"; 320 | } 321 | -------------------------------------------------------------------------------- /tool/bitops.c: -------------------------------------------------------------------------------- 1 | /* Bit operations. 2 | * 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include "redis.h" 32 | 33 | /* ----------------------------------------------------------------------------- 34 | * Helpers and low level bit functions. 35 | * -------------------------------------------------------------------------- */ 36 | 37 | /* This helper function used by GETBIT / SETBIT parses the bit offset argument 38 | * making sure an error is returned if it is negative or if it overflows 39 | * Redis 512 MB limit for the string value. */ 40 | static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) { 41 | long long loffset; 42 | char *err = "bit offset is not an integer or out of range"; 43 | 44 | if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK) 45 | return REDIS_ERR; 46 | 47 | /* Limit offset to 512MB in bytes */ 48 | if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024)) 49 | { 50 | addReplyError(c,err); 51 | return REDIS_ERR; 52 | } 53 | 54 | *offset = (size_t)loffset; 55 | return REDIS_OK; 56 | } 57 | 58 | /* Count number of bits set in the binary array pointed by 's' and long 59 | * 'count' bytes. The implementation of this function is required to 60 | * work with a input string length up to 512 MB. */ 61 | size_t redisPopcount(void *s, long count) { 62 | size_t bits = 0; 63 | unsigned char *p = s; 64 | uint32_t *p4; 65 | static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; 66 | 67 | /* Count initial bytes not aligned to 32 bit. */ 68 | while((unsigned long)p & 3 && count) { 69 | bits += bitsinbyte[*p++]; 70 | count--; 71 | } 72 | 73 | /* Count bits 16 bytes at a time */ 74 | p4 = (uint32_t*)p; 75 | while(count>=16) { 76 | uint32_t aux1, aux2, aux3, aux4; 77 | 78 | aux1 = *p4++; 79 | aux2 = *p4++; 80 | aux3 = *p4++; 81 | aux4 = *p4++; 82 | count -= 16; 83 | 84 | aux1 = aux1 - ((aux1 >> 1) & 0x55555555); 85 | aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333); 86 | aux2 = aux2 - ((aux2 >> 1) & 0x55555555); 87 | aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333); 88 | aux3 = aux3 - ((aux3 >> 1) & 0x55555555); 89 | aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333); 90 | aux4 = aux4 - ((aux4 >> 1) & 0x55555555); 91 | aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333); 92 | bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + 93 | ((((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + 94 | ((((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + 95 | ((((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); 96 | } 97 | /* Count the remaining bytes. */ 98 | p = (unsigned char*)p4; 99 | while(count--) bits += bitsinbyte[*p++]; 100 | return bits; 101 | } 102 | 103 | /* Return the position of the first bit set to one (if 'bit' is 1) or 104 | * zero (if 'bit' is 0) in the bitmap starting at 's' and long 'count' bytes. 105 | * 106 | * The function is guaranteed to return a value >= 0 if 'bit' is 0 since if 107 | * no zero bit is found, it returns count*8 assuming the string is zero 108 | * padded on the right. However if 'bit' is 1 it is possible that there is 109 | * not a single set bit in the bitmap. In this special case -1 is returned. */ 110 | long redisBitpos(void *s, unsigned long count, int bit) { 111 | unsigned long *l; 112 | unsigned char *c; 113 | unsigned long skipval, word = 0, one; 114 | long pos = 0; /* Position of bit, to return to the caller. */ 115 | unsigned long j; 116 | 117 | /* Process whole words first, seeking for first word that is not 118 | * all ones or all zeros respectively if we are lookig for zeros 119 | * or ones. This is much faster with large strings having contiguous 120 | * blocks of 1 or 0 bits compared to the vanilla bit per bit processing. 121 | * 122 | * Note that if we start from an address that is not aligned 123 | * to sizeof(unsigned long) we consume it byte by byte until it is 124 | * aligned. */ 125 | 126 | /* Skip initial bits not aligned to sizeof(unsigned long) byte by byte. */ 127 | skipval = bit ? 0 : UCHAR_MAX; 128 | c = (unsigned char*) s; 129 | while((unsigned long)c & (sizeof(*l)-1) && count) { 130 | if (*c != skipval) break; 131 | c++; 132 | count--; 133 | pos += 8; 134 | } 135 | 136 | /* Skip bits with full word step. */ 137 | skipval = bit ? 0 : ULONG_MAX; 138 | l = (unsigned long*) c; 139 | while (count >= sizeof(*l)) { 140 | if (*l != skipval) break; 141 | l++; 142 | count -= sizeof(*l); 143 | pos += sizeof(*l)*8; 144 | } 145 | 146 | /* Load bytes into "word" considering the first byte as the most significant 147 | * (we basically consider it as written in big endian, since we consider the 148 | * string as a set of bits from left to right, with the first bit at position 149 | * zero. 150 | * 151 | * Note that the loading is designed to work even when the bytes left 152 | * (count) are less than a full word. We pad it with zero on the right. */ 153 | c = (unsigned char*)l; 154 | for (j = 0; j < sizeof(*l); j++) { 155 | word <<= 8; 156 | if (count) { 157 | word |= *c; 158 | c++; 159 | count--; 160 | } 161 | } 162 | 163 | /* Special case: 164 | * If bits in the string are all zero and we are looking for one, 165 | * return -1 to signal that there is not a single "1" in the whole 166 | * string. This can't happen when we are looking for "0" as we assume 167 | * that the right of the string is zero padded. */ 168 | if (bit == 1 && word == 0) return -1; 169 | 170 | /* Last word left, scan bit by bit. The first thing we need is to 171 | * have a single "1" set in the most significant position in an 172 | * unsigned long. We don't know the size of the long so we use a 173 | * simple trick. */ 174 | one = ULONG_MAX; /* All bits set to 1.*/ 175 | one >>= 1; /* All bits set to 1 but the MSB. */ 176 | one = ~one; /* All bits set to 0 but the MSB. */ 177 | 178 | while(one) { 179 | if (((one & word) != 0) == bit) return pos; 180 | pos++; 181 | one >>= 1; 182 | } 183 | 184 | /* If we reached this point, there is a bug in the algorithm, since 185 | * the case of no match is handled as a special case before. */ 186 | redisPanic("End of redisBitpos() reached."); 187 | return 0; /* Just to avoid warnings. */ 188 | } 189 | 190 | /* ----------------------------------------------------------------------------- 191 | * Bits related string commands: GETBIT, SETBIT, BITCOUNT, BITOP. 192 | * -------------------------------------------------------------------------- */ 193 | 194 | #define BITOP_AND 0 195 | #define BITOP_OR 1 196 | #define BITOP_XOR 2 197 | #define BITOP_NOT 3 198 | 199 | /* SETBIT key offset bitvalue */ 200 | void setbitCommand(redisClient *c) { 201 | robj *o; 202 | char *err = "bit is not an integer or out of range"; 203 | size_t bitoffset; 204 | int byte, bit; 205 | int byteval, bitval; 206 | long on; 207 | 208 | if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK) 209 | return; 210 | 211 | if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK) 212 | return; 213 | 214 | /* Bits can only be set or cleared... */ 215 | if (on & ~1) { 216 | addReplyError(c,err); 217 | return; 218 | } 219 | 220 | o = lookupKeyWrite(c->db,c->argv[1]); 221 | if (o == NULL) { 222 | o = createObject(REDIS_STRING,sdsempty()); 223 | dbAdd(c->db,c->argv[1],o); 224 | } else { 225 | if (checkType(c,o,REDIS_STRING)) return; 226 | o = dbUnshareStringValue(c->db,c->argv[1],o); 227 | } 228 | 229 | /* Grow sds value to the right length if necessary */ 230 | byte = bitoffset >> 3; 231 | o->ptr = sdsgrowzero(o->ptr,byte+1); 232 | 233 | /* Get current values */ 234 | byteval = ((uint8_t*)o->ptr)[byte]; 235 | bit = 7 - (bitoffset & 0x7); 236 | bitval = byteval & (1 << bit); 237 | 238 | /* Update byte with new bit value and return original value */ 239 | byteval &= ~(1 << bit); 240 | byteval |= ((on & 0x1) << bit); 241 | ((uint8_t*)o->ptr)[byte] = byteval; 242 | signalModifiedKey(c->db,c->argv[1]); 243 | notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"setbit",c->argv[1],c->db->id); 244 | server.dirty++; 245 | addReply(c, bitval ? shared.cone : shared.czero); 246 | } 247 | 248 | /* GETBIT key offset */ 249 | void getbitCommand(redisClient *c) { 250 | robj *o; 251 | char llbuf[32]; 252 | size_t bitoffset; 253 | size_t byte, bit; 254 | size_t bitval = 0; 255 | 256 | if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK) 257 | return; 258 | 259 | if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 260 | checkType(c,o,REDIS_STRING)) return; 261 | 262 | byte = bitoffset >> 3; 263 | bit = 7 - (bitoffset & 0x7); 264 | if (o->encoding != REDIS_ENCODING_RAW) { 265 | if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr)) 266 | bitval = llbuf[byte] & (1 << bit); 267 | } else { 268 | if (byte < sdslen(o->ptr)) 269 | bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit); 270 | } 271 | 272 | addReply(c, bitval ? shared.cone : shared.czero); 273 | } 274 | 275 | /* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */ 276 | void bitopCommand(redisClient *c) { 277 | char *opname = c->argv[1]->ptr; 278 | robj *o, *targetkey = c->argv[2]; 279 | unsigned long op, j, numkeys; 280 | robj **objects; /* Array of source objects. */ 281 | unsigned char **src; /* Array of source strings pointers. */ 282 | unsigned long *len, maxlen = 0; /* Array of length of src strings, 283 | and max len. */ 284 | unsigned long minlen = 0; /* Min len among the input keys. */ 285 | unsigned char *res = NULL; /* Resulting string. */ 286 | 287 | /* Parse the operation name. */ 288 | if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,"and")) 289 | op = BITOP_AND; 290 | else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,"or")) 291 | op = BITOP_OR; 292 | else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,"xor")) 293 | op = BITOP_XOR; 294 | else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not")) 295 | op = BITOP_NOT; 296 | else { 297 | addReply(c,shared.syntaxerr); 298 | return; 299 | } 300 | 301 | /* Sanity check: NOT accepts only a single key argument. */ 302 | if (op == BITOP_NOT && c->argc != 4) { 303 | addReplyError(c,"BITOP NOT must be called with a single source key."); 304 | return; 305 | } 306 | 307 | /* Lookup keys, and store pointers to the string objects into an array. */ 308 | numkeys = c->argc - 3; 309 | src = zmalloc(sizeof(unsigned char*) * numkeys); 310 | len = zmalloc(sizeof(long) * numkeys); 311 | objects = zmalloc(sizeof(robj*) * numkeys); 312 | for (j = 0; j < numkeys; j++) { 313 | o = lookupKeyRead(c->db,c->argv[j+3]); 314 | /* Handle non-existing keys as empty strings. */ 315 | if (o == NULL) { 316 | objects[j] = NULL; 317 | src[j] = NULL; 318 | len[j] = 0; 319 | minlen = 0; 320 | continue; 321 | } 322 | /* Return an error if one of the keys is not a string. */ 323 | if (checkType(c,o,REDIS_STRING)) { 324 | unsigned long i; 325 | for (i = 0; i < j; i++) { 326 | if (objects[i]) 327 | decrRefCount(objects[i]); 328 | } 329 | zfree(src); 330 | zfree(len); 331 | zfree(objects); 332 | return; 333 | } 334 | objects[j] = getDecodedObject(o); 335 | src[j] = objects[j]->ptr; 336 | len[j] = sdslen(objects[j]->ptr); 337 | if (len[j] > maxlen) maxlen = len[j]; 338 | if (j == 0 || len[j] < minlen) minlen = len[j]; 339 | } 340 | 341 | /* Compute the bit operation, if at least one string is not empty. */ 342 | if (maxlen) { 343 | res = (unsigned char*) sdsnewlen(NULL,maxlen); 344 | unsigned char output, byte; 345 | unsigned long i; 346 | 347 | /* Fast path: as far as we have data for all the input bitmaps we 348 | * can take a fast path that performs much better than the 349 | * vanilla algorithm. */ 350 | j = 0; 351 | if (minlen && numkeys <= 16) { 352 | unsigned long *lp[16]; 353 | unsigned long *lres = (unsigned long*) res; 354 | 355 | /* Note: sds pointer is always aligned to 8 byte boundary. */ 356 | memcpy(lp,src,sizeof(unsigned long*)*numkeys); 357 | memcpy(res,src[0],minlen); 358 | 359 | /* Different branches per different operations for speed (sorry). */ 360 | if (op == BITOP_AND) { 361 | while(minlen >= sizeof(unsigned long)*4) { 362 | for (i = 1; i < numkeys; i++) { 363 | lres[0] &= lp[i][0]; 364 | lres[1] &= lp[i][1]; 365 | lres[2] &= lp[i][2]; 366 | lres[3] &= lp[i][3]; 367 | lp[i]+=4; 368 | } 369 | lres+=4; 370 | j += sizeof(unsigned long)*4; 371 | minlen -= sizeof(unsigned long)*4; 372 | } 373 | } else if (op == BITOP_OR) { 374 | while(minlen >= sizeof(unsigned long)*4) { 375 | for (i = 1; i < numkeys; i++) { 376 | lres[0] |= lp[i][0]; 377 | lres[1] |= lp[i][1]; 378 | lres[2] |= lp[i][2]; 379 | lres[3] |= lp[i][3]; 380 | lp[i]+=4; 381 | } 382 | lres+=4; 383 | j += sizeof(unsigned long)*4; 384 | minlen -= sizeof(unsigned long)*4; 385 | } 386 | } else if (op == BITOP_XOR) { 387 | while(minlen >= sizeof(unsigned long)*4) { 388 | for (i = 1; i < numkeys; i++) { 389 | lres[0] ^= lp[i][0]; 390 | lres[1] ^= lp[i][1]; 391 | lres[2] ^= lp[i][2]; 392 | lres[3] ^= lp[i][3]; 393 | lp[i]+=4; 394 | } 395 | lres+=4; 396 | j += sizeof(unsigned long)*4; 397 | minlen -= sizeof(unsigned long)*4; 398 | } 399 | } else if (op == BITOP_NOT) { 400 | while(minlen >= sizeof(unsigned long)*4) { 401 | lres[0] = ~lres[0]; 402 | lres[1] = ~lres[1]; 403 | lres[2] = ~lres[2]; 404 | lres[3] = ~lres[3]; 405 | lres+=4; 406 | j += sizeof(unsigned long)*4; 407 | minlen -= sizeof(unsigned long)*4; 408 | } 409 | } 410 | } 411 | 412 | /* j is set to the next byte to process by the previous loop. */ 413 | for (; j < maxlen; j++) { 414 | output = (len[0] <= j) ? 0 : src[0][j]; 415 | if (op == BITOP_NOT) output = ~output; 416 | for (i = 1; i < numkeys; i++) { 417 | byte = (len[i] <= j) ? 0 : src[i][j]; 418 | switch(op) { 419 | case BITOP_AND: output &= byte; break; 420 | case BITOP_OR: output |= byte; break; 421 | case BITOP_XOR: output ^= byte; break; 422 | } 423 | } 424 | res[j] = output; 425 | } 426 | } 427 | for (j = 0; j < numkeys; j++) { 428 | if (objects[j]) 429 | decrRefCount(objects[j]); 430 | } 431 | zfree(src); 432 | zfree(len); 433 | zfree(objects); 434 | 435 | /* Store the computed value into the target key */ 436 | if (maxlen) { 437 | o = createObject(REDIS_STRING,res); 438 | setKey(c->db,targetkey,o); 439 | notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",targetkey,c->db->id); 440 | decrRefCount(o); 441 | } else if (dbDelete(c->db,targetkey)) { 442 | signalModifiedKey(c->db,targetkey); 443 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",targetkey,c->db->id); 444 | } 445 | server.dirty++; 446 | addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */ 447 | } 448 | 449 | /* BITCOUNT key [start end] */ 450 | void bitcountCommand(redisClient *c) { 451 | robj *o; 452 | long start, end, strlen; 453 | unsigned char *p; 454 | char llbuf[32]; 455 | 456 | /* Lookup, check for type, and return 0 for non existing keys. */ 457 | if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 458 | checkType(c,o,REDIS_STRING)) return; 459 | 460 | /* Set the 'p' pointer to the string, that can be just a stack allocated 461 | * array if our string was integer encoded. */ 462 | if (o->encoding == REDIS_ENCODING_INT) { 463 | p = (unsigned char*) llbuf; 464 | strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); 465 | } else { 466 | p = (unsigned char*) o->ptr; 467 | strlen = sdslen(o->ptr); 468 | } 469 | 470 | /* Parse start/end range if any. */ 471 | if (c->argc == 4) { 472 | if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) 473 | return; 474 | if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK) 475 | return; 476 | /* Convert negative indexes */ 477 | if (start < 0) start = strlen+start; 478 | if (end < 0) end = strlen+end; 479 | if (start < 0) start = 0; 480 | if (end < 0) end = 0; 481 | if (end >= strlen) end = strlen-1; 482 | } else if (c->argc == 2) { 483 | /* The whole string. */ 484 | start = 0; 485 | end = strlen-1; 486 | } else { 487 | /* Syntax error. */ 488 | addReply(c,shared.syntaxerr); 489 | return; 490 | } 491 | 492 | /* Precondition: end >= 0 && end < strlen, so the only condition where 493 | * zero can be returned is: start > end. */ 494 | if (start > end) { 495 | addReply(c,shared.czero); 496 | } else { 497 | long bytes = end-start+1; 498 | 499 | addReplyLongLong(c,redisPopcount(p+start,bytes)); 500 | } 501 | } 502 | 503 | /* BITPOS key bit [start [end]] */ 504 | void bitposCommand(redisClient *c) { 505 | robj *o; 506 | long bit, start, end, strlen; 507 | unsigned char *p; 508 | char llbuf[32]; 509 | int end_given = 0; 510 | 511 | /* Parse the bit argument to understand what we are looking for, set 512 | * or clear bits. */ 513 | if (getLongFromObjectOrReply(c,c->argv[2],&bit,NULL) != REDIS_OK) 514 | return; 515 | if (bit != 0 && bit != 1) { 516 | addReplyError(c, "The bit argument must be 1 or 0."); 517 | return; 518 | } 519 | 520 | /* If the key does not exist, from our point of view it is an infinite 521 | * array of 0 bits. If the user is looking for the fist clear bit return 0, 522 | * If the user is looking for the first set bit, return -1. */ 523 | if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) { 524 | addReplyLongLong(c, bit ? -1 : 0); 525 | return; 526 | } 527 | if (checkType(c,o,REDIS_STRING)) return; 528 | 529 | /* Set the 'p' pointer to the string, that can be just a stack allocated 530 | * array if our string was integer encoded. */ 531 | if (o->encoding == REDIS_ENCODING_INT) { 532 | p = (unsigned char*) llbuf; 533 | strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); 534 | } else { 535 | p = (unsigned char*) o->ptr; 536 | strlen = sdslen(o->ptr); 537 | } 538 | 539 | /* Parse start/end range if any. */ 540 | if (c->argc == 4 || c->argc == 5) { 541 | if (getLongFromObjectOrReply(c,c->argv[3],&start,NULL) != REDIS_OK) 542 | return; 543 | if (c->argc == 5) { 544 | if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != REDIS_OK) 545 | return; 546 | end_given = 1; 547 | } else { 548 | end = strlen-1; 549 | } 550 | /* Convert negative indexes */ 551 | if (start < 0) start = strlen+start; 552 | if (end < 0) end = strlen+end; 553 | if (start < 0) start = 0; 554 | if (end < 0) end = 0; 555 | if (end >= strlen) end = strlen-1; 556 | } else if (c->argc == 3) { 557 | /* The whole string. */ 558 | start = 0; 559 | end = strlen-1; 560 | } else { 561 | /* Syntax error. */ 562 | addReply(c,shared.syntaxerr); 563 | return; 564 | } 565 | 566 | /* For empty ranges (start > end) we return -1 as an empty range does 567 | * not contain a 0 nor a 1. */ 568 | if (start > end) { 569 | addReplyLongLong(c, -1); 570 | } else { 571 | long bytes = end-start+1; 572 | long pos = redisBitpos(p+start,bytes,bit); 573 | 574 | /* If we are looking for clear bits, and the user specified an exact 575 | * range with start-end, we can't consider the right of the range as 576 | * zero padded (as we do when no explicit end is given). 577 | * 578 | * So if redisBitpos() returns the first bit outside the range, 579 | * we return -1 to the caller, to mean, in the specified range there 580 | * is not a single "0" bit. */ 581 | if (end_given && bit == 0 && pos == bytes*8) { 582 | addReplyLongLong(c,-1); 583 | return; 584 | } 585 | if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */ 586 | addReplyLongLong(c,pos); 587 | } 588 | } 589 | -------------------------------------------------------------------------------- /struct/t_set.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "redis.h" 31 | 32 | /*----------------------------------------------------------------------------- 33 | * Set Commands 34 | *----------------------------------------------------------------------------*/ 35 | 36 | void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op); 37 | 38 | /* Factory method to return a set that *can* hold "value". When the object has 39 | * an integer-encodable value, an intset will be returned. Otherwise a regular 40 | * hash table. */ 41 | robj *setTypeCreate(robj *value) { 42 | if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK) 43 | return createIntsetObject(); 44 | return createSetObject(); 45 | } 46 | 47 | int setTypeAdd(robj *subject, robj *value) { 48 | long long llval; 49 | if (subject->encoding == REDIS_ENCODING_HT) { 50 | if (dictAdd(subject->ptr,value,NULL) == DICT_OK) { 51 | incrRefCount(value); 52 | return 1; 53 | } 54 | } else if (subject->encoding == REDIS_ENCODING_INTSET) { 55 | if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { 56 | uint8_t success = 0; 57 | subject->ptr = intsetAdd(subject->ptr,llval,&success); 58 | if (success) { 59 | /* Convert to regular set when the intset contains 60 | * too many entries. */ 61 | if (intsetLen(subject->ptr) > server.set_max_intset_entries) 62 | setTypeConvert(subject,REDIS_ENCODING_HT); 63 | return 1; 64 | } 65 | } else { 66 | /* Failed to get integer from object, convert to regular set. */ 67 | setTypeConvert(subject,REDIS_ENCODING_HT); 68 | 69 | /* The set *was* an intset and this value is not integer 70 | * encodable, so dictAdd should always work. */ 71 | redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK); 72 | incrRefCount(value); 73 | return 1; 74 | } 75 | } else { 76 | redisPanic("Unknown set encoding"); 77 | } 78 | return 0; 79 | } 80 | 81 | int setTypeRemove(robj *setobj, robj *value) { 82 | long long llval; 83 | if (setobj->encoding == REDIS_ENCODING_HT) { 84 | if (dictDelete(setobj->ptr,value) == DICT_OK) { 85 | if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr); 86 | return 1; 87 | } 88 | } else if (setobj->encoding == REDIS_ENCODING_INTSET) { 89 | if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { 90 | int success; 91 | setobj->ptr = intsetRemove(setobj->ptr,llval,&success); 92 | if (success) return 1; 93 | } 94 | } else { 95 | redisPanic("Unknown set encoding"); 96 | } 97 | return 0; 98 | } 99 | 100 | int setTypeIsMember(robj *subject, robj *value) { 101 | long long llval; 102 | if (subject->encoding == REDIS_ENCODING_HT) { 103 | return dictFind((dict*)subject->ptr,value) != NULL; 104 | } else if (subject->encoding == REDIS_ENCODING_INTSET) { 105 | if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { 106 | return intsetFind((intset*)subject->ptr,llval); 107 | } 108 | } else { 109 | redisPanic("Unknown set encoding"); 110 | } 111 | return 0; 112 | } 113 | 114 | setTypeIterator *setTypeInitIterator(robj *subject) { 115 | setTypeIterator *si = zmalloc(sizeof(setTypeIterator)); 116 | si->subject = subject; 117 | si->encoding = subject->encoding; 118 | if (si->encoding == REDIS_ENCODING_HT) { 119 | si->di = dictGetIterator(subject->ptr); 120 | } else if (si->encoding == REDIS_ENCODING_INTSET) { 121 | si->ii = 0; 122 | } else { 123 | redisPanic("Unknown set encoding"); 124 | } 125 | return si; 126 | } 127 | 128 | void setTypeReleaseIterator(setTypeIterator *si) { 129 | if (si->encoding == REDIS_ENCODING_HT) 130 | dictReleaseIterator(si->di); 131 | zfree(si); 132 | } 133 | 134 | /* Move to the next entry in the set. Returns the object at the current 135 | * position. 136 | * 137 | * Since set elements can be internally be stored as redis objects or 138 | * simple arrays of integers, setTypeNext returns the encoding of the 139 | * set object you are iterating, and will populate the appropriate pointer 140 | * (eobj) or (llobj) accordingly. 141 | * 142 | * When there are no longer elements -1 is returned. 143 | * Returned objects ref count is not incremented, so this function is 144 | * copy on write friendly. */ 145 | int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { 146 | if (si->encoding == REDIS_ENCODING_HT) { 147 | dictEntry *de = dictNext(si->di); 148 | if (de == NULL) return -1; 149 | *objele = dictGetKey(de); 150 | } else if (si->encoding == REDIS_ENCODING_INTSET) { 151 | if (!intsetGet(si->subject->ptr,si->ii++,llele)) 152 | return -1; 153 | } 154 | return si->encoding; 155 | } 156 | 157 | /* The not copy on write friendly version but easy to use version 158 | * of setTypeNext() is setTypeNextObject(), returning new objects 159 | * or incrementing the ref count of returned objects. So if you don't 160 | * retain a pointer to this object you should call decrRefCount() against it. 161 | * 162 | * This function is the way to go for write operations where COW is not 163 | * an issue as the result will be anyway of incrementing the ref count. */ 164 | robj *setTypeNextObject(setTypeIterator *si) { 165 | int64_t intele; 166 | robj *objele; 167 | int encoding; 168 | 169 | encoding = setTypeNext(si,&objele,&intele); 170 | switch(encoding) { 171 | case -1: return NULL; 172 | case REDIS_ENCODING_INTSET: 173 | return createStringObjectFromLongLong(intele); 174 | case REDIS_ENCODING_HT: 175 | incrRefCount(objele); 176 | return objele; 177 | default: 178 | redisPanic("Unsupported encoding"); 179 | } 180 | return NULL; /* just to suppress warnings */ 181 | } 182 | 183 | /* Return random element from a non empty set. 184 | * The returned element can be a int64_t value if the set is encoded 185 | * as an "intset" blob of integers, or a redis object if the set 186 | * is a regular set. 187 | * 188 | * The caller provides both pointers to be populated with the right 189 | * object. The return value of the function is the object->encoding 190 | * field of the object and is used by the caller to check if the 191 | * int64_t pointer or the redis object pointer was populated. 192 | * 193 | * When an object is returned (the set was a real set) the ref count 194 | * of the object is not incremented so this function can be considered 195 | * copy on write friendly. */ 196 | int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) { 197 | if (setobj->encoding == REDIS_ENCODING_HT) { 198 | dictEntry *de = dictGetRandomKey(setobj->ptr); 199 | *objele = dictGetKey(de); 200 | } else if (setobj->encoding == REDIS_ENCODING_INTSET) { 201 | *llele = intsetRandom(setobj->ptr); 202 | } else { 203 | redisPanic("Unknown set encoding"); 204 | } 205 | return setobj->encoding; 206 | } 207 | 208 | unsigned long setTypeSize(robj *subject) { 209 | if (subject->encoding == REDIS_ENCODING_HT) { 210 | return dictSize((dict*)subject->ptr); 211 | } else if (subject->encoding == REDIS_ENCODING_INTSET) { 212 | return intsetLen((intset*)subject->ptr); 213 | } else { 214 | redisPanic("Unknown set encoding"); 215 | } 216 | } 217 | 218 | /* Convert the set to specified encoding. The resulting dict (when converting 219 | * to a hash table) is presized to hold the number of elements in the original 220 | * set. */ 221 | void setTypeConvert(robj *setobj, int enc) { 222 | setTypeIterator *si; 223 | redisAssertWithInfo(NULL,setobj,setobj->type == REDIS_SET && 224 | setobj->encoding == REDIS_ENCODING_INTSET); 225 | 226 | if (enc == REDIS_ENCODING_HT) { 227 | int64_t intele; 228 | dict *d = dictCreate(&setDictType,NULL); 229 | robj *element; 230 | 231 | /* Presize the dict to avoid rehashing */ 232 | dictExpand(d,intsetLen(setobj->ptr)); 233 | 234 | /* To add the elements we extract integers and create redis objects */ 235 | si = setTypeInitIterator(setobj); 236 | while (setTypeNext(si,NULL,&intele) != -1) { 237 | element = createStringObjectFromLongLong(intele); 238 | redisAssertWithInfo(NULL,element,dictAdd(d,element,NULL) == DICT_OK); 239 | } 240 | setTypeReleaseIterator(si); 241 | 242 | setobj->encoding = REDIS_ENCODING_HT; 243 | zfree(setobj->ptr); 244 | setobj->ptr = d; 245 | } else { 246 | redisPanic("Unsupported set conversion"); 247 | } 248 | } 249 | 250 | void saddCommand(redisClient *c) { 251 | robj *set; 252 | int j, added = 0; 253 | 254 | set = lookupKeyWrite(c->db,c->argv[1]); 255 | if (set == NULL) { 256 | set = setTypeCreate(c->argv[2]); 257 | dbAdd(c->db,c->argv[1],set); 258 | } else { 259 | if (set->type != REDIS_SET) { 260 | addReply(c,shared.wrongtypeerr); 261 | return; 262 | } 263 | } 264 | 265 | for (j = 2; j < c->argc; j++) { 266 | c->argv[j] = tryObjectEncoding(c->argv[j]); 267 | if (setTypeAdd(set,c->argv[j])) added++; 268 | } 269 | if (added) { 270 | signalModifiedKey(c->db,c->argv[1]); 271 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id); 272 | } 273 | server.dirty += added; 274 | addReplyLongLong(c,added); 275 | } 276 | 277 | void sremCommand(redisClient *c) { 278 | robj *set; 279 | int j, deleted = 0, keyremoved = 0; 280 | 281 | if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || 282 | checkType(c,set,REDIS_SET)) return; 283 | 284 | for (j = 2; j < c->argc; j++) { 285 | if (setTypeRemove(set,c->argv[j])) { 286 | deleted++; 287 | if (setTypeSize(set) == 0) { 288 | dbDelete(c->db,c->argv[1]); 289 | keyremoved = 1; 290 | break; 291 | } 292 | } 293 | } 294 | if (deleted) { 295 | signalModifiedKey(c->db,c->argv[1]); 296 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id); 297 | if (keyremoved) 298 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1], 299 | c->db->id); 300 | server.dirty += deleted; 301 | } 302 | addReplyLongLong(c,deleted); 303 | } 304 | 305 | void smoveCommand(redisClient *c) { 306 | robj *srcset, *dstset, *ele; 307 | srcset = lookupKeyWrite(c->db,c->argv[1]); 308 | dstset = lookupKeyWrite(c->db,c->argv[2]); 309 | ele = c->argv[3] = tryObjectEncoding(c->argv[3]); 310 | 311 | /* If the source key does not exist return 0 */ 312 | if (srcset == NULL) { 313 | addReply(c,shared.czero); 314 | return; 315 | } 316 | 317 | /* If the source key has the wrong type, or the destination key 318 | * is set and has the wrong type, return with an error. */ 319 | if (checkType(c,srcset,REDIS_SET) || 320 | (dstset && checkType(c,dstset,REDIS_SET))) return; 321 | 322 | /* If srcset and dstset are equal, SMOVE is a no-op */ 323 | if (srcset == dstset) { 324 | addReply(c,shared.cone); 325 | return; 326 | } 327 | 328 | /* If the element cannot be removed from the src set, return 0. */ 329 | if (!setTypeRemove(srcset,ele)) { 330 | addReply(c,shared.czero); 331 | return; 332 | } 333 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id); 334 | 335 | /* Remove the src set from the database when empty */ 336 | if (setTypeSize(srcset) == 0) { 337 | dbDelete(c->db,c->argv[1]); 338 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); 339 | } 340 | signalModifiedKey(c->db,c->argv[1]); 341 | signalModifiedKey(c->db,c->argv[2]); 342 | server.dirty++; 343 | 344 | /* Create the destination set when it doesn't exist */ 345 | if (!dstset) { 346 | dstset = setTypeCreate(ele); 347 | dbAdd(c->db,c->argv[2],dstset); 348 | } 349 | 350 | /* An extra key has changed when ele was successfully added to dstset */ 351 | if (setTypeAdd(dstset,ele)) { 352 | server.dirty++; 353 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[2],c->db->id); 354 | } 355 | addReply(c,shared.cone); 356 | } 357 | 358 | void sismemberCommand(redisClient *c) { 359 | robj *set; 360 | 361 | if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 362 | checkType(c,set,REDIS_SET)) return; 363 | 364 | c->argv[2] = tryObjectEncoding(c->argv[2]); 365 | if (setTypeIsMember(set,c->argv[2])) 366 | addReply(c,shared.cone); 367 | else 368 | addReply(c,shared.czero); 369 | } 370 | 371 | void scardCommand(redisClient *c) { 372 | robj *o; 373 | 374 | if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 375 | checkType(c,o,REDIS_SET)) return; 376 | 377 | addReplyLongLong(c,setTypeSize(o)); 378 | } 379 | 380 | void spopCommand(redisClient *c) { 381 | robj *set, *ele, *aux; 382 | int64_t llele; 383 | int encoding; 384 | 385 | if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || 386 | checkType(c,set,REDIS_SET)) return; 387 | 388 | encoding = setTypeRandomElement(set,&ele,&llele); 389 | if (encoding == REDIS_ENCODING_INTSET) { 390 | ele = createStringObjectFromLongLong(llele); 391 | set->ptr = intsetRemove(set->ptr,llele,NULL); 392 | } else { 393 | incrRefCount(ele); 394 | setTypeRemove(set,ele); 395 | } 396 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id); 397 | 398 | /* Replicate/AOF this command as an SREM operation */ 399 | aux = createStringObject("SREM",4); 400 | rewriteClientCommandVector(c,3,aux,c->argv[1],ele); 401 | decrRefCount(ele); 402 | decrRefCount(aux); 403 | 404 | addReplyBulk(c,ele); 405 | if (setTypeSize(set) == 0) { 406 | dbDelete(c->db,c->argv[1]); 407 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); 408 | } 409 | signalModifiedKey(c->db,c->argv[1]); 410 | server.dirty++; 411 | } 412 | 413 | /* handle the "SRANDMEMBER key " variant. The normal version of the 414 | * command is handled by the srandmemberCommand() function itself. */ 415 | 416 | /* How many times bigger should be the set compared to the requested size 417 | * for us to don't use the "remove elements" strategy? Read later in the 418 | * implementation for more info. */ 419 | #define SRANDMEMBER_SUB_STRATEGY_MUL 3 420 | 421 | void srandmemberWithCountCommand(redisClient *c) { 422 | long l; 423 | unsigned long count, size; 424 | int uniq = 1; 425 | robj *set, *ele; 426 | int64_t llele; 427 | int encoding; 428 | 429 | dict *d; 430 | 431 | if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != REDIS_OK) return; 432 | if (l >= 0) { 433 | count = (unsigned) l; 434 | } else { 435 | /* A negative count means: return the same elements multiple times 436 | * (i.e. don't remove the extracted element after every extraction). */ 437 | count = -l; 438 | uniq = 0; 439 | } 440 | 441 | if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) 442 | == NULL || checkType(c,set,REDIS_SET)) return; 443 | size = setTypeSize(set); 444 | 445 | /* If count is zero, serve it ASAP to avoid special cases later. */ 446 | if (count == 0) { 447 | addReply(c,shared.emptymultibulk); 448 | return; 449 | } 450 | 451 | /* CASE 1: The count was negative, so the extraction method is just: 452 | * "return N random elements" sampling the whole set every time. 453 | * This case is trivial and can be served without auxiliary data 454 | * structures. */ 455 | if (!uniq) { 456 | addReplyMultiBulkLen(c,count); 457 | while(count--) { 458 | encoding = setTypeRandomElement(set,&ele,&llele); 459 | if (encoding == REDIS_ENCODING_INTSET) { 460 | addReplyBulkLongLong(c,llele); 461 | } else { 462 | addReplyBulk(c,ele); 463 | } 464 | } 465 | return; 466 | } 467 | 468 | /* CASE 2: 469 | * The number of requested elements is greater than the number of 470 | * elements inside the set: simply return the whole set. */ 471 | if (count >= size) { 472 | sunionDiffGenericCommand(c,c->argv+1,1,NULL,REDIS_OP_UNION); 473 | return; 474 | } 475 | 476 | /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */ 477 | d = dictCreate(&setDictType,NULL); 478 | 479 | /* CASE 3: 480 | * The number of elements inside the set is not greater than 481 | * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements. 482 | * In this case we create a set from scratch with all the elements, and 483 | * subtract random elements to reach the requested number of elements. 484 | * 485 | * This is done because if the number of requsted elements is just 486 | * a bit less than the number of elements in the set, the natural approach 487 | * used into CASE 3 is highly inefficient. */ 488 | if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) { 489 | setTypeIterator *si; 490 | 491 | /* Add all the elements into the temporary dictionary. */ 492 | si = setTypeInitIterator(set); 493 | while((encoding = setTypeNext(si,&ele,&llele)) != -1) { 494 | int retval = DICT_ERR; 495 | 496 | if (encoding == REDIS_ENCODING_INTSET) { 497 | retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL); 498 | } else if (ele->encoding == REDIS_ENCODING_RAW) { 499 | retval = dictAdd(d,dupStringObject(ele),NULL); 500 | } else if (ele->encoding == REDIS_ENCODING_INT) { 501 | retval = dictAdd(d, 502 | createStringObjectFromLongLong((long)ele->ptr),NULL); 503 | } 504 | redisAssert(retval == DICT_OK); 505 | } 506 | setTypeReleaseIterator(si); 507 | redisAssert(dictSize(d) == size); 508 | 509 | /* Remove random elements to reach the right count. */ 510 | while(size > count) { 511 | dictEntry *de; 512 | 513 | de = dictGetRandomKey(d); 514 | dictDelete(d,dictGetKey(de)); 515 | size--; 516 | } 517 | } 518 | 519 | /* CASE 4: We have a big set compared to the requested number of elements. 520 | * In this case we can simply get random elements from the set and add 521 | * to the temporary set, trying to eventually get enough unique elements 522 | * to reach the specified count. */ 523 | else { 524 | unsigned long added = 0; 525 | 526 | while(added < count) { 527 | encoding = setTypeRandomElement(set,&ele,&llele); 528 | if (encoding == REDIS_ENCODING_INTSET) { 529 | ele = createStringObjectFromLongLong(llele); 530 | } else if (ele->encoding == REDIS_ENCODING_RAW) { 531 | ele = dupStringObject(ele); 532 | } else if (ele->encoding == REDIS_ENCODING_INT) { 533 | ele = createStringObjectFromLongLong((long)ele->ptr); 534 | } 535 | /* Try to add the object to the dictionary. If it already exists 536 | * free it, otherwise increment the number of objects we have 537 | * in the result dictionary. */ 538 | if (dictAdd(d,ele,NULL) == DICT_OK) 539 | added++; 540 | else 541 | decrRefCount(ele); 542 | } 543 | } 544 | 545 | /* CASE 3 & 4: send the result to the user. */ 546 | { 547 | dictIterator *di; 548 | dictEntry *de; 549 | 550 | addReplyMultiBulkLen(c,count); 551 | di = dictGetIterator(d); 552 | while((de = dictNext(di)) != NULL) 553 | addReplyBulk(c,dictGetKey(de)); 554 | dictReleaseIterator(di); 555 | dictRelease(d); 556 | } 557 | } 558 | 559 | void srandmemberCommand(redisClient *c) { 560 | robj *set, *ele; 561 | int64_t llele; 562 | int encoding; 563 | 564 | if (c->argc == 3) { 565 | srandmemberWithCountCommand(c); 566 | return; 567 | } else if (c->argc > 3) { 568 | addReply(c,shared.syntaxerr); 569 | return; 570 | } 571 | 572 | if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || 573 | checkType(c,set,REDIS_SET)) return; 574 | 575 | encoding = setTypeRandomElement(set,&ele,&llele); 576 | if (encoding == REDIS_ENCODING_INTSET) { 577 | addReplyBulkLongLong(c,llele); 578 | } else { 579 | addReplyBulk(c,ele); 580 | } 581 | } 582 | 583 | int qsortCompareSetsByCardinality(const void *s1, const void *s2) { 584 | return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2); 585 | } 586 | 587 | /* This is used by SDIFF and in this case we can receive NULL that should 588 | * be handled as empty sets. */ 589 | int qsortCompareSetsByRevCardinality(const void *s1, const void *s2) { 590 | robj *o1 = *(robj**)s1, *o2 = *(robj**)s2; 591 | 592 | return (o2 ? setTypeSize(o2) : 0) - (o1 ? setTypeSize(o1) : 0); 593 | } 594 | 595 | void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) { 596 | robj **sets = zmalloc(sizeof(robj*)*setnum); 597 | setTypeIterator *si; 598 | robj *eleobj, *dstset = NULL; 599 | int64_t intobj; 600 | void *replylen = NULL; 601 | unsigned long j, cardinality = 0; 602 | int encoding; 603 | 604 | for (j = 0; j < setnum; j++) { 605 | robj *setobj = dstkey ? 606 | lookupKeyWrite(c->db,setkeys[j]) : 607 | lookupKeyRead(c->db,setkeys[j]); 608 | if (!setobj) { 609 | zfree(sets); 610 | if (dstkey) { 611 | if (dbDelete(c->db,dstkey)) { 612 | signalModifiedKey(c->db,dstkey); 613 | server.dirty++; 614 | } 615 | addReply(c,shared.czero); 616 | } else { 617 | addReply(c,shared.emptymultibulk); 618 | } 619 | return; 620 | } 621 | if (checkType(c,setobj,REDIS_SET)) { 622 | zfree(sets); 623 | return; 624 | } 625 | sets[j] = setobj; 626 | } 627 | /* Sort sets from the smallest to largest, this will improve our 628 | * algorithm's performance */ 629 | qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality); 630 | 631 | /* The first thing we should output is the total number of elements... 632 | * since this is a multi-bulk write, but at this stage we don't know 633 | * the intersection set size, so we use a trick, append an empty object 634 | * to the output list and save the pointer to later modify it with the 635 | * right length */ 636 | if (!dstkey) { 637 | replylen = addDeferredMultiBulkLength(c); 638 | } else { 639 | /* If we have a target key where to store the resulting set 640 | * create this key with an empty set inside */ 641 | dstset = createIntsetObject(); 642 | } 643 | 644 | /* Iterate all the elements of the first (smallest) set, and test 645 | * the element against all the other sets, if at least one set does 646 | * not include the element it is discarded */ 647 | si = setTypeInitIterator(sets[0]); 648 | while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) { 649 | for (j = 1; j < setnum; j++) { 650 | if (sets[j] == sets[0]) continue; 651 | if (encoding == REDIS_ENCODING_INTSET) { 652 | /* intset with intset is simple... and fast */ 653 | if (sets[j]->encoding == REDIS_ENCODING_INTSET && 654 | !intsetFind((intset*)sets[j]->ptr,intobj)) 655 | { 656 | break; 657 | /* in order to compare an integer with an object we 658 | * have to use the generic function, creating an object 659 | * for this */ 660 | } else if (sets[j]->encoding == REDIS_ENCODING_HT) { 661 | eleobj = createStringObjectFromLongLong(intobj); 662 | if (!setTypeIsMember(sets[j],eleobj)) { 663 | decrRefCount(eleobj); 664 | break; 665 | } 666 | decrRefCount(eleobj); 667 | } 668 | } else if (encoding == REDIS_ENCODING_HT) { 669 | /* Optimization... if the source object is integer 670 | * encoded AND the target set is an intset, we can get 671 | * a much faster path. */ 672 | if (eleobj->encoding == REDIS_ENCODING_INT && 673 | sets[j]->encoding == REDIS_ENCODING_INTSET && 674 | !intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr)) 675 | { 676 | break; 677 | /* else... object to object check is easy as we use the 678 | * type agnostic API here. */ 679 | } else if (!setTypeIsMember(sets[j],eleobj)) { 680 | break; 681 | } 682 | } 683 | } 684 | 685 | /* Only take action when all sets contain the member */ 686 | if (j == setnum) { 687 | if (!dstkey) { 688 | if (encoding == REDIS_ENCODING_HT) 689 | addReplyBulk(c,eleobj); 690 | else 691 | addReplyBulkLongLong(c,intobj); 692 | cardinality++; 693 | } else { 694 | if (encoding == REDIS_ENCODING_INTSET) { 695 | eleobj = createStringObjectFromLongLong(intobj); 696 | setTypeAdd(dstset,eleobj); 697 | decrRefCount(eleobj); 698 | } else { 699 | setTypeAdd(dstset,eleobj); 700 | } 701 | } 702 | } 703 | } 704 | setTypeReleaseIterator(si); 705 | 706 | if (dstkey) { 707 | /* Store the resulting set into the target, if the intersection 708 | * is not an empty set. */ 709 | int deleted = dbDelete(c->db,dstkey); 710 | if (setTypeSize(dstset) > 0) { 711 | dbAdd(c->db,dstkey,dstset); 712 | addReplyLongLong(c,setTypeSize(dstset)); 713 | notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sinterstore", 714 | dstkey,c->db->id); 715 | } else { 716 | decrRefCount(dstset); 717 | addReply(c,shared.czero); 718 | if (deleted) 719 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", 720 | dstkey,c->db->id); 721 | } 722 | signalModifiedKey(c->db,dstkey); 723 | server.dirty++; 724 | } else { 725 | setDeferredMultiBulkLength(c,replylen,cardinality); 726 | } 727 | zfree(sets); 728 | } 729 | 730 | void sinterCommand(redisClient *c) { 731 | sinterGenericCommand(c,c->argv+1,c->argc-1,NULL); 732 | } 733 | 734 | void sinterstoreCommand(redisClient *c) { 735 | sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]); 736 | } 737 | 738 | #define REDIS_OP_UNION 0 739 | #define REDIS_OP_DIFF 1 740 | #define REDIS_OP_INTER 2 741 | 742 | void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) { 743 | robj **sets = zmalloc(sizeof(robj*)*setnum); 744 | setTypeIterator *si; 745 | robj *ele, *dstset = NULL; 746 | int j, cardinality = 0; 747 | int diff_algo = 1; 748 | 749 | for (j = 0; j < setnum; j++) { 750 | robj *setobj = dstkey ? 751 | lookupKeyWrite(c->db,setkeys[j]) : 752 | lookupKeyRead(c->db,setkeys[j]); 753 | if (!setobj) { 754 | sets[j] = NULL; 755 | continue; 756 | } 757 | if (checkType(c,setobj,REDIS_SET)) { 758 | zfree(sets); 759 | return; 760 | } 761 | sets[j] = setobj; 762 | } 763 | 764 | /* Select what DIFF algorithm to use. 765 | * 766 | * Algorithm 1 is O(N*M) where N is the size of the element first set 767 | * and M the total number of sets. 768 | * 769 | * Algorithm 2 is O(N) where N is the total number of elements in all 770 | * the sets. 771 | * 772 | * We compute what is the best bet with the current input here. */ 773 | if (op == REDIS_OP_DIFF && sets[0]) { 774 | long long algo_one_work = 0, algo_two_work = 0; 775 | 776 | for (j = 0; j < setnum; j++) { 777 | if (sets[j] == NULL) continue; 778 | 779 | algo_one_work += setTypeSize(sets[0]); 780 | algo_two_work += setTypeSize(sets[j]); 781 | } 782 | 783 | /* Algorithm 1 has better constant times and performs less operations 784 | * if there are elements in common. Give it some advantage. */ 785 | algo_one_work /= 2; 786 | diff_algo = (algo_one_work <= algo_two_work) ? 1 : 2; 787 | 788 | if (diff_algo == 1 && setnum > 1) { 789 | /* With algorithm 1 it is better to order the sets to subtract 790 | * by decreasing size, so that we are more likely to find 791 | * duplicated elements ASAP. */ 792 | qsort(sets+1,setnum-1,sizeof(robj*), 793 | qsortCompareSetsByRevCardinality); 794 | } 795 | } 796 | 797 | /* We need a temp set object to store our union. If the dstkey 798 | * is not NULL (that is, we are inside an SUNIONSTORE operation) then 799 | * this set object will be the resulting object to set into the target key*/ 800 | dstset = createIntsetObject(); 801 | 802 | if (op == REDIS_OP_UNION) { 803 | /* Union is trivial, just add every element of every set to the 804 | * temporary set. */ 805 | for (j = 0; j < setnum; j++) { 806 | if (!sets[j]) continue; /* non existing keys are like empty sets */ 807 | 808 | si = setTypeInitIterator(sets[j]); 809 | while((ele = setTypeNextObject(si)) != NULL) { 810 | if (setTypeAdd(dstset,ele)) cardinality++; 811 | decrRefCount(ele); 812 | } 813 | setTypeReleaseIterator(si); 814 | } 815 | } else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 1) { 816 | /* DIFF Algorithm 1: 817 | * 818 | * We perform the diff by iterating all the elements of the first set, 819 | * and only adding it to the target set if the element does not exist 820 | * into all the other sets. 821 | * 822 | * This way we perform at max N*M operations, where N is the size of 823 | * the first set, and M the number of sets. */ 824 | si = setTypeInitIterator(sets[0]); 825 | while((ele = setTypeNextObject(si)) != NULL) { 826 | for (j = 1; j < setnum; j++) { 827 | if (!sets[j]) continue; /* no key is an empty set. */ 828 | if (sets[j] == sets[0]) break; /* same set! */ 829 | if (setTypeIsMember(sets[j],ele)) break; 830 | } 831 | if (j == setnum) { 832 | /* There is no other set with this element. Add it. */ 833 | setTypeAdd(dstset,ele); 834 | cardinality++; 835 | } 836 | decrRefCount(ele); 837 | } 838 | setTypeReleaseIterator(si); 839 | } else if (op == REDIS_OP_DIFF && sets[0] && diff_algo == 2) { 840 | /* DIFF Algorithm 2: 841 | * 842 | * Add all the elements of the first set to the auxiliary set. 843 | * Then remove all the elements of all the next sets from it. 844 | * 845 | * This is O(N) where N is the sum of all the elements in every 846 | * set. */ 847 | for (j = 0; j < setnum; j++) { 848 | if (!sets[j]) continue; /* non existing keys are like empty sets */ 849 | 850 | si = setTypeInitIterator(sets[j]); 851 | while((ele = setTypeNextObject(si)) != NULL) { 852 | if (j == 0) { 853 | if (setTypeAdd(dstset,ele)) cardinality++; 854 | } else { 855 | if (setTypeRemove(dstset,ele)) cardinality--; 856 | } 857 | decrRefCount(ele); 858 | } 859 | setTypeReleaseIterator(si); 860 | 861 | /* Exit if result set is empty as any additional removal 862 | * of elements will have no effect. */ 863 | if (cardinality == 0) break; 864 | } 865 | } 866 | 867 | /* Output the content of the resulting set, if not in STORE mode */ 868 | if (!dstkey) { 869 | addReplyMultiBulkLen(c,cardinality); 870 | si = setTypeInitIterator(dstset); 871 | while((ele = setTypeNextObject(si)) != NULL) { 872 | addReplyBulk(c,ele); 873 | decrRefCount(ele); 874 | } 875 | setTypeReleaseIterator(si); 876 | decrRefCount(dstset); 877 | } else { 878 | /* If we have a target key where to store the resulting set 879 | * create this key with the result set inside */ 880 | int deleted = dbDelete(c->db,dstkey); 881 | if (setTypeSize(dstset) > 0) { 882 | dbAdd(c->db,dstkey,dstset); 883 | addReplyLongLong(c,setTypeSize(dstset)); 884 | notifyKeyspaceEvent(REDIS_NOTIFY_SET, 885 | op == REDIS_OP_UNION ? "sunionstore" : "sdiffstore", 886 | dstkey,c->db->id); 887 | } else { 888 | decrRefCount(dstset); 889 | addReply(c,shared.czero); 890 | if (deleted) 891 | notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", 892 | dstkey,c->db->id); 893 | } 894 | signalModifiedKey(c->db,dstkey); 895 | server.dirty++; 896 | } 897 | zfree(sets); 898 | } 899 | 900 | void sunionCommand(redisClient *c) { 901 | sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION); 902 | } 903 | 904 | void sunionstoreCommand(redisClient *c) { 905 | sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION); 906 | } 907 | 908 | void sdiffCommand(redisClient *c) { 909 | sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF); 910 | } 911 | 912 | void sdiffstoreCommand(redisClient *c) { 913 | sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF); 914 | } 915 | 916 | void sscanCommand(redisClient *c) { 917 | robj *set; 918 | unsigned long cursor; 919 | 920 | if (parseScanCursorOrReply(c,c->argv[2],&cursor) == REDIS_ERR) return; 921 | if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL || 922 | checkType(c,set,REDIS_SET)) return; 923 | scanGenericCommand(c,set,cursor); 924 | } 925 | -------------------------------------------------------------------------------- /tool/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "redis.h" 31 | #include "sha1.h" /* SHA1 is used for DEBUG DIGEST */ 32 | #include "crc64.h" 33 | 34 | #include 35 | #include 36 | 37 | #ifdef HAVE_BACKTRACE 38 | #include 39 | #include 40 | #include 41 | #include "bio.h" 42 | #endif /* HAVE_BACKTRACE */ 43 | 44 | #ifdef __CYGWIN__ 45 | #ifndef SA_ONSTACK 46 | #define SA_ONSTACK 0x08000000 47 | #endif 48 | #endif 49 | 50 | /* ================================= Debugging ============================== */ 51 | 52 | /* Compute the sha1 of string at 's' with 'len' bytes long. 53 | * The SHA1 is then xored against the string pointed by digest. 54 | * Since xor is commutative, this operation is used in order to 55 | * "add" digests relative to unordered elements. 56 | * 57 | * So digest(a,b,c,d) will be the same of digest(b,a,c,d) */ 58 | void xorDigest(unsigned char *digest, void *ptr, size_t len) { 59 | SHA1_CTX ctx; 60 | unsigned char hash[20], *s = ptr; 61 | int j; 62 | 63 | SHA1Init(&ctx); 64 | SHA1Update(&ctx,s,len); 65 | SHA1Final(hash,&ctx); 66 | 67 | for (j = 0; j < 20; j++) 68 | digest[j] ^= hash[j]; 69 | } 70 | 71 | void xorObjectDigest(unsigned char *digest, robj *o) { 72 | o = getDecodedObject(o); 73 | xorDigest(digest,o->ptr,sdslen(o->ptr)); 74 | decrRefCount(o); 75 | } 76 | 77 | /* This function instead of just computing the SHA1 and xoring it 78 | * against digest, also perform the digest of "digest" itself and 79 | * replace the old value with the new one. 80 | * 81 | * So the final digest will be: 82 | * 83 | * digest = SHA1(digest xor SHA1(data)) 84 | * 85 | * This function is used every time we want to preserve the order so 86 | * that digest(a,b,c,d) will be different than digest(b,c,d,a) 87 | * 88 | * Also note that mixdigest("foo") followed by mixdigest("bar") 89 | * will lead to a different digest compared to "fo", "obar". 90 | */ 91 | void mixDigest(unsigned char *digest, void *ptr, size_t len) { 92 | SHA1_CTX ctx; 93 | char *s = ptr; 94 | 95 | xorDigest(digest,s,len); 96 | SHA1Init(&ctx); 97 | SHA1Update(&ctx,digest,20); 98 | SHA1Final(digest,&ctx); 99 | } 100 | 101 | void mixObjectDigest(unsigned char *digest, robj *o) { 102 | o = getDecodedObject(o); 103 | mixDigest(digest,o->ptr,sdslen(o->ptr)); 104 | decrRefCount(o); 105 | } 106 | 107 | /* Compute the dataset digest. Since keys, sets elements, hashes elements 108 | * are not ordered, we use a trick: every aggregate digest is the xor 109 | * of the digests of their elements. This way the order will not change 110 | * the result. For list instead we use a feedback entering the output digest 111 | * as input in order to ensure that a different ordered list will result in 112 | * a different digest. */ 113 | void computeDatasetDigest(unsigned char *final) { 114 | unsigned char digest[20]; 115 | char buf[128]; 116 | dictIterator *di = NULL; 117 | dictEntry *de; 118 | int j; 119 | uint32_t aux; 120 | 121 | memset(final,0,20); /* Start with a clean result */ 122 | 123 | for (j = 0; j < server.dbnum; j++) { 124 | redisDb *db = server.db+j; 125 | 126 | if (dictSize(db->dict) == 0) continue; 127 | di = dictGetIterator(db->dict); 128 | 129 | /* hash the DB id, so the same dataset moved in a different 130 | * DB will lead to a different digest */ 131 | aux = htonl(j); 132 | mixDigest(final,&aux,sizeof(aux)); 133 | 134 | /* Iterate this DB writing every entry */ 135 | while((de = dictNext(di)) != NULL) { 136 | sds key; 137 | robj *keyobj, *o; 138 | long long expiretime; 139 | 140 | memset(digest,0,20); /* This key-val digest */ 141 | key = dictGetKey(de); 142 | keyobj = createStringObject(key,sdslen(key)); 143 | 144 | mixDigest(digest,key,sdslen(key)); 145 | 146 | o = dictGetVal(de); 147 | 148 | aux = htonl(o->type); 149 | mixDigest(digest,&aux,sizeof(aux)); 150 | expiretime = getExpire(db,keyobj); 151 | 152 | /* Save the key and associated value */ 153 | if (o->type == REDIS_STRING) { 154 | mixObjectDigest(digest,o); 155 | } else if (o->type == REDIS_LIST) { 156 | listTypeIterator *li = listTypeInitIterator(o,0,REDIS_TAIL); 157 | listTypeEntry entry; 158 | while(listTypeNext(li,&entry)) { 159 | robj *eleobj = listTypeGet(&entry); 160 | mixObjectDigest(digest,eleobj); 161 | decrRefCount(eleobj); 162 | } 163 | listTypeReleaseIterator(li); 164 | } else if (o->type == REDIS_SET) { 165 | setTypeIterator *si = setTypeInitIterator(o); 166 | robj *ele; 167 | while((ele = setTypeNextObject(si)) != NULL) { 168 | xorObjectDigest(digest,ele); 169 | decrRefCount(ele); 170 | } 171 | setTypeReleaseIterator(si); 172 | } else if (o->type == REDIS_ZSET) { 173 | unsigned char eledigest[20]; 174 | 175 | if (o->encoding == REDIS_ENCODING_ZIPLIST) { 176 | unsigned char *zl = o->ptr; 177 | unsigned char *eptr, *sptr; 178 | unsigned char *vstr; 179 | unsigned int vlen; 180 | long long vll; 181 | double score; 182 | 183 | eptr = ziplistIndex(zl,0); 184 | redisAssert(eptr != NULL); 185 | sptr = ziplistNext(zl,eptr); 186 | redisAssert(sptr != NULL); 187 | 188 | while (eptr != NULL) { 189 | redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll)); 190 | score = zzlGetScore(sptr); 191 | 192 | memset(eledigest,0,20); 193 | if (vstr != NULL) { 194 | mixDigest(eledigest,vstr,vlen); 195 | } else { 196 | ll2string(buf,sizeof(buf),vll); 197 | mixDigest(eledigest,buf,strlen(buf)); 198 | } 199 | 200 | snprintf(buf,sizeof(buf),"%.17g",score); 201 | mixDigest(eledigest,buf,strlen(buf)); 202 | xorDigest(digest,eledigest,20); 203 | zzlNext(zl,&eptr,&sptr); 204 | } 205 | } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { 206 | zset *zs = o->ptr; 207 | dictIterator *di = dictGetIterator(zs->dict); 208 | dictEntry *de; 209 | 210 | while((de = dictNext(di)) != NULL) { 211 | robj *eleobj = dictGetKey(de); 212 | double *score = dictGetVal(de); 213 | 214 | snprintf(buf,sizeof(buf),"%.17g",*score); 215 | memset(eledigest,0,20); 216 | mixObjectDigest(eledigest,eleobj); 217 | mixDigest(eledigest,buf,strlen(buf)); 218 | xorDigest(digest,eledigest,20); 219 | } 220 | dictReleaseIterator(di); 221 | } else { 222 | redisPanic("Unknown sorted set encoding"); 223 | } 224 | } else if (o->type == REDIS_HASH) { 225 | hashTypeIterator *hi; 226 | robj *obj; 227 | 228 | hi = hashTypeInitIterator(o); 229 | while (hashTypeNext(hi) != REDIS_ERR) { 230 | unsigned char eledigest[20]; 231 | 232 | memset(eledigest,0,20); 233 | obj = hashTypeCurrentObject(hi,REDIS_HASH_KEY); 234 | mixObjectDigest(eledigest,obj); 235 | decrRefCount(obj); 236 | obj = hashTypeCurrentObject(hi,REDIS_HASH_VALUE); 237 | mixObjectDigest(eledigest,obj); 238 | decrRefCount(obj); 239 | xorDigest(digest,eledigest,20); 240 | } 241 | hashTypeReleaseIterator(hi); 242 | } else { 243 | redisPanic("Unknown object type"); 244 | } 245 | /* If the key has an expire, add it to the mix */ 246 | if (expiretime != -1) xorDigest(digest,"!!expire!!",10); 247 | /* We can finally xor the key-val digest to the final digest */ 248 | xorDigest(final,digest,20); 249 | decrRefCount(keyobj); 250 | } 251 | dictReleaseIterator(di); 252 | } 253 | } 254 | 255 | void debugCommand(redisClient *c) { 256 | if (!strcasecmp(c->argv[1]->ptr,"segfault")) { 257 | *((char*)-1) = 'x'; 258 | } else if (!strcasecmp(c->argv[1]->ptr,"oom")) { 259 | void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */ 260 | zfree(ptr); 261 | addReply(c,shared.ok); 262 | } else if (!strcasecmp(c->argv[1]->ptr,"assert")) { 263 | if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]); 264 | redisAssertWithInfo(c,c->argv[0],1 == 2); 265 | } else if (!strcasecmp(c->argv[1]->ptr,"reload")) { 266 | if (rdbSave(server.rdb_filename) != REDIS_OK) { 267 | addReply(c,shared.err); 268 | return; 269 | } 270 | emptyDb(NULL); 271 | if (rdbLoad(server.rdb_filename) != REDIS_OK) { 272 | addReplyError(c,"Error trying to load the RDB dump"); 273 | return; 274 | } 275 | redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD"); 276 | addReply(c,shared.ok); 277 | } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) { 278 | emptyDb(NULL); 279 | if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) { 280 | addReply(c,shared.err); 281 | return; 282 | } 283 | server.dirty = 0; /* Prevent AOF / replication */ 284 | redisLog(REDIS_WARNING,"Append Only File loaded by DEBUG LOADAOF"); 285 | addReply(c,shared.ok); 286 | } else if (!strcasecmp(c->argv[1]->ptr,"object") && c->argc == 3) { 287 | dictEntry *de; 288 | robj *val; 289 | char *strenc; 290 | 291 | if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) { 292 | addReply(c,shared.nokeyerr); 293 | return; 294 | } 295 | val = dictGetVal(de); 296 | strenc = strEncoding(val->encoding); 297 | 298 | addReplyStatusFormat(c, 299 | "Value at:%p refcount:%d " 300 | "encoding:%s serializedlength:%lld " 301 | "lru:%d lru_seconds_idle:%lu", 302 | (void*)val, val->refcount, 303 | strenc, (long long) rdbSavedObjectLen(val), 304 | val->lru, estimateObjectIdleTime(val)); 305 | } else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) { 306 | dictEntry *de; 307 | robj *val; 308 | sds key; 309 | 310 | if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) { 311 | addReply(c,shared.nokeyerr); 312 | return; 313 | } 314 | val = dictGetVal(de); 315 | key = dictGetKey(de); 316 | 317 | if (val->type != REDIS_STRING || val->encoding != REDIS_ENCODING_RAW) { 318 | addReplyError(c,"Not an sds encoded string."); 319 | } else { 320 | addReplyStatusFormat(c, 321 | "key_sds_len:%lld, key_sds_avail:%lld, " 322 | "val_sds_len:%lld, val_sds_avail:%lld", 323 | (long long) sdslen(key), 324 | (long long) sdsavail(key), 325 | (long long) sdslen(val->ptr), 326 | (long long) sdsavail(val->ptr)); 327 | } 328 | } else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) { 329 | long keys, j; 330 | robj *key, *val; 331 | char buf[128]; 332 | 333 | if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK) 334 | return; 335 | dictExpand(c->db->dict,keys); 336 | for (j = 0; j < keys; j++) { 337 | snprintf(buf,sizeof(buf),"key:%lu",j); 338 | key = createStringObject(buf,strlen(buf)); 339 | if (lookupKeyRead(c->db,key) != NULL) { 340 | decrRefCount(key); 341 | continue; 342 | } 343 | snprintf(buf,sizeof(buf),"value:%lu",j); 344 | val = createStringObject(buf,strlen(buf)); 345 | dbAdd(c->db,key,val); 346 | decrRefCount(key); 347 | } 348 | addReply(c,shared.ok); 349 | } else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) { 350 | unsigned char digest[20]; 351 | sds d = sdsempty(); 352 | int j; 353 | 354 | computeDatasetDigest(digest); 355 | for (j = 0; j < 20; j++) 356 | d = sdscatprintf(d, "%02x",digest[j]); 357 | addReplyStatus(c,d); 358 | sdsfree(d); 359 | } else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) { 360 | double dtime = strtod(c->argv[2]->ptr,NULL); 361 | long long utime = dtime*1000000; 362 | struct timespec tv; 363 | 364 | tv.tv_sec = utime / 1000000; 365 | tv.tv_nsec = (utime % 1000000) * 1000; 366 | nanosleep(&tv, NULL); 367 | addReply(c,shared.ok); 368 | } else if (!strcasecmp(c->argv[1]->ptr,"set-active-expire") && 369 | c->argc == 3) 370 | { 371 | server.active_expire_enabled = atoi(c->argv[2]->ptr); 372 | addReply(c,shared.ok); 373 | } else if (!strcasecmp(c->argv[1]->ptr,"error") && c->argc == 3) { 374 | sds errstr = sdsnewlen("-",1); 375 | 376 | errstr = sdscatsds(errstr,c->argv[2]->ptr); 377 | errstr = sdsmapchars(errstr,"\n\r"," ",2); /* no newlines in errors. */ 378 | errstr = sdscatlen(errstr,"\r\n",2); 379 | addReplySds(c,errstr); 380 | } else { 381 | addReplyErrorFormat(c, "Unknown DEBUG subcommand or wrong number of arguments for '%s'", 382 | (char*)c->argv[1]->ptr); 383 | } 384 | } 385 | 386 | /* =========================== Crash handling ============================== */ 387 | 388 | void _redisAssert(char *estr, char *file, int line) { 389 | bugReportStart(); 390 | redisLog(REDIS_WARNING,"=== ASSERTION FAILED ==="); 391 | redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr); 392 | #ifdef HAVE_BACKTRACE 393 | server.assert_failed = estr; 394 | server.assert_file = file; 395 | server.assert_line = line; 396 | redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)"); 397 | #endif 398 | *((char*)-1) = 'x'; 399 | } 400 | 401 | void _redisAssertPrintClientInfo(redisClient *c) { 402 | int j; 403 | 404 | bugReportStart(); 405 | redisLog(REDIS_WARNING,"=== ASSERTION FAILED CLIENT CONTEXT ==="); 406 | redisLog(REDIS_WARNING,"client->flags = %d", c->flags); 407 | redisLog(REDIS_WARNING,"client->fd = %d", c->fd); 408 | redisLog(REDIS_WARNING,"client->argc = %d", c->argc); 409 | for (j=0; j < c->argc; j++) { 410 | char buf[128]; 411 | char *arg; 412 | 413 | if (c->argv[j]->type == REDIS_STRING && 414 | c->argv[j]->encoding == REDIS_ENCODING_RAW) 415 | { 416 | arg = (char*) c->argv[j]->ptr; 417 | } else { 418 | snprintf(buf,sizeof(buf),"Object type: %d, encoding: %d", 419 | c->argv[j]->type, c->argv[j]->encoding); 420 | arg = buf; 421 | } 422 | redisLog(REDIS_WARNING,"client->argv[%d] = \"%s\" (refcount: %d)", 423 | j, arg, c->argv[j]->refcount); 424 | } 425 | } 426 | 427 | void redisLogObjectDebugInfo(robj *o) { 428 | redisLog(REDIS_WARNING,"Object type: %d", o->type); 429 | redisLog(REDIS_WARNING,"Object encoding: %d", o->encoding); 430 | redisLog(REDIS_WARNING,"Object refcount: %d", o->refcount); 431 | if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_RAW) { 432 | redisLog(REDIS_WARNING,"Object raw string len: %zu", sdslen(o->ptr)); 433 | if (sdslen(o->ptr) < 4096) { 434 | sds repr = sdscatrepr(sdsempty(),o->ptr,sdslen(o->ptr)); 435 | redisLog(REDIS_WARNING,"Object raw string content: %s", repr); 436 | sdsfree(repr); 437 | } 438 | } else if (o->type == REDIS_LIST) { 439 | redisLog(REDIS_WARNING,"List length: %d", (int) listTypeLength(o)); 440 | } else if (o->type == REDIS_SET) { 441 | redisLog(REDIS_WARNING,"Set size: %d", (int) setTypeSize(o)); 442 | } else if (o->type == REDIS_HASH) { 443 | redisLog(REDIS_WARNING,"Hash size: %d", (int) hashTypeLength(o)); 444 | } else if (o->type == REDIS_ZSET) { 445 | redisLog(REDIS_WARNING,"Sorted set size: %d", (int) zsetLength(o)); 446 | if (o->encoding == REDIS_ENCODING_SKIPLIST) 447 | redisLog(REDIS_WARNING,"Skiplist level: %d", (int) ((zset*)o->ptr)->zsl->level); 448 | } 449 | } 450 | 451 | void _redisAssertPrintObject(robj *o) { 452 | bugReportStart(); 453 | redisLog(REDIS_WARNING,"=== ASSERTION FAILED OBJECT CONTEXT ==="); 454 | redisLogObjectDebugInfo(o); 455 | } 456 | 457 | void _redisAssertWithInfo(redisClient *c, robj *o, char *estr, char *file, int line) { 458 | if (c) _redisAssertPrintClientInfo(c); 459 | if (o) _redisAssertPrintObject(o); 460 | _redisAssert(estr,file,line); 461 | } 462 | 463 | void _redisPanic(char *msg, char *file, int line) { 464 | bugReportStart(); 465 | redisLog(REDIS_WARNING,"------------------------------------------------"); 466 | redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue"); 467 | redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line); 468 | #ifdef HAVE_BACKTRACE 469 | redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)"); 470 | #endif 471 | redisLog(REDIS_WARNING,"------------------------------------------------"); 472 | *((char*)-1) = 'x'; 473 | } 474 | 475 | void bugReportStart(void) { 476 | if (server.bug_report_start == 0) { 477 | redisLog(REDIS_WARNING, 478 | "\n\n=== REDIS BUG REPORT START: Cut & paste starting from here ==="); 479 | server.bug_report_start = 1; 480 | } 481 | } 482 | 483 | #ifdef HAVE_BACKTRACE 484 | static void *getMcontextEip(ucontext_t *uc) { 485 | #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) 486 | /* OSX < 10.6 */ 487 | #if defined(__x86_64__) 488 | return (void*) uc->uc_mcontext->__ss.__rip; 489 | #elif defined(__i386__) 490 | return (void*) uc->uc_mcontext->__ss.__eip; 491 | #else 492 | return (void*) uc->uc_mcontext->__ss.__srr0; 493 | #endif 494 | #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 495 | /* OSX >= 10.6 */ 496 | #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 497 | return (void*) uc->uc_mcontext->__ss.__rip; 498 | #else 499 | return (void*) uc->uc_mcontext->__ss.__eip; 500 | #endif 501 | #elif defined(__linux__) 502 | /* Linux */ 503 | #if defined(__i386__) 504 | return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */ 505 | #elif defined(__X86_64__) || defined(__x86_64__) 506 | return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */ 507 | #elif defined(__ia64__) /* Linux IA64 */ 508 | return (void*) uc->uc_mcontext.sc_ip; 509 | #endif 510 | #else 511 | return NULL; 512 | #endif 513 | } 514 | 515 | void logStackContent(void **sp) { 516 | int i; 517 | for (i = 15; i >= 0; i--) { 518 | unsigned long addr = (unsigned long) sp+i; 519 | unsigned long val = (unsigned long) sp[i]; 520 | 521 | if (sizeof(long) == 4) 522 | redisLog(REDIS_WARNING, "(%08lx) -> %08lx", addr, val); 523 | else 524 | redisLog(REDIS_WARNING, "(%016lx) -> %016lx", addr, val); 525 | } 526 | } 527 | 528 | void logRegisters(ucontext_t *uc) { 529 | redisLog(REDIS_WARNING, "--- REGISTERS"); 530 | 531 | /* OSX */ 532 | #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) 533 | /* OSX AMD64 */ 534 | #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) 535 | redisLog(REDIS_WARNING, 536 | "\n" 537 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 538 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 539 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 540 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 541 | "RIP:%016lx EFL:%016lx\nCS :%016lx FS:%016lx GS:%016lx", 542 | (unsigned long) uc->uc_mcontext->__ss.__rax, 543 | (unsigned long) uc->uc_mcontext->__ss.__rbx, 544 | (unsigned long) uc->uc_mcontext->__ss.__rcx, 545 | (unsigned long) uc->uc_mcontext->__ss.__rdx, 546 | (unsigned long) uc->uc_mcontext->__ss.__rdi, 547 | (unsigned long) uc->uc_mcontext->__ss.__rsi, 548 | (unsigned long) uc->uc_mcontext->__ss.__rbp, 549 | (unsigned long) uc->uc_mcontext->__ss.__rsp, 550 | (unsigned long) uc->uc_mcontext->__ss.__r8, 551 | (unsigned long) uc->uc_mcontext->__ss.__r9, 552 | (unsigned long) uc->uc_mcontext->__ss.__r10, 553 | (unsigned long) uc->uc_mcontext->__ss.__r11, 554 | (unsigned long) uc->uc_mcontext->__ss.__r12, 555 | (unsigned long) uc->uc_mcontext->__ss.__r13, 556 | (unsigned long) uc->uc_mcontext->__ss.__r14, 557 | (unsigned long) uc->uc_mcontext->__ss.__r15, 558 | (unsigned long) uc->uc_mcontext->__ss.__rip, 559 | (unsigned long) uc->uc_mcontext->__ss.__rflags, 560 | (unsigned long) uc->uc_mcontext->__ss.__cs, 561 | (unsigned long) uc->uc_mcontext->__ss.__fs, 562 | (unsigned long) uc->uc_mcontext->__ss.__gs 563 | ); 564 | logStackContent((void**)uc->uc_mcontext->__ss.__rsp); 565 | #else 566 | /* OSX x86 */ 567 | redisLog(REDIS_WARNING, 568 | "\n" 569 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 570 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 571 | "SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx\n" 572 | "DS:%08lx ES:%08lx FS :%08lx GS :%08lx", 573 | (unsigned long) uc->uc_mcontext->__ss.__eax, 574 | (unsigned long) uc->uc_mcontext->__ss.__ebx, 575 | (unsigned long) uc->uc_mcontext->__ss.__ecx, 576 | (unsigned long) uc->uc_mcontext->__ss.__edx, 577 | (unsigned long) uc->uc_mcontext->__ss.__edi, 578 | (unsigned long) uc->uc_mcontext->__ss.__esi, 579 | (unsigned long) uc->uc_mcontext->__ss.__ebp, 580 | (unsigned long) uc->uc_mcontext->__ss.__esp, 581 | (unsigned long) uc->uc_mcontext->__ss.__ss, 582 | (unsigned long) uc->uc_mcontext->__ss.__eflags, 583 | (unsigned long) uc->uc_mcontext->__ss.__eip, 584 | (unsigned long) uc->uc_mcontext->__ss.__cs, 585 | (unsigned long) uc->uc_mcontext->__ss.__ds, 586 | (unsigned long) uc->uc_mcontext->__ss.__es, 587 | (unsigned long) uc->uc_mcontext->__ss.__fs, 588 | (unsigned long) uc->uc_mcontext->__ss.__gs 589 | ); 590 | logStackContent((void**)uc->uc_mcontext->__ss.__esp); 591 | #endif 592 | /* Linux */ 593 | #elif defined(__linux__) 594 | /* Linux x86 */ 595 | #if defined(__i386__) 596 | redisLog(REDIS_WARNING, 597 | "\n" 598 | "EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n" 599 | "EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx\n" 600 | "SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx\n" 601 | "DS :%08lx ES :%08lx FS :%08lx GS:%08lx", 602 | (unsigned long) uc->uc_mcontext.gregs[11], 603 | (unsigned long) uc->uc_mcontext.gregs[8], 604 | (unsigned long) uc->uc_mcontext.gregs[10], 605 | (unsigned long) uc->uc_mcontext.gregs[9], 606 | (unsigned long) uc->uc_mcontext.gregs[4], 607 | (unsigned long) uc->uc_mcontext.gregs[5], 608 | (unsigned long) uc->uc_mcontext.gregs[6], 609 | (unsigned long) uc->uc_mcontext.gregs[7], 610 | (unsigned long) uc->uc_mcontext.gregs[18], 611 | (unsigned long) uc->uc_mcontext.gregs[17], 612 | (unsigned long) uc->uc_mcontext.gregs[14], 613 | (unsigned long) uc->uc_mcontext.gregs[15], 614 | (unsigned long) uc->uc_mcontext.gregs[3], 615 | (unsigned long) uc->uc_mcontext.gregs[2], 616 | (unsigned long) uc->uc_mcontext.gregs[1], 617 | (unsigned long) uc->uc_mcontext.gregs[0] 618 | ); 619 | logStackContent((void**)uc->uc_mcontext.gregs[7]); 620 | #elif defined(__X86_64__) || defined(__x86_64__) 621 | /* Linux AMD64 */ 622 | redisLog(REDIS_WARNING, 623 | "\n" 624 | "RAX:%016lx RBX:%016lx\nRCX:%016lx RDX:%016lx\n" 625 | "RDI:%016lx RSI:%016lx\nRBP:%016lx RSP:%016lx\n" 626 | "R8 :%016lx R9 :%016lx\nR10:%016lx R11:%016lx\n" 627 | "R12:%016lx R13:%016lx\nR14:%016lx R15:%016lx\n" 628 | "RIP:%016lx EFL:%016lx\nCSGSFS:%016lx", 629 | (unsigned long) uc->uc_mcontext.gregs[13], 630 | (unsigned long) uc->uc_mcontext.gregs[11], 631 | (unsigned long) uc->uc_mcontext.gregs[14], 632 | (unsigned long) uc->uc_mcontext.gregs[12], 633 | (unsigned long) uc->uc_mcontext.gregs[8], 634 | (unsigned long) uc->uc_mcontext.gregs[9], 635 | (unsigned long) uc->uc_mcontext.gregs[10], 636 | (unsigned long) uc->uc_mcontext.gregs[15], 637 | (unsigned long) uc->uc_mcontext.gregs[0], 638 | (unsigned long) uc->uc_mcontext.gregs[1], 639 | (unsigned long) uc->uc_mcontext.gregs[2], 640 | (unsigned long) uc->uc_mcontext.gregs[3], 641 | (unsigned long) uc->uc_mcontext.gregs[4], 642 | (unsigned long) uc->uc_mcontext.gregs[5], 643 | (unsigned long) uc->uc_mcontext.gregs[6], 644 | (unsigned long) uc->uc_mcontext.gregs[7], 645 | (unsigned long) uc->uc_mcontext.gregs[16], 646 | (unsigned long) uc->uc_mcontext.gregs[17], 647 | (unsigned long) uc->uc_mcontext.gregs[18] 648 | ); 649 | logStackContent((void**)uc->uc_mcontext.gregs[15]); 650 | #endif 651 | #else 652 | redisLog(REDIS_WARNING, 653 | " Dumping of registers not supported for this OS/arch"); 654 | #endif 655 | } 656 | 657 | /* Logs the stack trace using the backtrace() call. This function is designed 658 | * to be called from signal handlers safely. */ 659 | void logStackTrace(ucontext_t *uc) { 660 | void *trace[100]; 661 | int trace_size = 0, fd; 662 | int log_to_stdout = server.logfile[0] == '\0'; 663 | 664 | /* Open the log file in append mode. */ 665 | fd = log_to_stdout ? 666 | STDOUT_FILENO : 667 | open(server.logfile, O_APPEND|O_CREAT|O_WRONLY, 0644); 668 | if (fd == -1) return; 669 | 670 | /* Generate the stack trace */ 671 | trace_size = backtrace(trace, 100); 672 | 673 | /* overwrite sigaction with caller's address */ 674 | if (getMcontextEip(uc) != NULL) 675 | trace[1] = getMcontextEip(uc); 676 | 677 | /* Write symbols to log file */ 678 | backtrace_symbols_fd(trace, trace_size, fd); 679 | 680 | /* Cleanup */ 681 | if (!log_to_stdout) close(fd); 682 | } 683 | 684 | /* Log information about the "current" client, that is, the client that is 685 | * currently being served by Redis. May be NULL if Redis is not serving a 686 | * client right now. */ 687 | void logCurrentClient(void) { 688 | if (server.current_client == NULL) return; 689 | 690 | redisClient *cc = server.current_client; 691 | sds client; 692 | int j; 693 | 694 | redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO"); 695 | client = catClientInfoString(sdsempty(),cc); 696 | redisLog(REDIS_WARNING,"client: %s", client); 697 | sdsfree(client); 698 | for (j = 0; j < cc->argc; j++) { 699 | robj *decoded; 700 | 701 | decoded = getDecodedObject(cc->argv[j]); 702 | redisLog(REDIS_WARNING,"argv[%d]: '%s'", j, (char*)decoded->ptr); 703 | decrRefCount(decoded); 704 | } 705 | /* Check if the first argument, usually a key, is found inside the 706 | * selected DB, and if so print info about the associated object. */ 707 | if (cc->argc >= 1) { 708 | robj *val, *key; 709 | dictEntry *de; 710 | 711 | key = getDecodedObject(cc->argv[1]); 712 | de = dictFind(cc->db->dict, key->ptr); 713 | if (de) { 714 | val = dictGetVal(de); 715 | redisLog(REDIS_WARNING,"key '%s' found in DB containing the following object:", (char*)key->ptr); 716 | redisLogObjectDebugInfo(val); 717 | } 718 | decrRefCount(key); 719 | } 720 | } 721 | 722 | #if defined(HAVE_PROC_MAPS) 723 | void memtest_non_destructive_invert(void *addr, size_t size); 724 | void memtest_non_destructive_swap(void *addr, size_t size); 725 | #define MEMTEST_MAX_REGIONS 128 726 | 727 | int memtest_test_linux_anonymous_maps(void) { 728 | FILE *fp = fopen("/proc/self/maps","r"); 729 | char line[1024]; 730 | size_t start_addr, end_addr, size; 731 | size_t start_vect[MEMTEST_MAX_REGIONS]; 732 | size_t size_vect[MEMTEST_MAX_REGIONS]; 733 | int regions = 0, j; 734 | uint64_t crc1 = 0, crc2 = 0, crc3 = 0; 735 | 736 | while(fgets(line,sizeof(line),fp) != NULL) { 737 | char *start, *end, *p = line; 738 | 739 | start = p; 740 | p = strchr(p,'-'); 741 | if (!p) continue; 742 | *p++ = '\0'; 743 | end = p; 744 | p = strchr(p,' '); 745 | if (!p) continue; 746 | *p++ = '\0'; 747 | if (strstr(p,"stack") || 748 | strstr(p,"vdso") || 749 | strstr(p,"vsyscall")) continue; 750 | if (!strstr(p,"00:00")) continue; 751 | if (!strstr(p,"rw")) continue; 752 | 753 | start_addr = strtoul(start,NULL,16); 754 | end_addr = strtoul(end,NULL,16); 755 | size = end_addr-start_addr; 756 | 757 | start_vect[regions] = start_addr; 758 | size_vect[regions] = size; 759 | printf("Testing %lx %lu\n", (unsigned long) start_vect[regions], 760 | (unsigned long) size_vect[regions]); 761 | regions++; 762 | } 763 | 764 | /* Test all the regions as an unique sequential region. 765 | * 1) Take the CRC64 of the memory region. */ 766 | for (j = 0; j < regions; j++) { 767 | crc1 = crc64(crc1,(void*)start_vect[j],size_vect[j]); 768 | } 769 | 770 | /* 2) Invert bits, swap adjacent words, swap again, invert bits. 771 | * This is the error amplification step. */ 772 | for (j = 0; j < regions; j++) 773 | memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]); 774 | for (j = 0; j < regions; j++) 775 | memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]); 776 | for (j = 0; j < regions; j++) 777 | memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]); 778 | for (j = 0; j < regions; j++) 779 | memtest_non_destructive_invert((void*)start_vect[j],size_vect[j]); 780 | 781 | /* 3) Take the CRC64 sum again. */ 782 | for (j = 0; j < regions; j++) 783 | crc2 = crc64(crc2,(void*)start_vect[j],size_vect[j]); 784 | 785 | /* 4) Swap + Swap again */ 786 | for (j = 0; j < regions; j++) 787 | memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]); 788 | for (j = 0; j < regions; j++) 789 | memtest_non_destructive_swap((void*)start_vect[j],size_vect[j]); 790 | 791 | /* 5) Take the CRC64 sum again. */ 792 | for (j = 0; j < regions; j++) 793 | crc3 = crc64(crc3,(void*)start_vect[j],size_vect[j]); 794 | 795 | /* NOTE: It is very important to close the file descriptor only now 796 | * because closing it before may result into unmapping of some memory 797 | * region that we are testing. */ 798 | fclose(fp); 799 | 800 | /* If the two CRC are not the same, we trapped a memory error. */ 801 | return crc1 != crc2 || crc2 != crc3; 802 | } 803 | #endif 804 | 805 | void sigsegvHandler(int sig, siginfo_t *info, void *secret) { 806 | ucontext_t *uc = (ucontext_t*) secret; 807 | sds infostring, clients; 808 | struct sigaction act; 809 | REDIS_NOTUSED(info); 810 | 811 | bugReportStart(); 812 | redisLog(REDIS_WARNING, 813 | " Redis %s crashed by signal: %d", REDIS_VERSION, sig); 814 | redisLog(REDIS_WARNING, 815 | " Failed assertion: %s (%s:%d)", server.assert_failed, 816 | server.assert_file, server.assert_line); 817 | 818 | /* Log the stack trace */ 819 | redisLog(REDIS_WARNING, "--- STACK TRACE"); 820 | logStackTrace(uc); 821 | 822 | /* Log INFO and CLIENT LIST */ 823 | redisLog(REDIS_WARNING, "--- INFO OUTPUT"); 824 | infostring = genRedisInfoString("all"); 825 | infostring = sdscatprintf(infostring, "hash_init_value: %u\n", 826 | dictGetHashFunctionSeed()); 827 | redisLogRaw(REDIS_WARNING, infostring); 828 | redisLog(REDIS_WARNING, "--- CLIENT LIST OUTPUT"); 829 | clients = getAllClientsInfoString(); 830 | redisLogRaw(REDIS_WARNING, clients); 831 | sdsfree(infostring); 832 | sdsfree(clients); 833 | 834 | /* Log the current client */ 835 | logCurrentClient(); 836 | 837 | /* Log dump of processor registers */ 838 | logRegisters(uc); 839 | 840 | #if defined(HAVE_PROC_MAPS) 841 | /* Test memory */ 842 | redisLog(REDIS_WARNING, "--- FAST MEMORY TEST"); 843 | bioKillThreads(); 844 | if (memtest_test_linux_anonymous_maps()) { 845 | redisLog(REDIS_WARNING, 846 | "!!! MEMORY ERROR DETECTED! Check your memory ASAP !!!"); 847 | } else { 848 | redisLog(REDIS_WARNING, 849 | "Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible."); 850 | } 851 | #endif 852 | 853 | redisLog(REDIS_WARNING, 854 | "\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n" 855 | " Please report the crash opening an issue on github:\n\n" 856 | " http://github.com/antirez/redis/issues\n\n" 857 | " Suspect RAM error? Use redis-server --test-memory to verify it.\n\n" 858 | ); 859 | /* free(messages); Don't call free() with possibly corrupted memory. */ 860 | if (server.daemonize) unlink(server.pidfile); 861 | 862 | /* Make sure we exit with the right signal at the end. So for instance 863 | * the core will be dumped if enabled. */ 864 | sigemptyset (&act.sa_mask); 865 | act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; 866 | act.sa_handler = SIG_DFL; 867 | sigaction (sig, &act, NULL); 868 | kill(getpid(),sig); 869 | } 870 | #endif /* HAVE_BACKTRACE */ 871 | 872 | /* ==================== Logging functions for debugging ===================== */ 873 | 874 | void redisLogHexDump(int level, char *descr, void *value, size_t len) { 875 | char buf[65], *b; 876 | unsigned char *v = value; 877 | char charset[] = "0123456789abcdef"; 878 | 879 | redisLog(level,"%s (hexdump):", descr); 880 | b = buf; 881 | while(len) { 882 | b[0] = charset[(*v)>>4]; 883 | b[1] = charset[(*v)&0xf]; 884 | b[2] = '\0'; 885 | b += 2; 886 | len--; 887 | v++; 888 | if (b-buf == 64 || len == 0) { 889 | redisLogRaw(level|REDIS_LOG_RAW,buf); 890 | b = buf; 891 | } 892 | } 893 | redisLogRaw(level|REDIS_LOG_RAW,"\n"); 894 | } 895 | 896 | /* =========================== Software Watchdog ============================ */ 897 | #include 898 | 899 | void watchdogSignalHandler(int sig, siginfo_t *info, void *secret) { 900 | #ifdef HAVE_BACKTRACE 901 | ucontext_t *uc = (ucontext_t*) secret; 902 | #endif 903 | REDIS_NOTUSED(info); 904 | REDIS_NOTUSED(sig); 905 | 906 | redisLogFromHandler(REDIS_WARNING,"\n--- WATCHDOG TIMER EXPIRED ---"); 907 | #ifdef HAVE_BACKTRACE 908 | logStackTrace(uc); 909 | #else 910 | redisLogFromHandler(REDIS_WARNING,"Sorry: no support for backtrace()."); 911 | #endif 912 | redisLogFromHandler(REDIS_WARNING,"--------\n"); 913 | } 914 | 915 | /* Schedule a SIGALRM delivery after the specified period in milliseconds. 916 | * If a timer is already scheduled, this function will re-schedule it to the 917 | * specified time. If period is 0 the current timer is disabled. */ 918 | void watchdogScheduleSignal(int period) { 919 | struct itimerval it; 920 | 921 | /* Will stop the timer if period is 0. */ 922 | it.it_value.tv_sec = period/1000; 923 | it.it_value.tv_usec = (period%1000)*1000; 924 | /* Don't automatically restart. */ 925 | it.it_interval.tv_sec = 0; 926 | it.it_interval.tv_usec = 0; 927 | setitimer(ITIMER_REAL, &it, NULL); 928 | } 929 | 930 | /* Enable the software watchdog with the specified period in milliseconds. */ 931 | void enableWatchdog(int period) { 932 | int min_period; 933 | 934 | if (server.watchdog_period == 0) { 935 | struct sigaction act; 936 | 937 | /* Watchdog was actually disabled, so we have to setup the signal 938 | * handler. */ 939 | sigemptyset(&act.sa_mask); 940 | act.sa_flags = SA_ONSTACK | SA_SIGINFO; 941 | act.sa_sigaction = watchdogSignalHandler; 942 | sigaction(SIGALRM, &act, NULL); 943 | } 944 | /* If the configured period is smaller than twice the timer period, it is 945 | * too short for the software watchdog to work reliably. Fix it now 946 | * if needed. */ 947 | min_period = (1000/server.hz)*2; 948 | if (period < min_period) period = min_period; 949 | watchdogScheduleSignal(period); /* Adjust the current timer. */ 950 | server.watchdog_period = period; 951 | } 952 | 953 | /* Disable the software watchdog. */ 954 | void disableWatchdog(void) { 955 | struct sigaction act; 956 | if (server.watchdog_period == 0) return; /* Already disabled. */ 957 | watchdogScheduleSignal(0); /* Stop the current timer. */ 958 | 959 | /* Set the signal handler to SIG_IGN, this will also remove pending 960 | * signals from the queue. */ 961 | sigemptyset(&act.sa_mask); 962 | act.sa_flags = 0; 963 | act.sa_handler = SIG_IGN; 964 | sigaction(SIGALRM, &act, NULL); 965 | server.watchdog_period = 0; 966 | } 967 | --------------------------------------------------------------------------------